diff --git a/.github/workflows/actions_release.yml b/.github/workflows/actions_release.yml
new file mode 100644
index 0000000..8e484ee
--- /dev/null
+++ b/.github/workflows/actions_release.yml
@@ -0,0 +1,22 @@
+name: Release GitHub Actions
+
+on:
+ workflow_dispatch:
+ inputs:
+ tag:
+ description: "Tag for the release"
+ required: true
+
+permissions:
+ contents: read
+
+jobs:
+ release:
+ permissions:
+ actions: read
+ id-token: write
+ contents: write
+
+ uses: step-security/reusable-workflows/.github/workflows/actions_release.yaml@v1
+ with:
+ tag: "${{ github.event.inputs.tag }}"
diff --git a/.github/workflows/auto_cherry_pick.yml b/.github/workflows/auto_cherry_pick.yml
new file mode 100644
index 0000000..3845638
--- /dev/null
+++ b/.github/workflows/auto_cherry_pick.yml
@@ -0,0 +1,37 @@
+name: Auto Cherry-Pick from Upstream
+
+on:
+ workflow_run:
+ workflows: [ "Release GitHub Actions" ]
+ types:
+ - completed
+
+ workflow_dispatch:
+ inputs:
+ base_branch:
+ description: "Base branch to create the PR against"
+ required: true
+ default: "main"
+ mode:
+ description: "Run mode: cherry-pick or verify"
+ required: false
+ default: "cherry-pick"
+
+ pull_request:
+ types: [ opened, synchronize, labeled ]
+
+permissions:
+ contents: write
+ pull-requests: write
+ packages: read
+ issues: write
+
+jobs:
+ cherry-pick:
+ if: (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') || github.event_name == 'workflow_dispatch' || contains(fromJson(toJson(github.event.pull_request.labels)).*.name, 'review-required')
+ uses: step-security/reusable-workflows/.github/workflows/auto_cherry_pick.yaml@v1
+ with:
+ original-owner: "taiki-e"
+ repo-name: "upload-rust-binary-action"
+ base_branch: ${{ inputs.base_branch || 'main' }}
+ mode: ${{ github.event_name == 'pull_request' && 'verify' || inputs.mode || 'cherry-pick' }}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..504de96
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,214 @@
+name: CI
+
+permissions: {}
+
+on:
+ pull_request:
+ push:
+ branches:
+ - main
+ workflow_dispatch:
+
+env:
+ CARGO_INCREMENTAL: 0
+ CARGO_NET_GIT_FETCH_WITH_CLI: true
+ CARGO_NET_RETRY: 10
+ CARGO_TERM_COLOR: always
+ RUST_BACKTRACE: 1
+ RUSTFLAGS: -D warnings
+ RUSTUP_MAX_RETRIES: 10
+
+defaults:
+ run:
+ shell: bash --noprofile --norc -CeEuxo pipefail {0}
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
+ cancel-in-progress: true
+
+jobs:
+ # TODO: test container
+ test:
+ if: github.actor != 'dependabot[bot]'
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - os: ubuntu-24.04
+ - os: ubuntu-24.04-arm
+ - os: ubuntu-24.04
+ target: aarch64-unknown-linux-gnu
+ package: test-crate
+ - os: ubuntu-24.04
+ target: aarch64-unknown-linux-gnu
+ build-tool: cargo
+ package: test-crate,test-crate
+ - os: ubuntu-24.04
+ target: x86_64-unknown-linux-gnu.2.17
+ build-tool: cargo-zigbuild
+ - os: macos-latest
+ checksums: sha256,sha512,sha1,md5
+ - os: macos-latest
+ checksums: |
+ sha256
+ sha512
+ sha1
+ md5
+ target: x86_64-apple-darwin
+ workspace: true
+ - os: macos-latest
+ checksums: sha256 sha512 sha1 md5
+ target: aarch64-apple-darwin
+ - os: macos-latest
+ checksums: sha256, sha512, sha1, md5
+ target: universal-apple-darwin
+ - os: macos-latest
+ checksums: sha256,sha512,sha1,md5
+ target: universal-apple-darwin
+ build-tool: cargo
+ - os: windows-latest
+ - os: windows-latest
+ target: x86_64-pc-windows-gnu
+ - os: windows-latest
+ target: aarch64-pc-windows-msvc
+ - os: windows-11-arm
+ runs-on: ${{ matrix.os || 'ubuntu-24.04' }}
+ timeout-minutes: 60
+ steps:
+ - uses: taiki-e/checkout-action@v1
+ - uses: taiki-e/install-action@v2
+ with:
+ tool: rust@stable
+ fallback: none
+ - uses: step-security/setup-cross-toolchain-action@main # main post release
+ with:
+ target: ${{ matrix.target }}
+ runner: none
+ if: matrix.target != '' && matrix.build-tool == 'cargo'
+ - run: cargo new --bin test-crate
+ - uses: ./
+ id: upload-rust-binary-action
+ with:
+ dry-run: true
+ bin: test-crate
+ target: ${{ matrix.target }}
+ build-tool: ${{ matrix.build-tool }}
+ no-default-features: true
+ all-features: true
+ package: ${{ matrix.package }}
+ workspace: ${{ matrix.workspace || 'false' }}
+ checksum: ${{ matrix.checksums || 'b2,sha256,sha512,sha1,md5' }}
+ tar: all
+ tar-xz: all
+ zip: all
+ manifest-path: test-crate/Cargo.toml
+ codesign: '-'
+ codesign-prefix: 'com.example.'
+ codesign-options: 'runtime'
+ - name: Check action outputs
+ run: |
+ printf 'outputs.archive should not be empty\n'
+ test -n "${OUTPUT_ARCHIVE}"
+
+ printf 'outputs.zip should be a file\n'
+ test -f "${OUTPUT_ZIP}"
+
+ printf 'outputs.tar should be a file\n'
+ test -f "${OUTPUT_TAR}"
+
+ printf 'outputs.tar-xz should be a file\n'
+ test -f "${OUTPUT_TAR_XZ}"
+
+ printf 'outputs.sha256 should be a file\n'
+ test -f "${OUTPUT_SHA256}"
+
+ printf 'outputs.sha512 should be a file\n'
+ test -f "${OUTPUT_SHA512}"
+
+ printf 'outputs.sha1 should be a file\n'
+ test -f "${OUTPUT_SHA1}"
+
+ printf 'outputs.md5 should be a file\n'
+ test -f "${OUTPUT_MD5}"
+ env:
+ OUTPUT_ARCHIVE: ${{ steps.upload-rust-binary-action.outputs.archive }}
+ OUTPUT_ZIP: ${{ steps.upload-rust-binary-action.outputs.zip }}
+ OUTPUT_TAR: ${{ steps.upload-rust-binary-action.outputs.tar }}
+ OUTPUT_TAR_XZ: ${{ steps.upload-rust-binary-action.outputs.tar-xz }}
+ OUTPUT_SHA256: ${{ steps.upload-rust-binary-action.outputs.sha256 }}
+ OUTPUT_SHA512: ${{ steps.upload-rust-binary-action.outputs.sha512 }}
+ OUTPUT_SHA1: ${{ steps.upload-rust-binary-action.outputs.sha1 }}
+ OUTPUT_MD5: ${{ steps.upload-rust-binary-action.outputs.md5 }}
+ - name: Check b2 output
+ if: ${{ contains(matrix.checksums || 'b2,sha256,sha512,sha1,md5', 'b2') }}
+ run: |
+ printf 'outputs.b2 should not be empty\n'
+ test -n "${OUTPUT_B2}"
+ env:
+ OUTPUT_B2: ${{ steps.upload-rust-binary-action.outputs.b2 }}
+
+ test-dependabot:
+ if: github.actor == 'dependabot[bot]'
+ runs-on: ubuntu-24.04
+ timeout-minutes: 60
+ steps:
+ - uses: taiki-e/checkout-action@v1
+ - uses: taiki-e/install-action@v2
+ with:
+ tool: rust@stable
+ fallback: none
+ - run: cargo new --bin test-crate
+ - uses: ./
+ id: upload-rust-binary-action
+ with:
+ dry-run: true
+ bin: test-crate
+ workspace: 'false'
+ checksum: 'b2,sha256,sha512,sha1,md5'
+ tar: all
+ tar-xz: all
+ zip: all
+ manifest-path: test-crate/Cargo.toml
+ codesign: '-'
+ codesign-prefix: 'com.example.'
+ codesign-options: 'runtime'
+ - name: Check action outputs
+ run: |
+ printf 'outputs.archive should not be empty\n'
+ test -n "${OUTPUT_ARCHIVE}"
+
+ printf 'outputs.zip should be a file\n'
+ test -f "${OUTPUT_ZIP}"
+
+ printf 'outputs.tar should be a file\n'
+ test -f "${OUTPUT_TAR}"
+
+ printf 'outputs.tar-xz should be a file\n'
+ test -f "${OUTPUT_TAR_XZ}"
+
+ printf 'outputs.sha256 should be a file\n'
+ test -f "${OUTPUT_SHA256}"
+
+ printf 'outputs.sha512 should be a file\n'
+ test -f "${OUTPUT_SHA512}"
+
+ printf 'outputs.sha1 should be a file\n'
+ test -f "${OUTPUT_SHA1}"
+
+ printf 'outputs.md5 should be a file\n'
+ test -f "${OUTPUT_MD5}"
+ env:
+ OUTPUT_ARCHIVE: ${{ steps.upload-rust-binary-action.outputs.archive }}
+ OUTPUT_ZIP: ${{ steps.upload-rust-binary-action.outputs.zip }}
+ OUTPUT_TAR: ${{ steps.upload-rust-binary-action.outputs.tar }}
+ OUTPUT_TAR_XZ: ${{ steps.upload-rust-binary-action.outputs.tar-xz }}
+ OUTPUT_SHA256: ${{ steps.upload-rust-binary-action.outputs.sha256 }}
+ OUTPUT_SHA512: ${{ steps.upload-rust-binary-action.outputs.sha512 }}
+ OUTPUT_SHA1: ${{ steps.upload-rust-binary-action.outputs.sha1 }}
+ OUTPUT_MD5: ${{ steps.upload-rust-binary-action.outputs.md5 }}
+ - name: Check b2 output
+ run: |
+ printf 'outputs.b2 should not be empty\n'
+ test -n "${OUTPUT_B2}"
+ env:
+ OUTPUT_B2: ${{ steps.upload-rust-binary-action.outputs.b2 }}
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..1d59427
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2026 StepSecurity
+
+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.
diff --git a/README.md b/README.md
index c2df69a..f9ff1de 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,746 @@
-# upload-rust-binary-action
\ No newline at end of file
+[](https://docs.stepsecurity.io/actions/stepsecurity-maintained-actions)
+
+# upload-rust-binary-action
+
+[](https://github.com/step-security/upload-rust-binary-action/releases/latest)
+
+GitHub Action for building and uploading Rust binary to GitHub Releases.
+
+- [Usage](#usage)
+ - [Inputs](#inputs)
+ - [Outputs](#outputs)
+ - [Example workflow: Basic usage](#example-workflow-basic-usage)
+ - [Example workflow: Basic usage (multiple platforms)](#example-workflow-basic-usage-multiple-platforms)
+ - [Example workflow: Customize archive name](#example-workflow-customize-archive-name)
+ - [Example workflow: Build with different features on different platforms](#example-workflow-build-with-different-features-on-different-platforms)
+ - [Example workflow: Cross-compilation](#example-workflow-cross-compilation)
+ - [cross](#cross)
+ - [setup-cross-toolchain-action](#setup-cross-toolchain-action)
+ - [cargo-zigbuild](#cargo-zigbuild)
+ - [Example workflow: Include additional files](#example-workflow-include-additional-files)
+ - [Optimize Rust binary](#optimize-rust-binary)
+- [Supported events](#supported-events)
+- [Security](#security)
+- [Compatibility](#compatibility)
+- [License](#license)
+
+## Usage
+
+This action builds and uploads Rust binary that specified by `bin` option to
+GitHub Releases.
+
+Currently, this action is basically intended to be used in combination with an action like [create-gh-release-action] that creates a GitHub release when a tag is pushed. See also [supported events](#supported-events).
+
+### Inputs
+
+| Name | Required | Description | Type | Default |
+| ---- | :------: | ----------- | ---- | ------- |
+| bin | **✓** | Binary names (non-extension portion of filename) to build and upload (whitespace or comma separated list) | String | |
+| token | | GitHub token for uploading assets to GitHub Releases (see [action.yml](action.yml) for more) | String | `${{ github.token }}` |
+| archive | | Archive name (non-extension portion of filename) to be uploaded | String | `$bin-$target` |
+| target | \[1] | Target triple, default is host triple | String | (host triple) |
+| features | | Cargo build features to enable (space or comma separated list) | String | |
+| no-default-features | | Whether to disable cargo build default features | Boolean | `false` |
+| all-features | | Whether to build with `--all-features` flag | Boolean | `false` |
+| package | | Package names to build (whitespace or comma separated list) | String | |
+| workspace | | Whether to build with `--workspace` flag | Boolean | `false` |
+| locked | | Whether to build with `--locked` flag | Boolean | `false` |
+| tar | | On which platform to distribute the `.tar.gz` file (all, unix, windows, or none) | String | `unix` |
+| tar-xz | | On which platform to distribute the `.tar.xz` file (all, unix, windows, or none) | String | `none` |
+| zip | | On which platform to distribute the `.zip` file (all, unix, windows, or none) | String | `windows` |
+| checksum | | Algorithms to be used for checksum (sha256, sha512, b2, sha1, or md5) (whitespace or comma separated list).
Note: b2 is not available by default on macOS, install `b2sum` to use it. sha1 and md5 are insecure and strongly discouraged. | String | |
+| include | | Additional files to be included to the archive (whitespace or comma separated list) | String | |
+| asset | | Additional files to be uploaded separately (whitespace or comma separated list) | String | |
+| leading-dir | | Whether to create the leading directory in the archive or not | Boolean | `false` |
+| bin-leading-dir | | Create extra leading directory(s) for binary file(s) specified by `bin` option | String | |
+| build-tool | | Tool to build binaries (cargo, cross, or cargo-zigbuild, see [cross-compilation example](#example-workflow-cross-compilation) for more) | String | |
+| ref | | Fully-formed tag ref for this release (see [action.yml](action.yml) for more) | String | |
+| manifest-path | | Path to Cargo.toml | String | `Cargo.toml` |
+| profile | | The cargo profile to build. This defaults to the release profile. | String | `release` |
+| dry-run | | Build and compress binaries, but do not upload them (see [action.yml](action.yml) for more) | Boolean | `false` |
+| dry-run-intended | | Suppress informational `dry-run` warnings, keeping the rest | Boolean | `false` |
+| codesign | | Sign build products using `codesign` on macOS | String | |
+| codesign-prefix | | Prefix for the `codesign` identifier on macOS | String | |
+| codesign-options | | Specifies a set of option flags to be embedded in the code signature on macOS. See the `codesign` manpage for details. | String | |
+
+\[1] This is optional but it is recommended that this always be set to clarify which target you are building for if macOS is included in the matrix because GitHub Actions changed the default architecture of macos-latest since macos-14.
+
+(Previously, option names were only in "snake_case", but now both "kebab-case" and "snake_case" are available.)
+
+### Outputs
+
+| Name | Description |
+| ---- | ----------- |
+| archive | Archive base name. |
+| zip | `.zip` archive file name. |
+| tar | `.tar.gz` archive file name. |
+| tar-xz | `.tar.xz` archive file name. |
+| sha256 | SHA256 checksum file name. |
+| sha512 | SHA512 checksum file name. |
+| b2 | BLAKE2 checksum file name. |
+| sha1 | SHA1 checksum file name. |
+| md5 | MD5 checksum file name. |
+
+### Example workflow: Basic usage
+
+In this example, when a new tag is pushed, creating a new GitHub Release by
+using [create-gh-release-action], then uploading Rust binary to the created
+GitHub Release.
+
+An archive file with a name like `$bin-$target.tar.gz` will be uploaded to
+GitHub Release.
+
+```yaml
+name: Release
+
+permissions:
+ contents: read
+
+on:
+ push:
+ tags:
+ - v[0-9]+.*
+
+jobs:
+ create-release:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write # for taiki-e/create-gh-release-action
+ steps:
+ - uses: actions/checkout@v7
+ - uses: taiki-e/create-gh-release-action@v1
+ with:
+ # (optional) Path to changelog.
+ changelog: CHANGELOG.md
+
+ upload-assets:
+ needs: create-release
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write # for step-security/upload-rust-binary-action
+ steps:
+ - uses: actions/checkout@v7
+ - uses: step-security/upload-rust-binary-action@v1
+ with:
+ # (required) Binary names (non-extension portion of filename) to build and upload (whitespace or comma separated list).
+ # Note that glob pattern is not supported yet.
+ bin: ...
+```
+
+You can specify multiple binaries when the root manifest is a virtual manifest or specified binaries are in the same crate.
+
+```yaml
+- uses: step-security/upload-rust-binary-action@v1
+ with:
+ # (required) Binary names (non-extension portion of filename) to build and upload (whitespace or comma separated list).
+ # Note that glob pattern is not supported yet.
+ bin: app1,app2
+ # (optional) Archive name (non-extension portion of filename) to be uploaded.
+ # [default value: $bin-$target]
+ # [possible values: the following variables and any string]
+ # variables:
+ # - $bin - Binary name (non-extension portion of filename).
+ # - $target - Target triple.
+ # - $tag - Tag of this release.
+ # When multiple binary names are specified, default archive name or $bin variable cannot be used.
+ archive: app-$target
+```
+
+### Example workflow: Basic usage (multiple platforms)
+
+This action supports Linux, macOS, and Windows as a host OS and supports
+binaries for various targets.
+
+See also [cross-compilation example](#example-workflow-cross-compilation).
+
+```yaml
+name: Release
+
+permissions:
+ contents: read
+
+on:
+ push:
+ tags:
+ - v[0-9]+.*
+
+jobs:
+ create-release:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write # for taiki-e/create-gh-release-action
+ steps:
+ - uses: actions/checkout@v7
+ - uses: taiki-e/create-gh-release-action@v1
+ with:
+ # (optional) Path to changelog.
+ changelog: CHANGELOG.md
+
+ upload-assets:
+ needs: create-release
+ strategy:
+ matrix:
+ include:
+ - target: x86_64-unknown-linux-gnu
+ os: ubuntu-latest
+ - target: x86_64-apple-darwin
+ os: macos-latest
+ - target: x86_64-pc-windows-msvc
+ os: windows-latest
+ runs-on: ${{ matrix.os }}
+ permissions:
+ contents: write # for step-security/upload-rust-binary-action
+ steps:
+ - uses: actions/checkout@v7
+ - uses: step-security/upload-rust-binary-action@v1
+ with:
+ # (required) Binary names (non-extension portion of filename) to build and upload (whitespace or comma separated list).
+ # Note that glob pattern is not supported yet.
+ bin: ...
+ # (optional) Target triple, default is host triple.
+ # This is optional but it is recommended that this always be set to
+ # clarify which target you are building for if macOS is included in
+ # the matrix because GitHub Actions changed the default architecture
+ # of macos-latest since macos-14.
+ target: ${{ matrix.target }}
+ # (optional) On which platform to distribute the `.tar.gz` file.
+ # [default value: unix]
+ # [possible values: all, unix, windows, none]
+ tar: unix
+ # (optional) On which platform to distribute the `.zip` file.
+ # [default value: windows]
+ # [possible values: all, unix, windows, none]
+ zip: windows
+```
+
+### Example workflow: Customize archive name
+
+By default, this action will upload an archive file with a name like
+`$bin-$target.$extension`.
+
+You can customize archive name by `archive` option.
+
+```yaml
+name: Release
+
+permissions:
+ contents: read
+
+on:
+ push:
+ tags:
+ - v[0-9]+.*
+
+jobs:
+ create-release:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write # for taiki-e/create-gh-release-action
+ steps:
+ - uses: actions/checkout@v7
+ - uses: taiki-e/create-gh-release-action@v1
+ with:
+ # (optional) Path to changelog.
+ changelog: CHANGELOG.md
+
+ upload-assets:
+ needs: create-release
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write # for step-security/upload-rust-binary-action
+ steps:
+ - uses: actions/checkout@v7
+ - uses: step-security/upload-rust-binary-action@v1
+ with:
+ bin: ...
+ # (optional) Archive name (non-extension portion of filename) to be uploaded.
+ # [default value: $bin-$target]
+ # [possible values: the following variables and any string]
+ # variables:
+ # - $bin - Binary name (non-extension portion of filename).
+ # - $target - Target triple.
+ # - $tag - Tag of this release.
+ # When multiple binary names are specified, default archive name or $bin variable cannot be used.
+ archive: $bin-$tag-$target
+```
+
+### Example workflow: Build with different features on different platforms
+
+This action enables the `systemd` and `io_uring` features for Linux, and leave macOS, and Windows with default set of features.
+
+```yaml
+name: Release
+
+permissions:
+ contents: read
+
+on:
+ push:
+ tags:
+ - v[0-9]+.*
+
+jobs:
+ create-release:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write # for taiki-e/create-gh-release-action
+ steps:
+ - uses: actions/checkout@v7
+ - uses: taiki-e/create-gh-release-action@v1
+ with:
+ # (optional) Path to changelog.
+ changelog: CHANGELOG.md
+
+ upload-assets:
+ needs: create-release
+ strategy:
+ matrix:
+ include:
+ - target: x86_64-unknown-linux-gnu
+ os: ubuntu-latest
+ features: systemd,io_uring
+ - target: x86_64-apple-darwin
+ os: macos-latest
+ - target: x86_64-pc-windows-msvc
+ os: windows-latest
+ runs-on: ${{ matrix.os }}
+ permissions:
+ contents: write # for step-security/upload-rust-binary-action
+ steps:
+ - uses: actions/checkout@v7
+ - uses: step-security/upload-rust-binary-action@v1
+ with:
+ # (required) Binary names (non-extension portion of filename) to build and upload (whitespace or comma separated list).
+ # Note that glob pattern is not supported yet.
+ bin: ...
+ # (optional) Target triple, default is host triple.
+ target: ${{ matrix.target }}
+ # (optional) On which platform to distribute the `.tar.gz` file.
+ # [default value: unix]
+ # [possible values: all, unix, windows, none]
+ tar: unix
+ # (optional) On which platform to distribute the `.zip` file.
+ # [default value: windows]
+ # [possible values: all, unix, windows, none]
+ zip: windows
+ # (optional) Build with the given set of features if any.
+ features: ${{ matrix.features || '' }}
+```
+
+### Example workflow: Cross-compilation
+
+#### cross
+
+By default, this action uses [cross] for cross-compilation (if cross supports that target). In the following example, only aarch64-unknown-linux-gnu uses cross, the rest use cargo.
+
+If cross is not installed, this action calls `cargo install cross --locked` to install cross. If you want to speed up the installation of cross or use an older version of cross, consider using [install-action].
+
+```yaml
+name: Release
+
+permissions:
+ contents: read
+
+on:
+ push:
+ tags:
+ - v[0-9]+.*
+
+jobs:
+ create-release:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write # for taiki-e/create-gh-release-action
+ steps:
+ - uses: actions/checkout@v7
+ - uses: taiki-e/create-gh-release-action@v1
+ with:
+ # (optional) Path to changelog.
+ changelog: CHANGELOG.md
+
+ upload-assets:
+ needs: create-release
+ strategy:
+ matrix:
+ include:
+ - target: aarch64-unknown-linux-gnu
+ os: ubuntu-latest
+ - target: aarch64-apple-darwin
+ os: macos-latest
+ - target: x86_64-unknown-linux-gnu
+ os: ubuntu-latest
+ - target: x86_64-apple-darwin
+ os: macos-latest
+ # Universal macOS binary is supported as universal-apple-darwin.
+ - target: universal-apple-darwin
+ os: macos-latest
+ runs-on: ${{ matrix.os }}
+ permissions:
+ contents: write # for step-security/upload-rust-binary-action
+ steps:
+ - uses: actions/checkout@v7
+ - uses: step-security/upload-rust-binary-action@v1
+ with:
+ # (required) Binary names (non-extension portion of filename) to build and upload (whitespace or comma separated list).
+ # Note that glob pattern is not supported yet.
+ bin: ...
+ # (optional) Target triple, default is host triple.
+ target: ${{ matrix.target }}
+```
+
+#### setup-cross-toolchain-action
+
+However, if the host has another cross-compilation setup, it will be respected.
+The following is an example using [setup-cross-toolchain-action]. In this example, this action uses cargo for all targets.
+
+```yaml
+name: Release
+
+permissions:
+ contents: read
+
+on:
+ push:
+ tags:
+ - v[0-9]+.*
+
+jobs:
+ create-release:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write # for taiki-e/create-gh-release-action
+ steps:
+ - uses: actions/checkout@v7
+ - uses: taiki-e/create-gh-release-action@v1
+ with:
+ # (optional) Path to changelog.
+ changelog: CHANGELOG.md
+
+ upload-assets:
+ needs: create-release
+ strategy:
+ matrix:
+ include:
+ - target: aarch64-unknown-linux-gnu
+ os: ubuntu-latest
+ - target: aarch64-apple-darwin
+ os: macos-latest
+ - target: x86_64-unknown-linux-gnu
+ os: ubuntu-latest
+ - target: x86_64-apple-darwin
+ os: macos-latest
+ runs-on: ${{ matrix.os }}
+ permissions:
+ contents: write # for step-security/upload-rust-binary-action
+ steps:
+ - uses: actions/checkout@v7
+ - name: Install cross-compilation tools
+ uses: step-security/setup-cross-toolchain-action@v1
+ with:
+ target: ${{ matrix.target }}
+ runner: none # Skip installation of cross-testing related tools because we only do cross-compilation.
+ if: startsWith(matrix.os, 'ubuntu')
+ - uses: step-security/upload-rust-binary-action@v1
+ with:
+ # (required) Binary names (non-extension portion of filename) to build and upload (whitespace or comma separated list).
+ # Note that glob pattern is not supported yet.
+ bin: ...
+ # (optional) Target triple, default is host triple.
+ target: ${{ matrix.target }}
+```
+
+#### cargo-zigbuild
+
+if you want to use [cargo-zigbuild], if the heuristic to detect host cross-compilation setups does not work well, or if you want to force the use of cargo or cross, you can use the `build-tool` input option.
+
+If cargo-zigbuild is not installed, this action calls `pip3 install cargo-zigbuild` to install cargo-zigbuild.
+
+```yaml
+name: Release
+
+permissions:
+ contents: read
+
+on:
+ push:
+ tags:
+ - v[0-9]+.*
+
+jobs:
+ create-release:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write # for taiki-e/create-gh-release-action
+ steps:
+ - uses: actions/checkout@v7
+ - uses: taiki-e/create-gh-release-action@v1
+ with:
+ # (optional)
+ changelog: CHANGELOG.md
+
+ upload-assets:
+ needs: create-release
+ strategy:
+ matrix:
+ include:
+ - target: x86_64-unknown-linux-gnu
+ os: ubuntu-latest
+ build-tool: cargo-zigbuild
+ # cargo-zigbuild's glibc version suffix is also supported.
+ - target: aarch64-unknown-linux-gnu.2.17
+ os: ubuntu-latest
+ build-tool: cargo-zigbuild
+ - target: aarch64-apple-darwin
+ os: macos-latest
+ build-tool: cargo
+ runs-on: ${{ matrix.os }}
+ permissions:
+ contents: write # for step-security/upload-rust-binary-action
+ steps:
+ - uses: actions/checkout@v7
+ - uses: step-security/upload-rust-binary-action@v1
+ with:
+ # (required)
+ bin: ...
+ # (optional) Target triple, default is host triple.
+ target: ${{ matrix.target }}
+ # (optional) Tool to build binaries (cargo, cross, or cargo-zigbuild)
+ build-tool: ${{ matrix.build-tool }}
+```
+
+### Example workflow: Include additional files
+
+If you want include additional file *to the archive*, you can use the `include` option.
+
+```yaml
+name: Release
+
+permissions:
+ contents: read
+
+on:
+ push:
+ tags:
+ - v[0-9]+.*
+
+jobs:
+ create-release:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write # for taiki-e/create-gh-release-action
+ steps:
+ - uses: actions/checkout@v7
+ - uses: taiki-e/create-gh-release-action@v1
+ with:
+ # (optional) Path to changelog.
+ changelog: CHANGELOG.md
+
+ upload-assets:
+ needs: create-release
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write # for step-security/upload-rust-binary-action
+ steps:
+ - uses: actions/checkout@v7
+ - uses: step-security/upload-rust-binary-action@v1
+ with:
+ # (required) Binary names (non-extension portion of filename) to build and upload (whitespace or comma separated list).
+ # Note that glob pattern is not supported yet.
+ bin: ...
+ # (optional) Additional files to be included to the archive (whitespace or comma separated list).
+ # Note that glob pattern is not supported yet.
+ include: LICENSE,README.md
+```
+
+By default, the expanded archive does not include the leading directory. In the above example, the directory structure of the archive would be as follows:
+
+```text
+/
+/LICENSE
+/README.md
+```
+
+You can use the `leading-dir` option to create the leading directory.
+
+```yaml
+- uses: step-security/upload-rust-binary-action@v1
+ with:
+ # (required) Binary names (non-extension portion of filename) to build and upload (whitespace or comma separated list).
+ # Note that glob pattern is not supported yet.
+ bin: ...
+ # (optional) Additional files to be included to the archive (whitespace or comma separated list).
+ # Note that glob pattern is not supported yet.
+ include: LICENSE,README.md
+ # (optional) Whether to create the leading directory in the archive or not. default to false.
+ leading-dir: true
+```
+
+In the above example, the directory structure of the archive would be as follows:
+
+```text
+//
+//
+//LICENSE
+//README.md
+```
+
+You can use the `bin-leading-dir` option to create extra leading directory(s) for binary file(s) specified by `bin` option.
+
+```yaml
+- uses: step-security/upload-rust-binary-action@v1
+ with:
+ # (required) Binary names (non-extension portion of filename) to build and upload (whitespace or comma separated list).
+ # Note that glob pattern is not supported yet.
+ bin: ...
+ # (optional) Additional files to be included to the archive (whitespace or comma separated list).
+ # Note that glob pattern is not supported yet.
+ include: LICENSE,README.md
+ # (optional) Whether to create the leading directory in the archive or not. default to false.
+ leading-dir: true
+ # (optional) Create extra leading directory(s) for binary file(s) specified by `bin` option. default to empty.
+ bin-leading-dir: opt/leading
+```
+
+In the above example, the directory structure of the archive would be as follows:
+
+```text
+//
+//opt/leading/
+//LICENSE
+//README.md
+```
+
+If you want upload additional file *separately*, you can use the `asset` option.
+
+```yaml
+upload-assets:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v7
+ - uses: step-security/upload-rust-binary-action@v1
+ with:
+ # (required) Binary names (non-extension portion of filename) to build and upload (whitespace or comma separated list).
+ # Note that glob pattern is not supported yet.
+ bin: ...
+ # (optional) Additional files to be uploaded separately (whitespace or comma separated list).
+ # Note that glob pattern is not supported yet.
+ asset: LICENSE,README.md
+```
+
+In the above example, the following 3 files will be uploaded:
+
+```text
+-.tar.gz
+LICENSE
+README.md
+```
+
+### Optimize Rust binary
+
+You can optimize performance or size of Rust binaries by passing the profile options.
+The profile options can be specified by [`[profile]` table in `Cargo.toml`](https://doc.rust-lang.org/cargo/reference/profiles.html), [cargo config](https://doc.rust-lang.org/cargo/reference/config.html), [environment variables](https://doc.rust-lang.org/cargo/reference/environment-variables.html#configuration-environment-variables), etc.
+
+The followings are examples to specify profile options:
+
+- [lto](https://doc.rust-lang.org/cargo/reference/profiles.html#lto)
+
+ With profile:
+
+ ```toml
+ [profile.release]
+ lto = true
+ ```
+
+ With environment variable:
+
+ ```yaml
+ env:
+ CARGO_PROFILE_RELEASE_LTO: true
+ ```
+
+- [codegen-units](https://doc.rust-lang.org/cargo/reference/profiles.html#codegen-units)
+
+ With profile:
+
+ ```toml
+ [profile.release]
+ codegen-units = 1
+ ```
+
+ With environment variable:
+
+ ```yaml
+ env:
+ CARGO_PROFILE_RELEASE_CODEGEN_UNITS: 1
+ ```
+
+- [strip](https://doc.rust-lang.org/cargo/reference/profiles.html#strip)
+
+ With profile:
+
+ ```toml
+ [profile.release]
+ strip = "symbols"
+ ```
+
+ [Default is `strip = debuginfo`.](https://github.com/rust-lang/cargo/pull/13257)
+
+**Note:** Some of these options may increase the build time.
+
+## Supported events
+
+The following two events are supported by default:
+
+- tags ([`on.push.tags`](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push))
+
+ For example:
+
+ ```yaml
+ on:
+ push:
+ tags:
+ - v[0-9]+.*
+ ```
+
+- GitHub release ([`on.release`](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#release))
+
+ For example:
+
+ ```yaml
+ on:
+ release:
+ types: [created]
+ ```
+
+You can upload binaries from arbitrary event to arbitrary tag by specifying the `ref` input option.
+
+For example, to upload binaries to the `my_tag` tag, specify `ref` input option as follows:
+
+```yaml
+with:
+ ref: refs/tags/my_tag
+```
+
+## Security
+
+The `@v` tags are updated with each release. If you want to enhance workflow stability and security against supply chain attacks, consider using the `@v..` tag or their hash to pin the version and regularly updating with dependency cooldown. Since all releases are immutable, pinning the version in either way should have the same effect.
+
+## Compatibility
+
+This action has been tested for GitHub-hosted runners (Ubuntu, macOS, Windows).
+
+To use this action in self-hosted runners or in containers, at least the following tools are required:
+
+- rustup, cargo, rustc
+- bash
+- GNU tar
+- [gh (GitHub CLI)](https://github.com/cli/cli#installation)
+- zip (only Unix-like)
+- 7z (only Windows)
+
+Note that what this action installs for its setup (such as above tools) is considered an implementation detail if they are installed by this action's side, and there is no guarantee that they will be available in subsequent steps, because this action is not an action for installing those tools.
+
+## License
+
+Licensed under [MIT license](LICENSE)
+
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..77568b2
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,5 @@
+# Security Policy
+
+## Reporting a Vulnerability
+
+Please report security vulnerabilities to security@stepsecurity.io
diff --git a/action.yml b/action.yml
new file mode 100644
index 0000000..472f56a
--- /dev/null
+++ b/action.yml
@@ -0,0 +1,251 @@
+name: Build and upload Rust binary to GitHub Releases
+description: GitHub Action for building and uploading Rust binary to GitHub Releases
+
+inputs:
+ bin:
+ description: >
+ Binary names (non-extension portion of filename) to build and upload (whitespace or comma separated list).
+ Note that glob pattern is not supported yet.
+ required: true
+ archive:
+ description: Archive name (non-extension portion of filename) to be uploaded (variables `$bin`, `$target`, `$tag`, and any string)
+ required: false
+ default: '$bin-$target'
+ target:
+ description: Target name, default is host triple
+ required: false
+ features:
+ description: Cargo build features to enable (space or comma separated list)
+ required: false
+ no-default-features:
+ description: Whether to disable cargo build default features
+ required: false
+ no_default_features:
+ description: Alias for 'no-default-features'
+ required: false
+ default: 'false'
+ all-features:
+ description: Whether to build with `--all-features` flag
+ required: false
+ default: 'false'
+ package:
+ description: Package names to build (whitespace or comma separated list)
+ required: false
+ workspace:
+ description: Whether to build with `--workspace` flag
+ required: false
+ default: 'false'
+ locked:
+ description: Whether to build with `--locked` flag
+ required: false
+ default: 'false'
+ manifest-path:
+ description: Override cargo manifest path
+ required: false
+ manifest_path:
+ description: Alias for 'manifest-path'
+ required: false
+ tar:
+ description: On which platform to distribute the `.tar.gz` file (all, unix, windows, or none)
+ required: false
+ default: 'unix'
+ tar-xz:
+ description: On which platform to distribute the `.tar.xz` file (all, unix, windows, or none)
+ required: false
+ default: 'none'
+ zip:
+ description: On which platform to distribute the `.zip` file (all, unix, windows, or none)
+ required: false
+ default: 'windows'
+ include:
+ description: >
+ Additional files to be included to the archive (whitespace or comma separated list).
+ Note that glob pattern is not supported yet.
+ required: false
+ asset:
+ description: >
+ Additional files to be uploaded separately (whitespace or comma separated list).
+ Note that glob pattern is not supported yet.
+ required: false
+ leading-dir:
+ description: Whether to create the leading directory in the archive or not
+ required: false
+ leading_dir:
+ description: Alias for 'leading-dir'
+ required: false
+ default: 'false'
+ bin-leading-dir:
+ description: Create extra leading directory(s) for binary file(s) specified by 'bin' option
+ required: false
+ build-tool:
+ description: Tool to build binaries (cargo, cross, or cargo-zigbuild)
+ required: false
+ build_tool:
+ description: Alias for 'build-tool'
+ required: false
+ checksum:
+ description: >
+ Algorithms to be used for checksum (sha256, sha512, b2, sha1, or md5) (whitespace or comma separated list)
+
+ Note: b2 is not available by default on macOS, install `b2sum` to use it.
+ sha1 and md5 are insecure and strongly discouraged.
+ required: false
+ token:
+ description: >
+ GitHub token for uploading assets to GitHub Releases.
+
+ If not set this option, the GITHUB_TOKEN environment variable will be used.
+ If not set both this option and the GITHUB_TOKEN environment variable, github.token will be used.
+ required: false
+ ref:
+ description: >
+ Fully-formed tag ref for this release.
+
+ If not set this option, the GITHUB_REF environment variable (automatically set by GitHub Actions) will be used.
+ required: false
+ profile:
+ description: The cargo profile to build. This defaults to the release profile.
+ required: false
+ default: 'release'
+ dry-run:
+ description: >
+ Build and compress binaries, but do not upload them.
+
+ Note that some errors are downgraded to warnings in this mode.
+ required: false
+ dry-run-intended:
+ description: >
+ Suppress informational warnings for `dry-run` keeping the rest
+ required: false
+ default: 'false'
+ dry_run:
+ description: Alias for 'dry-run'
+ required: false
+ default: 'false'
+ codesign:
+ description: Sign build products using `codesign` on macOS
+ required: false
+ codesign-prefix:
+ description: Prefix for the `codesign` identifier on macOS
+ required: false
+ codesign_prefix:
+ description: Alias for 'codesign-prefix'
+ required: false
+ codesign-options:
+ description: Specifies a set of option flags to be embedded in the code signature on macOS. See the codesign manpage for details.
+ required: false
+ codesign_options:
+ description: Alias for 'codesign-options'
+ required: false
+
+outputs:
+ archive:
+ description: 'Archive base name'
+ value: ${{ steps.upload-rust-binary-action.outputs.archive }}
+ zip:
+ description: '.zip archive file name'
+ value: ${{ steps.upload-rust-binary-action.outputs.zip }}
+ tar:
+ description: '.tar.gz archive file name'
+ value: ${{ steps.upload-rust-binary-action.outputs.tar }}
+ tar-xz:
+ description: '.tar.xz archive file name'
+ value: ${{ steps.upload-rust-binary-action.outputs.tar-xz }}
+ b2:
+ description: 'BLAKE2 checksum file name'
+ value: ${{ steps.upload-rust-binary-action.outputs.b2 }}
+ sha256:
+ description: 'SHA256 checksum file name'
+ value: ${{ steps.upload-rust-binary-action.outputs.sha256 }}
+ sha512:
+ description: 'SHA512 checksum file name'
+ value: ${{ steps.upload-rust-binary-action.outputs.sha512 }}
+ sha1:
+ description: 'SHA1 checksum file name'
+ value: ${{ steps.upload-rust-binary-action.outputs.sha1 }}
+ md5:
+ description: 'MD5 checksum file name'
+ value: ${{ steps.upload-rust-binary-action.outputs.md5 }}
+
+# Note:
+# - inputs.* should be manually mapped to INPUT_* due to https://github.com/actions/runner/issues/665
+# - Use GITHUB_*/RUNNER_* instead of github.*/runner.* due to https://github.com/actions/runner/issues/2185
+runs:
+ using: composite
+ steps:
+ - name: Subscription check
+ env:
+ REPO_PRIVATE: ${{ github.event.repository.private }}
+ run: |
+ # validate subscription status
+ UPSTREAM="taiki-e/upload-rust-binary-action"
+ ACTION_REPO="${GITHUB_ACTION_REPOSITORY:-}"
+ DOCS_URL="https://docs.stepsecurity.io/actions/stepsecurity-maintained-actions"
+
+ echo ""
+ echo -e "\033[1;36mStepSecurity Maintained Action\033[0m"
+ echo "Secure drop-in replacement for $UPSTREAM"
+ if [ "$REPO_PRIVATE" = "false" ]; then
+ echo -e "\033[32m✓ Free for public repositories\033[0m"
+ fi
+ echo -e "\033[36mLearn more:\033[0m $DOCS_URL"
+ echo ""
+
+ if [ "$REPO_PRIVATE" != "false" ]; then
+ SERVER_URL="${GITHUB_SERVER_URL:-https://github.com}"
+
+ if [ "$SERVER_URL" != "https://github.com" ]; then
+ BODY=$(printf '{"action":"%s","ghes_server":"%s"}' "$ACTION_REPO" "$SERVER_URL")
+ else
+ BODY=$(printf '{"action":"%s"}' "$ACTION_REPO")
+ fi
+
+ API_URL="https://agent.api.stepsecurity.io/v1/github/$GITHUB_REPOSITORY/actions/maintained-actions-subscription"
+
+ RESPONSE=$(curl --max-time 3 -s -w "%{http_code}" \
+ -X POST \
+ -H "Content-Type: application/json" \
+ -d "$BODY" \
+ "$API_URL" -o /dev/null) && CURL_EXIT_CODE=0 || CURL_EXIT_CODE=$?
+
+ if [ $CURL_EXIT_CODE -ne 0 ]; then
+ echo "Timeout or API not reachable. Continuing to next step."
+ elif [ "$RESPONSE" = "403" ]; then
+ echo -e "::error::\033[1;31mThis action requires a StepSecurity subscription for private repositories.\033[0m"
+ echo -e "::error::\033[31mLearn how to enable a subscription: $DOCS_URL\033[0m"
+ exit 1
+ fi
+ fi
+ shell: bash
+ - id: upload-rust-binary-action
+ run: bash --noprofile --norc "${GITHUB_ACTION_PATH:?}/main.sh"
+ shell: bash
+ env:
+ INPUT_BIN: ${{ inputs.bin }}
+ INPUT_ARCHIVE: ${{ inputs.archive }}
+ INPUT_TARGET: ${{ inputs.target }}
+ INPUT_FEATURES: ${{ inputs.features }}
+ INPUT_NO_DEFAULT_FEATURES: ${{ inputs.no-default-features || inputs.no_default_features }}
+ INPUT_ALL_FEATURES: ${{ inputs.all-features }}
+ INPUT_PACKAGE: ${{ inputs.package }}
+ INPUT_WORKSPACE: ${{ inputs.workspace }}
+ INPUT_LOCKED: ${{ inputs.locked }}
+ INPUT_MANIFEST_PATH: ${{ inputs.manifest-path || inputs.manifest_path }}
+ INPUT_TAR: ${{ inputs.tar }}
+ INPUT_TAR_XZ: ${{ inputs.tar-xz }}
+ INPUT_ZIP: ${{ inputs.zip }}
+ INPUT_INCLUDE: ${{ inputs.include }}
+ INPUT_ASSET: ${{ inputs.asset }}
+ INPUT_LEADING_DIR: ${{ inputs.leading-dir || inputs.leading_dir }}
+ INPUT_BIN_LEADING_DIR: ${{ inputs.bin-leading-dir }}
+ INPUT_BUILD_TOOL: ${{ inputs.build-tool || inputs.build_tool }}
+ INPUT_CHECKSUM: ${{ inputs.checksum }}
+ INPUT_TOKEN: ${{ (inputs.dry-run || inputs.dry_run) != 'true' && inputs.token || '' }}
+ INPUT_REF: ${{ inputs.ref }}
+ INPUT_PROFILE: ${{ inputs.profile }}
+ INPUT_DRY_RUN: ${{ inputs.dry-run || inputs.dry_run }}
+ INPUT_DRY_RUN_INTENDED: ${{ inputs.dry-run-intended }}
+ INPUT_CODESIGN: ${{ inputs.codesign }}
+ INPUT_CODESIGN_PREFIX: ${{ inputs.codesign-prefix || inputs.codesign_prefix }}
+ INPUT_CODESIGN_OPTIONS: ${{ inputs.codesign-options || inputs.codesign_options }}
+ DEFAULT_GITHUB_TOKEN: ${{ (inputs.dry-run || inputs.dry_run) != 'true' && github.token || '' }}
diff --git a/main.sh b/main.sh
new file mode 100755
index 0000000..484b69b
--- /dev/null
+++ b/main.sh
@@ -0,0 +1,640 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: Apache-2.0 OR MIT
+set -CeEuo pipefail
+IFS=$'\n\t'
+
+x() {
+ (
+ set -x
+ "$@"
+ )
+}
+retry() {
+ for i in {1..10}; do
+ if "$@"; then
+ return 0
+ else
+ sleep "${i}"
+ fi
+ done
+ "$@"
+}
+bail() {
+ printf '::error::%s\n' "$*"
+ exit 1
+}
+warn() {
+ printf '::warning::%s\n' "$*"
+}
+info() {
+ printf >&2 'info: %s\n' "$*"
+}
+normalize_comma_or_space_separated() {
+ # Normalize whitespace characters into space because it's hard to handle single input contains lines with POSIX sed alone.
+ local list="${1//[$'\r\n\t']/ }"
+ if [[ "${list}" == *","* ]]; then
+ # If a comma is contained, consider it is a comma-separated list.
+ # Drop leading and trailing whitespaces in each element.
+ sed -E 's/ *, */,/g; s/^.//; s/,,$/,/' <<<",${list},"
+ else
+ # Otherwise, consider it is a whitespace-separated list.
+ # Convert whitespace characters into comma.
+ sed -E 's/ +/,/g; s/^.//' <<<" ${list} "
+ fi
+}
+
+export CARGO_NET_RETRY=10
+export RUSTUP_MAX_RETRIES=10
+
+if [[ $# -gt 0 ]]; then
+ bail "invalid argument '$1'"
+fi
+
+dry_run="${INPUT_DRY_RUN:-}"
+case "${dry_run}" in
+ true) dry_run=1 ;;
+ false) dry_run='' ;;
+ *) bail "'dry-run' input option must be 'true' or 'false': '${dry_run}'" ;;
+esac
+
+dry_run_intended="${INPUT_DRY_RUN_INTENDED:-}"
+case "${dry_run_intended}" in
+ true) dry_run_intended=1 ;;
+ false) dry_run_intended='' ;;
+ '') dry_run_intended='' ;;
+ *) bail "'dry-run-intended' input option must be 'true' or 'false': '${dry_run_intended}'" ;;
+esac
+
+if [[ -n "${INPUT_TOKEN}" ]] || [[ -n "${DEFAULT_GITHUB_TOKEN}" ]]; then
+ if [[ -n "${dry_run}" ]]; then
+ bail "internal error: INPUT_TOKEN and DEFAULT_GITHUB_TOKEN must be empty for dry-run"
+ fi
+fi
+token="${INPUT_TOKEN:-"${GITHUB_TOKEN:-"${DEFAULT_GITHUB_TOKEN:-}"}"}"
+# This prevents tokens from being exposed to subprocesses via environment variables.
+# Note that this does not prevent token leaks via reading `/proc/*/environ` on Linux or
+# via `ps -Eww` on macOS. It only reduces the risk of leaks.
+unset INPUT_TOKEN GITHUB_TOKEN GH_TOKEN DEFAULT_GITHUB_TOKEN
+ref="${INPUT_REF:-"${GITHUB_REF:-}"}"
+
+if [[ -z "${token}" ]]; then
+ if [[ -z "${dry_run}" ]]; then
+ bail "neither GITHUB_TOKEN environment variable nor 'token' input option is set"
+ fi
+fi
+
+if [[ "${ref}" != "refs/tags/"* ]]; then
+ if [[ -n "${dry_run}" ]]; then
+ if [[ -z "${dry_run_intended}" ]]; then
+ # TODO: The warnings are somewhat noisy if we have a lot of build matrix:
+ # https://github.com/taiki-e/upload-rust-binary-action/pull/55#discussion_r1349880455
+ warn "tag ref should start with 'refs/tags/': '${ref}'; this action only supports events from tag or release by default; see for more (downgraded error to info because action is running in dry-run mode)"
+ fi
+ ref='refs/tags/dry-run'
+ else
+ bail "tag ref should start with 'refs/tags/': '${ref}'; this action only supports events from tag or release by default; see for more"
+ fi
+fi
+tag="${ref#refs/tags/}"
+
+features="${INPUT_FEATURES:-}"
+archive="${INPUT_ARCHIVE:?}"
+
+if [[ ! "${INPUT_TAR}" =~ ^(all|unix|windows|none)$ ]]; then
+ bail "invalid input 'tar': ${INPUT_TAR}"
+elif [[ ! "${INPUT_TAR_XZ}" =~ ^(all|unix|windows|none)$ ]]; then
+ bail "invalid input 'tar-xz': ${INPUT_TAR_XZ}"
+elif [[ ! "${INPUT_ZIP}" =~ ^(all|unix|windows|none)$ ]]; then
+ bail "invalid input 'zip': ${INPUT_ZIP}"
+fi
+
+leading_dir="${INPUT_LEADING_DIR:-}"
+case "${leading_dir}" in
+ true) leading_dir=1 ;;
+ false) leading_dir='' ;;
+ *) bail "'leading-dir' input option must be 'true' or 'false': '${leading_dir}'" ;;
+esac
+
+bin_leading_dir="${INPUT_BIN_LEADING_DIR:-}"
+
+no_default_features="${INPUT_NO_DEFAULT_FEATURES:-}"
+case "${no_default_features}" in
+ true) no_default_features=1 ;;
+ false) no_default_features='' ;;
+ *) bail "'no-default-features' input option must be 'true' or 'false': '${no_default_features}'" ;;
+esac
+
+all_features="${INPUT_ALL_FEATURES:-}"
+case "${all_features}" in
+ true) all_features=1 ;;
+ false) all_features='' ;;
+ *) bail "'all-features' input option must be 'true' or 'false': '${all_features}'" ;;
+esac
+
+build_locked="${INPUT_LOCKED:-}"
+case "${build_locked}" in
+ true) build_locked=1 ;;
+ false) build_locked='' ;;
+ *) bail "'locked' input option must be 'true' or 'false': '${build_locked}'" ;;
+esac
+
+build_workspace="${INPUT_WORKSPACE:-}"
+case "${build_workspace}" in
+ true) build_workspace=1 ;;
+ false) build_workspace='' ;;
+ *) bail "'workspace' input option must be 'true' or 'false': '${build_workspace}'" ;;
+esac
+
+build_package="${INPUT_PACKAGE:-}"
+build_packages=()
+if [[ -n "${build_package}" ]]; then
+ while IFS= read -rd,; do
+ build_packages+=("${REPLY}")
+ done < <(normalize_comma_or_space_separated "${build_package}")
+fi
+
+bin_name="${INPUT_BIN:?}"
+bin_names=()
+if [[ -n "${bin_name}" ]]; then
+ # We can expand a glob by expanding a variable without quote, but that way
+ # has a security issue of shell injection.
+ if [[ "${bin_name}" == *"?"* ]] || [[ "${bin_name}" == *"*"* ]] || [[ "${bin_name}" == *"["* ]]; then
+ # This check is not for security but for diagnostic purposes.
+ # We quote the filename, so without this uses get an error like
+ # "cp: cannot stat 'app-*': No such file or directory".
+ bail "glob pattern in 'bin' input option is not supported yet"
+ fi
+ while IFS= read -rd,; do
+ bin_names+=("${REPLY}")
+ done < <(normalize_comma_or_space_separated "${bin_name}")
+fi
+if [[ ${#bin_names[@]} -gt 1 ]] && [[ "${archive}" == *"\$bin"* ]]; then
+ bail "when multiple binary names are specified, default archive name or '\$bin' variable cannot be used in 'archive' option"
+fi
+
+include="${INPUT_INCLUDE:-}"
+includes=()
+if [[ -n "${include}" ]]; then
+ # We can expand a glob by expanding a variable without quote, but that way
+ # has a security issue of shell injection.
+ if [[ "${include}" == *"?"* ]] || [[ "${include}" == *"*"* ]] || [[ "${include}" == *"["* ]]; then
+ # This check is not for security but for diagnostic purposes.
+ # We quote the filename, so without this uses get an error like
+ # "cp: cannot stat 'LICENSE-*': No such file or directory".
+ bail "glob pattern in 'include' input option is not supported yet"
+ fi
+ while IFS= read -rd,; do
+ includes+=("${REPLY}")
+ done < <(normalize_comma_or_space_separated "${include}")
+fi
+
+asset="${INPUT_ASSET:-}"
+assets=()
+if [[ -n "${asset}" ]]; then
+ # We can expand a glob by expanding a variable without quote, but that way
+ # has a security issue of shell injection.
+ if [[ "${asset}" == *"?"* ]] || [[ "${asset}" == *"*"* ]] || [[ "${asset}" == *"["* ]]; then
+ # This check is not for security but for diagnostic purposes.
+ # We quote the filename, so without this uses get an error like
+ # "cp: cannot stat 'LICENSE-*': No such file or directory".
+ bail "glob pattern in 'asset' input option is not supported yet"
+ fi
+ while IFS= read -rd,; do
+ assets+=("${REPLY}")
+ done < <(normalize_comma_or_space_separated "${asset}")
+fi
+
+checksum="${INPUT_CHECKSUM:-}"
+checksums=()
+if [[ -n "${checksum}" ]]; then
+ while IFS= read -rd,; do
+ checksums+=("${REPLY}")
+ case "${REPLY}" in
+ b2 | sha256 | sha512 | sha1 | md5) ;;
+ *) bail "'checksum' input option must be 'b2', 'sha256', 'sha512', 'sha1', or 'md5': '${REPLY}'" ;;
+ esac
+ done < <(normalize_comma_or_space_separated "${checksum}")
+fi
+
+host=$(rustc -vV | grep -E '^host:' | cut -d' ' -f2)
+rustc_version=$(rustc -vV | grep -E '^release:' | cut -d' ' -f2)
+rustc_minor_version="${rustc_version#*.}"
+rustc_minor_version="${rustc_minor_version%%.*}"
+target="${INPUT_TARGET:-"${host}"}"
+zigbuild_target=''
+build_tool="${INPUT_BUILD_TOOL:-}"
+if [[ "${build_tool}" == "cargo-zigbuild" ]]; then
+ # cargo-zigbuild supports . suffix
+ zigbuild_target="${target}"
+ target="${target%%.*}"
+fi
+case "${target}" in
+ wasm*) exe=.wasm ;;
+ *-windows*) exe=.exe ;;
+ *) exe='' ;;
+esac
+target_lower="${target//-/_}"
+target_lower="${target_lower//./_}"
+target_upper=$(tr '[:lower:]' '[:upper:]' <<<"${target_lower}")
+
+case "$(uname -s)" in
+ Linux)
+ platform=unix
+ host_os=linux
+ ;;
+ Darwin)
+ platform=unix
+ host_os=macos
+ # Work around https://github.com/actions/cache/issues/403 by using GNU tar
+ # instead of BSD tar.
+ if ! type -P gtar >/dev/null; then
+ brew install gnu-tar &>/dev/null
+ fi
+ tar() { gtar "$@"; }
+ if [[ -z "${INPUT_TARGET:-}" ]]; then
+ warn "GitHub Actions changed default architecture of macos-latest since macos-14; consider passing 'target' input option to clarify which target you are building for"
+ fi
+ ;;
+ MINGW* | MSYS* | CYGWIN* | Windows_NT)
+ platform=windows
+ host_os=windows
+ ;;
+ *) bail "unrecognized OS type '$(uname -s)'" ;;
+esac
+
+if [[ -z "${build_tool}" ]]; then
+ build_tool=cargo
+ if [[ "${host}" != "${target}" ]]; then
+ # If any of these are set, it is obvious that the user has set up a cross-compilation environment on the host.
+ if [[ -z "$(eval "printf '%s\n' \${CARGO_TARGET_${target_upper}_LINKER:-}")" ]] && [[ -z "$(eval "printf '%s\n' \${CARGO_TARGET_${target_upper}_RUNNER:-}")" ]]; then
+ case "${target}" in
+ # https://github.com/cross-rs/cross#supported-targets
+ *-windows*)
+ case "${host_os}" in
+ windows) ;;
+ *) build_tool=cross ;;
+ esac
+ ;;
+ *-apple-*)
+ case "${host_os}" in
+ macos) ;;
+ *) build_tool=cross ;;
+ esac
+ ;;
+ *-fuchsia* | *-redox*) ;;
+ *) build_tool=cross ;;
+ esac
+ fi
+ fi
+fi
+
+if [[ "${build_tool}" == "cargo" ]]; then
+ case "${target}" in
+ universal-apple-darwin) retry rustup target add aarch64-apple-darwin x86_64-apple-darwin ;;
+ *) retry rustup target add "${target}" ;;
+ esac
+fi
+
+archive="${archive/\$bin/${bin_names[0]}}"
+archive="${archive/\$target/${target}}"
+archive="${archive/\$tag/${tag}}"
+
+if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
+ printf 'archive=%s\n' "${archive}" >>"${GITHUB_OUTPUT}"
+else
+ warn "GITHUB_OUTPUT is not set; skip setting the 'archive' output"
+ printf 'archive: %s\n' "${archive}"
+fi
+
+input_profile=${INPUT_PROFILE:-release}
+case "${input_profile}" in
+ release) build_options=(--release) ;;
+ *) build_options=(--profile "${input_profile}") ;;
+esac
+
+# There are some special profiles that correspond to different target directory
+# names. If we don't hit one of those conditionals then we just use the profile
+# name.
+# See: https://doc.rust-lang.org/nightly/cargo/reference/profiles.html#custom-profiles
+case "${input_profile}" in
+ bench) profile_directory=release ;;
+ dev | test) profile_directory=debug ;;
+ *) profile_directory=${input_profile} ;;
+esac
+
+bins=()
+for bin_name in "${bin_names[@]}"; do
+ bins+=("${bin_name}${exe}")
+ build_options+=(--bin "${bin_name}")
+done
+if [[ -n "${features}" ]]; then
+ build_options+=(--features "${features}")
+fi
+if [[ -n "${no_default_features}" ]]; then
+ build_options+=(--no-default-features)
+fi
+if [[ -n "${all_features}" ]]; then
+ build_options+=(--all-features)
+fi
+if [[ -n "${build_locked}" ]]; then
+ build_options+=(--locked)
+fi
+if [[ -n "${build_workspace}" ]]; then
+ build_options+=(--workspace)
+fi
+for build_package in ${build_packages[@]+"${build_packages[@]}"}; do
+ build_options+=(--package "${build_package}")
+done
+metadata_options=(--format-version=1 --no-deps)
+manifest_path="${INPUT_MANIFEST_PATH:-}"
+if [[ -n "${manifest_path}" ]]; then
+ build_options+=(--manifest-path "${manifest_path}")
+ metadata_options+=(--manifest-path "${manifest_path}")
+fi
+eval "$(cargo metadata "${metadata_options[@]}" | jq -r '@sh "target_dir=\(.target_directory) WORKSPACE_ROOT=\(.workspace_root)"')"
+
+# Skip setting the strip option (requires Cargo 1.59) if it is unavailable or already set.
+# TODO: This check is somewhat rough as it does not look at the type of profile.
+if [[ "${rustc_minor_version}" -ge 59 ]] && [[ -z "${CARGO_PROFILE_RELEASE_STRIP:-}" ]] && ! grep -Eq '^\s*strip\s*=' "${WORKSPACE_ROOT}/Cargo.toml"; then
+ if [[ "${rustc_minor_version}" -lt 79 ]]; then
+ # On pre-1.77, align to Cargo 1.77+'s default: https://github.com/rust-lang/cargo/pull/13257
+ # However, set environment variable on pre-1.79 because it is 1.79+ that actually works correctly due to https://github.com/rust-lang/cargo/issues/13617.
+ case "${target}" in
+ # Do not strip debuginfo on MSVC https://github.com/rust-lang/cargo/pull/13630
+ # This is the same behavior as pre-1.19.0 upload-rust-binary-action.
+ *-windows-msvc) strip_default=none ;;
+ *) strip_default=debuginfo ;;
+ esac
+ export CARGO_PROFILE_RELEASE_STRIP="${strip_default}"
+ fi
+ # On pre-1.84, do not strip debuginfo on these targets in cross-compilation (including docker via cross).
+ # https://github.com/rust-lang/rust/pull/131405
+ # https://github.com/nextest-rs/nextest/commit/d4f982b3184f07ff5c40cc90c52d3fc6567be0b9#commitcomment-140325483
+ # https://github.com/rust-lang/rust/issues/123151#issuecomment-2024743520
+ # https://github.com/rust-lang/rust/blob/1.83.0/compiler/rustc_codegen_ssa/src/back/link.rs#L1089-L1137
+ if [[ "${rustc_minor_version}" -lt 84 ]]; then
+ case "${target}" in
+ *-apple-*)
+ case "${host_os}" in
+ macos) ;; # apple to apple cross-compilation is okay
+ *) export CARGO_PROFILE_RELEASE_STRIP=none ;;
+ esac
+ ;;
+ # illumos/AIX host is not supported on GitHub Actions.
+ *-illumos* | *-aix*) export CARGO_PROFILE_RELEASE_STRIP=none ;;
+ esac
+ fi
+fi
+
+build() {
+ case "${build_tool}" in
+ cargo) x cargo build "${build_options[@]}" "$@" ;;
+ cross)
+ if ! type -P cross >/dev/null; then
+ x cargo install cross --locked
+ fi
+ x cross build "${build_options[@]}" "$@"
+ ;;
+ cargo-zigbuild)
+ if ! type -P cargo-zigbuild >/dev/null; then
+ x pip3 install cargo-zigbuild
+ fi
+ case "${INPUT_TARGET:-}" in
+ '') ;;
+ universal2-apple-darwin) retry rustup target add aarch64-apple-darwin x86_64-apple-darwin ;;
+ *) retry rustup target add "${target}" ;;
+ esac
+ x cargo zigbuild "${build_options[@]}" "$@"
+ ;;
+ *) bail "unrecognized build tool '${build_tool}'" ;;
+ esac
+}
+do_codesign() {
+ if [[ -n "${INPUT_CODESIGN:-}" ]]; then
+ local codesign_options=(--sign "${INPUT_CODESIGN}")
+ if [[ -n "${INPUT_CODESIGN_PREFIX:-}" ]]; then
+ codesign_options+=(--prefix "${INPUT_CODESIGN_PREFIX}")
+ fi
+ if [[ -n "${INPUT_CODESIGN_OPTIONS:-}" ]]; then
+ codesign_options+=(--options "${INPUT_CODESIGN_OPTIONS}")
+ fi
+
+ for bin_exe in "${bins[@]}"; do
+ x codesign "${codesign_options[@]}" "${target_dir}/${bin_exe}"
+ done
+ fi
+}
+
+case "${INPUT_TARGET:-}" in
+ '')
+ build
+ target_dir="${target_dir}/${profile_directory}"
+ ;;
+ universal-apple-darwin)
+ # Refs: https://developer.apple.com/documentation/apple-silicon/building-a-universal-macos-binary
+ # multi-target builds requires 1.64
+ if [[ "${rustc_minor_version}" -ge 64 ]]; then
+ build --target aarch64-apple-darwin --target x86_64-apple-darwin
+ else
+ build --target aarch64-apple-darwin
+ build --target x86_64-apple-darwin
+ fi
+ aarch64_target_dir="${target_dir}/aarch64-apple-darwin/${profile_directory}"
+ x86_64_target_dir="${target_dir}/x86_64-apple-darwin/${profile_directory}"
+ target_dir="${target_dir}/${target}/${profile_directory}"
+ mkdir -p -- "${target_dir}"
+ for bin_exe in "${bins[@]}"; do
+ x lipo -create -output "${target_dir}/${bin_exe}" "${aarch64_target_dir}/${bin_exe}" "${x86_64_target_dir}/${bin_exe}"
+ done
+ ;;
+ *)
+ build --target "${zigbuild_target:-"${target}"}"
+ target_dir="${target_dir}/${target}/${profile_directory}"
+ ;;
+esac
+
+case "${host_os}" in
+ macos)
+ if type -P codesign >/dev/null; then
+ do_codesign
+ fi
+ ;;
+esac
+
+if [[ "${INPUT_TAR/all/${platform}}" == "${platform}" ]] \
+ || [[ "${INPUT_TAR_XZ/all/${platform}}" == "${platform}" ]] \
+ || [[ "${INPUT_ZIP/all/${platform}}" == "${platform}" ]]; then
+ cwd="${PWD}"
+ tmpdir=$(mktemp -d)
+ mkdir -- "${tmpdir:?}/${archive}"
+ if [[ -n "${bin_leading_dir}" ]]; then
+ mkdir -p -- "${tmpdir}/${archive}/${bin_leading_dir}"/
+ # TODO: %%/* is wrong if bin_leading_dir starts with /
+ filenames=("${bin_leading_dir%%/*}")
+ else
+ filenames=("${bins[@]}")
+ fi
+ for bin_exe in "${bins[@]}"; do
+ if [[ -n "${bin_leading_dir}" ]]; then
+ x cp -- "${target_dir}/${bin_exe}" "${tmpdir}/${archive}/${bin_leading_dir}"/
+ else
+ x cp -- "${target_dir}/${bin_exe}" "${tmpdir}/${archive}"/
+ fi
+ done
+ for include in ${includes[@]+"${includes[@]}"}; do
+ x cp -r -- "${include}" "${tmpdir}/${archive}"/
+ filenames+=("$(basename -- "${include}")")
+ done
+ pushd -- "${tmpdir}" >/dev/null
+ if [[ -n "${leading_dir}" ]]; then
+ # with leading directory
+ #
+ # /${archive}
+ # /${archive}/${bins}
+ # /${archive}/${includes}
+ if [[ "${INPUT_TAR/all/${platform}}" == "${platform}" ]]; then
+ assets+=("${archive}.tar.gz")
+
+ if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
+ printf 'tar=%s.tar.gz\n' "${archive}" >>"${GITHUB_OUTPUT}"
+ else
+ warn "GITHUB_OUTPUT is not set; skip setting the 'tar' output"
+ printf 'tar: %s.tar.gz\n' "${archive}"
+ fi
+
+ x tar acf "${cwd}/${archive}.tar.gz" "${archive}"
+ fi
+ if [[ "${INPUT_TAR_XZ/all/${platform}}" == "${platform}" ]]; then
+ assets+=("${archive}.tar.xz")
+
+ if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
+ printf 'tar-xz=%s.tar.xz\n' "${archive}" >>"${GITHUB_OUTPUT}"
+ else
+ warn "GITHUB_OUTPUT is not set; skip setting the 'tar-xz' output"
+ printf 'tar-xz: %s.tar.xz\n' "${archive}"
+ fi
+
+ x tar acf "${cwd}/${archive}.tar.xz" "${archive}"
+ fi
+ if [[ "${INPUT_ZIP/all/${platform}}" == "${platform}" ]]; then
+ assets+=("${archive}.zip")
+
+ if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
+ printf 'zip=%s.zip\n' "${archive}" >>"${GITHUB_OUTPUT}"
+ else
+ warn "GITHUB_OUTPUT is not set; skip setting the 'zip' output"
+ printf 'zip: %s.zip\n' "${archive}"
+ fi
+
+ if [[ "${platform}" == "unix" ]]; then
+ x zip -r "${cwd}/${archive}.zip" "${archive}"
+ else
+ x 7z a "${cwd}/${archive}.zip" "${archive}"
+ fi
+ fi
+ else
+ # without leading directory
+ #
+ # /${bins}
+ # /${includes}
+ pushd -- "${archive}" >/dev/null
+ if [[ "${INPUT_TAR/all/${platform}}" == "${platform}" ]]; then
+ assets+=("${archive}.tar.gz")
+
+ if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
+ printf 'tar=%s.tar.gz\n' "${archive}" >>"${GITHUB_OUTPUT}"
+ else
+ warn "GITHUB_OUTPUT is not set; skip setting the 'tar' output"
+ printf 'tar: %s.tar.gz\n' "${archive}"
+ fi
+
+ x tar acf "${cwd}/${archive}.tar.gz" "${filenames[@]}"
+ fi
+ if [[ "${INPUT_TAR_XZ/all/${platform}}" == "${platform}" ]]; then
+ assets+=("${archive}.tar.xz")
+
+ if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
+ printf 'tar-xz=%s.tar.xz\n' "${archive}" >>"${GITHUB_OUTPUT}"
+ else
+ warn "GITHUB_OUTPUT is not set; skip setting the 'tar-xz' output"
+ printf 'tar-xz: %s.tar.xz\n' "${archive}"
+ fi
+
+ x tar acf "${cwd}/${archive}.tar.xz" "${filenames[@]}"
+ fi
+ if [[ "${INPUT_ZIP/all/${platform}}" == "${platform}" ]]; then
+ assets+=("${archive}.zip")
+
+ if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
+ printf 'zip=%s.zip\n' "${archive}" >>"${GITHUB_OUTPUT}"
+ else
+ warn "GITHUB_OUTPUT is not set; skip setting the 'zip' output"
+ printf 'zip: %s.zip\n' "${archive}"
+ fi
+
+ if [[ "${platform}" == "unix" ]]; then
+ x zip -r "${cwd}/${archive}.zip" "${filenames[@]}"
+ else
+ x 7z a "${cwd}/${archive}.zip" "${filenames[@]}"
+ fi
+ fi
+ popd >/dev/null
+ fi
+ popd >/dev/null
+ rm -rf -- "${tmpdir:?}/${archive}"
+fi
+
+# Checksum of all assets except for . files.
+final_assets=("${assets[@]}")
+for checksum in ${checksums[@]+"${checksums[@]}"}; do
+ # TODO: Should we allow customizing the name of checksum files?
+ if type -P "${checksum}sum" >/dev/null; then
+ "${checksum}sum" "${assets[@]}" >"${archive}.${checksum}"
+ else
+ # GitHub-hosted macOS runner does not install GNU Coreutils by default.
+ # https://github.com/actions/runner-images/issues/90
+ case "${checksum}" in
+ b2)
+ bail "checksum for '${checksum}' requires '${checksum}sum' command; consider installing it"
+ ;;
+ sha*)
+ if type -P shasum >/dev/null; then
+ shasum -a "${checksum#sha}" "${assets[@]}" >"${archive}.${checksum}"
+ else
+ bail "checksum for '${checksum}' requires '${checksum}sum' or 'shasum' command; consider installing one of them"
+ fi
+ ;;
+ md5)
+ if type -P md5 >/dev/null; then
+ md5 "${assets[@]}" >"${archive}.${checksum}"
+ else
+ bail "checksum for '${checksum}' requires '${checksum}sum' or 'md5' command; consider installing one of them"
+ fi
+ ;;
+ *) bail "unrecognized 'checksum' input option '${checksum}'" ;;
+ esac
+ fi
+ x cat -- "${archive}.${checksum}"
+
+ if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
+ printf '%s=%s.%s\n' "${checksum}" "${archive}" "${checksum}" >>"${GITHUB_OUTPUT}"
+ else
+ warn "GITHUB_OUTPUT is not set; skip setting the '${checksum}' output"
+ printf '%s: %s.%s\n' "${checksum}" "${archive}" "${checksum}"
+ fi
+
+ final_assets+=("${archive}.${checksum}")
+done
+
+if [[ -n "${dry_run}" ]]; then
+ if [[ -z "${dry_run_intended}" ]]; then
+ info "skipped upload because action is running in dry-run mode"
+ printf "tag: %s ('dry-run' if tag ref is not start with 'refs/tags/')\n" "${tag}"
+ fi
+ IFS=','
+ printf 'assets: %s\n' "${final_assets[*]}"
+ IFS=$'\n\t'
+else
+ # https://cli.github.com/manual/gh_release_upload
+ GITHUB_TOKEN="${token}" retry gh release upload "${tag}" "${final_assets[@]}" --clobber
+fi