Something went wrong. Try again.
My version of https://github.com/vercel-labs/fx
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329name: Release
on: push: branches: [main] workflow_dispatch: inputs: validate_only: description: Build all platforms and compare arm64 signing pages without publishing type: boolean default: false
permissions: contents: read
concurrency: group: stable-release cancel-in-progress: false
jobs: check-version: if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest outputs: needed: ${{ steps.check.outputs.needed }} publish: ${{ steps.check.outputs.publish }} version: ${{ steps.check.outputs.version }} steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: fetch-depth: 0
- name: Check if release needed id: check env: VALIDATE_ONLY: ${{ github.event_name == 'workflow_dispatch' && inputs.validate_only }} run: | VERSION=$(grep 'pub const version' src/main.zig | head -1 | sed 's/.*"\(.*\)".*/\1/') if ! [[ "$VERSION" =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]]; then echo "::error::src/main.zig version is not strict SemVer" exit 1 fi TAG="v${VERSION}" echo "version=${TAG}" >> "$GITHUB_OUTPUT" if [[ "$VALIDATE_ONLY" == true ]]; then echo "needed=true" >> "$GITHUB_OUTPUT" echo "publish=false" >> "$GITHUB_OUTPUT" echo "Validating release artifacts without publishing" elif git rev-parse "$TAG" >/dev/null 2>&1; then echo "needed=false" >> "$GITHUB_OUTPUT" echo "publish=false" >> "$GITHUB_OUTPUT" echo "Tag ${TAG} exists, no release needed" else echo "needed=true" >> "$GITHUB_OUTPUT" echo "publish=true" >> "$GITHUB_OUTPUT" echo "Tag ${TAG} missing, release needed" fi
build-linux: needs: check-version if: needs.check-version.outputs.needed == 'true' name: Build ${{ matrix.name }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: include: - name: linux-x86_64 target: x86_64-linux - name: linux-aarch64 target: aarch64-linux steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Setup Zig uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2 with: version: "0.16.0"
- name: Build run: zig build -Doptimize=ReleaseSafe -Dtarget=${{ matrix.target }}
- name: Package run: | python3 -c 'import pathlib; n = pathlib.Path("zig-out/bin/fx").stat().st_size; print(f"linux executable: {n:,} bytes ({n / 1048576:.6f} MiB)")' >> "$GITHUB_STEP_SUMMARY" package_dir="$RUNNER_TEMP/fx-package-${{ matrix.name }}" mkdir -p "$package_dir" cp zig-out/bin/fx LICENSE THIRD_PARTY_NOTICES.md "$package_dir/" tar -czf fx-${{ matrix.name }}.tar.gz -C "$package_dir" fx LICENSE THIRD_PARTY_NOTICES.md sha256sum fx-${{ matrix.name }}.tar.gz > fx-${{ matrix.name }}.tar.gz.sha256
- name: Upload artifact uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: fx-${{ matrix.name }} path: | fx-${{ matrix.name }}.tar.gz fx-${{ matrix.name }}.tar.gz.sha256
build-macos-x86_64: needs: check-version if: needs.check-version.outputs.needed == 'true' name: Build macos-x86_64 runs-on: macos-15-intel environment: apple-signing steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Setup Zig uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2 with: version: "0.16.0"
- name: Build run: zig build -Doptimize=ReleaseSafe -Dtarget=x86_64-macos
- name: Sign and notarize env: APPLE_DEVELOPER_ID_P12_BASE64: ${{ secrets.APPLE_DEVELOPER_ID_P12_BASE64 }} APPLE_DEVELOPER_ID_P12_PASSWORD: ${{ secrets.APPLE_DEVELOPER_ID_P12_PASSWORD }} APPLE_NOTARY_KEY_P8_BASE64: ${{ secrets.APPLE_NOTARY_KEY_P8_BASE64 }} APPLE_NOTARY_KEY_ID: ${{ secrets.APPLE_NOTARY_KEY_ID }} APPLE_NOTARY_ISSUER_ID: ${{ secrets.APPLE_NOTARY_ISSUER_ID }} run: scripts/sign-and-notarize-macos.sh zig-out/bin/fx
- name: Verify notarized Intel binary if: github.event_name == 'workflow_dispatch' && inputs.validate_only run: | codesign --verify --strict --check-notarization -R=notarized ./zig-out/bin/fx ./zig-out/bin/fx --version ./zig-out/bin/fx help
- name: Package run: | python3 -c 'import pathlib; n = pathlib.Path("zig-out/bin/fx").stat().st_size; print(f"macOS Intel signed executable: {n:,} bytes ({n / 1048576:.6f} MiB)")' >> "$GITHUB_STEP_SUMMARY" package_dir="$RUNNER_TEMP/fx-package-macos-x86_64" mkdir -p "$package_dir" cp zig-out/bin/fx LICENSE THIRD_PARTY_NOTICES.md "$package_dir/" tar -czf fx-macos-x86_64.tar.gz -C "$package_dir" fx LICENSE THIRD_PARTY_NOTICES.md shasum -a 256 fx-macos-x86_64.tar.gz > fx-macos-x86_64.tar.gz.sha256
- name: Upload artifact uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: fx-macos-x86_64 path: | fx-macos-x86_64.tar.gz fx-macos-x86_64.tar.gz.sha256
build-macos-arm64: needs: check-version if: needs.check-version.outputs.needed == 'true' permissions: actions: read contents: read uses: ./.github/workflows/pgso-macos-arm64.yml
sign-macos-arm64: needs: [check-version, build-macos-arm64] if: needs.check-version.outputs.needed == 'true' name: Sign macos-arm64 runs-on: macos-15 environment: apple-signing timeout-minutes: 20 permissions: actions: read contents: read steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Require native arm64 host run: test "$(uname -m)" = arm64
- name: Download eligible candidate uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: artifact-ids: ${{ needs.build-macos-arm64.outputs.artifact-id }} merge-multiple: true path: ${{ runner.temp }}/fx-pgso-aggregate
- name: Restore executable mode run: chmod +x "$RUNNER_TEMP/fx-pgso-aggregate/candidate/fx"
- name: Confirm release eligibility run: python3 -m scripts.pgso report --output-dir "$RUNNER_TEMP/fx-pgso-aggregate" >/dev/null
- name: Sign and notarize stable release candidate env: VALIDATE_ONLY: ${{ github.event_name == 'workflow_dispatch' && inputs.validate_only }} APPLE_DEVELOPER_ID_P12_BASE64: ${{ secrets.APPLE_DEVELOPER_ID_P12_BASE64 }} APPLE_DEVELOPER_ID_P12_PASSWORD: ${{ secrets.APPLE_DEVELOPER_ID_P12_PASSWORD }} APPLE_NOTARY_KEY_P8_BASE64: ${{ secrets.APPLE_NOTARY_KEY_P8_BASE64 }} APPLE_NOTARY_KEY_ID: ${{ secrets.APPLE_NOTARY_KEY_ID }} APPLE_NOTARY_ISSUER_ID: ${{ secrets.APPLE_NOTARY_ISSUER_ID }} run: | if [[ "$VALIDATE_ONLY" == true ]]; then control_dir="$RUNNER_TEMP/fx-signing-control" mkdir -p "$control_dir" cp "$RUNNER_TEMP/fx-pgso-aggregate/candidate/fx" "$control_dir/fx" scripts/sign-and-notarize-macos.sh "$control_dir/fx" 4096 scripts/sign-and-notarize-macos.sh "$RUNNER_TEMP/fx-pgso-aggregate/candidate/fx" 16384 else scripts/sign-and-notarize-macos.sh "$RUNNER_TEMP/fx-pgso-aggregate/candidate/fx" fi
- name: Verify notarized arm64 binaries if: github.event_name == 'workflow_dispatch' && inputs.validate_only run: | for binary in "$RUNNER_TEMP/fx-signing-control/fx" "$RUNNER_TEMP/fx-pgso-aggregate/candidate/fx"; do codesign --verify --strict --check-notarization -R=notarized "$binary" "$binary" --version "$binary" help python3 -c 'import pathlib, sys; n = pathlib.Path(sys.argv[1]).stat().st_size; print(f"{sys.argv[1]}: {n:,} bytes ({n / 1048576:.6f} MiB)")' "$binary" >> "$GITHUB_STEP_SUMMARY" shasum -a 256 "$binary" >> "$GITHUB_STEP_SUMMARY" done tar -czf "$RUNNER_TEMP/fx-signing-control.tar.gz" -C "$RUNNER_TEMP/fx-signing-control" fx
- name: Upload arm64 signing control if: github.event_name == 'workflow_dispatch' && inputs.validate_only uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: signing-control-macos-arm64 path: ${{ runner.temp }}/fx-signing-control.tar.gz if-no-files-found: error
- name: Package stable release candidate run: | archive="$RUNNER_TEMP/fx-macos-aarch64.tar.gz" package_dir="$RUNNER_TEMP/fx-release-package" mkdir -p "$package_dir" cp "$RUNNER_TEMP/fx-pgso-aggregate/candidate/fx" LICENSE THIRD_PARTY_NOTICES.md "$package_dir/" tar -czf "$archive" -C "$package_dir" fx LICENSE THIRD_PARTY_NOTICES.md cd "$RUNNER_TEMP" shasum -a 256 fx-macos-aarch64.tar.gz > fx-macos-aarch64.tar.gz.sha256
- name: Upload stable release artifact uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: fx-macos-aarch64 path: | ${{ runner.temp }}/fx-macos-aarch64.tar.gz ${{ runner.temp }}/fx-macos-aarch64.tar.gz.sha256 if-no-files-found: error
release: needs: [check-version, build-linux, build-macos-x86_64, sign-macos-arm64] if: needs.check-version.outputs.publish == 'true' && !(github.event_name == 'workflow_dispatch' && inputs.validate_only) runs-on: ubuntu-latest environment: release permissions: contents: write steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- name: Download artifacts uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: pattern: fx-* merge-multiple: true
- name: Create git tag env: RELEASE_TAG: ${{ needs.check-version.outputs.version }} run: | set -euo pipefail [[ "$RELEASE_TAG" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]] git tag -- "$RELEASE_TAG" git push origin "refs/tags/${RELEASE_TAG}"
- name: Extract changelog entry run: | awk '/<!-- release:start -->/{found=1; next} /<!-- release:end -->/{found=0} found{print}' CHANGELOG.md > /tmp/release-notes.md LINES=$(wc -l < /tmp/release-notes.md | tr -d ' ') if [ "$LINES" -lt 2 ]; then echo "Error: No release notes found between <!-- release:start --> and <!-- release:end --> markers in CHANGELOG.md" exit 1 fi echo "Extracted release notes ($LINES lines)"
- name: Create GitHub Release uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2 with: tag_name: ${{ needs.check-version.outputs.version }} body_path: /tmp/release-notes.md files: | fx-*.tar.gz fx-*.tar.gz.sha256
- name: Publish to CDN env: BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }} VERSION: ${{ needs.check-version.outputs.version }} run: | set -euo pipefail [[ "$VERSION" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$ ]] CDN_BASE="cli"
for FILE in fx-*.tar.gz fx-*.tar.gz.sha256; do [ -f "$FILE" ] || continue CONTENT_TYPE="application/octet-stream" [[ "$FILE" == *.sha256 ]] && CONTENT_TYPE="text/plain" [[ "$FILE" == *.tar.gz ]] && [[ "$FILE" != *.sha256 ]] && CONTENT_TYPE="application/gzip"
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \ -X PUT "https://blob.vercel-storage.com/${CDN_BASE}/${VERSION}/${FILE}" \ -H "Authorization: Bearer ${BLOB_READ_WRITE_TOKEN}" \ -H "x-api-version: 7" \ -H "x-content-type: ${CONTENT_TYPE}" \ -H "x-add-random-suffix: 0" \ -H "x-cache-control-max-age: 31536000" \ --data-binary "@${FILE}") echo "Uploaded ${FILE} -> ${CDN_BASE}/${VERSION}/${FILE} ($HTTP_CODE)" if [ "$HTTP_CODE" -lt 200 ] || [ "$HTTP_CODE" -ge 300 ]; then echo "::error::Failed to upload ${FILE} to CDN ($HTTP_CODE)" exit 1 fi done
echo -n "$VERSION" > /tmp/latest.txt HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \ -X PUT "https://blob.vercel-storage.com/${CDN_BASE}/latest.txt" \ -H "Authorization: Bearer ${BLOB_READ_WRITE_TOKEN}" \ -H "x-api-version: 7" \ -H "x-content-type: text/plain" \ -H "x-add-random-suffix: 0" \ -H "x-cache-control-max-age: 60" \ --data-binary "@/tmp/latest.txt") echo "Updated latest.txt to $VERSION ($HTTP_CODE)"