From a74102cf6594dddfb55964653e6ea33bc9f6c7b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Rzepecki?= Date: Tue, 10 Feb 2026 14:39:18 +0100 Subject: [PATCH 1/2] refactor(ci): consolidate and optimize native build workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Merged duplicate workflows and significantly improved the native build pipeline: Consolidation & DRY: - Merged build-native.yml and build-native-scanner.yml into single workflow - Removed unused scripts: bin/build-native, bin/hash, bin/hash.js Release Management: - Centralized all release uploads in finalize-release job - Replaced svenstaro/upload-release-action with native gh CLI - Removed manual checksum generation (GitHub provides manifests) Security & Best Practices: - Added explicit least-privilege permissions (contents: read by default) - Only finalize-release has contents: write permission - Replaced custom PAT with built-in GITHUB_TOKEN - Added concurrency control to cancel outdated PR builds - Added timeouts to all jobs (5-60 min based on complexity) - Use ubuntu-slim for bookkeeping jobs Dependencies & Fixes: - Updated all GitHub Actions to latest versions: • checkout: v3 → v6 • cache: v3 → v5 • setup-node: v4 → v6 • upload-artifact: v4 → v6 • download-artifact: v4 → v7 - Fixed deprecated set-output command in setup-node action - Fixed missing Node.js setup in finalize-release job - Actually use x64 macos runner to build for x64 - Made code signing optional for non-tag builds --- .github/actions/setup-node/action.yml | 8 +- .github/workflows/build-native-scanner.yml | 332 --------------------- .github/workflows/build-native.yml | 310 ++++++++----------- bin/build-native | 15 - bin/hash | 12 - bin/hash.js | 9 - 6 files changed, 135 insertions(+), 551 deletions(-) delete mode 100644 .github/workflows/build-native-scanner.yml delete mode 100755 bin/build-native delete mode 100755 bin/hash delete mode 100644 bin/hash.js diff --git a/.github/actions/setup-node/action.yml b/.github/actions/setup-node/action.yml index c800b41a54..b0dfab39d5 100644 --- a/.github/actions/setup-node/action.yml +++ b/.github/actions/setup-node/action.yml @@ -4,20 +4,20 @@ runs: using: 'composite' steps: - name: Setup Node - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: 18 - name: Detect Yarn cache dir id: yarn-cache-dir-path shell: bash - run: echo "::set-output name=dir::$(yarn config get cacheFolder)" + run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT - name: Restore Yarn packages - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v5 with: path: ${{ steps.yarn-cache-dir-path.outputs.dir }} key: ${{ github.job }}-yarn-${{ hashFiles('yarn.lock') }} - name: Restore node_modules - uses: actions/cache@v3 + uses: actions/cache@v5 with: path: ./node_modules key: ${{ github.job }}-node-modules-${{ hashFiles('yarn.lock') }} diff --git a/.github/workflows/build-native-scanner.yml b/.github/workflows/build-native-scanner.yml deleted file mode 100644 index 13f2eef513..0000000000 --- a/.github/workflows/build-native-scanner.yml +++ /dev/null @@ -1,332 +0,0 @@ -name: Build Native (scanner) -# TODO: DRY it up and merge with build-native.yml - -on: - pull_request: - push: - tags: - - '@appland/scanner-v*' - -jobs: - linux-x64: - runs-on: ubuntu-latest - - if: - ${{ contains(github.event.pull_request.labels.*.name, 'build native') || - startsWith(github.ref, 'refs/tags/@appland/scanner-v') }} - - env: - PUPPETEER_SKIP_DOWNLOAD: 1 - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup Node - uses: ./.github/actions/setup-node - - - name: Build - run: | - yarn build - cd packages/scanner - yarn build-native linux x64 - ${GITHUB_WORKSPACE}/bin/hash release/scanner-linux-x64 - - - name: Publish scanner-linux-x64 artifact - uses: actions/upload-artifact@v4 - with: - name: scanner-linux-x64 - path: packages/scanner/release/scanner-linux-x64 - - - name: 'Release: scanner-linux-x64' - uses: svenstaro/upload-release-action@v2 - if: github.ref_type == 'tag' - with: - repo_token: ${{ secrets.GH_TOKEN }} - file: packages/scanner/release/scanner-linux-x64 - asset_name: scanner-linux-x64 - tag: ${{ github.ref }} - overwrite: true - - - name: 'Release: scanner-linux-x64.sha256' - uses: svenstaro/upload-release-action@v2 - if: github.ref_type == 'tag' - with: - repo_token: ${{ secrets.GH_TOKEN }} - file: packages/scanner/release/scanner-linux-x64.sha256 - asset_name: scanner-linux-x64.sha256 - tag: ${{ github.ref }} - overwrite: true - - linux-arm64: - runs-on: linux-arm64 - - if: - ${{ contains(github.event.pull_request.labels.*.name, 'build native') || - startsWith(github.ref, 'refs/tags/@appland/scanner-v') }} - - env: - PUPPETEER_SKIP_DOWNLOAD: 1 - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup Node - uses: ./.github/actions/setup-node - - - name: Build - run: | - yarn build - cd packages/scanner - yarn build-native linux arm64 - ${GITHUB_WORKSPACE}/bin/hash release/scanner-linux-arm64 - - - name: Publish scanner-linux-arm64 artifact - uses: actions/upload-artifact@v4 - with: - name: scanner-linux-arm64 - path: packages/scanner/release/scanner-linux-arm64 - - - name: 'Release: scanner-linux-arm64' - uses: svenstaro/upload-release-action@v2 - if: github.ref_type == 'tag' - with: - repo_token: ${{ secrets.GH_TOKEN }} - file: packages/scanner/release/scanner-linux-arm64 - asset_name: scanner-linux-arm64 - tag: ${{ github.ref }} - overwrite: true - - - name: 'Release: scanner-linux-arm64.sha256' - uses: svenstaro/upload-release-action@v2 - if: github.ref_type == 'tag' - with: - repo_token: ${{ secrets.GH_TOKEN }} - file: packages/scanner/release/scanner-linux-arm64.sha256 - asset_name: scanner-linux-arm64.sha256 - tag: ${{ github.ref }} - overwrite: true - - macos-x64: - runs-on: macos-latest - - if: - ${{ contains(github.event.pull_request.labels.*.name, 'build native') || - startsWith(github.ref, 'refs/tags/@appland/scanner-v') }} - - env: - PUPPETEER_SKIP_DOWNLOAD: 1 - APPLE_IDENTITY_PRIVATE_KEY: ${{ secrets.APPLE_IDENTITY_PRIVATE_KEY }} - APPLE_IDENTITY_CERTIFICATE: ${{ secrets.APPLE_IDENTITY_CERTIFICATE }} - APPLE_CONNECT_KEY_B64: ${{ secrets.APPLE_CONNECT_KEY_B64 }} - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Install deps - run: | - python3 -m pip install --break-system-packages setuptools - - - name: Setup Node - uses: ./.github/actions/setup-node - - - name: Build - run: | - yarn build - cd packages/scanner - yarn build-native macos x64 - ${GITHUB_WORKSPACE}/bin/presign - ${GITHUB_WORKSPACE}/bin/sign release/scanner-macos-x64 - ${GITHUB_WORKSPACE}/bin/notarize release/scanner-macos-x64 - ${GITHUB_WORKSPACE}/bin/hash release/scanner-macos-x64 - - - name: Publish scanner-macos-x64 - uses: actions/upload-artifact@v4 - with: - name: scanner-macos-x64 - path: packages/scanner/release/scanner-macos-x64 - - - name: 'Release: scanner-macos-x64' - uses: svenstaro/upload-release-action@v2 - if: github.ref_type == 'tag' - with: - repo_token: ${{ secrets.GH_TOKEN }} - file: packages/scanner/release/scanner-macos-x64 - asset_name: scanner-macos-x64 - tag: ${{ github.ref }} - overwrite: true - - - name: 'Release: scanner-macos-x64.sha256' - uses: svenstaro/upload-release-action@v2 - if: github.ref_type == 'tag' - with: - repo_token: ${{ secrets.GH_TOKEN }} - file: packages/scanner/release/scanner-macos-x64.sha256 - asset_name: scanner-macos-x64.sha256 - tag: ${{ github.ref }} - overwrite: true - - macos-arm: - runs-on: macos-latest - - if: - ${{ contains(github.event.pull_request.labels.*.name, 'build native') || - startsWith(github.ref, 'refs/tags/@appland/scanner-v') }} - - env: - PUPPETEER_SKIP_DOWNLOAD: 1 - APPLE_IDENTITY_PRIVATE_KEY: ${{ secrets.APPLE_IDENTITY_PRIVATE_KEY }} - APPLE_IDENTITY_CERTIFICATE: ${{ secrets.APPLE_IDENTITY_CERTIFICATE }} - APPLE_CONNECT_KEY_B64: ${{ secrets.APPLE_CONNECT_KEY_B64 }} - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Install deps - run: | - python3 -m pip install --break-system-packages setuptools - - - name: Setup Node - uses: ./.github/actions/setup-node - - - name: Restore cargo cache - uses: actions/cache@v3 - with: - path: ~/.cargo - key: ${{ runner.os }}-${{ runner.arch }}-cargo - - - name: Build - run: | - yarn build - cd packages/scanner - yarn build-native macos arm64 - ${GITHUB_WORKSPACE}/bin/presign - ${GITHUB_WORKSPACE}/bin/sign release/scanner-macos-arm64 - ${GITHUB_WORKSPACE}/bin/notarize release/scanner-macos-arm64 - ${GITHUB_WORKSPACE}/bin/hash release/scanner-macos-arm64 - - - name: Publish scanner-macos-arm64 - uses: actions/upload-artifact@v4 - with: - name: scanner-macos-arm64 - path: packages/scanner/release/scanner-macos-arm64 - - - name: 'Release: scanner-macos-arm64' - uses: svenstaro/upload-release-action@v2 - if: github.ref_type == 'tag' - with: - repo_token: ${{ secrets.GH_TOKEN }} - file: packages/scanner/release/scanner-macos-arm64 - asset_name: scanner-macos-arm64 - tag: ${{ github.ref }} - overwrite: true - - - name: 'Release: scanner-macos-arm64.sha256' - uses: svenstaro/upload-release-action@v2 - if: github.ref_type == 'tag' - with: - repo_token: ${{ secrets.GH_TOKEN }} - file: packages/scanner/release/scanner-macos-arm64.sha256 - asset_name: scanner-macos-arm64.sha256 - tag: ${{ github.ref }} - overwrite: true - - windows: - runs-on: windows-latest - - if: - ${{ contains(github.event.pull_request.labels.*.name, 'build native') || - startsWith(github.ref, 'refs/tags/@appland/scanner-v') }} - - env: - PUPPETEER_SKIP_DOWNLOAD: 1 - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup Node - uses: ./.github/actions/setup-node - - - name: Restore cargo cache - uses: actions/cache@v3 - with: - path: ~/.cargo - key: ${{ runner.os }}-${{ runner.arch }}-cargo - - - name: Build - shell: bash - run: | - choco install rsync - yarn build - cd packages/scanner - yarn build-native win x64 - node ${GITHUB_WORKSPACE}/bin/hash.js release/scanner-win-x64.exe - - - name: Sign the release with Trusted Signing - uses: azure/trusted-signing-action@v0.3.16 - with: - azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} - azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} - azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} - endpoint: https://eus.codesigning.azure.net/ - code-signing-account-name: appmap - certificate-profile-name: appmap - files-folder: ${{ github.workspace }}/packages/scanner/release/ - files-folder-filter: exe,dll - file-digest: SHA256 - timestamp-rfc3161: http://timestamp.acs.microsoft.com - timestamp-digest: SHA256 - - - name: Publish scanner-win-x64 - uses: actions/upload-artifact@v4 - with: - name: scanner-win-x64 - path: packages/scanner/release/scanner-win-x64.exe - - - name: 'Release: scanner-win-x64.exe' - uses: svenstaro/upload-release-action@v2 - if: github.ref_type == 'tag' - with: - repo_token: ${{ secrets.GH_TOKEN }} - file: packages/scanner/release/scanner-win-x64.exe - asset_name: scanner-win-x64.exe - tag: ${{ github.ref }} - overwrite: true - - - name: 'Release: scanner-win-x64.exe.sha256' - uses: svenstaro/upload-release-action@v2 - if: github.ref_type == 'tag' - with: - repo_token: ${{ secrets.GH_TOKEN }} - file: packages/scanner/release/scanner-win-x64.exe.sha256 - asset_name: scanner-win-x64.exe.sha256 - tag: ${{ github.ref }} - overwrite: true - - finalize-release: - name: finalize release - if: ${{ startsWith(github.ref, 'refs/tags/@appland/scanner-v') }} - runs-on: ubuntu-latest - needs: - - linux-x64 - - linux-arm64 - - macos-x64 - - macos-arm - - windows - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Tag npm package as latest - run: yarn npm tag add `echo ${{ github.ref_name }} | sed -e s/-v/@/` latest - env: - YARN_NPM_AUTH_TOKEN: ${{ secrets.YARN_NPM_AUTH_TOKEN }} - - - name: Publish draft release - run: gh release edit ${{ github.ref_name }} --draft=false - env: - GH_TOKEN: ${{ secrets.GH_TOKEN }} diff --git a/.github/workflows/build-native.yml b/.github/workflows/build-native.yml index e95a501b76..d05e0881ad 100644 --- a/.github/workflows/build-native.yml +++ b/.github/workflows/build-native.yml @@ -5,21 +5,58 @@ on: push: tags: - '@appland/appmap-v*' + - '@appland/scanner-v*' -jobs: - linux-x64: - runs-on: ubuntu-latest +permissions: + contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/') }} + +jobs: + setup: + runs-on: ubuntu-slim + timeout-minutes: 5 if: ${{ contains(github.event.pull_request.labels.*.name, 'build native') || - startsWith(github.ref, 'refs/tags/@appland/appmap-v') }} + startsWith(github.ref, 'refs/tags/@appland/') }} + outputs: + package_name: ${{ steps.determine.outputs.name }} + package_dir: ${{ steps.determine.outputs.dir }} + steps: + - name: Determine package + id: determine + run: | + ref="${{ github.ref }}" + + # Match scanner tags: @appland/scanner-v + if [[ "$ref" =~ refs/tags/@appland/scanner-v[0-9] ]]; then + echo "name=scanner" >> $GITHUB_OUTPUT + echo "dir=packages/scanner" >> $GITHUB_OUTPUT + # Match appmap tags: @appland/appmap-v (not appmap--v) + elif [[ "$ref" =~ refs/tags/@appland/appmap-v[0-9] ]]; then + echo "name=appmap" >> $GITHUB_OUTPUT + echo "dir=packages/cli" >> $GITHUB_OUTPUT + # For PR builds with label, default to appmap + elif [[ "${{ github.event_name }}" == "pull_request" ]]; then + echo "name=appmap" >> $GITHUB_OUTPUT + echo "dir=packages/cli" >> $GITHUB_OUTPUT + else + echo "ERROR: Tag does not match expected pattern" >&2 + exit 1 + fi + linux-x64: + needs: setup + runs-on: ubuntu-latest + timeout-minutes: 30 env: PUPPETEER_SKIP_DOWNLOAD: 1 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v6 - name: Setup Node uses: ./.github/actions/setup-node @@ -27,49 +64,25 @@ jobs: - name: Build run: | yarn build - cd packages/cli + cd ${{ needs.setup.outputs.package_dir }} yarn build-native linux x64 - ${GITHUB_WORKSPACE}/bin/hash release/appmap-linux-x64 - - name: Publish appmap-linux-x64 artifact - uses: actions/upload-artifact@v4 + - name: Publish artifact + uses: actions/upload-artifact@v6 with: - name: appmap-linux-x64 - path: packages/cli/release/appmap-linux-x64 - - - name: 'Release: appmap-linux-x64' - uses: svenstaro/upload-release-action@v2 - if: github.ref_type == 'tag' - with: - repo_token: ${{ secrets.GH_TOKEN }} - file: packages/cli/release/appmap-linux-x64 - asset_name: appmap-linux-x64 - tag: ${{ github.ref }} - overwrite: true - - - name: 'Release: appmap-linux-x64.sha256' - uses: svenstaro/upload-release-action@v2 - if: github.ref_type == 'tag' - with: - repo_token: ${{ secrets.GH_TOKEN }} - file: packages/cli/release/appmap-linux-x64.sha256 - asset_name: appmap-linux-x64.sha256 - tag: ${{ github.ref }} - overwrite: true + name: ${{ needs.setup.outputs.package_name }}-linux-x64 + path: ${{ needs.setup.outputs.package_dir }}/release/${{ needs.setup.outputs.package_name }}-linux-x64 linux-arm64: + needs: setup runs-on: linux-arm64 - - if: - ${{ contains(github.event.pull_request.labels.*.name, 'build native') || - startsWith(github.ref, 'refs/tags/@appland/appmap-v') }} - + timeout-minutes: 30 env: PUPPETEER_SKIP_DOWNLOAD: 1 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v6 - name: Setup Node uses: ./.github/actions/setup-node @@ -77,43 +90,19 @@ jobs: - name: Build run: | yarn build - cd packages/cli + cd ${{ needs.setup.outputs.package_dir }} yarn build-native linux arm64 - ${GITHUB_WORKSPACE}/bin/hash release/appmap-linux-arm64 - - name: Publish appmap-linux-arm64 artifact - uses: actions/upload-artifact@v4 + - name: Publish artifact + uses: actions/upload-artifact@v6 with: - name: appmap-linux-arm64 - path: packages/cli/release/appmap-linux-arm64 - - - name: 'Release: appmap-linux-arm64' - uses: svenstaro/upload-release-action@v2 - if: github.ref_type == 'tag' - with: - repo_token: ${{ secrets.GH_TOKEN }} - file: packages/cli/release/appmap-linux-arm64 - asset_name: appmap-linux-arm64 - tag: ${{ github.ref }} - overwrite: true - - - name: 'Release: appmap-linux-arm64.sha256' - uses: svenstaro/upload-release-action@v2 - if: github.ref_type == 'tag' - with: - repo_token: ${{ secrets.GH_TOKEN }} - file: packages/cli/release/appmap-linux-arm64.sha256 - asset_name: appmap-linux-arm64.sha256 - tag: ${{ github.ref }} - overwrite: true + name: ${{ needs.setup.outputs.package_name }}-linux-arm64 + path: ${{ needs.setup.outputs.package_dir }}/release/${{ needs.setup.outputs.package_name }}-linux-arm64 macos-x64: - runs-on: macos-latest - - if: - ${{ contains(github.event.pull_request.labels.*.name, 'build native') || - startsWith(github.ref, 'refs/tags/@appland/appmap-v') }} - + needs: setup + runs-on: macos-15-intel + timeout-minutes: 60 env: PUPPETEER_SKIP_DOWNLOAD: 1 APPLE_IDENTITY_PRIVATE_KEY: ${{ secrets.APPLE_IDENTITY_PRIVATE_KEY }} @@ -122,7 +111,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v6 - name: Install deps run: | @@ -131,49 +120,35 @@ jobs: - name: Setup Node uses: ./.github/actions/setup-node - - name: Build + - name: Restore cargo cache + uses: actions/cache@v5 + with: + path: ~/.cargo + key: ${{ runner.os }}-${{ runner.arch }}-cargo + + - name: Build binary run: | yarn build - cd packages/cli + cd ${{ needs.setup.outputs.package_dir }} yarn build-native macos x64 - ${GITHUB_WORKSPACE}/bin/presign - ${GITHUB_WORKSPACE}/bin/sign release/appmap-macos-x64 - ${GITHUB_WORKSPACE}/bin/notarize release/appmap-macos-x64 - ${GITHUB_WORKSPACE}/bin/hash release/appmap-macos-x64 - - name: Publish appmap-macos-x64 - uses: actions/upload-artifact@v4 - with: - name: appmap-macos-x64 - path: packages/cli/release/appmap-macos-x64 + - name: Sign and notarize binary + continue-on-error: ${{ github.ref_type != 'tag' }} + run: | + ${GITHUB_WORKSPACE}/bin/presign + ${GITHUB_WORKSPACE}/bin/sign ${{ needs.setup.outputs.package_dir }}/release/${{ needs.setup.outputs.package_name }}-macos-x64 + ${GITHUB_WORKSPACE}/bin/notarize ${{ needs.setup.outputs.package_dir }}/release/${{ needs.setup.outputs.package_name }}-macos-x64 - - name: 'Release: appmap-macos-x64' - uses: svenstaro/upload-release-action@v2 - if: github.ref_type == 'tag' - with: - repo_token: ${{ secrets.GH_TOKEN }} - file: packages/cli/release/appmap-macos-x64 - asset_name: appmap-macos-x64 - tag: ${{ github.ref }} - overwrite: true - - - name: 'Release: appmap-macos-x64.sha256' - uses: svenstaro/upload-release-action@v2 - if: github.ref_type == 'tag' + - name: Publish artifact + uses: actions/upload-artifact@v6 with: - repo_token: ${{ secrets.GH_TOKEN }} - file: packages/cli/release/appmap-macos-x64.sha256 - asset_name: appmap-macos-x64.sha256 - tag: ${{ github.ref }} - overwrite: true + name: ${{ needs.setup.outputs.package_name }}-macos-x64 + path: ${{ needs.setup.outputs.package_dir }}/release/${{ needs.setup.outputs.package_name }}-macos-x64 - macos-arm: + macos-arm64: + needs: setup runs-on: macos-latest - - if: - ${{ contains(github.event.pull_request.labels.*.name, 'build native') || - startsWith(github.ref, 'refs/tags/@appland/appmap-v') }} - + timeout-minutes: 60 env: PUPPETEER_SKIP_DOWNLOAD: 1 APPLE_IDENTITY_PRIVATE_KEY: ${{ secrets.APPLE_IDENTITY_PRIVATE_KEY }} @@ -182,7 +157,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v6 - name: Install deps run: | @@ -192,66 +167,46 @@ jobs: uses: ./.github/actions/setup-node - name: Restore cargo cache - uses: actions/cache@v3 + uses: actions/cache@v5 with: path: ~/.cargo key: ${{ runner.os }}-${{ runner.arch }}-cargo - - name: Build + - name: Build binary run: | yarn build - cd packages/cli + cd ${{ needs.setup.outputs.package_dir }} yarn build-native macos arm64 - ${GITHUB_WORKSPACE}/bin/presign - ${GITHUB_WORKSPACE}/bin/sign release/appmap-macos-arm64 - ${GITHUB_WORKSPACE}/bin/notarize release/appmap-macos-arm64 - ${GITHUB_WORKSPACE}/bin/hash release/appmap-macos-arm64 - - name: Publish appmap-macos-arm64 - uses: actions/upload-artifact@v4 - with: - name: appmap-macos-arm64 - path: packages/cli/release/appmap-macos-arm64 + - name: Sign and notarize binary + continue-on-error: ${{ github.ref_type != 'tag' }} + run: | + ${GITHUB_WORKSPACE}/bin/presign + ${GITHUB_WORKSPACE}/bin/sign ${{ needs.setup.outputs.package_dir }}/release/${{ needs.setup.outputs.package_name }}-macos-arm64 + ${GITHUB_WORKSPACE}/bin/notarize ${{ needs.setup.outputs.package_dir }}/release/${{ needs.setup.outputs.package_name }}-macos-arm64 - - name: 'Release: appmap-macos-arm64' - uses: svenstaro/upload-release-action@v2 - if: github.ref_type == 'tag' + - name: Publish artifact + uses: actions/upload-artifact@v6 with: - repo_token: ${{ secrets.GH_TOKEN }} - file: packages/cli/release/appmap-macos-arm64 - asset_name: appmap-macos-arm64 - tag: ${{ github.ref }} - overwrite: true - - - name: 'Release: appmap-macos-arm64.sha256' - uses: svenstaro/upload-release-action@v2 - if: github.ref_type == 'tag' - with: - repo_token: ${{ secrets.GH_TOKEN }} - file: packages/cli/release/appmap-macos-arm64.sha256 - asset_name: appmap-macos-arm64.sha256 - tag: ${{ github.ref }} - overwrite: true + name: ${{ needs.setup.outputs.package_name }}-macos-arm64 + path: ${{ needs.setup.outputs.package_dir }}/release/${{ needs.setup.outputs.package_name }}-macos-arm64 windows: + needs: setup runs-on: windows-latest - - if: - ${{ contains(github.event.pull_request.labels.*.name, 'build native') || - startsWith(github.ref, 'refs/tags/@appland/appmap-v') }} - + timeout-minutes: 30 env: PUPPETEER_SKIP_DOWNLOAD: 1 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v6 - name: Setup Node uses: ./.github/actions/setup-node - name: Restore cargo cache - uses: actions/cache@v3 + uses: actions/cache@v5 with: path: ~/.cargo key: ${{ runner.os }}-${{ runner.arch }}-cargo @@ -261,13 +216,12 @@ jobs: run: | choco install rsync yarn build - cd packages/cli + cd ${{ needs.setup.outputs.package_dir }} yarn build-native win x64 - node ${GITHUB_WORKSPACE}/bin/hash.js release/appmap-win-x64.exe - name: Sign the release with Trusted Signing uses: azure/trusted-signing-action@v0.3.16 - continue-on-error: ${{ github.ref_type == 'branch' }} + continue-on-error: ${{ github.ref_type != 'tag' }} with: azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} @@ -275,58 +229,56 @@ jobs: endpoint: https://eus.codesigning.azure.net/ code-signing-account-name: appmap certificate-profile-name: appmap - files-folder: ${{ github.workspace }}/packages/cli/release/ + files-folder: ${{ github.workspace }}/${{ needs.setup.outputs.package_dir }}/release/ files-folder-filter: exe,dll file-digest: SHA256 timestamp-rfc3161: http://timestamp.acs.microsoft.com timestamp-digest: SHA256 - - name: Publish appmap-win-x64 - uses: actions/upload-artifact@v4 + - name: Publish artifact + uses: actions/upload-artifact@v6 with: - name: appmap-win-x64 - path: packages/cli/release/appmap-win-x64.exe - - - name: 'Release: appmap-win-x64.exe' - uses: svenstaro/upload-release-action@v2 - if: github.ref_type == 'tag' - with: - repo_token: ${{ secrets.GH_TOKEN }} - file: packages/cli/release/appmap-win-x64.exe - asset_name: appmap-win-x64.exe - tag: ${{ github.ref }} - overwrite: true - - - name: 'Release: appmap-win-x64.exe.sha256' - uses: svenstaro/upload-release-action@v2 - if: github.ref_type == 'tag' - with: - repo_token: ${{ secrets.GH_TOKEN }} - file: packages/cli/release/appmap-win-x64.exe.sha256 - asset_name: appmap-win-x64.exe.sha256 - tag: ${{ github.ref }} - overwrite: true + name: ${{ needs.setup.outputs.package_name }}-win-x64 + path: ${{ needs.setup.outputs.package_dir }}/release/${{ needs.setup.outputs.package_name }}-win-x64.exe finalize-release: name: finalize release - if: ${{ startsWith(github.ref, 'refs/tags/@appland/appmap-v') }} - runs-on: ubuntu-latest needs: + - setup - linux-x64 - linux-arm64 - macos-x64 - - macos-arm + - macos-arm64 - windows + if: github.ref_type == 'tag' + runs-on: ubuntu-slim + timeout-minutes: 10 + permissions: + contents: write steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v6 - - name: Tag npm package as latest - run: yarn npm tag add `echo ${{ github.ref_name }} | sed -e s/-v/@/` latest + - name: Setup Node + uses: ./.github/actions/setup-node + + - name: Download all artifacts + uses: actions/download-artifact@v7 + with: + path: artifacts + merge-multiple: true + + - name: Publish binaries to GitHub release + run: gh release upload ${{ github.ref_name }} artifacts/* --clobber env: - YARN_NPM_AUTH_TOKEN: ${{ secrets.YARN_NPM_AUTH_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Publish draft release run: gh release edit ${{ github.ref_name }} --draft=false env: - GH_TOKEN: ${{ secrets.GH_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Tag npm package as latest + run: yarn npm tag add `echo ${{ github.ref_name }} | sed -e s/-v/@/` latest + env: + YARN_NPM_AUTH_TOKEN: ${{ secrets.YARN_NPM_AUTH_TOKEN }} diff --git a/bin/build-native b/bin/build-native deleted file mode 100755 index 8e34c5fa11..0000000000 --- a/bin/build-native +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -# -# Build native binaries for the current package. Also performs signing and notarization on macOS -# Mach-O binaries. This script is intended to be run via semantic-release during CI deployments. -set -e - -# Only continue if the package we're releasing has a build-native script. -# We assume this binary is run from within a package directory, i.e. a package.json exists in the -# current directory. -[ "$(jq '.scripts["build-native"]' package.json)" = "null" ] && exit 0 - -yarn build-native -"${PROJECT_CWD}/bin/sign" release/*-macos-* -"${PROJECT_CWD}/bin/notarize" release/*-macos-* -"${PROJECT_CWD}/bin/hash" release/* diff --git a/bin/hash b/bin/hash deleted file mode 100755 index ebfee04c29..0000000000 --- a/bin/hash +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -# -# Create hashes for any files passed as arguments. -set -e - -for HASH_TARGET_PATH in "$@"; do - HASH_PATH="${HASH_TARGET_PATH}.sha256" - rm -rf "${HASH_PATH}" - shasum -a 256 -b "${HASH_TARGET_PATH}" \ - | awk '{printf $1}' \ - > "${HASH_PATH}" -done diff --git a/bin/hash.js b/bin/hash.js deleted file mode 100644 index 4fa5b99fe8..0000000000 --- a/bin/hash.js +++ /dev/null @@ -1,9 +0,0 @@ -const { readFileSync, writeFileSync } = require('node:fs'); -const { createHash } = require('node:crypto'); - -// This script is essentially `shasum -a 256 -b`. - -const input = process.argv[2]; -const buffer = readFileSync(input); -const hash = createHash('sha256').update(buffer).digest('hex'); -writeFileSync(input + '.sha256', hash); From d9d9aab3e794f72ae9c4427c429e3362d988bd2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Rzepecki?= Date: Tue, 10 Feb 2026 15:19:49 +0100 Subject: [PATCH 2/2] ci(build-native): Implement release manifest publishing with integrity verification - Add `merge-multiple: true` to `download-artifact` to flatten asset structure. - Split GitHub release process into binary upload and final publication steps. - Implement manifest generation using `gh` and `jq`. - Add strict SHA256 integrity verification using `sha256sum --check` before release finalization. - Add automated publishing of manifests to the `release-manifests` orphan branch. This ensures a secure, verifiable release pipeline where public manifests and tags are only updated after successful asset validation. --- .github/workflows/build-native.yml | 42 +++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-native.yml b/.github/workflows/build-native.yml index d05e0881ad..78d7637886 100644 --- a/.github/workflows/build-native.yml +++ b/.github/workflows/build-native.yml @@ -273,7 +273,36 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Publish draft release + - name: Generate Release Manifests + id: generate_manifest + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + mkdir -p dist + + VERSION_TAG="${{ github.ref_name }}" + BASE_TAG="${VERSION_TAG#@appland/}" + TOOL_NAME="${BASE_TAG%%-v*}" + + echo "Fetching release data for $VERSION_TAG..." + gh release view "$VERSION_TAG" --json tagName,assets > release.json + + echo "Generating manifest..." + jq '{tag_name: .tagName, assets: [.assets[] | {name, url: .url, digest}]}' release.json > "dist/${BASE_TAG}.json" + cp "dist/${BASE_TAG}.json" "dist/${TOOL_NAME}-latest.json" + + echo "Verifying checksums..." + jq -r '.assets[] | .digest + " " + .name' release.json > checksums.txt + + if grep -v '^sha256:' checksums.txt; then + echo "::error::Invalid digest format found. Expected sha256:" + exit 1 + fi + + cd artifacts + cut -c8- ../checksums.txt | sha256sum --check + + - name: Publish GitHub Release run: gh release edit ${{ github.ref_name }} --draft=false env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -282,3 +311,14 @@ jobs: run: yarn npm tag add `echo ${{ github.ref_name }} | sed -e s/-v/@/` latest env: YARN_NPM_AUTH_TOKEN: ${{ secrets.YARN_NPM_AUTH_TOKEN }} + + - name: Publish Manifests to release-manifests branch + uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_branch: release-manifests + publish_dir: ./dist + keep_files: true + user_name: 'appland-release' + user_email: 'release@app.land' + commit_message: 'Update manifest for ${{ github.ref_name }}'