additional tweaks to release process #51
Workflow file for this run
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 | |
| on: | |
| push: | |
| tags: | |
| - "v[0-9]+.[0-9]+.[0-9]+" | |
| - "v[0-9]+.[0-9]+.[0-9]+-*" | |
| concurrency: | |
| group: release | |
| cancel-in-progress: false | |
| env: | |
| DIST: dist-${{ github.ref_name }} | |
| jobs: | |
| verify-tag: | |
| name: Verify tag | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| environment: | |
| name: release | |
| # inspired by Caddy's release process: https://github.com/caddyserver/caddy/blob/987375297862d9cd0a3fa33cfb199c25e504ad1b/.github/workflows/release.yml#L29C1-L143C54 | |
| outputs: | |
| verification_passed: ${{ steps.verify.outputs.passed }} | |
| tag_version: ${{ steps.info.outputs.version }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| - name: Force fetch upstream tags | |
| run: git fetch --tags --force | |
| - name: Get tag info | |
| id: info | |
| run: | | |
| echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT | |
| echo "version_tag=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT | |
| echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT | |
| - name: Validate commits and tag signatures | |
| id: verify | |
| env: | |
| RELEASE_MANAGERS: ${{ vars.RELEASE_MANAGERS }} | |
| run: | | |
| if [ -z "${RELEASE_MANAGERS}" ]; then | |
| echo "RELEASE_MANAGERS variable is not set" | |
| echo "passed=false" >> $GITHUB_OUTPUT | |
| exit 1 | |
| fi | |
| IFS=',' read -ra rms <<< "${RELEASE_MANAGERS}" | |
| for u in "${rms[@]}"; do | |
| curl -fsSL "https://github.com/${u}.gpg" | gpg --batch --import >/dev/null | |
| done | |
| echo "Verifying the tag: ${{ steps.info.outputs.version_tag }}" | |
| if ! git verify-tag -v "${{ steps.info.outputs.version_tag }}" 2>&1; then | |
| echo "❌ Tag verification failed!" | |
| echo "passed=false" >> $GITHUB_OUTPUT | |
| exit 1 | |
| fi | |
| # Run it again to capture the output | |
| git verify-tag -v "${{ steps.info.outputs.version_tag }}" 2>&1 | tee /tmp/verify-output.txt; | |
| # SSH verification output typically includes the key fingerprint | |
| # Use GNU grep with Perl regex for cleaner extraction (Linux environment) | |
| KEY_SHA256=$(grep -oP "SHA256:[\"']?\K[A-Za-z0-9+/=]+(?=[\"']?)" /tmp/verify-output.txt | head -1 || echo "") | |
| if [ -z "$KEY_SHA256" ]; then | |
| # Try alternative pattern with "key" prefix | |
| KEY_SHA256=$(grep -oP "key SHA256:[\"']?\K[A-Za-z0-9+/=]+(?=[\"']?)" /tmp/verify-output.txt | head -1 || echo "") | |
| fi | |
| if [ -z "$KEY_SHA256" ]; then | |
| # Fallback: extract any base64-like string (40+ chars) | |
| KEY_SHA256=$(grep -oP '[A-Za-z0-9+/]{40,}=?' /tmp/verify-output.txt | head -1 || echo "") | |
| fi | |
| if [ -z "$KEY_SHA256" ]; then | |
| echo "Somehow could not extract SSH key fingerprint from git verify-tag output" | |
| echo "passed=false" >> $GITHUB_OUTPUT | |
| exit 1 | |
| fi | |
| echo "✅ Tag verification succeeded!" | |
| echo "passed=true" >> $GITHUB_OUTPUT | |
| echo "key_id=$KEY_SHA256" >> $GITHUB_OUTPUT | |
| - name: Summary | |
| run: | | |
| echo "## Tag Verification Summary 🔐" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Tag:** ${{ steps.info.outputs.version }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Commit:** ${{ steps.info.outputs.sha }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Signature:** ✅ Verified" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Signed by:** ${{ steps.verify.outputs.key_id }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Proceeding with release build..." >> $GITHUB_STEP_SUMMARY | |
| publish-crates: | |
| name: Publish crates | |
| needs: verify-tag | |
| if: ${{ needs.verify-tag.outputs.verification_passed == 'true' }} | |
| uses: ./.github/workflows/publish-crates.yaml | |
| with: | |
| dry_run: false | |
| secrets: inherit | |
| publish-npmjs: | |
| name: Publish npmjs packages | |
| needs: verify-tag | |
| if: ${{ needs.verify-tag.outputs.verification_passed == 'true' }} | |
| uses: ./.github/workflows/publish-npmjs.yaml | |
| with: | |
| dry_run: false | |
| secrets: inherit | |
| publish-dockerhub: | |
| name: Publish Docker image to Dockerhub | |
| runs-on: ubuntu-latest | |
| needs: verify-tag | |
| if: ${{ needs.verify-tag.outputs.verification_passed == 'true' }} | |
| permissions: | |
| contents: read | |
| id-token: write | |
| attestations: write | |
| artifact-metadata: write | |
| environment: | |
| name: release | |
| env: | |
| IMAGE_NAME: solanafoundation/anchor | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Login to Docker Hub | |
| uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 | |
| with: | |
| username: ${{ vars.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 | |
| - name: Docker metadata | |
| id: meta | |
| uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0 | |
| with: | |
| images: ${{ env.IMAGE_NAME }} | |
| tags: | | |
| type=ref,event=tag | |
| type=sha | |
| - name: Build and push | |
| id: build | |
| uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2 | |
| with: | |
| context: docker/build | |
| push: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| build-args: | | |
| SOLANA_CLI=v2.3.0 | |
| ANCHOR_CLI=${{ github.ref_name }} | |
| provenance: mode=max | |
| sbom: true | |
| - name: Attest build provenance | |
| uses: actions/attest-build-provenance@96278af6caaf10aea03fd8d33a09a777ca52d62f # v3.2.0 | |
| id: attest | |
| with: | |
| subject-name: ${{ env.IMAGE_NAME }} | |
| subject-digest: ${{ steps.build.outputs.digest }} | |
| push-to-registry: false # provenance: mode=max above already pushes it to Dockerhub | |
| build-cli: | |
| name: Build binaries | |
| needs: verify-tag | |
| if: ${{ needs.verify-tag.outputs.verification_passed == 'true' }} | |
| uses: ./.github/workflows/build-cli.yaml | |
| with: | |
| dry_run: false | |
| dist: dist-${{ github.ref_name }} | |
| upload-cli: | |
| name: Upload binaries to release | |
| runs-on: ubuntu-latest | |
| needs: [verify-tag, build-cli] | |
| if: ${{ needs.verify-tag.outputs.verification_passed == 'true' }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 | |
| with: | |
| pattern: anchor-* | |
| path: ${{ env.DIST }} | |
| - name: Upload | |
| shell: bash | |
| run: | | |
| publish_args=() | |
| # If version looks like vX.Y.Z-<something> (e.g. v1.0.0-rc.2), publish as a pre-release | |
| if [[ "$GITHUB_REF_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-.+ ]]; then | |
| publish_args+=(--prerelease) | |
| # avm (and its older versions) do not filter out pre-releases right now, | |
| # meaning that if we were to create a pre-release, this would change the | |
| # default version installed for new users | |
| echo "Skipping creating GitHub release for a pre-release." | |
| exit 0 | |
| fi | |
| GH_TOKEN=${{ secrets.GITHUB_TOKEN }} gh release create $GITHUB_REF_NAME $DIST/*/* --title "$GITHUB_REF_NAME" "${publish_args[@]}" | |
| publish-cli: | |
| name: Publish CLI onto npmjs | |
| runs-on: ubuntu-latest | |
| needs: [verify-tag, build-cli] | |
| if: ${{ needs.verify-tag.outputs.verification_passed == 'true' }} | |
| permissions: | |
| id-token: write | |
| contents: read | |
| environment: | |
| name: release | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 | |
| with: | |
| node-version: "24" | |
| registry-url: "https://registry.npmjs.org" | |
| package-manager-cache: false | |
| - name: Enable corepack (yarn) | |
| run: corepack enable | |
| - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 | |
| with: | |
| pattern: anchor-* | |
| path: ${{ env.DIST }} | |
| - run: | | |
| set -xeuo pipefail | |
| cp $DIST/anchor-*-x86_64-unknown-linux-gnu/anchor-*-x86_64-unknown-linux-gnu cli/npm-package/anchor | |
| cd cli/npm-package/ | |
| chmod +x anchor | |
| version="$(node -p "require('./package.json').version")" | |
| publish_args=() | |
| # If version looks like X.Y.Z-<something> (e.g. 1.0.0-rc.2), publish under dist-tag "next" | |
| if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+-.+ ]]; then | |
| publish_args+=(--tag next) | |
| fi | |
| echo "Publishing CLI" | |
| npm publish "${publish_args[@]}" --provenance --access public |