release #124
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: release | |
| permissions: {} | |
| on: | |
| push: | |
| tags: | |
| - "v*.*.*" | |
| schedule: | |
| - cron: "0 6 * * *" | |
| workflow_dispatch: | |
| concurrency: | |
| group: release-${{ github.repository }}-${{ github.ref }}-${{ github.sha }} | |
| cancel-in-progress: false | |
| env: | |
| CARGO_TERM_COLOR: always | |
| IS_NIGHTLY: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} | |
| # Keep the base features in sync with `docker-publish.yml`. | |
| # Platform-specific release features are added in the build step. | |
| RUST_PROFILE: dist | |
| RUST_FEATURES: aws-kms,gcp-kms,turnkey,cli,asm-keccak,js-tracer,monad,optimism | |
| jobs: | |
| prepare: | |
| name: Prepare release | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| permissions: | |
| contents: write | |
| pull-requests: read | |
| outputs: | |
| tag_name: ${{ steps.release_info.outputs.tag_name }} | |
| release_tag_name: ${{ steps.release_info.outputs.release_tag_name }} | |
| release_name: ${{ steps.release_info.outputs.release_name }} | |
| changelog: ${{ steps.build_changelog.outputs.changelog }} | |
| skip_assets: ${{ steps.existing_release.outputs.skip_assets || 'false' }} | |
| steps: | |
| - name: Validate manual release ref | |
| if: ${{ github.event_name == 'workflow_dispatch' && github.ref != 'refs/heads/master' }} | |
| env: | |
| REF: ${{ github.ref }} | |
| run: | | |
| echo "::error::Manual nightly releases must run from refs/heads/master, not $REF." | |
| exit 1 | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| fetch-depth: 0 | |
| - name: Compute release name and tag | |
| id: release_info | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| stable_pattern='^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$' | |
| rc_pattern='^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)-rc([1-9][0-9]*)$' | |
| previous_tag() { | |
| local current="$1" | |
| local pattern="$2" | |
| { | |
| while read -r tag; do | |
| if [[ "$tag" =~ $pattern ]]; then | |
| printf '%s\n' "$tag" | |
| fi | |
| done < <(git tag -l) | |
| printf '%s\n' "$current" | |
| } | | |
| sort -V -u | | |
| awk -v current="$current" ' | |
| $0 == current { found=1 } | |
| !found { previous=$0 } | |
| END { print previous } | |
| ' | |
| } | |
| if [[ ${IS_NIGHTLY} == 'true' ]]; then | |
| tag_name="nightly-${GITHUB_SHA}" | |
| release_tag_name="staging-release-${GITHUB_SHA}" | |
| release_name="Nightly ($(date '+%Y-%m-%d'))" | |
| is_prerelease=true | |
| # Find the previous nightly tag for changelog generation, | |
| # sorted by tag creation date (most recent first). | |
| from_tag=$(git for-each-ref --sort=-creatordate --count=1 \ | |
| --format='%(refname:strip=2)' 'refs/tags/nightly-*') | |
| else | |
| tag_name="$GITHUB_REF_NAME" | |
| release_tag_name="$tag_name" | |
| release_name="$GITHUB_REF_NAME" | |
| if [[ "$GITHUB_REF_NAME" =~ $stable_pattern ]]; then | |
| PREV_STABLE=$(previous_tag "$GITHUB_REF_NAME" "$stable_pattern") | |
| if [[ -z "$PREV_STABLE" ]]; then | |
| echo "::error::No preceding strict stable tag found for $GITHUB_REF_NAME." | |
| exit 1 | |
| fi | |
| is_prerelease=false | |
| from_tag="$PREV_STABLE" | |
| elif [[ "$GITHUB_REF_NAME" =~ $rc_pattern ]]; then | |
| core="v${BASH_REMATCH[1]}.${BASH_REMATCH[2]}.${BASH_REMATCH[3]}" | |
| core_rc_pattern="^v${BASH_REMATCH[1]}\.${BASH_REMATCH[2]}\.${BASH_REMATCH[3]}-rc([1-9][0-9]*)$" | |
| rc_number=${BASH_REMATCH[4]} | |
| is_prerelease=true | |
| # Prefer the preceding strict RC for this version. For rc1, fall | |
| # back to the preceding strict stable release. | |
| PREV_RC=$(previous_tag "$GITHUB_REF_NAME" "$core_rc_pattern") | |
| if [[ -n "$PREV_RC" ]]; then | |
| from_tag="$PREV_RC" | |
| elif [[ "$rc_number" == 1 ]]; then | |
| from_tag=$(previous_tag "$core" "$stable_pattern") | |
| else | |
| echo "::error::No preceding strict RC tag found for $GITHUB_REF_NAME." | |
| exit 1 | |
| fi | |
| if [[ -z "$from_tag" ]]; then | |
| echo "::error::No preceding strict stable tag found for $GITHUB_REF_NAME." | |
| exit 1 | |
| fi | |
| else | |
| echo "::error::Release tag must be vX.Y.Z or vX.Y.Z-rcN without leading zeroes." | |
| exit 1 | |
| fi | |
| fi | |
| { | |
| printf 'tag_name=%s\n' "$tag_name" | |
| printf 'release_tag_name=%s\n' "$release_tag_name" | |
| printf 'release_name=%s\n' "$release_name" | |
| printf 'is_prerelease=%s\n' "$is_prerelease" | |
| printf 'from_tag=%s\n' "$from_tag" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Check existing nightly release | |
| id: existing_release | |
| if: ${{ env.IS_NIGHTLY == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| TAG_NAME: ${{ steps.release_info.outputs.tag_name }} | |
| RELEASE_TAG_NAME: ${{ steps.release_info.outputs.release_tag_name }} | |
| EXPECTED_SHA: ${{ github.sha }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| skip_assets=false | |
| releases="$(gh api --paginate --slurp "repos/$GH_REPO/releases?per_page=100" | jq 'add')" | |
| final="$(jq -c --arg tag "$TAG_NAME" '[.[] | select(.tag_name == $tag)]' <<< "$releases")" | |
| staging="$(jq -c --arg tag "$RELEASE_TAG_NAME" '[.[] | select(.tag_name == $tag)]' <<< "$releases")" | |
| final_count="$(jq 'length' <<< "$final")" | |
| staging_count="$(jq 'length' <<< "$staging")" | |
| if (( final_count > 1 || staging_count > 1 )); then | |
| echo "::error::Found duplicate final or staging nightly releases." | |
| exit 1 | |
| fi | |
| if (( final_count == 1 && staging_count == 1 )); then | |
| echo "::error::Both final release $TAG_NAME and staging release $RELEASE_TAG_NAME exist." | |
| exit 1 | |
| fi | |
| if (( final_count == 0 )); then | |
| tag_error="$(mktemp)" | |
| if final_ref="$(gh api "repos/$GH_REPO/git/ref/tags/$TAG_NAME" 2>"$tag_error")"; then | |
| if [[ "$(jq -r '.object.type' <<< "$final_ref")" != "commit" || "$(jq -r '.object.sha' <<< "$final_ref")" != "$EXPECTED_SHA" ]]; then | |
| echo "::error::Final nightly tag $TAG_NAME does not point to $EXPECTED_SHA." | |
| exit 1 | |
| fi | |
| echo "Final nightly tag $TAG_NAME already points to $EXPECTED_SHA; allowing publication retry." | |
| elif ! grep -q 'HTTP 404' "$tag_error"; then | |
| cat "$tag_error" >&2 | |
| echo "::error::Could not determine whether final nightly tag $TAG_NAME exists." | |
| exit 1 | |
| fi | |
| fi | |
| if (( final_count == 1 )); then | |
| if [[ "$(jq -r '.[0].draft' <<< "$final")" == "true" ]]; then | |
| echo "::error::Final nightly release $TAG_NAME unexpectedly exists as a draft." | |
| exit 1 | |
| fi | |
| if [[ "$(jq -r '.[0].prerelease' <<< "$final")" != "true" ]]; then | |
| echo "::error::Final nightly release $TAG_NAME is not a prerelease." | |
| exit 1 | |
| fi | |
| echo "Published nightly release $TAG_NAME already exists; skipping assets and retrying finalization." | |
| skip_assets=true | |
| elif (( staging_count == 1 )); then | |
| if [[ "$(jq -r '.[0].draft' <<< "$staging")" != "true" ]]; then | |
| echo "::error::Staging nightly release $RELEASE_TAG_NAME is already published." | |
| exit 1 | |
| fi | |
| if [[ "$(jq -r '.[0].prerelease' <<< "$staging")" != "true" ]]; then | |
| echo "::error::Staging nightly release $RELEASE_TAG_NAME is not a prerelease." | |
| exit 1 | |
| fi | |
| if [[ "$(jq -r '.[0].target_commitish' <<< "$staging")" != "$EXPECTED_SHA" ]]; then | |
| echo "::error::Staging nightly release $RELEASE_TAG_NAME does not target $EXPECTED_SHA." | |
| exit 1 | |
| fi | |
| echo "Draft nightly release $RELEASE_TAG_NAME already exists; continuing so assets can be uploaded." | |
| fi | |
| printf 'skip_assets=%s\n' "$skip_assets" >> "$GITHUB_OUTPUT" | |
| - name: Build changelog | |
| id: build_changelog | |
| if: ${{ steps.existing_release.outputs.skip_assets != 'true' }} | |
| uses: mikepenz/release-changelog-builder-action@c9bcd8238b6f41e05561348339429d360b1c0247 # v6.2.3 | |
| with: | |
| configuration: "./.github/changelog.json" | |
| fromTag: ${{ steps.release_info.outputs.from_tag || '' }} | |
| # The canonical nightly tag is created only when the complete release | |
| # is published, so use its commit directly while building the notes. | |
| toTag: ${{ (env.IS_NIGHTLY == 'true' && github.sha) || steps.release_info.outputs.tag_name }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Create the GitHub release as a draft up-front so that all matrix jobs | |
| # only upload assets to an existing draft. With immutable releases | |
| # enabled, an immutable release is sealed when it is published, so all | |
| # assets must be attached before that. Tagged releases are then left as | |
| # a draft for the protected `finalize release` workflow; nightlies are | |
| # published by the `publish-nightly` job at the end of this workflow. | |
| - name: Create draft release | |
| if: ${{ steps.existing_release.outputs.skip_assets != 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| TAG_NAME: ${{ steps.release_info.outputs.release_tag_name }} | |
| FINAL_TAG_NAME: ${{ steps.release_info.outputs.tag_name }} | |
| RELEASE_NAME: ${{ steps.release_info.outputs.release_name }} | |
| CHANGELOG: ${{ steps.build_changelog.outputs.changelog }} | |
| EXPECTED_PRERELEASE: ${{ steps.release_info.outputs.is_prerelease }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if release_state="$(gh release view "$TAG_NAME" --json isDraft,isPrerelease,targetCommitish --jq '[.isDraft, .isPrerelease, .targetCommitish] | @tsv' 2>/dev/null)"; then | |
| read -r is_draft is_prerelease target_commitish <<< "$release_state" | |
| if [[ "$is_draft" == "true" ]]; then | |
| if [[ "$is_prerelease" != "$EXPECTED_PRERELEASE" ]]; then | |
| echo "::error::Draft release $TAG_NAME has prerelease=$is_prerelease; expected $EXPECTED_PRERELEASE." | |
| exit 1 | |
| fi | |
| if [[ "$TAG_NAME" != "$FINAL_TAG_NAME" && "$target_commitish" != "$GITHUB_SHA" ]]; then | |
| echo "::error::Draft release $TAG_NAME targets $target_commitish; expected $GITHUB_SHA." | |
| exit 1 | |
| fi | |
| echo "Draft release $TAG_NAME already exists; reusing it." | |
| exit 0 | |
| fi | |
| echo "::error::Release $TAG_NAME is already published; cannot upload more assets to an immutable release." | |
| exit 1 | |
| fi | |
| notes_file="$(mktemp)" | |
| printf '%s' "$CHANGELOG" > "$notes_file" | |
| flags=(--draft --title "$RELEASE_NAME" --notes-file "$notes_file") | |
| if [[ "$TAG_NAME" == "$FINAL_TAG_NAME" ]]; then | |
| flags+=(--verify-tag) | |
| else | |
| flags+=(--target "$GITHUB_SHA") | |
| fi | |
| if [[ "$EXPECTED_PRERELEASE" == "true" ]]; then | |
| flags+=(--prerelease) | |
| fi | |
| gh release create "$TAG_NAME" "${flags[@]}" | |
| release-docker: | |
| name: Release Docker | |
| needs: prepare | |
| uses: ./.github/workflows/docker-publish.yml | |
| permissions: | |
| actions: read | |
| attestations: write | |
| artifact-metadata: write | |
| contents: read | |
| id-token: write | |
| packages: write | |
| with: | |
| tag_name: ${{ needs.prepare.outputs.tag_name }} | |
| release-docker-metadata: | |
| name: Record Docker digest | |
| runs-on: ubuntu-latest | |
| needs: [prepare, release-docker] | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Record release Docker digest | |
| env: | |
| DIGEST: ${{ needs.release-docker.outputs.digest }} | |
| run: | | |
| if [[ ! "$DIGEST" =~ ^sha256:[0-9a-f]{64}$ ]]; then | |
| echo "::error::Invalid Docker digest: $DIGEST" | |
| exit 1 | |
| fi | |
| printf '%s\n' "$DIGEST" > release-docker-digest.txt | |
| - name: Upload release Docker digest | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: release-docker-digest | |
| path: release-docker-digest.txt | |
| retention-days: 90 | |
| if-no-files-found: error | |
| # This job uploads assets to the draft release created in `prepare`. | |
| # Tagged releases stay as drafts for the protected finalization workflow; | |
| # nightlies are auto-published by the `publish-nightly` job below. Either | |
| # way, GitHub's immutable-releases setting seals the release at publish. | |
| release: | |
| permissions: | |
| attestations: write | |
| artifact-metadata: write | |
| contents: write | |
| id-token: write | |
| name: release ${{ matrix.target }} (${{ matrix.runner }}) | |
| runs-on: ${{ matrix.runner }} | |
| timeout-minutes: 240 | |
| needs: prepare | |
| if: ${{ needs.prepare.outputs.skip_assets != 'true' }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # `runner`: GHA runner label | |
| # `target`: Rust build target triple | |
| # `platform` and `arch`: Used in tarball names | |
| # `svm`: target platform to use for the Solc binary: https://github.com/roynalnaruto/svm-rs/blob/84cbe0ac705becabdc13168bae28a45ad2299749/svm-builds/build.rs#L4-L24 | |
| # These are pinned to the oldest runner versions to support old libc/SDK versions. | |
| - runner: depot-ubuntu-22.04-16 | |
| target: x86_64-unknown-linux-gnu | |
| svm_target_platform: linux-amd64 | |
| platform: linux | |
| arch: amd64 | |
| - runner: depot-ubuntu-22.04-16 | |
| target: x86_64-unknown-linux-musl | |
| svm_target_platform: linux-amd64 | |
| platform: alpine | |
| arch: amd64 | |
| - runner: depot-ubuntu-22.04-arm-16 | |
| target: aarch64-unknown-linux-gnu | |
| svm_target_platform: linux-aarch64 | |
| platform: linux | |
| arch: arm64 | |
| - runner: depot-ubuntu-22.04-16 | |
| target: aarch64-unknown-linux-musl | |
| svm_target_platform: linux-aarch64 | |
| platform: alpine | |
| arch: arm64 | |
| - runner: macos-14-large | |
| target: x86_64-apple-darwin | |
| svm_target_platform: macosx-amd64 | |
| platform: darwin | |
| arch: amd64 | |
| - runner: macos-latest-large | |
| target: aarch64-apple-darwin | |
| svm_target_platform: macosx-aarch64 | |
| platform: darwin | |
| arch: arm64 | |
| - runner: depot-windows-latest-16 | |
| target: x86_64-pc-windows-msvc | |
| svm_target_platform: windows-amd64 | |
| platform: win32 | |
| arch: amd64 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # v1 | |
| with: | |
| toolchain: stable | |
| targets: ${{ matrix.target }} | |
| - uses: rui314/setup-mold@7e4f20ad28a2e8ca6fd0892ccf72e2abb706b9c3 # v1 | |
| - name: Apple Silicon setup | |
| if: matrix.target == 'aarch64-apple-darwin' | |
| run: | | |
| printf 'SDKROOT=%s\n' "$(xcrun -sdk macosx --show-sdk-path)" >> "$GITHUB_ENV" | |
| # Apple Silicon and the Touch ID shim require macOS 11 or newer. | |
| printf 'MACOSX_DEPLOYMENT_TARGET=11.0\n' >> "$GITHUB_ENV" | |
| - name: cross setup | |
| if: contains(matrix.target, 'musl') | |
| run: | | |
| cargo install cross --locked \ | |
| --git https://github.com/cross-rs/cross \ | |
| --rev 64b5bb4d3d34de062552b9a2093affe77b4ad16a | |
| - name: Build binaries | |
| env: | |
| TAG_NAME: ${{ (env.IS_NIGHTLY == 'true' && 'nightly') || needs.prepare.outputs.tag_name }} | |
| SVM_TARGET_PLATFORM: ${{ matrix.svm_target_platform }} | |
| PLATFORM_NAME: ${{ matrix.platform }} | |
| TARGET: ${{ matrix.target }} | |
| OUT_DIR: target/${{ matrix.target }}/${{ env.RUST_PROFILE }} | |
| shell: bash | |
| run: | | |
| set -eo pipefail | |
| features="$RUST_FEATURES" | |
| # The Touch ID Swift shim requires macOS 11. Keep the packaged Intel | |
| # artifact on its existing deployment target and enable Touch ID only | |
| # for the Apple Silicon release artifact. | |
| if [[ "$TARGET" == "aarch64-apple-darwin" ]]; then | |
| features="${features},touch-id" | |
| fi | |
| echo "Building $TARGET with features: $features" | |
| flags=(--locked --target "$TARGET" --profile "$RUST_PROFILE" --bins | |
| --no-default-features --features "$features") | |
| # `jemalloc` is not fully supported on MSVC or aarch64 Linux. | |
| if [[ "$TARGET" != *msvc* && "$TARGET" != "aarch64-unknown-linux-gnu" ]]; then | |
| flags+=(--features jemalloc) | |
| fi | |
| [[ "$TARGET" == *windows* ]] && ext=".exe" | |
| if [[ "$TARGET" == *-musl ]]; then | |
| cross build "${flags[@]}" | |
| else | |
| cargo build "${flags[@]}" | |
| fi | |
| bins=(anvil cast chisel forge solar) | |
| for name in "${bins[@]}"; do | |
| bin="$OUT_DIR/$name$ext" | |
| printf '\n' | |
| file "$bin" || true | |
| du -h "$bin" || true | |
| ldd "$bin" || true | |
| $bin --version || true | |
| printf '%s_bin_path=%s\n' "$name" "$bin" >> "$GITHUB_ENV" | |
| done | |
| - name: Archive binaries | |
| id: artifacts | |
| env: | |
| PLATFORM_NAME: ${{ matrix.platform }} | |
| OUT_DIR: target/${{ matrix.target }}/${{ env.RUST_PROFILE }} | |
| VERSION_NAME: ${{ (env.IS_NIGHTLY == 'true' && 'nightly') || needs.prepare.outputs.tag_name }} | |
| ARCH: ${{ matrix.arch }} | |
| shell: bash | |
| run: | | |
| if [[ "$PLATFORM_NAME" == "linux" || "$PLATFORM_NAME" == "alpine" ]]; then | |
| tar -czvf "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" -C "$OUT_DIR" forge cast anvil chisel solar | |
| file_name="foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" | |
| elif [ "$PLATFORM_NAME" == "darwin" ]; then | |
| # We need to use gtar here otherwise the archive is corrupt. | |
| # See: https://github.com/actions/virtual-environments/issues/2619 | |
| gtar -czvf "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" -C "$OUT_DIR" forge cast anvil chisel solar | |
| file_name="foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.tar.gz" | |
| else | |
| cd "$OUT_DIR" | |
| 7z a -tzip "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.zip" forge.exe cast.exe anvil.exe chisel.exe solar.exe | |
| mv "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.zip" ../../../ | |
| file_name="foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.zip" | |
| fi | |
| { | |
| printf "file_name=%s\n" "$file_name" | |
| printf "foundry_attestation=%s\n" "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.attestation.txt" | |
| printf "foundry_sbom=%s\n" "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.spdx.json" | |
| printf "foundry_checksum=%s\n" "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.sha256" | |
| printf "foundry_signature=%s\n" "foundry_${VERSION_NAME}_${PLATFORM_NAME}_${ARCH}.sigstore.json" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Generate archive checksum | |
| env: | |
| FILE_NAME: ${{ steps.artifacts.outputs.file_name }} | |
| FOUNDRY_CHECKSUM: ${{ steps.artifacts.outputs.foundry_checksum }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if command -v sha256sum >/dev/null 2>&1; then | |
| sha256sum "$FILE_NAME" > "$FOUNDRY_CHECKSUM" | |
| else | |
| shasum -a 256 "$FILE_NAME" > "$FOUNDRY_CHECKSUM" | |
| fi | |
| cat "$FOUNDRY_CHECKSUM" | |
| - name: Install Syft | |
| uses: anchore/sbom-action/download-syft@e22c389904149dbc22b58101806040fa8d37a610 # v0.24.0 | |
| - name: Generate SBOM (SPDX) | |
| env: | |
| FOUNDRY_SBOM: ${{ steps.artifacts.outputs.foundry_sbom }} | |
| VERSION_NAME: ${{ (env.IS_NIGHTLY == 'true' && 'nightly') || needs.prepare.outputs.tag_name }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| syft scan dir:. \ | |
| --source-name foundry \ | |
| --source-version "$VERSION_NAME" \ | |
| -o spdx-json="$FOUNDRY_SBOM" | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| retention-days: 1 | |
| name: ${{ steps.artifacts.outputs.file_name }} | |
| path: ${{ steps.artifacts.outputs.file_name }} | |
| - name: Build man page | |
| id: man | |
| if: matrix.target == 'x86_64-unknown-linux-gnu' | |
| env: | |
| OUT_DIR: target/${{ matrix.target }}/${{ env.RUST_PROFILE }} | |
| VERSION_NAME: ${{ (env.IS_NIGHTLY == 'true' && 'nightly') || needs.prepare.outputs.tag_name }} | |
| shell: bash | |
| run: | | |
| sudo apt-get -y install help2man | |
| help2man -N "$OUT_DIR/forge" > forge.1 | |
| help2man -N "$OUT_DIR/cast" > cast.1 | |
| help2man -N "$OUT_DIR/anvil" > anvil.1 | |
| help2man -N "$OUT_DIR/chisel" > chisel.1 | |
| help2man -N "$OUT_DIR/solar" > solar.1 | |
| gzip forge.1 | |
| gzip cast.1 | |
| gzip anvil.1 | |
| gzip chisel.1 | |
| gzip solar.1 | |
| tar -czvf "foundry_man_${VERSION_NAME}.tar.gz" forge.1.gz cast.1.gz anvil.1.gz chisel.1.gz solar.1.gz | |
| printf 'foundry_man=%s\n' "foundry_man_${VERSION_NAME}.tar.gz" >> "$GITHUB_OUTPUT" | |
| - name: Binaries and archive provenance attestation | |
| id: attestation | |
| uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2 | |
| with: | |
| subject-path: | | |
| ${{ env.anvil_bin_path }} | |
| ${{ env.cast_bin_path }} | |
| ${{ env.chisel_bin_path }} | |
| ${{ env.forge_bin_path }} | |
| ${{ env.solar_bin_path }} | |
| ${{ steps.artifacts.outputs.file_name }} | |
| - name: Archive SBOM attestation | |
| uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2 | |
| with: | |
| subject-path: ${{ steps.artifacts.outputs.file_name }} | |
| sbom-path: ${{ steps.artifacts.outputs.foundry_sbom }} | |
| - name: Install cosign | |
| uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2 | |
| - name: Sign archive with cosign (keyless) | |
| env: | |
| FILE_NAME: ${{ steps.artifacts.outputs.file_name }} | |
| FOUNDRY_SIGNATURE: ${{ steps.artifacts.outputs.foundry_signature }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| cosign sign-blob \ | |
| --yes \ | |
| --bundle "$FOUNDRY_SIGNATURE" \ | |
| "$FILE_NAME" | |
| - name: Record attestation URL | |
| env: | |
| ATTESTATION_URL: ${{ steps.attestation.outputs.attestation-url }} | |
| FOUNDRY_ATTESTATION: ${{ steps.artifacts.outputs.foundry_attestation }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| printf '%s\n' "$ATTESTATION_URL" > "$FOUNDRY_ATTESTATION" | |
| # Upload assets to the draft release created in `prepare`. Tagged releases | |
| # stay as drafts after this workflow finishes; a maintainer runs the | |
| # protected finalization workflow. Nightlies are auto-published below. | |
| - name: Upload assets to draft release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| TAG_NAME: ${{ needs.prepare.outputs.release_tag_name }} | |
| FILE_NAME: ${{ steps.artifacts.outputs.file_name }} | |
| FOUNDRY_ATTESTATION: ${{ steps.artifacts.outputs.foundry_attestation }} | |
| FOUNDRY_SBOM: ${{ steps.artifacts.outputs.foundry_sbom }} | |
| FOUNDRY_CHECKSUM: ${{ steps.artifacts.outputs.foundry_checksum }} | |
| FOUNDRY_SIGNATURE: ${{ steps.artifacts.outputs.foundry_signature }} | |
| FOUNDRY_MAN: ${{ steps.man.outputs.foundry_man }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| files=( | |
| "$FILE_NAME" | |
| "$FOUNDRY_ATTESTATION" | |
| "$FOUNDRY_SBOM" | |
| "$FOUNDRY_CHECKSUM" | |
| "$FOUNDRY_SIGNATURE" | |
| ) | |
| if [[ -n "${FOUNDRY_MAN:-}" ]]; then | |
| files+=("$FOUNDRY_MAN") | |
| fi | |
| gh release upload "$TAG_NAME" "${files[@]}" --clobber | |
| # Auto-publish nightly releases once all assets have been uploaded so that | |
| # foundryup and other consumers see them immediately. Tagged releases are | |
| # left as drafts for the protected finalization workflow. | |
| publish-nightly: | |
| name: Publish nightly release | |
| runs-on: ubuntu-latest | |
| needs: [prepare, release-docker, release-docker-metadata, release] | |
| if: ${{ always() && (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && needs.prepare.result == 'success' && needs.release-docker.result == 'success' && needs.release-docker-metadata.result == 'success' && (needs.release.result == 'success' || (needs.prepare.outputs.skip_assets == 'true' && needs.release.result == 'skipped')) }} | |
| concurrency: | |
| group: release-nightly-promotion | |
| cancel-in-progress: false | |
| queue: max | |
| permissions: | |
| contents: write | |
| packages: write | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Publish release and promote nightly image | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| TAG_NAME: ${{ needs.prepare.outputs.tag_name }} | |
| RELEASE_TAG_NAME: ${{ needs.prepare.outputs.release_tag_name }} | |
| DIGEST: ${{ needs.release-docker.outputs.digest }} | |
| shell: bash | |
| run: python3 .github/scripts/finalize-release.py nightly --tag "$TAG_NAME" --release-tag "$RELEASE_TAG_NAME" --digest "$DIGEST" | |
| # If any of the jobs fail, this will create a high-priority issue to signal so. | |
| issue: | |
| name: Open an issue | |
| runs-on: ubuntu-latest | |
| needs: [ | |
| prepare, | |
| release-docker, | |
| release-docker-metadata, | |
| release, | |
| publish-nightly, | |
| ] | |
| if: failure() | |
| permissions: | |
| contents: read | |
| issues: write | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - uses: JasonEtco/create-an-issue@1b14a70e4d8dc185e5cc76d3bec9eab20257b2c5 # v2.9.2 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| WORKFLOW_URL: | | |
| ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| with: | |
| update_existing: true | |
| filename: .github/RELEASE_FAILURE_ISSUE_TEMPLATE.md |