Merge pull request #132 from TeamCadenceAI/release-please--branches--… #64
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*" | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: "Release tag to build and publish (for example v0.2.0)" | |
| required: true | |
| type: string | |
| permissions: | |
| contents: write | |
| jobs: | |
| build: | |
| name: Build (${{ matrix.target }}) | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| matrix: | |
| include: | |
| - target: aarch64-apple-darwin | |
| runner: macos-latest | |
| - target: x86_64-apple-darwin | |
| runner: macos-15-intel | |
| - target: x86_64-unknown-linux-gnu | |
| runner: ubuntu-latest | |
| - target: aarch64-unknown-linux-gnu | |
| runner: ubuntu-latest | |
| - target: x86_64-pc-windows-msvc | |
| runner: windows-latest | |
| - target: aarch64-pc-windows-msvc | |
| runner: windows-latest | |
| steps: | |
| - name: Check out source | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref }} | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| - name: Setup Linux cross toolchain | |
| if: matrix.target == 'aarch64-unknown-linux-gnu' | |
| uses: taiki-e/setup-cross-toolchain-action@v1 | |
| with: | |
| target: aarch64-unknown-linux-gnu | |
| - name: Build | |
| run: cargo build --release --target ${{ matrix.target }} --bins | |
| - name: Import Apple Developer Certificate | |
| if: runner.os == 'macOS' | |
| env: | |
| APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} | |
| APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} | |
| KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} | |
| run: | | |
| echo "$APPLE_CERTIFICATE" | base64 --decode > certificate.p12 | |
| security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain | |
| security default-keychain -s build.keychain | |
| security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain | |
| security set-keychain-settings -t 3600 -u build.keychain | |
| security import certificate.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign | |
| security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain | |
| CERT_IDENTITY=$(security find-identity -v -p codesigning build.keychain | grep "Developer ID Application" | head -n 1 | awk -F'"' '{print $2}') | |
| if [ -z "$CERT_IDENTITY" ]; then | |
| echo "::error::Developer ID Application identity not found" | |
| exit 1 | |
| fi | |
| echo "APPLE_CERT_IDENTITY=$CERT_IDENTITY" >> "$GITHUB_ENV" | |
| - name: Sign macOS binaries | |
| if: runner.os == 'macOS' | |
| run: | | |
| codesign --force --options runtime --timestamp --sign "$APPLE_CERT_IDENTITY" "target/${{ matrix.target }}/release/cadence" | |
| codesign --force --options runtime --timestamp --sign "$APPLE_CERT_IDENTITY" "target/${{ matrix.target }}/release/cadence-updater" | |
| - name: Verify macOS binaries | |
| if: runner.os == 'macOS' | |
| run: | | |
| codesign --verify --strict --verbose=2 "target/${{ matrix.target }}/release/cadence" | |
| codesign --verify --strict --verbose=2 "target/${{ matrix.target }}/release/cadence-updater" | |
| codesign -dv --verbose=4 "target/${{ matrix.target }}/release/cadence" | |
| codesign -dv --verbose=4 "target/${{ matrix.target }}/release/cadence-updater" | |
| - name: Package (macOS) | |
| if: runner.os == 'macOS' | |
| run: | | |
| STAGE_DIR="cadence-cli-${{ matrix.target }}" | |
| rm -rf "$STAGE_DIR" | |
| mkdir -p "$STAGE_DIR" | |
| cp "target/${{ matrix.target }}/release/cadence" "$STAGE_DIR/" | |
| cp "target/${{ matrix.target }}/release/cadence-updater" "$STAGE_DIR/" | |
| tar czf "cadence-cli-${{ matrix.target }}.tar.gz" "$STAGE_DIR" | |
| /usr/bin/ditto -c -k --keepParent "$STAGE_DIR" "cadence-cli-${{ matrix.target }}.zip" | |
| - name: Package (Linux) | |
| if: runner.os == 'Linux' | |
| run: | | |
| STAGE_DIR="cadence-cli-${{ matrix.target }}" | |
| rm -rf "$STAGE_DIR" | |
| mkdir -p "$STAGE_DIR" | |
| cp "target/${{ matrix.target }}/release/cadence" "$STAGE_DIR/" | |
| cp "target/${{ matrix.target }}/release/cadence-updater" "$STAGE_DIR/" | |
| tar czf "cadence-cli-${{ matrix.target }}.tar.gz" "$STAGE_DIR" | |
| - name: Package (Windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| $archive = "cadence-cli-${{ matrix.target }}.zip" | |
| $stageDir = "cadence-cli-${{ matrix.target }}" | |
| Remove-Item -Recurse -Force $stageDir -ErrorAction SilentlyContinue | |
| New-Item -ItemType Directory -Path $stageDir | Out-Null | |
| Copy-Item "target/${{ matrix.target }}/release/cadence.exe" "$stageDir/cadence.exe" | |
| Copy-Item "target/${{ matrix.target }}/release/cadence-updater.exe" "$stageDir/cadence-updater.exe" | |
| Compress-Archive -Path $stageDir -DestinationPath $archive | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: cadence-cli-${{ matrix.target }} | |
| path: | | |
| cadence-cli-${{ matrix.target }}.tar.gz | |
| cadence-cli-${{ matrix.target }}.zip | |
| publish-release: | |
| name: Publish GitHub release | |
| needs: build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out source | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref }} | |
| - name: Download artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Generate checksums | |
| run: | | |
| CHECKSUM_FILE="checksums-sha256.txt" | |
| # Collect all release archives (tar.gz and zip) from downloaded artifacts | |
| mapfile -t ARCHIVES < <(find artifacts -type f \( -name '*.tar.gz' -o -name '*.zip' \) | sort) | |
| # Fail fast if no artifacts were found | |
| if [ "${#ARCHIVES[@]}" -eq 0 ]; then | |
| echo "::error::No release artifacts (*.tar.gz, *.zip) found in artifacts/" | |
| exit 1 | |
| fi | |
| echo "Found ${#ARCHIVES[@]} release artifacts:" | |
| printf ' %s\n' "${ARCHIVES[@]}" | |
| # Generate SHA256 checksums with basename-only filenames in GNU coreutils format | |
| for archive in "${ARCHIVES[@]}"; do | |
| HASH=$(sha256sum "$archive" | cut -d' ' -f1) | |
| BASENAME=$(basename "$archive") | |
| printf '%s %s\n' "$HASH" "$BASENAME" | |
| done > "artifacts/${CHECKSUM_FILE}" | |
| echo "Generated artifacts/${CHECKSUM_FILE}:" | |
| cat "artifacts/${CHECKSUM_FILE}" | |
| - name: Validate checksums | |
| run: | | |
| CHECKSUM_FILE="artifacts/checksums-sha256.txt" | |
| # Ensure checksum file exists and is non-empty | |
| if [ ! -s "$CHECKSUM_FILE" ]; then | |
| echo "::error::Checksum file is missing or empty: ${CHECKSUM_FILE}" | |
| exit 1 | |
| fi | |
| LINE_COUNT=$(wc -l < "$CHECKSUM_FILE" | tr -d ' ') | |
| ARTIFACT_COUNT=$(find artifacts -type f \( -name '*.tar.gz' -o -name '*.zip' \) | wc -l | tr -d ' ') | |
| # Verify line count matches artifact count | |
| if [ "$LINE_COUNT" -ne "$ARTIFACT_COUNT" ]; then | |
| echo "::error::Checksum line count ($LINE_COUNT) does not match artifact count ($ARTIFACT_COUNT)" | |
| exit 1 | |
| fi | |
| # Validate each line matches GNU coreutils sha256sum format: <64-hex> <filename> | |
| LINE_NUM=0 | |
| while IFS= read -r line; do | |
| LINE_NUM=$((LINE_NUM + 1)) | |
| if ! echo "$line" | grep -qE '^[0-9a-f]{64} [^ ]+$'; then | |
| echo "::error::Malformed checksum line ${LINE_NUM}: ${line}" | |
| exit 1 | |
| fi | |
| done < "$CHECKSUM_FILE" | |
| echo "Checksum validation passed: ${LINE_COUNT} artifacts verified" | |
| - name: Update GitHub release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }} | |
| draft: false | |
| files: | | |
| artifacts/**/*.tar.gz | |
| artifacts/**/*.zip | |
| artifacts/checksums-sha256.txt |