Skip to content

release

release #124

Workflow file for this run

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