Repository: axodotdev/cargo-dist
Pull request #361 (2023-08-24 22:21:28)

chore: update own cargo-dist
I actually just did this to test the Homebrew-installed binary, but we might as well bump to run the next build on prerelease 5.


Patch:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index b4cbd00a..85797427 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -30,7 +30,7 @@ permissions:
 # package (erroring out if it doesn't have the given version or isn't cargo-dist-able).
 #
 # If PACKAGE_NAME isn't specified, then we will create a Github Release™ for all
-# (cargo-dist-able) packages in the workspace with that version (this is mode is
+# (cargo-dist-able) packages in the workspace with that version (this mode is
 # intended for workspaces with only one dist-able package, or with all dist-able
 # packages versioned/released in lockstep).
 #
@@ -58,7 +58,7 @@ jobs:
         with:
           submodules: recursive
       - name: Install cargo-dist
-        run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0-prerelease.4/cargo-dist-installer.sh | sh"
+        run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0-prerelease.5/cargo-dist-installer.sh | sh"
       - id: create-release
         run: |
           cargo dist plan --tag=${{ github.ref_name }} --output-format=json > dist-manifest.json
@@ -93,16 +93,16 @@ jobs:
         include:
         - os: "macos-11"
           dist-args: "--artifacts=local --target=aarch64-apple-darwin"
-          install-dist: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0-prerelease.4/cargo-dist-installer.sh | sh"
+          install-dist: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0-prerelease.5/cargo-dist-installer.sh | sh"
         - os: "macos-11"
           dist-args: "--artifacts=local --target=x86_64-apple-darwin"
-          install-dist: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0-prerelease.4/cargo-dist-installer.sh | sh"
+          install-dist: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0-prerelease.5/cargo-dist-installer.sh | sh"
         - os: "windows-2019"
           dist-args: "--artifacts=local --target=x86_64-pc-windows-msvc"
-          install-dist: "irm  https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0-prerelease.4/cargo-dist-installer.ps1 | iex"
+          install-dist: "irm  https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0-prerelease.5/cargo-dist-installer.ps1 | iex"
         - os: "ubuntu-20.04"
           dist-args: "--artifacts=local --target=x86_64-unknown-linux-gnu"
-          install-dist: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0-prerelease.4/cargo-dist-installer.sh | sh"
+          install-dist: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0-prerelease.5/cargo-dist-installer.sh | sh"
     runs-on: ${{ matrix.os }}
     env:
       GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -141,7 +141,7 @@ jobs:
         with:
           submodules: recursive
       - name: Install cargo-dist
-        run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0-prerelease.4/cargo-dist-installer.sh | sh"
+        run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.2.0-prerelease.5/cargo-dist-installer.sh | sh"
       # Get all the local artifacts for the global tasks to use (for e.g. checksums)
       - name: Fetch local artifacts
         run: |
diff --git a/Cargo.lock b/Cargo.lock
index c4a1af23..a934c41c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -288,7 +288,7 @@ dependencies = [
  "axoproject",
  "camino",
  "cargo-dist-schema",
- "cargo_metadata",
+ "cargo_metadata 0.17.0",
  "clap",
  "clap-cargo",
  "console",
@@ -347,6 +347,20 @@ dependencies = [
  "thiserror",
 ]
 
+[[package]]
+name = "cargo_metadata"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7daec1a2a2129eeba1644b220b4647ec537b0b5d4bfd6876fcc5a540056b592"
+dependencies = [
+ "camino",
+ "cargo-platform",
+ "semver",
+ "serde",
+ "serde_json",
+ "thiserror",
+]
+
 [[package]]
 name = "cc"
 version = "1.0.83"
@@ -832,7 +846,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f822a2716041492e071691606474f5a7e4fa7c2acbfd7da7b29884fb448291c7"
 dependencies = [
  "camino",
- "cargo_metadata",
+ "cargo_metadata 0.15.4",
  "cfg-if",
  "debug-ignore",
  "fixedbitset",
diff --git a/Cargo.toml b/Cargo.toml
index b1bd1fd2..7b290e7e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,7 @@ pre-release-commit-message = "release: {{version}}"
 # Config for 'cargo dist'
 [workspace.metadata.dist]
 # The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax)
-cargo-dist-version = "0.2.0-prerelease.4"
+cargo-dist-version = "0.2.0-prerelease.5"
 # CI backends to support (see 'cargo dist generate-ci')
 ci = ["github"]
 # The installers to generate for each app
diff --git a/book/src/config.md b/book/src/config.md
index c670f39c..0a80e256 100644
--- a/book/src/config.md
+++ b/book/src/config.md
@@ -305,6 +305,24 @@ Prior to 0.1.0 we didn't set the correct flags in our CI scripts to do this, but
 This flag was introduced to allow you to restore the old behaviour if you prefer.
 
 
+### create-release
+
+> since 0.2.0
+
+Example: `create-release = false`
+
+**This can only be set globally**
+
+Whether we should create the Github Release for you in your Release CI.
+
+If true (default), cargo-dist will create a new Github Release and generate
+a title/body for it based on your changelog.
+
+If false, cargo-dist will assume a draft Github Release for the current git tag
+already exists with the title/body you want, and just upload artifacts to it.
+At the end of a successful publish it will undraft the Github Release.
+
+
 ### install-path
 
 > since 0.1.0
diff --git a/cargo-dist/Cargo.toml b/cargo-dist/Cargo.toml
index c5401163..f362a9d3 100644
--- a/cargo-dist/Cargo.toml
+++ b/cargo-dist/Cargo.toml
@@ -35,7 +35,7 @@ miette = { version = "5.6.0" }
 thiserror = "1.0.35"
 tracing = { version = "0.1.36", features = ["log"] }
 serde = { version = "1.0.144", features = ["derive"] }
-cargo_metadata = "0.15.0"
+cargo_metadata = "0.17.0"
 cruet = "0.13.3"
 guppy = "0.15.0"
 camino = "1.1.1"
diff --git a/cargo-dist/src/backend/ci/github.rs b/cargo-dist/src/backend/ci/github.rs
index f329faa2..d323f46e 100644
--- a/cargo-dist/src/backend/ci/github.rs
+++ b/cargo-dist/src/backend/ci/github.rs
@@ -18,6 +18,7 @@ struct CiInfo {
     install_dist_sh: String,
     install_dist_ps1: String,
     fail_fast: bool,
+    create_release: bool,
     local_tasks: Vec<CiTask>,
     global_task: Option<CiTask>,
     tap: Option<String>,
@@ -64,6 +65,7 @@ fn compute_ci_info(dist: &DistGraph) -> CiInfo {
         .as_ref()
         .unwrap_or(&self_dist_version);
     let fail_fast = dist.fail_fast;
+    let create_release = dist.create_release;
 
     // Figure out what builds we need to do
     let mut needs_global_build = false;
@@ -123,6 +125,7 @@ fn compute_ci_info(dist: &DistGraph) -> CiInfo {
         install_dist_sh,
         install_dist_ps1,
         fail_fast,
+        create_release,
         local_tasks,
         global_task,
         tap,
diff --git a/cargo-dist/src/cli.rs b/cargo-dist/src/cli.rs
index 9703ee1d..7513db71 100644
--- a/cargo-dist/src/cli.rs
+++ b/cargo-dist/src/cli.rs
@@ -86,7 +86,7 @@ pub struct Cli {
     ///
     /// If you use the prefixed version then we will only Announce/Release that package's apps
     /// (and return an error if that is not in fact the package's current version). This is
-    /// approp
+    /// appropriate for workspaces that have more than one app.
     ///
     /// If you use the unprefixed version then we will assume you're Announcing/Releasing all
     /// packages in the workspace that have that version. This is appropriate for workspaces
diff --git a/cargo-dist/src/config.rs b/cargo-dist/src/config.rs
index d6d692d0..7edf6081 100644
--- a/cargo-dist/src/config.rs
+++ b/cargo-dist/src/config.rs
@@ -228,6 +228,18 @@ pub struct DistMetadata {
     #[serde(skip_serializing_if = "Option::is_none")]
     #[serde(rename = "publish-jobs")]
     pub publish_jobs: Option<Vec<PublishStyle>>,
+
+    /// Whether we should create the Github Release for you when you push a tag.
+    ///
+    /// If true (default), cargo-dist will create a new Github Release and generate
+    /// a title/body for it based on your changelog.
+    ///
+    /// If false, cargo-dist will assume a draft Github Release already exists
+    /// with the title/body you want. At the end of a successful publish it will
+    /// undraft the Github Release.
+    #[serde(skip_serializing_if = "Option::is_none")]
+    #[serde(rename = "create-release")]
+    pub create_release: Option<bool>,
 }
 
 impl DistMetadata {
@@ -256,6 +268,7 @@ impl DistMetadata {
             default_features: _,
             all_features: _,
             publish_jobs: _,
+            create_release: _,
         } = self;
         if let Some(include) = include {
             for include in include {
@@ -293,6 +306,7 @@ impl DistMetadata {
             default_features,
             all_features,
             publish_jobs,
+            create_release,
         } = self;
 
         // Check for global settings on local packages
@@ -314,6 +328,9 @@ impl DistMetadata {
         if fail_fast.is_some() {
             warn!("package.metadata.dist.fail-fast is set, but this is only accepted in workspace.metadata (value is being ignored): {}", package_manifest_path);
         }
+        if create_release.is_some() {
+            warn!("package.metadata.dist.create-release is set, but this is only accepted in workspace.metadata (value is being ignored): {}", package_manifest_path);
+        }
 
         // Merge non-global settings
         if installers.is_none() {
diff --git a/cargo-dist/src/init.rs b/cargo-dist/src/init.rs
index 3018cdf6..2d1401c2 100644
--- a/cargo-dist/src/init.rs
+++ b/cargo-dist/src/init.rs
@@ -204,6 +204,7 @@ fn get_new_dist_metadata(
             default_features: None,
             all_features: None,
             publish_jobs: None,
+            create_release: None,
         }
     };
 
@@ -632,6 +633,7 @@ fn update_toml_metadata(
         all_features,
         default_features,
         publish_jobs,
+        create_release,
     } = &meta;
 
     apply_optional_value(
@@ -746,6 +748,13 @@ fn update_toml_metadata(
         *fail_fast,
     );
 
+    apply_optional_value(
+        table,
+        "create-release",
+        "# Whether cargo-dist should create a Github Release or use an existing draft\n",
+        *create_release,
+    );
+
     apply_optional_value(
         table,
         "install-path",
diff --git a/cargo-dist/src/lib.rs b/cargo-dist/src/lib.rs
index f7ae1c5c..651a86e3 100644
--- a/cargo-dist/src/lib.rs
+++ b/cargo-dist/src/lib.rs
@@ -345,7 +345,7 @@ fn build_cargo_target(dist_graph: &DistGraph, target: &CargoBuildStep) -> Result
         .arg("build")
         .arg("--profile")
         .arg(&target.profile)
-        .arg("--message-format=json")
+        .arg("--message-format=json-render-diagnostics")
         .arg("--target")
         .arg(&target.target_triple)
         .env("RUSTFLAGS", &target.rustflags)
diff --git a/cargo-dist/src/tasks.rs b/cargo-dist/src/tasks.rs
index 5da51dcf..9d80a124 100644
--- a/cargo-dist/src/tasks.rs
+++ b/cargo-dist/src/tasks.rs
@@ -149,6 +149,8 @@ pub struct DistGraph {
     pub merge_tasks: bool,
     /// Whether failing tasks should make us give up on all other tasks
     pub fail_fast: bool,
+    /// Whether to creat a github release or edit an existing draft
+    pub create_release: bool,
     /// The desired cargo-dist version for handling this project
     pub desired_cargo_dist_version: Option<Version>,
     /// The desired rust toolchain for handling this project
@@ -628,6 +630,7 @@ impl<'pkg_graph> DistGraphBuilder<'pkg_graph> {
             features,
             default_features: no_default_features,
             all_features,
+            create_release,
         } = &workspace_metadata;
 
         let desired_cargo_dist_version = cargo_dist_version.clone();
@@ -637,6 +640,7 @@ impl<'pkg_graph> DistGraphBuilder<'pkg_graph> {
         }
         let merge_tasks = merge_tasks.unwrap_or(false);
         let fail_fast = fail_fast.unwrap_or(false);
+        let create_release = create_release.unwrap_or(true);
         let mut packages_with_mismatched_features = vec![];
         // Compute/merge package configs
         let mut package_metadata = vec![];
@@ -684,6 +688,7 @@ impl<'pkg_graph> DistGraphBuilder<'pkg_graph> {
                 precise_builds,
                 fail_fast,
                 merge_tasks,
+                create_release,
                 desired_cargo_dist_version,
                 desired_rust_toolchain,
                 tools,
diff --git a/cargo-dist/templates/ci/github_ci.yml.j2 b/cargo-dist/templates/ci/github_ci.yml.j2
index 5be9660b..c63e771d 100644
--- a/cargo-dist/templates/ci/github_ci.yml.j2
+++ b/cargo-dist/templates/ci/github_ci.yml.j2
@@ -30,14 +30,14 @@ permissions:
 # package (erroring out if it doesn't have the given version or isn't cargo-dist-able).
 #
 # If PACKAGE_NAME isn't specified, then we will create a Github Release™ for all
-# (cargo-dist-able) packages in the workspace with that version (this is mode is
+# (cargo-dist-able) packages in the workspace with that version (this mode is
 # intended for workspaces with only one dist-able package, or with all dist-able
 # packages versioned/released in lockstep).
 #
 # If you push multiple tags at once, separate instances of this workflow will
 # spin up, creating an independent Github Release™ for each one.
 #
-# If there's a prerelease-style suffix to the version then the Github Release™
+# If there's a prerelease-style suffix to the version, then the Github Release™
 # will be marked as a prerelease.
 on:
   push:
@@ -69,12 +69,18 @@ jobs:
           echo "dist plan ran successfully"
           cat dist-manifest.json
 
+      {{%- if create_release %}}
+
           # Create the Github Release™ based on what cargo-dist thinks it should be
           ANNOUNCEMENT_TITLE=$(jq --raw-output ".announcement_title" dist-manifest.json)
           IS_PRERELEASE=$(jq --raw-output ".announcement_is_prerelease" dist-manifest.json)
           jq --raw-output ".announcement_github_body" dist-manifest.json > new_dist_announcement.md
           gh release create ${{ github.ref_name }} --draft --prerelease="$IS_PRERELEASE" --title="$ANNOUNCEMENT_TITLE" --notes-file=new_dist_announcement.md
           echo "created announcement!"
+      {{%- else %}}
+
+          # We're assuming a draft Github Release™ with the desired name/tag/body already exists
+      {{%- endif %}}
 
           # Upload the manifest to the Github Release™
           gh release upload ${{ github.ref_name }} dist-manifest.json
@@ -133,7 +139,7 @@ jobs:
 
   {{%- if global_task %}}
 
-  # Build and packages all the platform-agnostic(ish) things
+  # Build and package all the platform-agnostic(ish) things
   upload-global-artifacts:
     needs: upload-local-artifacts
     runs-on: {{{ global_task.runner }}}


Test patch:
diff --git a/cargo-dist/tests/integration-tests.rs b/cargo-dist/tests/integration-tests.rs
index a7216956..acbcd5c0 100644
--- a/cargo-dist/tests/integration-tests.rs
+++ b/cargo-dist/tests/integration-tests.rs
@@ -89,6 +89,39 @@ scope = "@axodotdev"
     })
 }
 
+#[test]
+fn axolotlsay_edit_existing() -> Result<(), miette::Report> {
+    let test_name = _function_name!();
+    AXOLOTLSAY.run_test(|ctx| {
+        let dist_version = ctx.tools.cargo_dist.version().unwrap();
+        ctx.patch_cargo_toml(format!(r#"
+[workspace.metadata.dist]
+cargo-dist-version = "{dist_version}"
+installers = ["shell", "powershell", "homebrew", "npm"]
+tap = "axodotdev/homebrew-packages"
+publish-jobs = ["homebrew"]
+targets = ["x86_64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-pc-windows-msvc", "aarch64-apple-darwin"]
+ci = ["github"]
+unix-archive = ".tar.gz"
+windows-archive = ".tar.gz"
+scope = "@axodotdev"
+create-release = false
+
+"#
+        ))?;
+
+        // Do usual build+plan checks
+        let main_result = ctx.cargo_dist_build_and_plan(test_name)?;
+        let main_snap = main_result.check_all(ctx, ".cargo/bin/")?;
+        // Check generate-ci
+        let ci_result = ctx.cargo_dist_generate_ci(test_name)?;
+        let ci_snap = ci_result.check_all()?;
+        // snapshot all
+        main_snap.join(ci_snap).snap();
+        Ok(())
+    })
+}
+
 #[test]
 fn akaikatana_basic() -> Result<(), miette::Report> {
     let test_name = _function_name!();
diff --git a/cargo-dist/tests/snapshots/akaikatana_basic.snap b/cargo-dist/tests/snapshots/akaikatana_basic.snap
index 4556c0af..621182c7 100644
--- a/cargo-dist/tests/snapshots/akaikatana_basic.snap
+++ b/cargo-dist/tests/snapshots/akaikatana_basic.snap
@@ -1271,14 +1271,14 @@ permissions:
 # package (erroring out if it doesn't have the given version or isn't cargo-dist-able).
 #
 # If PACKAGE_NAME isn't specified, then we will create a Github Release™ for all
-# (cargo-dist-able) packages in the workspace with that version (this is mode is
+# (cargo-dist-able) packages in the workspace with that version (this mode is
 # intended for workspaces with only one dist-able package, or with all dist-able
 # packages versioned/released in lockstep).
 #
 # If you push multiple tags at once, separate instances of this workflow will
 # spin up, creating an independent Github Release™ for each one.
 #
-# If there's a prerelease-style suffix to the version then the Github Release™
+# If there's a prerelease-style suffix to the version, then the Github Release™
 # will be marked as a prerelease.
 on:
   push:
@@ -1375,7 +1375,7 @@ jobs:
           gh release upload ${{ github.ref_name }} $(cat uploads.txt)
           echo "uploaded!"
 
-  # Build and packages all the platform-agnostic(ish) things
+  # Build and package all the platform-agnostic(ish) things
   upload-global-artifacts:
     needs: upload-local-artifacts
     runs-on: "ubuntu-20.04"
diff --git a/cargo-dist/tests/snapshots/akaikatana_repo_with_dot_git.snap b/cargo-dist/tests/snapshots/akaikatana_repo_with_dot_git.snap
index 4556c0af..621182c7 100644
--- a/cargo-dist/tests/snapshots/akaikatana_repo_with_dot_git.snap
+++ b/cargo-dist/tests/snapshots/akaikatana_repo_with_dot_git.snap
@@ -1271,14 +1271,14 @@ permissions:
 # package (erroring out if it doesn't have the given version or isn't cargo-dist-able).
 #
 # If PACKAGE_NAME isn't specified, then we will create a Github Release™ for all
-# (cargo-dist-able) packages in the workspace with that version (this is mode is
+# (cargo-dist-able) packages in the workspace with that version (this mode is
 # intended for workspaces with only one dist-able package, or with all dist-able
 # packages versioned/released in lockstep).
 #
 # If you push multiple tags at once, separate instances of this workflow will
 # spin up, creating an independent Github Release™ for each one.
 #
-# If there's a prerelease-style suffix to the version then the Github Release™
+# If there's a prerelease-style suffix to the version, then the Github Release™
 # will be marked as a prerelease.
 on:
   push:
@@ -1375,7 +1375,7 @@ jobs:
           gh release upload ${{ github.ref_name }} $(cat uploads.txt)
           echo "uploaded!"
 
-  # Build and packages all the platform-agnostic(ish) things
+  # Build and package all the platform-agnostic(ish) things
   upload-global-artifacts:
     needs: upload-local-artifacts
     runs-on: "ubuntu-20.04"
diff --git a/cargo-dist/tests/snapshots/axolotlsay_basic.snap b/cargo-dist/tests/snapshots/axolotlsay_basic.snap
index 10210d0b..58eb0ce5 100644
--- a/cargo-dist/tests/snapshots/axolotlsay_basic.snap
+++ b/cargo-dist/tests/snapshots/axolotlsay_basic.snap
@@ -2144,14 +2144,14 @@ permissions:
 # package (erroring out if it doesn't have the given version or isn't cargo-dist-able).
 #
 # If PACKAGE_NAME isn't specified, then we will create a Github Release™ for all
-# (cargo-dist-able) packages in the workspace with that version (this is mode is
+# (cargo-dist-able) packages in the workspace with that version (this mode is
 # intended for workspaces with only one dist-able package, or with all dist-able
 # packages versioned/released in lockstep).
 #
 # If you push multiple tags at once, separate instances of this workflow will
 # spin up, creating an independent Github Release™ for each one.
 #
-# If there's a prerelease-style suffix to the version then the Github Release™
+# If there's a prerelease-style suffix to the version, then the Github Release™
 # will be marked as a prerelease.
 on:
   push:
@@ -2244,7 +2244,7 @@ jobs:
           gh release upload ${{ github.ref_name }} $(cat uploads.txt)
           echo "uploaded!"
 
-  # Build and packages all the platform-agnostic(ish) things
+  # Build and package all the platform-agnostic(ish) things
   upload-global-artifacts:
     needs: upload-local-artifacts
     runs-on: "ubuntu-20.04"
diff --git a/cargo-dist/tests/snapshots/axolotlsay_edit_existing.snap b/cargo-dist/tests/snapshots/axolotlsay_edit_existing.snap
new file mode 100644
index 00000000..243a6276
--- /dev/null
+++ b/cargo-dist/tests/snapshots/axolotlsay_edit_existing.snap
@@ -0,0 +1,2322 @@
+---
+source: cargo-dist/tests/gallery/dist.rs
+expression: self.payload
+---
+================ installer.sh ================
+#!/bin/sh
+# shellcheck shell=dash
+#
+# Licensed under the MIT license
+# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+if [ "$KSH_VERSION" = 'Version JM 93t+ 2010-03-05' ]; then
+    # The version of ksh93 that ships with many illumos systems does not
+    # support the "local" extension.  Print a message rather than fail in
+    # subtle ways later on:
+    echo 'this installer does not work with this ksh93 version; please try bash!' >&2
+    exit 1
+fi
+
+set -u
+
+APP_NAME="axolotlsay"
+APP_VERSION="0.1.0"
+ARTIFACT_DOWNLOAD_URL="${INSTALLER_DOWNLOAD_URL:-https://github.com/axodotdev/axolotlsay/releases/download/v0.1.0}"
+PRINT_VERBOSE=${INSTALLER_PRINT_VERBOSE:-0}
+PRINT_QUIET=${INSTALLER_PRINT_QUIET:-0}
+NO_MODIFY_PATH=${INSTALLER_NO_MODIFY_PATH:-0}
+
+usage() {
+    # print help (this cat/EOF stuff is a "heredoc" string)
+    cat <<EOF
+axolotlsay-installer.sh
+
+The installer for axolotlsay 0.1.0
+
+This script detects what platform you're on and fetches an appropriate archive from
+https://github.com/axodotdev/axolotlsay/releases/download/v0.1.0
+then unpacks the binaries and installs them to \$CARGO_HOME/bin (\$HOME/.cargo/bin)
+
+It will then add that dir to PATH by adding the appropriate line to \$HOME/.profile
+
+USAGE:
+    axolotlsay-installer.sh [OPTIONS]
+
+OPTIONS:
+    -v, --verbose
+            Enable verbose output
+
+    -q, --quiet
+            Disable progress output
+
+        --no-modify-path
+            Don't configure the PATH environment variable
+
+    -h, --help
+            Print help information
+EOF
+}
+
+download_binary_and_run_installer() {
+    downloader --check
+    need_cmd uname
+    need_cmd mktemp
+    need_cmd chmod
+    need_cmd mkdir
+    need_cmd rm
+    need_cmd tar
+    need_cmd which
+    need_cmd grep
+    need_cmd cat
+
+    for arg in "$@"; do
+        case "$arg" in
+            --help)
+                usage
+                exit 0
+                ;;
+            --quiet)
+                PRINT_QUIET=1
+                ;;
+            --verbose)
+                PRINT_VERBOSE=1
+                ;;
+            --no-modify-path)
+                NO_MODIFY_PATH=1
+                ;;
+            *)
+                OPTIND=1
+                if [ "${arg%%--*}" = "" ]; then
+                    err "unknown option $arg"
+                fi
+                while getopts :hvq sub_arg "$arg"; do
+                    case "$sub_arg" in
+                        h)
+                            usage
+                            exit 0
+                            ;;
+                        v)
+                            # user wants to skip the prompt --
+                            # we don't need /dev/tty
+                            PRINT_VERBOSE=1
+                            ;;
+                        q)
+                            # user wants to skip the prompt --
+                            # we don't need /dev/tty
+                            PRINT_QUIET=1
+                            ;;
+                        *)
+                            err "unknown option -$OPTARG"
+                            ;;
+                        esac
+                done
+                ;;
+        esac
+    done
+
+    get_architecture || return 1
+    local _arch="$RETVAL"
+    assert_nz "$_arch" "arch"
+
+    local _bins
+    local _zip_ext
+    local _artifact_name
+
+    # Lookup what to download/unpack based on platform
+    case "$_arch" in 
+        "aarch64-apple-darwin")
+            _artifact_name="axolotlsay-aarch64-apple-darwin.tar.gz"
+            _zip_ext=".tar.gz"
+            _bins="axolotlsay"
+            ;;
+        "x86_64-apple-darwin")
+            _artifact_name="axolotlsay-x86_64-apple-darwin.tar.gz"
+            _zip_ext=".tar.gz"
+            _bins="axolotlsay"
+            ;;
+        "x86_64-unknown-linux-gnu")
+            _artifact_name="axolotlsay-x86_64-unknown-linux-gnu.tar.gz"
+            _zip_ext=".tar.gz"
+            _bins="axolotlsay"
+            ;;
+        *)
+            err "there isn't a package for $_arch"
+            ;;
+    esac
+
+    # download the archive
+    local _url="$ARTIFACT_DOWNLOAD_URL/$_artifact_name"
+    local _dir
+    if ! _dir="$(ensure mktemp -d)"; then
+        # Because the previous command ran in a subshell, we must manually
+        # propagate exit status.
+        exit 1
+    fi
+    local _file="$_dir/input$_zip_ext"
+
+    say "downloading $APP_NAME $APP_VERSION ${_arch}" 1>&2
+    say_verbose "  from $_url" 1>&2
+    say_verbose "  to $_file" 1>&2
+
+    ensure mkdir -p "$_dir"
+
+    if ! downloader "$_url" "$_file"; then
+      say "failed to download $_url"
+      say "this may be a standard network error, but it may also indicate"
+      say "that $APP_NAME's release process is not working. When in doubt"
+      say "please feel free to open an issue!"
+      exit 1
+    fi
+
+    # unpack the archive
+    case "$_zip_ext" in
+        ".zip")
+            ensure unzip -q "$_file" -d "$_dir"
+            ;;
+
+        ".tar."*)
+            ensure tar xf "$_file" --strip-components 1 -C "$_dir"
+            ;;
+        *)
+            err "unknown archive format: $_zip_ext"
+            ;;
+    esac
+
+    install "$_dir" "$_bins" "$@"
+    local _retval=$?
+
+    ignore rm -rf "$_dir"
+
+    return "$_retval"
+}
+
+# See discussion of late-bound vs early-bound for why we use single-quotes with env vars
+# shellcheck disable=SC2016
+install() {
+    # This code needs to both compute certain paths for itself to write to, and
+    # also write them to shell/rc files so that they can look them up to e.g.
+    # add them to PATH. This requires an active distinction between paths
+    # and expressions that can compute them.
+    #
+    # The distinction lies in when we want env-vars to be evaluated. For instance
+    # if we determine that we want to install to $HOME/.myapp, which do we add
+    # to e.g. $HOME/.profile:
+    #
+    # * early-bound: export PATH="/home/myuser/.myapp:$PATH"
+    # * late-bound:  export PATH="$HOME/.myapp:$PATH"
+    #
+    # In this case most people would prefer the late-bound version, but in other
+    # cases the early-bound version might be a better idea. In particular when using
+    # other env-vars than $HOME, they are more likely to be only set temporarily
+    # for the duration of this install script, so it's more advisable to erase their
+    # existence with early-bounding.
+    #
+    # This distinction is handled by "double-quotes" (early) vs 'single-quotes' (late).
+    #
+    # This script has a few different variants, the most complex one being the
+    # CARGO_HOME version which attempts to install things to Cargo's bin dir,
+    # potentially setting up a minimal version if the user hasn't ever installed Cargo.
+    #
+    # In this case we need to:
+    #
+    # * Install to $HOME/.cargo/bin/
+    # * Create a shell script at $HOME/.cargo/env that:
+    #   * Checks if $HOME/.cargo/bin/ is on PATH
+    #   * and if not prepends it to PATH
+    # * Edits $HOME/.profile to run $HOME/.cargo/env (if the line doesn't exist)
+    #
+    # To do this we need these 4 values:
+
+    # The actual path we're going to install to
+    local _install_dir
+    # Path to the an shell script that adds install_dir to PATH
+    local _env_script_path
+    # Potentially-late-bound version of install_dir to write env_script
+    local _install_dir_expr
+    # Potentially-late-bound version of env_script_path to write to rcfiles like $HOME/.profile
+    local _env_script_path_expr
+
+
+    # first try CARGO_HOME, then fallback to HOME
+    if [ -n "${CARGO_HOME:-}" ]; then
+        _install_dir="$CARGO_HOME/bin"
+        _env_script_path="$CARGO_HOME/env"
+        # If CARGO_HOME was set but it ended up being the default $HOME-based path,
+        # then keep things late-bound. Otherwise bake the value for safety.
+        # This is what rustup does, and accurately reproducing it is useful.
+        if [ -n "${HOME:-}" ]; then
+            if [ "$HOME/.cargo/bin" = "$_install_dir" ]; then
+                _install_dir_expr='$HOME/.cargo/bin'
+                _env_script_path_expr='$HOME/.cargo/env'
+            else
+                _install_dir_expr="$_install_dir"
+                _env_script_path_expr="$_env_script_path"
+            fi
+        else
+            _install_dir_expr="$_install_dir"
+            _env_script_path_expr="$_env_script_path"
+        fi
+    elif [ -n "${HOME:-}" ]; then
+        _install_dir="$HOME/.cargo/bin"
+        _env_script_path="$HOME/.cargo/env"
+        _install_dir_expr='$HOME/.cargo/bin'
+        _env_script_path_expr='$HOME/.cargo/env'
+    else
+        err "could not find your CARGO_HOME or HOME dir to install binaries to"
+    fi
+
+    say "installing to $_install_dir"
+    ensure mkdir -p "$_install_dir"
+
+    # copy all the binaries to the install dir
+    local _src_dir="$1"
+    local _bins="$2"
+    for _bin_name in $_bins; do
+        local _bin="$_src_dir/$_bin_name"
+        ensure cp "$_bin" "$_install_dir"
+        # unzip seems to need this chmod
+        ensure chmod +x "$_install_dir/$_bin_name"
+        say "  $_bin_name"
+    done
+
+    say "everything's installed!"
+
+    if [ "0" = "$NO_MODIFY_PATH" ]; then
+        add_install_dir_to_path "$_install_dir_expr" "$_env_script_path" "$_env_script_path_expr"
+    fi
+}
+
+add_install_dir_to_path() {
+    # Edit rcfiles ($HOME/.profile) to add install_dir to $PATH
+    #
+    # We do this slightly indirectly by creating an "env" shell script which checks if install_dir
+    # is on $PATH already, and prepends it if not. The actual line we then add to rcfiles
+    # is to just source that script. This allows us to blast it into lots of different rcfiles and
+    # have it run multiple times without causing problems. It's also specifically compatible
+    # with the system rustup uses, so that we don't conflict with it.
+    local _install_dir_expr="$1"
+    local _env_script_path="$2"
+    local _env_script_path_expr="$3"
+    if [ -n "${HOME:-}" ]; then
+        local _rcfile="$HOME/.profile"
+        # `source x` is an alias for `. x`, and the latter is more portable/actually-posix.
+        # This apparently comes up a lot on freebsd. It's easy enough to always add
+        # the more robust line to rcfiles, but when telling the user to apply the change
+        # to their current shell ". x" is pretty easy to misread/miscopy, so we use the
+        # prettier "source x" line there. Hopefully people with Weird Shells are aware
+        # this is a thing and know to tweak it (or just restart their shell).
+        local _robust_line=". \"$_env_script_path_expr\""
+        local _pretty_line="source \"$_env_script_path_expr\""
+
+        # Add the env script if it doesn't already exist
+        if [ ! -f "$_env_script_path" ]; then
+            say_verbose "creating $_env_script_path"
+            write_env_script "$_install_dir_expr" "$_env_script_path"
+        else
+            say_verbose "$_env_script_path already exists"
+        fi
+
+        # Check if the line is already in the rcfile
+        # grep: 0 if matched, 1 if no match, and 2 if an error occurred
+        #
+        # Ideally we could use quiet grep (-q), but that makes "match" and "error"
+        # have the same behaviour, when we want "no match" and "error" to be the same
+        # (on error we want to create the file, which >> conveniently does)
+        #
+        # We search for both kinds of line here just to do the right thing in more cases.
+        if ! grep -F "$_robust_line" "$_rcfile" > /dev/null 2>/dev/null && \
+           ! grep -F "$_pretty_line" "$_rcfile" > /dev/null 2>/dev/null
+        then
+            # If the script now exists, add the line to source it to the rcfile
+            # (This will also create the rcfile if it doesn't exist)
+            if [ -f "$_env_script_path" ]; then
+                say_verbose "adding $_robust_line to $_rcfile"
+                ensure echo "$_robust_line" >> "$_rcfile"
+                say ""
+                say "To add $_install_dir_expr to your PATH, either restart your shell or run:"
+                say ""
+                say "    $_pretty_line"
+            fi
+        else
+            say_verbose "$_install_dir already on PATH"
+        fi
+    fi
+}
+
+write_env_script() {
+    # write this env script to the given path (this cat/EOF stuff is a "heredoc" string)
+    local _install_dir_expr="$1"
+    local _env_script_path="$2"
+    ensure cat <<EOF > "$_env_script_path"
+#!/bin/sh
+# add binaries to PATH if they aren't added yet
+# affix colons on either side of \$PATH to simplify matching
+case ":\${PATH}:" in
+    *:"$_install_dir_expr":*)
+        ;;
+    *)
+        # Prepending path in case a system-installed binary needs to be overridden
+        export PATH="$_install_dir_expr:\$PATH"
+        ;;
+esac
+EOF
+}
+
+check_proc() {
+    # Check for /proc by looking for the /proc/self/exe link
+    # This is only run on Linux
+    if ! test -L /proc/self/exe ; then
+        err "fatal: Unable to find /proc/self/exe.  Is /proc mounted?  Installation cannot proceed without /proc."
+    fi
+}
+
+get_bitness() {
+    need_cmd head
+    # Architecture detection without dependencies beyond coreutils.
+    # ELF files start out "\x7fELF", and the following byte is
+    #   0x01 for 32-bit and
+    #   0x02 for 64-bit.
+    # The printf builtin on some shells like dash only supports octal
+    # escape sequences, so we use those.
+    local _current_exe_head
+    _current_exe_head=$(head -c 5 /proc/self/exe )
+    if [ "$_current_exe_head" = "$(printf '\177ELF\001')" ]; then
+        echo 32
+    elif [ "$_current_exe_head" = "$(printf '\177ELF\002')" ]; then
+        echo 64
+    else
+        err "unknown platform bitness"
+    fi
+}
+
+is_host_amd64_elf() {
+    need_cmd head
+    need_cmd tail
+    # ELF e_machine detection without dependencies beyond coreutils.
+    # Two-byte field at offset 0x12 indicates the CPU,
+    # but we're interested in it being 0x3E to indicate amd64, or not that.
+    local _current_exe_machine
+    _current_exe_machine=$(head -c 19 /proc/self/exe | tail -c 1)
+    [ "$_current_exe_machine" = "$(printf '\076')" ]
+}
+
+get_endianness() {
+    local cputype=$1
+    local suffix_eb=$2
+    local suffix_el=$3
+
+    # detect endianness without od/hexdump, like get_bitness() does.
+    need_cmd head
+    need_cmd tail
+
+    local _current_exe_endianness
+    _current_exe_endianness="$(head -c 6 /proc/self/exe | tail -c 1)"
+    if [ "$_current_exe_endianness" = "$(printf '\001')" ]; then
+        echo "${cputype}${suffix_el}"
+    elif [ "$_current_exe_endianness" = "$(printf '\002')" ]; then
+        echo "${cputype}${suffix_eb}"
+    else
+        err "unknown platform endianness"
+    fi
+}
+
+get_architecture() {
+    local _ostype
+    local _cputype
+    _ostype="$(uname -s)"
+    _cputype="$(uname -m)"
+    local _clibtype="gnu"
+
+    if [ "$_ostype" = Linux ]; then
+        if [ "$(uname -o)" = Android ]; then
+            _ostype=Android
+        fi
+        if ldd --version 2>&1 | grep -q 'musl'; then
+            _clibtype="musl"
+        fi
+    fi
+
+    if [ "$_ostype" = Darwin ] && [ "$_cputype" = i386 ]; then
+        # Darwin `uname -m` lies
+        if sysctl hw.optional.x86_64 | grep -q ': 1'; then
+            _cputype=x86_64
+        fi
+    fi
+
+    if [ "$_ostype" = SunOS ]; then
+        # Both Solaris and illumos presently announce as "SunOS" in "uname -s"
+        # so use "uname -o" to disambiguate.  We use the full path to the
+        # system uname in case the user has coreutils uname first in PATH,
+        # which has historically sometimes printed the wrong value here.
+        if [ "$(/usr/bin/uname -o)" = illumos ]; then
+            _ostype=illumos
+        fi
+
+        # illumos systems have multi-arch userlands, and "uname -m" reports the
+        # machine hardware name; e.g., "i86pc" on both 32- and 64-bit x86
+        # systems.  Check for the native (widest) instruction set on the
+        # running kernel:
+        if [ "$_cputype" = i86pc ]; then
+            _cputype="$(isainfo -n)"
+        fi
+    fi
+
+    case "$_ostype" in
+
+        Android)
+            _ostype=linux-android
+            ;;
+
+        Linux)
+            check_proc
+            _ostype=unknown-linux-$_clibtype
+            _bitness=$(get_bitness)
+            ;;
+
+        FreeBSD)
+            _ostype=unknown-freebsd
+            ;;
+
+        NetBSD)
+            _ostype=unknown-netbsd
+            ;;
+
+        DragonFly)
+            _ostype=unknown-dragonfly
+            ;;
+
+        Darwin)
+            _ostype=apple-darwin
+            ;;
+
+        illumos)
+            _ostype=unknown-illumos
+            ;;
+
+        MINGW* | MSYS* | CYGWIN* | Windows_NT)
+            _ostype=pc-windows-gnu
+            ;;
+
+        *)
+            err "unrecognized OS type: $_ostype"
+            ;;
+
+    esac
+
+    case "$_cputype" in
+
+        i386 | i486 | i686 | i786 | x86)
+            _cputype=i686
+            ;;
+
+        xscale | arm)
+            _cputype=arm
+            if [ "$_ostype" = "linux-android" ]; then
+                _ostype=linux-androideabi
+            fi
+            ;;
+
+        armv6l)
+            _cputype=arm
+            if [ "$_ostype" = "linux-android" ]; then
+                _ostype=linux-androideabi
+            else
+                _ostype="${_ostype}eabihf"
+            fi
+            ;;
+
+        armv7l | armv8l)
+            _cputype=armv7
+            if [ "$_ostype" = "linux-android" ]; then
+                _ostype=linux-androideabi
+            else
+                _ostype="${_ostype}eabihf"
+            fi
+            ;;
+
+        aarch64 | arm64)
+            _cputype=aarch64
+            ;;
+
+        x86_64 | x86-64 | x64 | amd64)
+            _cputype=x86_64
+            ;;
+
+        mips)
+            _cputype=$(get_endianness mips '' el)
+            ;;
+
+        mips64)
+            if [ "$_bitness" -eq 64 ]; then
+                # only n64 ABI is supported for now
+                _ostype="${_ostype}abi64"
+                _cputype=$(get_endianness mips64 '' el)
+            fi
+            ;;
+
+        ppc)
+            _cputype=powerpc
+            ;;
+
+        ppc64)
+            _cputype=powerpc64
+            ;;
+
+        ppc64le)
+            _cputype=powerpc64le
+            ;;
+
+        s390x)
+            _cputype=s390x
+            ;;
+        riscv64)
+            _cputype=riscv64gc
+            ;;
+        loongarch64)
+            _cputype=loongarch64
+            ;;
+        *)
+            err "unknown CPU type: $_cputype"
+
+    esac
+
+    # Detect 64-bit linux with 32-bit userland
+    if [ "${_ostype}" = unknown-linux-gnu ] && [ "${_bitness}" -eq 32 ]; then
+        case $_cputype in
+            x86_64)
+                # 32-bit executable for amd64 = x32
+                if is_host_amd64_elf; then {
+                    err "x32 linux unsupported"
+                }; else
+                    _cputype=i686
+                fi
+                ;;
+            mips64)
+                _cputype=$(get_endianness mips '' el)
+                ;;
+            powerpc64)
+                _cputype=powerpc
+                ;;
+            aarch64)
+                _cputype=armv7
+                if [ "$_ostype" = "linux-android" ]; then
+                    _ostype=linux-androideabi
+                else
+                    _ostype="${_ostype}eabihf"
+                fi
+                ;;
+            riscv64gc)
+                err "riscv64 with 32-bit userland unsupported"
+                ;;
+        esac
+    fi
+
+    # treat armv7 systems without neon as plain arm
+    if [ "$_ostype" = "unknown-linux-gnueabihf" ] && [ "$_cputype" = armv7 ]; then
+        if ensure grep '^Features' /proc/cpuinfo | grep -q -v neon; then
+            # At least one processor does not have NEON.
+            _cputype=arm
+        fi
+    fi
+
+    _arch="${_cputype}-${_ostype}"
+
+    RETVAL="$_arch"
+}
+
+say() {
+    if [ "0" = "$PRINT_QUIET" ]; then
+        echo "$1"
+    fi
+}
+
+say_verbose() {
+    if [ "1" = "$PRINT_VERBOSE" ]; then
+        echo "$1"
+    fi
+}
+
+err() {
+    if [ "0" = "$PRINT_QUIET" ]; then
+        local red
+        local reset
+        red=$(tput setaf 1 2>/dev/null || echo '')
+        reset=$(tput sgr0 2>/dev/null || echo '')
+        say "${red}ERROR${reset}: $1" >&2
+    fi
+    exit 1
+}
+
+need_cmd() {
+    if ! check_cmd "$1"
+    then err "need '$1' (command not found)"
+    fi
+}
+
+check_cmd() {
+    command -v "$1" > /dev/null 2>&1
+    return $?
+}
+
+assert_nz() {
+    if [ -z "$1" ]; then err "assert_nz $2"; fi
+}
+
+# Run a command that should never fail. If the command fails execution
+# will immediately terminate with an error showing the failing
+# command.
+ensure() {
+    if ! "$@"; then err "command failed: $*"; fi
+}
+
+# This is just for indicating that commands' results are being
+# intentionally ignored. Usually, because it's being executed
+# as part of error handling.
+ignore() {
+    "$@"
+}
+
+# This wraps curl or wget. Try curl first, if not installed,
+# use wget instead.
+downloader() {
+    if check_cmd curl
+    then _dld=curl
+    elif check_cmd wget
+    then _dld=wget
+    else _dld='curl or wget' # to be used in error message of need_cmd
+    fi
+
+    if [ "$1" = --check ]
+    then need_cmd "$_dld"
+    elif [ "$_dld" = curl ]
+    then curl -sSfL "$1" -o "$2"
+    elif [ "$_dld" = wget ]
+    then wget "$1" -O "$2"
+    else err "Unknown downloader"   # should not reach here
+    fi
+}
+
+download_binary_and_run_installer "$@" || exit 1
+
+================ formula.rb ================
+class Axolotlsay < Formula
+  desc "💬 a CLI for learning to distribute CLIs in rust"
+  if Hardware::CPU.type == :arm
+    url "https://github.com/axodotdev/axolotlsay/releases/download/v0.1.0/axolotlsay-aarch64-apple-darwin.tar.gz"
+  else
+    url "https://github.com/axodotdev/axolotlsay/releases/download/v0.1.0/axolotlsay-x86_64-apple-darwin.tar.gz"
+  end
+  version "0.1.0"
+  license "MIT OR Apache-2.0"
+
+  def install
+    bin.install "axolotlsay"
+
+    # Homebrew will automatically install these, so we don't need to do that
+    doc_files = Dir["README.*", "readme.*", "LICENSE", "LICENSE.*", "CHANGELOG.*"]
+    leftover_contents = Dir["*"] - doc_files
+
+    # Install any leftover files in pkgshare; these are probably config or
+    # sample files.
+    pkgshare.install *leftover_contents unless leftover_contents.empty?
+  end
+end
+
+================ installer.ps1 ================
+# Licensed under the MIT license
+# <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+<#
+.SYNOPSIS
+
+The installer for axolotlsay 0.1.0
+
+.DESCRIPTION
+
+This script detects what platform you're on and fetches an appropriate archive from
+https://github.com/axodotdev/axolotlsay/releases/download/v0.1.0
+then unpacks the binaries and installs them to $env:CARGO_HOME\bin ($HOME\.cargo\bin)
+
+It will then add that dir to PATH by editing your Environment.Path registry key
+
+.PARAMETER ArtifactDownloadUrl
+The URL of the directory where artifacts can be fetched from
+
+.PARAMETER NoModifyPath
+Don't add the install directory to PATH
+
+.PARAMETER Help
+Print help
+
+#>
+
+param (
+    [Parameter(HelpMessage = "The URL of the directory where artifacts can be fetched from")]
+    [string]$ArtifactDownloadUrl = 'https://github.com/axodotdev/axolotlsay/releases/download/v0.1.0',
+    [Parameter(HelpMessage = "Don't add the install directory to PATH")]
+    [switch]$NoModifyPath,
+    [Parameter(HelpMessage = "Print Help")]
+    [switch]$Help
+)
+
+$app_name = 'axolotlsay'
+$app_version = '0.1.0'
+
+function Install-Binary($install_args) {
+  if ($Help) {
+    Get-Help $PSCommandPath -Detailed
+    Exit
+  }
+  $old_erroractionpreference = $ErrorActionPreference
+  $ErrorActionPreference = 'stop'
+
+  Initialize-Environment
+
+  # Platform info injected by cargo-dist
+  $platforms = @{
+    "x86_64-pc-windows-msvc" = @{
+      "artifact_name" = "axolotlsay-x86_64-pc-windows-msvc.tar.gz"
+      "bins" = "axolotlsay.exe"
+      "zip_ext" = ".tar.gz"
+    }
+  }
+
+  $fetched = Download "$ArtifactDownloadUrl" $platforms
+  # FIXME: add a flag that lets the user not do this step
+  Invoke-Installer $fetched "$install_args"
+
+  $ErrorActionPreference = $old_erroractionpreference
+}
+
+function Get-TargetTriple() {
+  try {
+    # NOTE: this might return X64 on ARM64 Windows, which is OK since emulation is available.
+    # It works correctly starting in PowerShell Core 7.3 and Windows PowerShell in Win 11 22H2.
+    # Ideally this would just be
+    #   [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture
+    # but that gets a type from the wrong assembly on Windows PowerShell (i.e. not Core)
+    $a = [System.Reflection.Assembly]::LoadWithPartialName("System.Runtime.InteropServices.RuntimeInformation")
+    $t = $a.GetType("System.Runtime.InteropServices.RuntimeInformation")
+    $p = $t.GetProperty("OSArchitecture")
+    # Possible OSArchitecture Values: https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.architecture
+    # Rust supported platforms: https://doc.rust-lang.org/stable/rustc/platform-support.html
+    switch ($p.GetValue($null).ToString())
+    {
+      "X86" { return "i686-pc-windows-msvc" }
+      "X64" { return "x86_64-pc-windows-msvc" }
+      "Arm" { return "thumbv7a-pc-windows-msvc" }
+      "Arm64" { return "aarch64-pc-windows-msvc" }
+    }
+  } catch {
+    # The above was added in .NET 4.7.1, so Windows PowerShell in versions of Windows
+    # prior to Windows 10 v1709 may not have this API.
+    Write-Verbose "Get-TargetTriple: Exception when trying to determine OS architecture."
+    Write-Verbose $_
+  }
+
+  # This is available in .NET 4.0. We already checked for PS 5, which requires .NET 4.5.
+  Write-Verbose("Get-TargetTriple: falling back to Is64BitOperatingSystem.")
+  if ([System.Environment]::Is64BitOperatingSystem) {
+    return "x86_64-pc-windows-msvc"
+  } else {
+    return "i686-pc-windows-msvc"
+  }
+}
+
+function Download($download_url, $platforms) {
+  $arch = Get-TargetTriple
+
+  if (-not $platforms.ContainsKey($arch)) {
+    # X64 is well-supported, including in emulation on ARM64
+    Write-Verbose "$arch is not availablem falling back to X64"
+    $arch = "x86_64-pc-windows-msvc"
+  }
+
+  if (-not $platforms.ContainsKey($arch)) {
+    # should not be possible, as currently we always produce X64 binaries.
+    $platforms_json = ConvertTo-Json $platforms
+    throw "ERROR: could not find binaries for this platform. Last platform tried: $arch platform info: $platforms_json"
+  }
+
+  # Lookup what we expect this platform to look like
+  $info = $platforms[$arch]
+  $zip_ext = $info["zip_ext"]
+  $bin_names = $info["bins"]
+  $artifact_name = $info["artifact_name"]
+
+  # Make a new temp dir to unpack things to
+  $tmp = New-Temp-Dir
+  $dir_path = "$tmp\$app_name$zip_ext"
+
+  # Download and unpack!
+  $url = "$download_url/$artifact_name"
+  Write-Information "Downloading $app_name $app_version ($arch)"
+  Write-Verbose "  from $url"
+  Write-Verbose "  to $dir_path"
+  $wc = New-Object Net.Webclient
+  $wc.downloadFile($url, $dir_path)
+
+  Write-Verbose "Unpacking to $tmp"
+
+  # Select the tool to unpack the files with.
+  #
+  # As of windows 10(?), powershell comes with tar preinstalled, but in practice
+  # it only seems to support .tar.gz, and not xz/zstd. Still, we should try to
+  # forward all tars to it in case the user has a machine that can handle it!
+  switch -Wildcard ($zip_ext) {
+    ".zip" {
+      Expand-Archive -Path $dir_path -DestinationPath "$tmp";
+      Break
+    }
+    ".tar.*" {
+      tar xf $dir_path --strip-components 1 -C "$tmp";
+      Break
+    }
+    Default {
+      throw "ERROR: unknown archive format $zip_ext"
+    }
+  }
+
+  # Let the next step know what to copy
+  $bin_paths = @()
+  foreach ($bin_name in $bin_names) {
+    Write-Verbose "  Unpacked $bin_name"
+    $bin_paths += "$tmp\$bin_name"
+  }
+  return $bin_paths
+}
+
+function Invoke-Installer($bin_paths) {
+
+  # first try CARGO_HOME, then fallback to HOME
+  # (for whatever reason $HOME is not a normal env var and doesn't need the $env: prefix)
+  $dest_dir = if (($base_dir = $env:CARGO_HOME)) {
+    Join-Path $base_dir "bin"
+  } elseif (($base_dir = $HOME)) {
+    Join-Path $base_dir ".cargo\bin"
+  } else {
+    throw "ERROR: could not find your HOME dir or CARGO_HOME to install binaries to"
+  }
+
+  $dest_dir = New-Item -Force -ItemType Directory -Path $dest_dir
+  Write-Information "Installing to $dest_dir"
+  # Just copy the binaries from the temp location to the install dir
+  foreach ($bin_path in $bin_paths) {
+    $installed_file = Split-Path -Path "$bin_path" -Leaf
+    Copy-Item "$bin_path" -Destination "$dest_dir"
+    Remove-Item "$bin_path" -Recurse -Force
+    Write-Information "  $installed_file"
+  }
+
+  Write-Information "Everything's installed!"
+  if (-not $NoModifyPath) {
+    if (Add-Path $dest_dir) {
+        Write-Information ""
+        Write-Information "$dest_dir was added to your PATH, you may need to restart your shell for that to take effect."
+    }
+  }
+}
+
+# Try to add the given path to PATH via the registry
+#
+# Returns true if the registry was modified, otherwise returns false
+# (indicating it was already on PATH)
+function Add-Path($OrigPathToAdd) {
+  $RegistryPath = "HKCU:\Environment"
+  $PropertyName = "Path"
+  $PathToAdd = $OrigPathToAdd
+
+  $Item = if (Test-Path $RegistryPath) {
+    # If the registry key exists, get it
+    Get-Item -Path $RegistryPath
+  } else {
+    # If the registry key doesn't exist, create it
+    Write-Verbose  "Creating $RegistryPath"
+    New-Item -Path $RegistryPath -Force
+  }
+
+  $OldPath = ""
+  try {
+    # Try to get the old PATH value. If that fails, assume we're making it from scratch.
+    # Otherwise assume there's already paths in here and use a ; separator
+    $OldPath = $Item | Get-ItemPropertyValue -Name $PropertyName
+    $PathToAdd = "$PathToAdd;"
+  } catch {
+    # We'll be creating the PATH from scratch
+    Write-Verbose "Adding $PropertyName Property to $RegistryPath"
+  }
+
+  # Check if the path is already there
+  #
+  # We don't want to incorrectly match "C:\blah\" to "C:\blah\blah\", so we include the semicolon
+  # delimiters when searching, ensuring exact matches. To avoid corner cases we add semicolons to
+  # both sides of the input, allowing us to pretend we're always in the middle of a list.
+  if (";$OldPath;" -like "*;$OrigPathToAdd;*") {
+    # Already on path, nothing to do
+    Write-Verbose "install dir already on PATH, all done!"
+    return $false
+  } else {
+    # Actually update PATH
+    Write-Verbose "Adding $OrigPathToAdd to your PATH"
+    $NewPath = $PathToAdd + $OldPath
+    # We use -Force here to make the value already existing not be an error
+    $Item | New-ItemProperty -Name $PropertyName -Value $NewPath -PropertyType String -Force | Out-Null
+    return $true
+  }
+}
+
+function Initialize-Environment() {
+  If (($PSVersionTable.PSVersion.Major) -lt 5) {
+    Write-Error "PowerShell 5 or later is required to install $app_name."
+    Write-Error "Upgrade PowerShell: https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-windows-powershell"
+    break
+  }
+
+  # show notification to change execution policy:
+  $allowedExecutionPolicy = @('Unrestricted', 'RemoteSigned', 'ByPass')
+  If ((Get-ExecutionPolicy).ToString() -notin $allowedExecutionPolicy) {
+    Write-Error "PowerShell requires an execution policy in [$($allowedExecutionPolicy -join ", ")] to run $app_name."
+    Write-Error "For example, to set the execution policy to 'RemoteSigned' please run :"
+    Write-Error "'Set-ExecutionPolicy RemoteSigned -scope CurrentUser'"
+    break
+  }
+
+  # GitHub requires TLS 1.2
+  If ([System.Enum]::GetNames([System.Net.SecurityProtocolType]) -notcontains 'Tls12') {
+    Write-Error "Installing $app_name requires at least .NET Framework 4.5"
+    Write-Error "Please download and install it first:"
+    Write-Error "https://www.microsoft.com/net/download"
+    break
+  }
+}
+
+function New-Temp-Dir() {
+  [CmdletBinding(SupportsShouldProcess)]
+  param()
+  $parent = [System.IO.Path]::GetTempPath()
+  [string] $name = [System.Guid]::NewGuid()
+  New-Item -ItemType Directory -Path (Join-Path $parent $name)
+}
+
+# PSScriptAnalyzer doesn't like how we use our params as globals, this calms it
+$Null = $ArtifactDownloadUrl, $NoModifyPath, $Help
+# Make Write-Information statements be visible
+$InformationPreference = "Continue"
+Install-Binary "$Args"
+
+================ npm-package.tar.gz/package/.gitignore ================
+/node_modules
+
+================ npm-package.tar.gz/package/CHANGELOG.md ================
+# Version 0.1.0
+
+```text
+         +------------------------+
+         | the initial release!!! |
+         +------------------------+
+        /
+≽(◕ ᴗ ◕)≼
+```
+================ npm-package.tar.gz/package/LICENSE-APACHE ================
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+   To apply the Apache License to your work, attach the following
+   boilerplate notice, with the fields enclosed by brackets "[]"
+   replaced with your own identifying information. (Don't include
+   the brackets!)  The text should be enclosed in the appropriate
+   comment syntax for the file format. We also recommend that a
+   file or class name and description of purpose be included on the
+   same "printed page" as the copyright notice for easier
+   identification within third-party archives.
+
+Copyright 2023 Axo Developer Co.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+================ npm-package.tar.gz/package/LICENSE-MIT ================
+Copyright (c) 2023 Axo Developer Co.
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+================ npm-package.tar.gz/package/README.md ================
+# axolotlsay
+> 💬 a CLI for learning to distribute CLIs in rust
+
+
+## Usage
+
+```sh
+> axolotlsay "hello world"
+
+         +-------------+
+         | hello world |
+         +-------------+
+        /
+≽(◕ ᴗ ◕)≼
+```
+
+## License
+
+Licensed under either of
+
+* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or [apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0))
+* MIT license ([LICENSE-MIT](LICENSE-MIT) or [opensource.org/licenses/MIT](https://opensource.org/licenses/MIT))
+
+at your option.
+
+================ npm-package.tar.gz/package/binary.js ================
+const { Binary } = require("binary-install");
+const os = require("os");
+const cTable = require("console.table");
+const libc = require("detect-libc");
+const { configureProxy } = require("axios-proxy-builder");
+
+const error = (msg) => {
+  console.error(msg);
+  process.exit(1);
+};
+
+const { version } = require("./package.json");
+const name = "axolotlsay";
+const artifact_download_url = "https://github.com/axodotdev/axolotlsay/releases/download/v0.1.0";
+
+const supportedPlatforms = {
+  "aarch64-apple-darwin": {
+    "artifact_name": "axolotlsay-aarch64-apple-darwin.tar.gz",
+    "bins": ["axolotlsay"],
+    "zip_ext": ".tar.gz"
+  },
+  "x86_64-apple-darwin": {
+    "artifact_name": "axolotlsay-x86_64-apple-darwin.tar.gz",
+    "bins": ["axolotlsay"],
+    "zip_ext": ".tar.gz"
+  },
+  "x86_64-pc-windows-msvc": {
+    "artifact_name": "axolotlsay-x86_64-pc-windows-msvc.tar.gz",
+    "bins": ["axolotlsay.exe"],
+    "zip_ext": ".tar.gz"
+  },
+  "x86_64-unknown-linux-gnu": {
+    "artifact_name": "axolotlsay-x86_64-unknown-linux-gnu.tar.gz",
+    "bins": ["axolotlsay"],
+    "zip_ext": ".tar.gz"
+  }
+};
+
+const getPlatform = () => {
+  const raw_os_type = os.type();
+  const raw_architecture = os.arch();
+
+  // We want to use rust-style target triples as the canonical key
+  // for a platform, so translate the "os" library's concepts into rust ones
+  let os_type = "";
+  switch (raw_os_type) {
+    case "Windows_NT":
+      os_type = "pc-windows-msvc";
+      break;
+    case "Darwin":
+      os_type = "apple-darwin";
+      break;
+    case "Linux":
+      os_type = "unknown-linux-gnu"
+      break;
+  }
+
+  let arch = "";
+  switch (raw_architecture) {
+    case "x64":
+      arch = "x86_64";
+      break;
+    case "arm64":
+      arch = "aarch64";
+      break;
+  }
+
+  // Assume the above succeeded and build a target triple to look things up with.
+  // If any of it failed, this lookup will fail and we'll handle it like normal.
+  let target_triple = `${arch}-${os_type}`;
+  let platform = supportedPlatforms[target_triple];
+
+  if (!platform) {
+    error(
+      `Platform with type "${raw_os_type}" and architecture "${raw_architecture}" is not supported by ${name}.\nYour system must be one of the following:\n\n${Object.keys(supportedPlatforms).join(",")}`
+    );
+  }
+
+  // These are both situation where you might toggle to unknown-linux-musl but we don't support that yet
+  if (raw_os_type === "Linux") {
+    if (libc.isNonGlibcLinuxSync()) {
+      error("This operating system does not support dynamic linking to glibc.");
+    } else {
+      let libc_version = libc.versionSync();
+      let split_libc_version = libc_version.split(".");
+      let libc_major_version = split_libc_version[0];
+      let libc_minor_version = split_libc_version[1];
+      let min_major_version = 2;
+      let min_minor_version = 17;
+      if (
+        libc_major_version < min_major_version ||
+        libc_minor_version < min_minor_version
+      ) {
+        error(
+          `This operating system needs glibc >= ${min_major_version}.${min_minor_version}, but only has ${libc_version} installed.`
+        );
+      }
+    }
+  }
+
+  return platform;
+};
+
+const getBinary = () => {
+  const platform = getPlatform();
+  const url = `${artifact_download_url}/${platform.artifact_name}`;
+
+  if (platform.bins.length > 1) {
+    // Not yet supported
+    error("this app has multiple binaries, which isn't yet implemented");
+  }
+  let binary = new Binary(platform.bins[0], url);
+
+  return binary;
+};
+
+const install = (suppressLogs) => {
+  const binary = getBinary();
+  const proxy = configureProxy(binary.url);
+
+  return binary.install(proxy, suppressLogs);
+};
+
+const run = () => {
+  const binary = getBinary();
+  binary.run();
+};
+
+module.exports = {
+  install,
+  run,
+  getBinary,
+};
+
+================ npm-package.tar.gz/package/install.js ================
+#!/usr/bin/env node
+
+const { install } = require("./binary");
+install(false);
+
+================ npm-package.tar.gz/package/npm-shrinkwrap.json ================
+{
+  "name": "axolotlsay",
+  "version": "0.1.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "axolotlsay",
+      "version": "0.1.0",
+      "license": "MIT OR Apache-2.0",
+      "hasInstallScript": true,
+      "dependencies": {
+        "axios-proxy-builder": "^0.1.1",
+        "binary-install": "^1.0.6",
+        "console.table": "^0.10.0",
+        "detect-libc": "^2.0.0"
+      },
+      "bin": {
+        "rover": "run.js"
+      },
+      "devDependencies": {
+        "prettier": "2.8.4"
+      },
+      "engines": {
+        "node": ">=14",
+        "npm": ">=6"
+      }
+    },
+    "node_modules/axios": {
+      "version": "0.26.1",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
+      "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
+      "dependencies": {
+        "follow-redirects": "^1.14.8"
+      }
+    },
+    "node_modules/axios-proxy-builder": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/axios-proxy-builder/-/axios-proxy-builder-0.1.2.tgz",
+      "integrity": "sha512-6uBVsBZzkB3tCC8iyx59mCjQckhB8+GQrI9Cop8eC7ybIsvs/KtnNgEBfRMSEa7GqK2VBGUzgjNYMdPIfotyPA==",
+      "dependencies": {
+        "tunnel": "^0.0.6"
+      }
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+    },
+    "node_modules/binary-install": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/binary-install/-/binary-install-1.0.6.tgz",
+      "integrity": "sha512-h3K4jaC4jEauK3csXI9GxGBJldkpuJlHCIBv8i+XBNhPuxnlERnD1PWVczQYDqvhJfv0IHUbB3lhDrZUMHvSgw==",
+      "dependencies": {
+        "axios": "^0.26.1",
+        "rimraf": "^3.0.2",
+        "tar": "^6.1.11"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/chownr": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+      "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/clone": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+      "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
+      "optional": true,
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
+    },
+    "node_modules/console.table": {
+      "version": "0.10.0",
+      "resolved": "https://registry.npmjs.org/console.table/-/console.table-0.10.0.tgz",
+      "integrity": "sha512-dPyZofqggxuvSf7WXvNjuRfnsOk1YazkVP8FdxH4tcH2c37wc79/Yl6Bhr7Lsu00KMgy2ql/qCMuNu8xctZM8g==",
+      "dependencies": {
+        "easy-table": "1.1.0"
+      },
+      "engines": {
+        "node": "> 0.10"
+      }
+    },
+    "node_modules/defaults": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
+      "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+      "optional": true,
+      "dependencies": {
+        "clone": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/detect-libc": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
+      "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/easy-table": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz",
+      "integrity": "sha512-oq33hWOSSnl2Hoh00tZWaIPi1ievrD9aFG82/IgjlycAnW9hHx5PkJiXpxPsgEE+H7BsbVQXFVFST8TEXS6/pA==",
+      "optionalDependencies": {
+        "wcwidth": ">=1.0.1"
+      }
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.2",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
+      "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/fs-minipass": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+      "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+      "dependencies": {
+        "minipass": "^3.0.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/fs-minipass/node_modules/minipass": {
+      "version": "3.3.6",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+      "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
+    },
+    "node_modules/glob": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.1.1",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+      "dependencies": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+    },
+    "node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/minipass": {
+      "version": "4.2.4",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.4.tgz",
+      "integrity": "sha512-lwycX3cBMTvcejsHITUgYj6Gy6A7Nh4Q6h9NP4sTHY1ccJlC7yKzDmiShEHsJ16Jf1nKGDEaiHxiltsJEvk0nQ==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/minizlib": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+      "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+      "dependencies": {
+        "minipass": "^3.0.0",
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/minizlib/node_modules/minipass": {
+      "version": "3.3.6",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+      "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+      "dependencies": {
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/mkdirp": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+      "bin": {
+        "mkdirp": "bin/cmd.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/prettier": {
+      "version": "2.8.4",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
+      "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
+      "dev": true,
+      "bin": {
+        "prettier": "bin-prettier.js"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      },
+      "funding": {
+        "url": "https://github.com/prettier/prettier?sponsor=1"
+      }
+    },
+    "node_modules/rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "dependencies": {
+        "glob": "^7.1.3"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/tar": {
+      "version": "6.1.13",
+      "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz",
+      "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==",
+      "dependencies": {
+        "chownr": "^2.0.0",
+        "fs-minipass": "^2.0.0",
+        "minipass": "^4.0.0",
+        "minizlib": "^2.1.1",
+        "mkdirp": "^1.0.3",
+        "yallist": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/tunnel": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
+      "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
+      "engines": {
+        "node": ">=0.6.11 <=0.7.0 || >=0.7.3"
+      }
+    },
+    "node_modules/wcwidth": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+      "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+      "optional": true,
+      "dependencies": {
+        "defaults": "^1.0.3"
+      }
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+    },
+    "node_modules/yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+    }
+  }
+}
+
+================ npm-package.tar.gz/package/package.json ================
+{
+  "name": "axolotlsay",
+  "version": "0.1.0",
+  "description": "💬 a CLI for learning to distribute CLIs in rust",
+  "repository": "https://github.com/axodotdev/axolotlsay",
+  "license": "MIT OR Apache-2.0",
+  "bin": {
+    "axolotlsay": "run.js"
+  },
+  "scripts": {
+    "postinstall": "node ./install.js",
+    "fmt": "prettier --write **/*.js",
+    "fmt:check": "prettier --check **/*.js"
+  },
+  "engines": {
+    "node": ">=14",
+    "npm": ">=6"
+  },
+  "volta": {
+    "node": "18.14.1",
+    "npm": "9.5.0"
+  },
+  "dependencies": {
+    "axios-proxy-builder": "^0.1.1",
+    "binary-install": "^1.0.6",
+    "console.table": "^0.10.0",
+    "detect-libc": "^2.0.0"
+  },
+  "devDependencies": {
+    "prettier": "2.8.4"
+  }
+}
+
+================ npm-package.tar.gz/package/run.js ================
+#!/usr/bin/env node
+
+const { run, install: maybeInstall } = require("./binary");
+maybeInstall(true).then(run);
+
+================ dist-manifest.json ================
+{
+  "dist_version": "CENSORED",
+  "announcement_tag": "v0.1.0",
+  "announcement_is_prerelease": false,
+  "announcement_title": "Version 0.1.0",
+  "announcement_changelog": "```text\n         +------------------------+\n         | the initial release!!! |\n         +------------------------+\n        /\n≽(◕ ᴗ ◕)≼\n```",
+  "announcement_github_body": "## Release Notes\n\n```text\n         +------------------------+\n         | the initial release!!! |\n         +------------------------+\n        /\n≽(◕ ᴗ ◕)≼\n```\n\n## Install axolotlsay 0.1.0\n\n### Install prebuilt binaries via shell script\n\n```sh\ncurl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/axolotlsay/releases/download/v0.1.0/axolotlsay-installer.sh | sh\n```\n\n### Install prebuilt binaries via powershell script\n\n```sh\nirm https://github.com/axodotdev/axolotlsay/releases/download/v0.1.0/axolotlsay-installer.ps1 | iex\n```\n\n### Install prebuilt binaries via Homebrew\n\n```sh\nbrew install axodotdev/homebrew-packages/axolotlsay\n```\n\n### Install prebuilt binaries into your npm project\n\n```sh\nnpm install axolotlsay@0.1.0\n```\n\n## Download axolotlsay 0.1.0\n\n|        |        |\n|--------|--------|\n| [axolotlsay-aarch64-apple-darwin.tar.gz](https://github.com/axodotdev/axolotlsay/releases/download/v0.1.0/axolotlsay-aarch64-apple-darwin.tar.gz) | [checksum](https://github.com/axodotdev/axolotlsay/releases/download/v0.1.0/axolotlsay-aarch64-apple-darwin.tar.gz.sha256) |\n| [axolotlsay-x86_64-apple-darwin.tar.gz](https://github.com/axodotdev/axolotlsay/releases/download/v0.1.0/axolotlsay-x86_64-apple-darwin.tar.gz) | [checksum](https://github.com/axodotdev/axolotlsay/releases/download/v0.1.0/axolotlsay-x86_64-apple-darwin.tar.gz.sha256) |\n| [axolotlsay-x86_64-pc-windows-msvc.tar.gz](https://github.com/axodotdev/axolotlsay/releases/download/v0.1.0/axolotlsay-x86_64-pc-windows-msvc.tar.gz) | [checksum](https://github.com/axodotdev/axolotlsay/releases/download/v0.1.0/axolotlsay-x86_64-pc-windows-msvc.tar.gz.sha256) |\n| [axolotlsay-x86_64-unknown-linux-gnu.tar.gz](https://github.com/axodotdev/axolotlsay/releases/download/v0.1.0/axolotlsay-x86_64-unknown-linux-gnu.tar.gz) | [checksum](https://github.com/axodotdev/axolotlsay/releases/download/v0.1.0/axolotlsay-x86_64-unknown-linux-gnu.tar.gz.sha256) |\n\n",
+  "system_info": {
+    "cargo_version_line": "CENSORED"
+  },
+  "releases": [
+    {
+      "app_name": "axolotlsay",
+      "app_version": "0.1.0",
+      "artifacts": [
+        "axolotlsay-installer.sh",
+        "axolotlsay-installer.ps1",
+        "axolotlsay.rb",
+        "axolotlsay-npm-package.tar.gz",
+        "axolotlsay-aarch64-apple-darwin.tar.gz",
+        "axolotlsay-aarch64-apple-darwin.tar.gz.sha256",
+        "axolotlsay-x86_64-apple-darwin.tar.gz",
+        "axolotlsay-x86_64-apple-darwin.tar.gz.sha256",
+        "axolotlsay-x86_64-pc-windows-msvc.tar.gz",
+        "axolotlsay-x86_64-pc-windows-msvc.tar.gz.sha256",
+        "axolotlsay-x86_64-unknown-linux-gnu.tar.gz",
+        "axolotlsay-x86_64-unknown-linux-gnu.tar.gz.sha256"
+      ]
+    }
+  ],
+  "artifacts": {
+    "axolotlsay-aarch64-apple-darwin.tar.gz": {
+      "name": "axolotlsay-aarch64-apple-darwin.tar.gz",
+      "kind": "executable-zip",
+      "target_triples": [
+        "aarch64-apple-darwin"
+      ],
+      "assets": [
+        {
+          "name": "CHANGELOG.md",
+          "path": "CHANGELOG.md",
+          "kind": "changelog"
+        },
+        {
+          "name": "LICENSE-APACHE",
+          "path": "LICENSE-APACHE",
+          "kind": "license"
+        },
+        {
+          "name": "LICENSE-MIT",
+          "path": "LICENSE-MIT",
+          "kind": "license"
+        },
+        {
+          "name": "README.md",
+          "path": "README.md",
+          "kind": "readme"
+        },
+        {
+          "name": "axolotlsay",
+          "path": "axolotlsay",
+          "kind": "executable"
+        }
+      ],
+      "checksum": "axolotlsay-aarch64-apple-darwin.tar.gz.sha256"
+    },
+    "axolotlsay-aarch64-apple-darwin.tar.gz.sha256": {
+      "name": "axolotlsay-aarch64-apple-darwin.tar.gz.sha256",
+      "kind": "checksum",
+      "target_triples": [
+        "aarch64-apple-darwin"
+      ]
+    },
+    "axolotlsay-installer.ps1": {
+      "name": "axolotlsay-installer.ps1",
+      "kind": "installer",
+      "target_triples": [
+        "x86_64-pc-windows-msvc"
+      ],
+      "install_hint": "irm https://github.com/axodotdev/axolotlsay/releases/download/v0.1.0/axolotlsay-installer.ps1 | iex",
+      "description": "Install prebuilt binaries via powershell script"
+    },
+    "axolotlsay-installer.sh": {
+      "name": "axolotlsay-installer.sh",
+      "kind": "installer",
+      "target_triples": [
+        "aarch64-apple-darwin",
+        "x86_64-apple-darwin",
+        "x86_64-unknown-linux-gnu"
+      ],
+      "install_hint": "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/axolotlsay/releases/download/v0.1.0/axolotlsay-installer.sh | sh",
+      "description": "Install prebuilt binaries via shell script"
+    },
+    "axolotlsay-npm-package.tar.gz": {
+      "name": "axolotlsay-npm-package.tar.gz",
+      "kind": "installer",
+      "target_triples": [
+        "aarch64-apple-darwin",
+        "x86_64-apple-darwin",
+        "x86_64-pc-windows-msvc",
+        "x86_64-unknown-linux-gnu"
+      ],
+      "assets": [
+        {
+          "name": ".gitignore",
+          "path": ".gitignore",
+          "kind": "unknown"
+        },
+        {
+          "name": "CHANGELOG.md",
+          "path": "CHANGELOG.md",
+          "kind": "changelog"
+        },
+        {
+          "name": "LICENSE-APACHE",
+          "path": "LICENSE-APACHE",
+          "kind": "license"
+        },
+        {
+          "name": "LICENSE-MIT",
+          "path": "LICENSE-MIT",
+          "kind": "license"
+        },
+        {
+          "name": "README.md",
+          "path": "README.md",
+          "kind": "readme"
+        },
+        {
+          "name": "binary.js",
+          "path": "binary.js",
+          "kind": "unknown"
+        },
+        {
+          "name": "install.js",
+          "path": "install.js",
+          "kind": "unknown"
+        },
+        {
+          "name": "npm-shrinkwrap.json",
+          "path": "npm-shrinkwrap.json",
+          "kind": "unknown"
+        },
+        {
+          "name": "package.json",
+          "path": "package.json",
+          "kind": "unknown"
+        },
+        {
+          "name": "run.js",
+          "path": "run.js",
+          "kind": "unknown"
+        }
+      ],
+      "install_hint": "npm install axolotlsay@0.1.0",
+      "description": "Install prebuilt binaries into your npm project"
+    },
+    "axolotlsay-x86_64-apple-darwin.tar.gz": {
+      "name": "axolotlsay-x86_64-apple-darwin.tar.gz",
+      "kind": "executable-zip",
+      "target_triples": [
+        "x86_64-apple-darwin"
+      ],
+      "assets": [
+        {
+          "name": "CHANGELOG.md",
+          "path": "CHANGELOG.md",
+          "kind": "changelog"
+        },
+        {
+          "name": "LICENSE-APACHE",
+          "path": "LICENSE-APACHE",
+          "kind": "license"
+        },
+        {
+          "name": "LICENSE-MIT",
+          "path": "LICENSE-MIT",
+          "kind": "license"
+        },
+        {
+          "name": "README.md",
+          "path": "README.md",
+          "kind": "readme"
+        },
+        {
+          "name": "axolotlsay",
+          "path": "axolotlsay",
+          "kind": "executable"
+        }
+      ],
+      "checksum": "axolotlsay-x86_64-apple-darwin.tar.gz.sha256"
+    },
+    "axolotlsay-x86_64-apple-darwin.tar.gz.sha256": {
+      "name": "axolotlsay-x86_64-apple-darwin.tar.gz.sha256",
+      "kind": "checksum",
+      "target_triples": [
+        "x86_64-apple-darwin"
+      ]
+    },
+    "axolotlsay-x86_64-pc-windows-msvc.tar.gz": {
+      "name": "axolotlsay-x86_64-pc-windows-msvc.tar.gz",
+      "kind": "executable-zip",
+      "target_triples": [
+        "x86_64-pc-windows-msvc"
+      ],
+      "assets": [
+        {
+          "name": "CHANGELOG.md",
+          "path": "CHANGELOG.md",
+          "kind": "changelog"
+        },
+        {
+          "name": "LICENSE-APACHE",
+          "path": "LICENSE-APACHE",
+          "kind": "license"
+        },
+        {
+          "name": "LICENSE-MIT",
+          "path": "LICENSE-MIT",
+          "kind": "license"
+        },
+        {
+          "name": "README.md",
+          "path": "README.md",
+          "kind": "readme"
+        },
+        {
+          "name": "axolotlsay",
+          "path": "axolotlsay.exe",
+          "kind": "executable"
+        }
+      ],
+      "checksum": "axolotlsay-x86_64-pc-windows-msvc.tar.gz.sha256"
+    },
+    "axolotlsay-x86_64-pc-windows-msvc.tar.gz.sha256": {
+      "name": "axolotlsay-x86_64-pc-windows-msvc.tar.gz.sha256",
+      "kind": "checksum",
+      "target_triples": [
+        "x86_64-pc-windows-msvc"
+      ]
+    },
+    "axolotlsay-x86_64-unknown-linux-gnu.tar.gz": {
+      "name": "axolotlsay-x86_64-unknown-linux-gnu.tar.gz",
+      "kind": "executable-zip",
+      "target_triples": [
+        "x86_64-unknown-linux-gnu"
+      ],
+      "assets": [
+        {
+          "name": "CHANGELOG.md",
+          "path": "CHANGELOG.md",
+          "kind": "changelog"
+        },
+        {
+          "name": "LICENSE-APACHE",
+          "path": "LICENSE-APACHE",
+          "kind": "license"
+        },
+        {
+          "name": "LICENSE-MIT",
+          "path": "LICENSE-MIT",
+          "kind": "license"
+        },
+        {
+          "name": "README.md",
+          "path": "README.md",
+          "kind": "readme"
+        },
+        {
+          "name": "axolotlsay",
+          "path": "axolotlsay",
+          "kind": "executable"
+        }
+      ],
+      "checksum": "axolotlsay-x86_64-unknown-linux-gnu.tar.gz.sha256"
+    },
+    "axolotlsay-x86_64-unknown-linux-gnu.tar.gz.sha256": {
+      "name": "axolotlsay-x86_64-unknown-linux-gnu.tar.gz.sha256",
+      "kind": "checksum",
+      "target_triples": [
+        "x86_64-unknown-linux-gnu"
+      ]
+    },
+    "axolotlsay.rb": {
+      "name": "axolotlsay.rb",
+      "kind": "installer",
+      "target_triples": [
+        "aarch64-apple-darwin",
+        "x86_64-apple-darwin"
+      ],
+      "install_hint": "brew install axodotdev/homebrew-packages/axolotlsay",
+      "description": "Install prebuilt binaries via Homebrew"
+    }
+  }
+}
+
+================ github-ci.yml ================
+# Copyright 2022-2023, axodotdev
+# SPDX-License-Identifier: MIT or Apache-2.0
+#
+# CI that:
+#
+# * checks for a Git Tag that looks like a release
+# * creates a Github Release™ and fills in its text
+# * builds artifacts with cargo-dist (executable-zips, installers)
+# * uploads those artifacts to the Github Release™
+#
+# Note that the Github Release™ will be created before the artifacts,
+# so there will be a few minutes where the release has no artifacts
+# and then they will slowly trickle in, possibly failing. To make
+# this more pleasant we mark the release as a "draft" until all
+# artifacts have been successfully uploaded. This allows you to
+# choose what to do with partial successes and avoids spamming
+# anyone with notifications before the release is actually ready.
+name: Release
+
+permissions:
+  contents: write
+
+# This task will run whenever you push a git tag that looks like a version
+# like "v1", "v1.2.0", "v0.1.0-prerelease01", "my-app-v1.0.0", etc.
+# The version will be roughly parsed as ({PACKAGE_NAME}-)?v{VERSION}, where
+# PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION
+# must be a Cargo-style SemVer Version.
+#
+# If PACKAGE_NAME is specified, then we will create a Github Release™ for that
+# package (erroring out if it doesn't have the given version or isn't cargo-dist-able).
+#
+# If PACKAGE_NAME isn't specified, then we will create a Github Release™ for all
+# (cargo-dist-able) packages in the workspace with that version (this is mode is
+# intended for workspaces with only one dist-able package, or with all dist-able
+# packages versioned/released in lockstep).
+#
+# If you push multiple tags at once, separate instances of this workflow will
+# spin up, creating an independent Github Release™ for each one.
+#
+# If there's a prerelease-style suffix to the version then the Github Release™
+# will be marked as a prerelease.
+on:
+  push:
+    tags:
+      - '*-?v[0-9]+*'
+
+jobs:
+  # Create the Github Release™ so the packages have something to be uploaded to
+  create-release:
+    runs-on: ubuntu-latest
+    outputs:
+      has-releases: ${{ steps.create-release.outputs.has-releases }}
+      releases: ${{ steps.create-release.outputs.releases }}
+    env:
+      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          submodules: recursive
+      - name: Install cargo-dist
+        run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/vSOME_VERSION/cargo-dist-installer.sh | sh"
+      - id: create-release
+        run: |
+          cargo dist plan --tag=${{ github.ref_name }} --output-format=json > dist-manifest.json
+          echo "dist plan ran successfully"
+          cat dist-manifest.json
+
+          # We're assuming a draft Github Release™ with the desired name/tag/body already exists
+
+          # Upload the manifest to the Github Release™
+          gh release upload ${{ github.ref_name }} dist-manifest.json
+          echo "uploaded manifest!"
+
+          # Disable all the upload-artifacts tasks if we have no actual releases
+          HAS_RELEASES=$(jq --raw-output ".releases != null" dist-manifest.json)
+          echo "has-releases=$HAS_RELEASES" >> "$GITHUB_OUTPUT"
+          echo "releases=$(jq --compact-output ".releases" dist-manifest.json)" >> "$GITHUB_OUTPUT"
+
+  # Build and packages all the platform-specific things
+  upload-local-artifacts:
+    # Let the initial task tell us to not run (currently very blunt)
+    needs: create-release
+    if: ${{ needs.create-release.outputs.has-releases == 'true' }}
+    strategy:
+      fail-fast: false
+      matrix:
+        # For these target platforms
+        include:
+        - os: "macos-11"
+          dist-args: "--artifacts=local --target=aarch64-apple-darwin"
+          install-dist: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/vSOME_VERSION/cargo-dist-installer.sh | sh"
+        - os: "macos-11"
+          dist-args: "--artifacts=local --target=x86_64-apple-darwin"
+          install-dist: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/vSOME_VERSION/cargo-dist-installer.sh | sh"
+        - os: "windows-2019"
+          dist-args: "--artifacts=local --target=x86_64-pc-windows-msvc"
+          install-dist: "irm  https://github.com/axodotdev/cargo-dist/releases/download/vSOME_VERSION/cargo-dist-installer.ps1 | iex"
+        - os: "ubuntu-20.04"
+          dist-args: "--artifacts=local --target=x86_64-unknown-linux-gnu"
+          install-dist: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/vSOME_VERSION/cargo-dist-installer.sh | sh"
+    runs-on: ${{ matrix.os }}
+    env:
+      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          submodules: recursive
+      - name: Install cargo-dist
+        run: ${{ matrix.install-dist }}
+      - name: Run cargo-dist
+        # This logic is a bit janky because it's trying to be a polyglot between
+        # powershell and bash since this will run on windows, macos, and linux!
+        # The two platforms don't agree on how to talk about env vars but they
+        # do agree on 'cat' and '$()' so we use that to marshal values between commands.
+        run: |
+          # Actually do builds and make zips and whatnot
+          cargo dist build --tag=${{ github.ref_name }} --output-format=json ${{ matrix.dist-args }} > dist-manifest.json
+          echo "dist ran successfully"
+          cat dist-manifest.json
+
+          # Parse out what we just built and upload it to the Github Release™
+          jq --raw-output ".artifacts[]?.path | select( . != null )" dist-manifest.json > uploads.txt
+          echo "uploading..."
+          cat uploads.txt
+          gh release upload ${{ github.ref_name }} $(cat uploads.txt)
+          echo "uploaded!"
+
+  # Build and packages all the platform-agnostic(ish) things
+  upload-global-artifacts:
+    needs: upload-local-artifacts
+    runs-on: "ubuntu-20.04"
+    env:
+      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          submodules: recursive
+      - name: Install cargo-dist
+        run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/vSOME_VERSION/cargo-dist-installer.sh | sh"
+      # Get all the local artifacts for the global tasks to use (for e.g. checksums)
+      - name: Fetch local artifacts
+        run: |
+          gh release download ${{ github.ref_name }} --dir target/distrib/
+      - name: Run cargo-dist
+        # This logic is a bit janky because it's trying to be a polyglot between
+        # powershell and bash since this will run on windows, macos, and linux!
+        # The two platforms don't agree on how to talk about env vars but they
+        # do agree on 'cat' and '$()' so we use that to marshal values between commands.
+        run: |
+          cargo dist build --tag=${{ github.ref_name }} --output-format=json "--artifacts=global" > dist-manifest.json
+          echo "dist ran successfully"
+          cat dist-manifest.json
+
+          # Parse out what we just built and upload it to the Github Release™
+          jq --raw-output ".artifacts[]?.path | select( . != null )" dist-manifest.json > uploads.txt
+          echo "uploading..."
+          cat uploads.txt
+          gh release upload ${{ github.ref_name }} $(cat uploads.txt)
+          echo "uploaded!"
+
+  upload-homebrew-formula:
+    needs: [create-release, upload-global-artifacts]
+    runs-on: "ubuntu-20.04"
+    env:
+      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+      RELEASES: ${{ needs.create-release.outputs.releases }}
+      GITHUB_USER: "axo bot"
+      GITHUB_EMAIL: "admin+bot@axo.dev"
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          repository: "axodotdev/homebrew-packages"
+          token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
+      # So we have access to the formula
+      - name: Fetch local artifacts
+        run: |
+          gh release download ${{ github.ref_name }} --dir Formula --repo ${GITHUB_REPOSITORY} --clobber
+      - name: Commit formula files
+        run: |
+          git config --global user.name "${GITHUB_USER}"
+          git config --global user.email "${GITHUB_EMAIL}"
+
+          for release in $(echo "$RELEASES" | jq --compact-output '.[]'); do
+            name=$(echo "$release" | jq .app_name --raw-output)
+            version=$(echo "$release" | jq .app_version --raw-output)
+
+            git add Formula/${name}.rb
+            git commit -m "${name} ${version}"
+          done
+          git push
+
+  # Mark the Github Release™ as a non-draft now that everything has succeeded!
+  publish-release:
+    # Only run after all the other tasks, but it's ok if upload-artifacts was skipped
+    needs: [create-release, upload-local-artifacts, upload-global-artifacts]
+    if: ${{ always() && needs.create-release.result == 'success' && (needs.upload-local-artifacts.result == 'skipped' || needs.upload-local-artifacts.result == 'success') && (needs.upload-global-artifacts.result == 'skipped' || needs.upload-global-artifacts.result == 'success') }}
+    runs-on: ubuntu-latest
+    env:
+      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+    steps:
+      - uses: actions/checkout@v3
+        with:
+          submodules: recursive
+      - name: mark release as non-draft
+        run: |
+          gh release edit ${{ github.ref_name }} --draft=false
+
+
diff --git a/cargo-dist/tests/snapshots/axolotlsay_no_homebrew_publish.snap b/cargo-dist/tests/snapshots/axolotlsay_no_homebrew_publish.snap
index a14084b8..9f49e20b 100644
--- a/cargo-dist/tests/snapshots/axolotlsay_no_homebrew_publish.snap
+++ b/cargo-dist/tests/snapshots/axolotlsay_no_homebrew_publish.snap
@@ -2144,14 +2144,14 @@ permissions:
 # package (erroring out if it doesn't have the given version or isn't cargo-dist-able).
 #
 # If PACKAGE_NAME isn't specified, then we will create a Github Release™ for all
-# (cargo-dist-able) packages in the workspace with that version (this is mode is
+# (cargo-dist-able) packages in the workspace with that version (this mode is
 # intended for workspaces with only one dist-able package, or with all dist-able
 # packages versioned/released in lockstep).
 #
 # If you push multiple tags at once, separate instances of this workflow will
 # spin up, creating an independent Github Release™ for each one.
 #
-# If there's a prerelease-style suffix to the version then the Github Release™
+# If there's a prerelease-style suffix to the version, then the Github Release™
 # will be marked as a prerelease.
 on:
   push:
@@ -2244,7 +2244,7 @@ jobs:
           gh release upload ${{ github.ref_name }} $(cat uploads.txt)
           echo "uploaded!"
 
-  # Build and packages all the platform-agnostic(ish) things
+  # Build and package all the platform-agnostic(ish) things
   upload-global-artifacts:
     needs: upload-local-artifacts
     runs-on: "ubuntu-20.04"
diff --git a/cargo-dist/tests/snapshots/long-help.snap b/cargo-dist/tests/snapshots/long-help.snap
index e64ea882..bb176e04 100644
--- a/cargo-dist/tests/snapshots/long-help.snap
+++ b/cargo-dist/tests/snapshots/long-help.snap
@@ -76,7 +76,7 @@ GLOBAL OPTIONS:
           
           The currently accepted formats are "v{VERSION}" and "{PACKAGE_NAME}-v{VERSION}" ("v1.0.0", "v0.1.0-prerelease1", "my-app-v1.0.0", etc).
           
-          If you use the prefixed version then we will only Announce/Release that package's apps (and return an error if that is not in fact the package's current version). This is approp
+          If you use the prefixed version then we will only Announce/Release that package's apps (and return an error if that is not in fact the package's current version). This is appropriate for workspaces that have more than one app.
           
           If you use the unprefixed version then we will assume you're Announcing/Releasing all packages in the workspace that have that version. This is appropriate for workspaces that only have one app, or for monorepos that version all their apps in lockstep.
           
diff --git a/cargo-dist/tests/snapshots/markdown-help.snap b/cargo-dist/tests/snapshots/markdown-help.snap
index 7de8a1f2..80576344 100644
--- a/cargo-dist/tests/snapshots/markdown-help.snap
+++ b/cargo-dist/tests/snapshots/markdown-help.snap
@@ -78,7 +78,7 @@ This tag serves two purposes: defining which apps we are Announcing new Releases
 
 The currently accepted formats are "v{VERSION}" and "{PACKAGE_NAME}-v{VERSION}" ("v1.0.0", "v0.1.0-prerelease1", "my-app-v1.0.0", etc).
 
-If you use the prefixed version then we will only Announce/Release that package's apps (and return an error if that is not in fact the package's current version). This is approp
+If you use the prefixed version then we will only Announce/Release that package's apps (and return an error if that is not in fact the package's current version). This is appropriate for workspaces that have more than one app.
 
 If you use the unprefixed version then we will assume you're Announcing/Releasing all packages in the workspace that have that version. This is appropriate for workspaces that only have one app, or for monorepos that version all their apps in lockstep.