feat: fix-visibility release artifacts (#67)

* feat: fix-visibility release artifacts

Signed-off-by: Thulio Ferraz Assis <3149049+f0rmiga@users.noreply.github.com>

* refactor: prefix plugins with aspect-

Signed-off-by: Thulio Ferraz Assis <3149049+f0rmiga@users.noreply.github.com>

* feat: release macros

Signed-off-by: Thulio Ferraz Assis <3149049+f0rmiga@users.noreply.github.com>

* fix: plugin path

Signed-off-by: Thulio Ferraz Assis <3149049+f0rmiga@users.noreply.github.com>
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 1d0c22a..dce5e37 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -16,18 +16,18 @@
         run: git status --porcelain
       - name: bazel test //...
         env:
-          # Bazelisk will download bazel to here
+          # Bazelisk will download bazel to here.
           XDG_CACHE_HOME: ~/.cache/bazel-repo
-        run:
+        run: |
+          # Test the repository.
           bazel --bazelrc=.github/workflows/ci.bazelrc --bazelrc=.bazelrc test
           --config=release //...
+          # Copy the release artifacts to /tmp/aspect/release.
+          rm -rf /tmp/aspect/release
+          bazel --bazelrc=.github/workflows/ci.bazelrc --bazelrc=.bazelrc run
+          --config=release //release -- /tmp/aspect/release
       - name: Release
         uses: softprops/action-gh-release@v1
         with:
           prerelease: true
-          files: |
-            bazel-out/*-opt-*/bin/cmd/aspect/aspect-linux_amd64
-            bazel-out/*-opt-*/bin/cmd/aspect/aspect-linux_arm64
-            bazel-out/*-opt-*/bin/cmd/aspect/aspect-darwin_amd64
-            bazel-out/*-opt-*/bin/cmd/aspect/aspect-darwin_arm64
-            bazel-out/*-opt-*/bin/cmd/aspect/aspect-windows_amd64.exe
+          files: /tmp/aspect/release/*
diff --git a/cmd/aspect/BUILD.bazel b/cmd/aspect/BUILD.bazel
index 9f25845..07585dc 100644
--- a/cmd/aspect/BUILD.bazel
+++ b/cmd/aspect/BUILD.bazel
@@ -4,7 +4,10 @@
     name = "aspect_lib",
     srcs = ["main.go"],
     importpath = "aspect.build/cli/cmd/aspect",
-    visibility = ["//cmd:__subpackages__"],
+    visibility = [
+        "//cmd:__subpackages__",
+        "//release:__pkg__",
+    ],
     deps = [
         "//cmd/aspect/root",
         "//pkg/aspecterrors",
@@ -16,83 +19,3 @@
     embed = [":aspect_lib"],
     visibility = ["//visibility:public"],
 )
-
-go_binary(
-    name = "aspect-darwin-amd64",
-    out = "aspect-darwin_amd64",
-    embed = [":aspect_lib"],
-    gc_linkopts = [
-        "-s",
-        "-w",
-    ],
-    goarch = "amd64",
-    goos = "darwin",
-    pure = "on",
-    visibility = ["//visibility:public"],
-)
-
-go_binary(
-    name = "aspect-darwin-arm64",
-    out = "aspect-darwin_arm64",
-    embed = [":aspect_lib"],
-    gc_linkopts = [
-        "-s",
-        "-w",
-    ],
-    goarch = "arm64",
-    goos = "darwin",
-    pure = "on",
-    visibility = ["//visibility:public"],
-)
-
-# genrule(
-#     name = "aspect-darwin-universal",
-#     srcs = [
-#         ":aspect-darwin_amd64",
-#         ":aspect-darwin_arm64",
-#     ],
-#     outs = ["aspect-darwin_universal"],
-#     cmd = "lipo -create -output \"$@\" $(SRCS)",
-#     output_to_bindir = 1,
-#     target_compatible_with = [
-#         "@platforms//os:macos",
-#     ],
-# )
-
-go_binary(
-    name = "aspect-linux-amd64",
-    out = "aspect-linux_amd64",
-    embed = [":aspect_lib"],
-    gc_linkopts = [
-        "-s",
-        "-w",
-    ],
-    goarch = "amd64",
-    goos = "linux",
-    pure = "on",
-    visibility = ["//visibility:public"],
-)
-
-go_binary(
-    name = "aspect-linux-arm64",
-    out = "aspect-linux_arm64",
-    embed = [":aspect_lib"],
-    gc_linkopts = [
-        "-s",
-        "-w",
-    ],
-    goarch = "arm64",
-    goos = "linux",
-    pure = "on",
-    visibility = ["//visibility:public"],
-)
-
-go_binary(
-    name = "aspect-windows-amd64",
-    out = "aspect-windows_amd64.exe",
-    embed = [":aspect_lib"],
-    goarch = "amd64",
-    goos = "windows",
-    pure = "on",
-    visibility = ["//visibility:public"],
-)
diff --git a/plugins/fix-visibility/BUILD.bazel b/plugins/fix-visibility/BUILD.bazel
index 0338707..595ed50 100644
--- a/plugins/fix-visibility/BUILD.bazel
+++ b/plugins/fix-visibility/BUILD.bazel
@@ -4,7 +4,7 @@
     name = "fix-visibility_lib",
     srcs = ["plugin.go"],
     importpath = "aspect.build/cli/plugins/fix-visibility",
-    visibility = ["//visibility:private"],
+    visibility = ["//release:__pkg__"],
     deps = [
         "//pkg/ioutils",
         "//pkg/plugin/sdk/v1alpha1/config",
diff --git a/release/BUILD.bazel b/release/BUILD.bazel
new file mode 100644
index 0000000..59afe0d
--- /dev/null
+++ b/release/BUILD.bazel
@@ -0,0 +1,20 @@
+load(":release.bzl", "multi_platform_binaries", "release")
+
+multi_platform_binaries(
+    name = "aspect",
+    embed = ["//cmd/aspect:aspect_lib"],
+)
+
+multi_platform_binaries(
+    name = "fix-visibility",
+    embed = ["//plugins/fix-visibility:fix-visibility_lib"],
+    prefix = "plugin-",
+)
+
+release(
+    name = "release",
+    targets = [
+        ":aspect",
+        ":fix-visibility",
+    ],
+)
diff --git a/release/create_release.sh b/release/create_release.sh
new file mode 100755
index 0000000..8293de1
--- /dev/null
+++ b/release/create_release.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+set -o errexit -o nounset -o pipefail
+
+echo '#!/bin/bash'
+echo 'set -o errexit -o nounset -o pipefail'
+# shellcheck disable=SC2016
+echo 'dst=$1'
+# shellcheck disable=SC2016
+echo 'mkdir -p "${dst}"'
+
+for artifact in "$@"; do
+  echo "echo \"Copying ${artifact} to \${dst}\""
+  echo "cp \"${artifact}\" \"\${dst}\""
+done
diff --git a/release/release.bzl b/release/release.bzl
new file mode 100644
index 0000000..b5a944b
--- /dev/null
+++ b/release/release.bzl
@@ -0,0 +1,63 @@
+"""This module provides the macros for performing a release.
+"""
+
+load("@io_bazel_rules_go//go:def.bzl", "go_binary")
+
+PLATFORMS = [
+    struct(os = "darwin", arch = "amd64", ext = "", gc_linkopts = ["-s", "-w"]),
+    struct(os = "darwin", arch = "arm64", ext = "", gc_linkopts = ["-s", "-w"]),
+    struct(os = "linux", arch = "amd64", ext = "", gc_linkopts = ["-s", "-w"]),
+    struct(os = "linux", arch = "arm64", ext = "", gc_linkopts = ["-s", "-w"]),
+    struct(os = "windows", arch = "amd64", ext = ".exe", gc_linkopts = []),
+]
+
+def multi_platform_binaries(name, embed, prefix = ""):
+    """The multi_platform_binaries macro creates a go_binary for each platform.
+
+    Args:
+        name: the name of the filegroup containing all go_binary targets produced
+            by this macro.
+        embed: the list of targets passed to each go_binary target in this
+            macro.
+        prefix: an optional prefix added to the output Go binary file name.
+    """
+    targets = []
+    for platform in PLATFORMS:
+        target_name = "{}-{}-{}".format(name, platform.os, platform.arch)
+        go_binary(
+            name = target_name,
+            out = "{}{}-{}_{}{}".format(prefix, name, platform.os, platform.arch, platform.ext),
+            embed = embed,
+            gc_linkopts = platform.gc_linkopts,
+            goarch = platform.arch,
+            goos = platform.os,
+            pure = "on",
+            visibility = ["//visibility:public"],
+        )
+        targets.append(Label("//{}:{}".format(native.package_name(), target_name)))
+
+    native.filegroup(
+        name = name,
+        srcs = targets,
+    )
+
+def release(name, targets):
+    """The release macro creates the artifact copier script.
+
+    It's an executable script that copies all artifacts produced by the given
+    targets into the provided destination. See .github/workflows/release.yml.
+
+    Args:
+        name: the name of the genrule.
+        targets: a list of filegroups passed to the artifact copier.
+    """
+    native.genrule(
+        name = name,
+        srcs = targets,
+        outs = ["release.sh"],
+        executable = True,
+        cmd = "./$(location //release:create_release.sh) {locations} > \"$@\"".format(
+            locations = " ".join(["$(locations {})".format(target) for target in targets]),
+        ),
+        tools = ["//release:create_release.sh"],
+    )