ci(release): invoke azuresigntool in lowercase for Linux compat #5
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 | |
| # Triggered only by tag pushes matching v*. The release environment's | |
| # deployment_branch_policy further restricts execution to tag refs | |
| # matching v*; subscribed environment reviewers must approve before | |
| # the publish job runs. Build jobs do NOT require approval (they | |
| # produce unsigned artifacts that are useless without the publish | |
| # step's signing + npm push), so review friction is one-time. | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| concurrency: | |
| group: release-${{ github.ref }} | |
| cancel-in-progress: false | |
| permissions: | |
| contents: write # gh release create | |
| id-token: write # OIDC for Azure + npm provenance | |
| jobs: | |
| build: | |
| name: Build ${{ matrix.rid }} | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| fail-fast: true | |
| matrix: | |
| include: | |
| - rid: win-x64 | |
| runner: windows-latest | |
| platform: win32-x64 | |
| exe: ripple.exe | |
| - rid: linux-x64 | |
| runner: ubuntu-latest | |
| platform: linux-x64 | |
| exe: ripple | |
| - rid: osx-arm64 | |
| runner: macos-latest | |
| platform: darwin-arm64 | |
| exe: ripple | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup .NET 9 | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: '9.0.x' | |
| - name: dotnet restore | |
| run: dotnet restore ripple.csproj -r ${{ matrix.rid }} | |
| - name: dotnet publish (NativeAOT) | |
| run: dotnet publish ripple.csproj -c Release -r ${{ matrix.rid }} -o dist --no-restore | |
| # Run the full --test suite on the produced binary. This is the | |
| # real gate — a build that compiles but breaks at runtime on | |
| # this platform should not be published. No special | |
| # adapter-test setup; the tests that exercise cross-cutting | |
| # concerns (capture, render, OSC parsing, etc.) don't need | |
| # external shells / REPLs to be installed. | |
| - name: Run unit tests | |
| shell: bash | |
| run: | | |
| chmod +x "dist/${{ matrix.exe }}" || true | |
| "./dist/${{ matrix.exe }}" --test | |
| - name: Upload binary artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ripple-${{ matrix.platform }} | |
| path: dist/${{ matrix.exe }} | |
| if-no-files-found: error | |
| retention-days: 7 | |
| publish: | |
| name: Sign, publish, release | |
| needs: build | |
| runs-on: ubuntu-latest | |
| environment: release # required-reviewer gate + NPM_TOKEN scope | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup .NET 9 | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: '9.0.x' | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| registry-url: 'https://registry.npmjs.org' | |
| - name: Verify tag matches all version fields | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| tag='${{ github.ref_name }}' | |
| version="${tag#v}" | |
| csproj_version=$(grep -oE '<Version>[^<]+</Version>' ripple.csproj | head -n1 | sed -E 's/<Version>([^<]+)<\/Version>/\1/') | |
| meta_version=$(node -p "require('./npm/package.json').version") | |
| echo "Tag: $version" | |
| echo "csproj: $csproj_version" | |
| echo "meta: $meta_version" | |
| [[ "$version" == "$csproj_version" ]] || { echo "csproj version mismatch" >&2; exit 1; } | |
| [[ "$version" == "$meta_version" ]] || { echo "meta package.json version mismatch" >&2; exit 1; } | |
| for plat in win32-x64 linux-x64 darwin-arm64; do | |
| pv=$(node -p "require('./npm/platforms/${plat}/package.json').version") | |
| echo "$plat: $pv" | |
| [[ "$version" == "$pv" ]] || { echo "$plat subpackage version mismatch" >&2; exit 1; } | |
| done | |
| - name: Download all platform binaries | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Place binaries into subpackages + fix executable bits | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| cp artifacts/ripple-win32-x64/ripple.exe npm/platforms/win32-x64/bin/ripple.exe | |
| cp artifacts/ripple-linux-x64/ripple npm/platforms/linux-x64/bin/ripple | |
| cp artifacts/ripple-darwin-arm64/ripple npm/platforms/darwin-arm64/bin/ripple | |
| chmod +x npm/platforms/linux-x64/bin/ripple | |
| chmod +x npm/platforms/darwin-arm64/bin/ripple | |
| # Remove placeholder .gitkeep files so they don't ship in the tarball. | |
| rm -f npm/platforms/*/bin/.gitkeep | |
| ls -la npm/platforms/*/bin/ | |
| - name: Azure OIDC login | |
| uses: azure/login@v2 | |
| with: | |
| client-id: ${{ secrets.AZURE_CLIENT_ID }} | |
| tenant-id: ${{ secrets.AZURE_TENANT_ID }} | |
| subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} | |
| - name: Install AzureSignTool | |
| # dotnet tool install --global lands the binary in $HOME/.dotnet/tools | |
| # but, on Linux runners, that directory is NOT on $PATH by default | |
| # (Windows runners add it automatically). Export it via $GITHUB_PATH | |
| # so the next step's `AzureSignTool` invocation resolves. | |
| run: | | |
| dotnet tool install --global AzureSignTool | |
| echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH" | |
| # AzureSignTool is a cross-platform .NET tool; Authenticode | |
| # signing works from Linux as long as we have the Key Vault | |
| # access token. The invocation is `azuresigntool` (lowercase) — | |
| # v7+ installs the binary under that name, and Linux's | |
| # case-sensitive filesystem won't resolve `AzureSignTool`. | |
| # Windows runners happen to be case-insensitive but the | |
| # lowercase form is portable. | |
| - name: Sign Windows binary via Azure Key Vault | |
| env: | |
| AZURE_KV_URL: ${{ vars.KEY_VAULT_URL }} | |
| AZURE_KV_CERT: ${{ vars.SIGNING_CERT_NAME }} | |
| EXPECTED_THUMBPRINT: '74E5208228DFB12A067747D536BF497B6E98C73C' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| token=$(az account get-access-token --resource https://vault.azure.net --query accessToken -o tsv) | |
| [[ -n "$token" ]] || { echo "Failed to acquire Key Vault access token" >&2; exit 1; } | |
| azuresigntool sign \ | |
| --azure-key-vault-url "$AZURE_KV_URL" \ | |
| --azure-key-vault-certificate "$AZURE_KV_CERT" \ | |
| --azure-key-vault-accesstoken "$token" \ | |
| --file-digest sha256 \ | |
| --timestamp-rfc3161 'http://timestamp.digicert.com' \ | |
| --description 'ripple - declarative shell adapter framework' \ | |
| --description-url 'https://github.com/yotsuda/ripple' \ | |
| npm/platforms/win32-x64/bin/ripple.exe | |
| # Publish subpackages BEFORE meta. Meta's optionalDependencies | |
| # reference these exact versions; if meta published first and a | |
| # subpackage publish later failed, users installing the meta | |
| # would see missing optional deps and degraded UX. Sequential | |
| # ordering also lets a failure halt before meta is published | |
| # (avoids broken meta pointing at a missing subpackage). | |
| - name: Publish subpackages with provenance | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| for plat in win32-x64 linux-x64 darwin-arm64; do | |
| echo "=== publishing @ytsuda/ripple-${plat} ===" | |
| (cd "npm/platforms/${plat}" && npm publish --access public --provenance) | |
| done | |
| - name: Publish meta package with provenance | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| cd npm | |
| npm publish --access public --provenance | |
| - name: Extract changelog entry | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| version='${{ github.ref_name }}' | |
| version="${version#v}" | |
| awk -v v="$version" ' | |
| /^## +\[/ { | |
| if (capture) exit | |
| if ($0 ~ "^## +\\[" v "\\]") { capture = 1; next } | |
| } | |
| capture { print } | |
| ' CHANGELOG.md > release-notes.md | |
| if [[ ! -s release-notes.md ]]; then | |
| echo "No CHANGELOG section for v$version" >&2 | |
| exit 1 | |
| fi | |
| wc -l release-notes.md | |
| - name: Create GitHub Release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh release create '${{ github.ref_name }}' \ | |
| --title "ripple ${{ github.ref_name }}" \ | |
| --notes-file release-notes.md \ | |
| --verify-tag \ | |
| 'npm/platforms/win32-x64/bin/ripple.exe#ripple-${{ github.ref_name }}-win32-x64.exe' \ | |
| 'npm/platforms/linux-x64/bin/ripple#ripple-${{ github.ref_name }}-linux-x64' \ | |
| 'npm/platforms/darwin-arm64/bin/ripple#ripple-${{ github.ref_name }}-darwin-arm64' |