From a71d97e7b936655e771d1534182f8a07841a3d3f Mon Sep 17 00:00:00 2001 From: Kayshen-X Date: Wed, 8 Jul 2026 22:47:21 +0800 Subject: [PATCH] chore(release): wire signing and npm publish secrets --- .github/workflows/rust-release.yml | 105 ++++++++++++++++++++--------- RELEASE_NOTES/v0.8.0.md | 6 +- 2 files changed, 78 insertions(+), 33 deletions(-) diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml index ceec73e31..41b554aa0 100644 --- a/.github/workflows/rust-release.yml +++ b/.github/workflows/rust-release.yml @@ -2,7 +2,8 @@ name: Rust release artifacts # Triggered on tag push (v*) — builds release binaries across desktop targets, # packages real installers per platform, builds the web Docker image and SDK -# tarballs, and publishes a GitHub prerelease. +# tarballs, publishes the web SDK packages to npm, and publishes a GitHub +# prerelease. # # Artifact matrix (per platform; = tag version, arch token = x64/arm64 to # match electron-builder's `${productName}-${version}-${arch}-…` convention @@ -82,8 +83,9 @@ jobs: runs-on: ${{ matrix.runner }} if: github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') env: - HAS_APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE_P12 != '' && 'true' || 'false' }} - HAS_WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERT_BASE64 != '' && 'true' || 'false' }} + HAS_APPLE_CERTIFICATE: ${{ secrets.CSC_LINK != '' && secrets.CSC_KEY_PASSWORD != '' && secrets.APPLE_ID != '' && secrets.APPLE_TEAM_ID != '' && secrets.APPLE_APP_SPECIFIC_PASSWORD != '' && 'true' || 'false' }} + HAS_WINDOWS_CERTIFICATE: ${{ secrets.WIN_CSC_LINK != '' && secrets.WIN_CSC_KEY_PASSWORD != '' && 'true' || 'false' }} + TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }} strategy: fail-fast: false matrix: @@ -177,6 +179,12 @@ jobs: Compress-Archive ` -Path target\${{ matrix.target }}\release\op.exe ` -DestinationPath op-cli-${{ matrix.label }}.zip + - name: Import codesign certificate (macos) + if: runner.os == 'macOS' && env.HAS_APPLE_CERTIFICATE == 'true' + uses: apple-actions/import-codesign-certs@v3 + with: + p12-file-base64: ${{ secrets.CSC_LINK }} + p12-password: ${{ secrets.CSC_KEY_PASSWORD }} - name: Package DMG (macos) if: runner.os == 'macOS' shell: bash @@ -188,12 +196,11 @@ jobs: # what this workflow built (and embeds the op CLI in Contents/MacOS). OPENPENCIL_BINARY: ${{ github.workspace }}/target/${{ matrix.target }}/release/openpencil-desktop OPENPENCIL_CLI_BINARY: ${{ github.workspace }}/target/${{ matrix.target }}/release/op - # Optional real-signing hook. No signing secrets exist today, so - # bundle-macos.sh falls back to ad-hoc ("-") signing. When a - # Developer ID cert lands, uncomment and add keychain import + - # notarization steps: - # MACOS_SIGN_IDENTITY: ${{ secrets.MACOS_SIGN_IDENTITY }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} run: | + if [ "$HAS_APPLE_CERTIFICATE" = "true" ]; then + export MACOS_SIGN_IDENTITY="Developer ID Application ($APPLE_TEAM_ID)" + fi bash scripts/bundle-macos.sh APP="target/${{ matrix.target }}/release/bundle/osx/OpenPencil.app" STAGE="$(mktemp -d)" @@ -215,41 +222,40 @@ jobs: # action needed to turn signing on. # # Required macOS secrets (Developer ID + notarization): - # APPLE_CERTIFICATE_P12 base64 of the Developer ID Application + # CSC_LINK base64 of the Developer ID Application # .p12 export - # APPLE_CERTIFICATE_PASSWORD password for that .p12 + # CSC_KEY_PASSWORD password for that .p12 # APPLE_TEAM_ID 10-char Apple Developer Team ID # APPLE_ID Apple ID email used for notarization - # APPLE_APP_PASSWORD app-specific password for notarytool + # APPLE_APP_SPECIFIC_PASSWORD app-specific password for notarytool # # Required Windows secrets (Authenticode): - # WINDOWS_CERT_BASE64 base64 of the code-signing .pfx - # WINDOWS_CERT_PASSWORD password for that .pfx - - name: Import codesign certificate (macos) - if: runner.os == 'macOS' && env.HAS_APPLE_CERTIFICATE == 'true' - uses: apple-actions/import-codesign-certs@v3 - with: - p12-file-base64: ${{ secrets.APPLE_CERTIFICATE_P12 }} - p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + # WIN_CSC_LINK base64/data URL/URL of the code-signing + # .pfx + # WIN_CSC_KEY_PASSWORD password for that .pfx + # + # Optional downstream publish secrets consumed by release packaging: + # NPM_TOKEN npm registry token for SDK publishing + # TAP_GITHUB_TOKEN GitHub token for tap/formula publishing - name: Codesign + notarize DMG (macos) if: runner.os == 'macOS' && env.HAS_APPLE_CERTIFICATE == 'true' shell: bash env: APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} APPLE_ID: ${{ secrets.APPLE_ID }} - APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }} run: | DMG="OpenPencil-$OP_VERSION-${{ matrix.arch }}-mac.dmg" - # The .app inside the DMG was ad-hoc signed by bundle-macos.sh; - # re-sign the DMG container with the real Developer ID identity, - # then notarize + staple so Gatekeeper accepts the download. + # The .app inside the DMG was signed by bundle-macos.sh after the + # Developer ID cert import above. Sign the DMG container too, then + # notarize + staple so Gatekeeper accepts the download. IDENTITY="Developer ID Application ($APPLE_TEAM_ID)" codesign --force --timestamp --options runtime \ --sign "$IDENTITY" "$DMG" xcrun notarytool submit "$DMG" \ --apple-id "$APPLE_ID" \ --team-id "$APPLE_TEAM_ID" \ - --password "$APPLE_APP_PASSWORD" \ + --password "$APPLE_APP_SPECIFIC_PASSWORD" \ --wait xcrun stapler staple "$DMG" - name: Package NSIS installer (windows) @@ -273,12 +279,20 @@ jobs: if: runner.os == 'Windows' && env.HAS_WINDOWS_CERTIFICATE == 'true' shell: pwsh env: - WINDOWS_CERT_BASE64: ${{ secrets.WINDOWS_CERT_BASE64 }} - WINDOWS_CERT_PASSWORD: ${{ secrets.WINDOWS_CERT_PASSWORD }} + WIN_CSC_LINK: ${{ secrets.WIN_CSC_LINK }} + WIN_CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }} run: | - # Materialize the .pfx from the base64 secret into a temp file. + # Materialize the .pfx from a base64/data-url/url secret into a temp file. $pfx = Join-Path $env:RUNNER_TEMP "codesign.pfx" - [IO.File]::WriteAllBytes($pfx, [Convert]::FromBase64String($env:WINDOWS_CERT_BASE64)) + $link = $env:WIN_CSC_LINK + if ($link -match '^https?://') { + Invoke-WebRequest -Uri $link -OutFile $pfx + } else { + if ($link -match '^data:.*;base64,(.+)$') { + $link = $Matches[1] + } + [IO.File]::WriteAllBytes($pfx, [Convert]::FromBase64String($link)) + } $exe = "OpenPencil-$env:OP_VERSION-${{ matrix.arch }}-win-setup.exe" # signtool ships with the Windows SDK preinstalled on # windows-latest. RFC-3161 timestamp so signatures outlive the @@ -287,7 +301,7 @@ jobs: Sort-Object FullName -Descending | Select-Object -First 1).FullName & $signtool sign ` /f $pfx ` - /p $env:WINDOWS_CERT_PASSWORD ` + /p $env:WIN_CSC_KEY_PASSWORD ` /fd SHA256 ` /tr http://timestamp.digicert.com ` /td SHA256 ` @@ -386,9 +400,11 @@ jobs: tags: ${{ steps.meta.outputs.tag }} sdk-packages: - name: Build web SDK npm tarballs + name: Build and publish web SDK npm packages runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/v') + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} steps: - uses: actions/checkout@v4 with: @@ -420,6 +436,16 @@ jobs: - name: Install package workspace dependencies working-directory: packages run: bun install --frozen-lockfile + - name: Configure npm auth + shell: bash + run: | + set -euo pipefail + if [ -z "${NPM_TOKEN:-}" ]; then + echo "::error::NPM_TOKEN is required to publish web SDK packages" + exit 1 + fi + printf '//registry.npmjs.org/:_authToken=%s\n' "$NPM_TOKEN" > ~/.npmrc + npm whoami - name: Build wasm-backed SDK packages shell: bash run: | @@ -440,6 +466,23 @@ jobs: (cd "packages/${pkg}" && bun pm pack --destination "${GITHUB_WORKSPACE}/sdk-packages") done ls -la sdk-packages + - name: Publish npm packages + shell: bash + run: | + set -euo pipefail + version="${GITHUB_REF_NAME#v}" + publish_pkg() { + local name="$1" + local tarball="$2" + if npm view "${name}@${version}" version >/dev/null 2>&1; then + echo "::notice::${name}@${version} is already published; skipping" + return + fi + npm publish "$tarball" --access public --tag next + } + publish_pkg "@zseven-w/op-web-sdk" "sdk-packages/zseven-w-op-web-sdk-${version}.tgz" + publish_pkg "@zseven-w/op-web-sdk-react" "sdk-packages/zseven-w-op-web-sdk-react-${version}.tgz" + publish_pkg "@zseven-w/op-web-sdk-vue" "sdk-packages/zseven-w-op-web-sdk-vue-${version}.tgz" - name: Upload SDK tarballs uses: actions/upload-artifact@v4 with: @@ -452,6 +495,8 @@ jobs: needs: [build, web-docker, sdk-packages] runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/v') + env: + TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 - uses: actions/download-artifact@v4 diff --git a/RELEASE_NOTES/v0.8.0.md b/RELEASE_NOTES/v0.8.0.md index 6809a3d67..af95eb994 100644 --- a/RELEASE_NOTES/v0.8.0.md +++ b/RELEASE_NOTES/v0.8.0.md @@ -7,7 +7,7 @@ - Rust desktop app is now the primary OpenPencil experience, with installer artifacts for macOS, Windows, and Linux. - `op` CLI ships as standalone release artifacts for the same desktop target matrix. - Rust web host is published as a GHCR Docker image for tagged releases. -- Web SDK tarballs are attached for `@zseven-w/op-web-sdk`, `@zseven-w/op-web-sdk-react`, and `@zseven-w/op-web-sdk-vue`. +- Web SDK packages are published to npm and attached as tarballs for `@zseven-w/op-web-sdk`, `@zseven-w/op-web-sdk-react`, and `@zseven-w/op-web-sdk-vue`. - Desktop update checks can discover pre-release GitHub releases, so `v0.8.0` can be offered before it is promoted to a stable release. ## Release Artifacts @@ -15,7 +15,7 @@ - macOS: `OpenPencil-0.8.0--mac.dmg`, plus raw desktop and `op` CLI archives. - Windows: `OpenPencil-0.8.0--win-setup.exe`, plus portable desktop and `op` CLI archives. - Linux: `OpenPencil-0.8.0--linux.AppImage`, `OpenPencil-0.8.0--linux.deb`, plus raw desktop and `op` CLI archives. -- Web SDK: npm tarballs for the base SDK, React adapter, and Vue adapter. +- Web SDK: published npm packages and attached npm tarballs for the base SDK, React adapter, and Vue adapter. ## Docker @@ -25,7 +25,7 @@ The release workflow publishes the web image to GHCR as: docker pull ghcr.io/zseven-w/openpencil-web:v0.8.0 ``` -Because this is a pre-release, the workflow does not publish or update a `latest` tag. +Because this is a pre-release, the workflow does not publish or update a Docker `latest` tag. The npm packages are published with the `next` dist-tag. ## Notes