fix(release): skip existing npm packages

This commit is contained in:
Kayshen-X 2026-07-08 23:50:39 +08:00
parent f6f3303635
commit 4abd5119e7
3 changed files with 112 additions and 11 deletions

View file

@ -48,7 +48,7 @@ name: Rust release artifacts
# against upstream source (present since v0.9.0)
# NOT verifiable locally — needs a real tag-push CI run:
# * makensis compile of scripts/package-windows.nsi (no makensis on macOS;
# NSIS is preinstalled on windows-latest per the runner-images manifest)
# CI installs NSIS on windows-latest before packaging)
# * actual AppImage assembly (appimagetool is a Linux ELF; it also
# downloads its type2-runtime from GitHub at pack time — needs network)
# * dpkg-deb --build (no dpkg locally; only the layout path was dry-run)
@ -185,6 +185,18 @@ jobs:
with:
p12-file-base64: ${{ secrets.CSC_LINK }}
p12-password: ${{ secrets.CSC_KEY_PASSWORD }}
- name: Resolve macOS signing identity
if: runner.os == 'macOS' && env.HAS_APPLE_CERTIFICATE == 'true'
shell: bash
run: |
set -euo pipefail
identity="$(security find-identity -v -p codesigning | awk -F'"' '/Developer ID Application/ {print $2; exit}')"
if [ -z "$identity" ]; then
security find-identity -v -p codesigning
echo "::error::Developer ID Application identity not found in imported keychain"
exit 1
fi
echo "MACOS_SIGN_IDENTITY=$identity" >> "$GITHUB_ENV"
- name: Package DMG (macos)
if: runner.os == 'macOS'
shell: bash
@ -199,7 +211,8 @@ jobs:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
if [ "$HAS_APPLE_CERTIFICATE" = "true" ]; then
export MACOS_SIGN_IDENTITY="Developer ID Application ($APPLE_TEAM_ID)"
: "${MACOS_SIGN_IDENTITY:?MACOS_SIGN_IDENTITY was not resolved}"
export MACOS_SIGN_IDENTITY
fi
bash scripts/bundle-macos.sh
APP="target/${{ matrix.target }}/release/bundle/osx/OpenPencil.app"
@ -249,22 +262,42 @@ jobs:
# 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"
--sign "$MACOS_SIGN_IDENTITY" "$DMG"
xcrun notarytool submit "$DMG" \
--apple-id "$APPLE_ID" \
--team-id "$APPLE_TEAM_ID" \
--password "$APPLE_APP_SPECIFIC_PASSWORD" \
--wait
xcrun stapler staple "$DMG"
- name: Install NSIS (windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
choco install nsis -y --no-progress
$nsisRoots = @(
"${env:ProgramFiles(x86)}\NSIS",
"$env:ProgramFiles\NSIS"
)
$makensis = $null
foreach ($root in $nsisRoots) {
$candidate = Join-Path $root "makensis.exe"
if (Test-Path $candidate) {
$makensis = $candidate
break
}
}
if (-not $makensis) {
throw "makensis.exe not found after NSIS install"
}
Split-Path $makensis | Out-File -FilePath $env:GITHUB_PATH -Append
& $makensis /VERSION
- name: Package NSIS installer (windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
# makensis is preinstalled on windows-latest (runner-images
# manifest). Defines are absolute because NSIS resolves relative
# paths against the .nsi's own directory (scripts/).
# Defines are absolute because NSIS resolves relative paths against
# the .nsi's own directory (scripts/).
makensis `
"/DVERSION=$env:OP_VERSION" `
"/DARCH=${{ matrix.arch }}" `
@ -475,10 +508,16 @@ jobs:
publish_pkg() {
local name="$1"
local tarball="$2"
if npm view "${name}@${version}" version >/dev/null 2>&1; then
local view_output
if view_output="$(npm view "${name}@${version}" version 2>&1)"; then
echo "::notice::${name}@${version} is already published; skipping"
return
fi
if ! grep -Eq 'E404|404 Not Found|is not in this registry' <<<"$view_output"; then
printf '%s\n' "$view_output"
echo "::error::failed to check npm package ${name}@${version}"
exit 1
fi
npm publish "./$tarball" --access public --tag next
}
publish_pkg "@zseven-w/op-web-sdk" "sdk-packages/zseven-w-op-web-sdk-${version}.tgz"

View file

@ -42,6 +42,68 @@ fn release_workflow_documents_current_canvaskit_bundle_path() {
);
}
#[test]
fn release_workflow_resolves_macos_signing_identity_from_keychain() {
let workflow = std::fs::read_to_string(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../.github/workflows/rust-release.yml"
))
.expect("rust-release workflow is readable");
assert!(
workflow.contains("Resolve macOS signing identity"),
"release workflow should resolve the imported Developer ID identity before signing"
);
assert!(
workflow.contains("security find-identity -v -p codesigning"),
"release workflow should query the imported keychain identity instead of guessing it"
);
assert!(
!workflow.contains("Developer ID Application ($APPLE_TEAM_ID)"),
"release workflow must not hard-code an incomplete Developer ID identity"
);
}
#[test]
fn release_workflow_installs_nsis_on_windows_runner() {
let workflow = std::fs::read_to_string(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../.github/workflows/rust-release.yml"
))
.expect("rust-release workflow is readable");
assert!(
workflow.contains("Install NSIS (windows)"),
"release workflow should install NSIS before invoking makensis"
);
assert!(
workflow.contains("choco install nsis"),
"release workflow should not assume makensis is preinstalled on windows-latest"
);
assert!(
workflow.contains("GITHUB_PATH") && workflow.contains("makensis.exe"),
"release workflow should add makensis.exe to PATH after installing NSIS"
);
}
#[test]
fn release_workflow_distinguishes_published_npm_packages_from_check_failures() {
let workflow = std::fs::read_to_string(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../../.github/workflows/rust-release.yml"
))
.expect("rust-release workflow is readable");
assert!(
workflow.contains("already published; skipping"),
"release workflow should skip npm packages that are already published"
);
assert!(
workflow.contains("E404") && workflow.contains("failed to check npm package"),
"release workflow should publish only on npm 404 and fail on other npm view errors"
);
}
#[test]
fn web_smoke_page_uses_current_canvaskit_bundle_command() {
let smoke = std::fs::read_to_string(concat!(env!("CARGO_MANIFEST_DIR"), "/smoke/step-1b.html"))

View file

@ -1,9 +1,9 @@
; OpenPencil Windows installer (NSIS).
;
; Why NSIS (not Inno Setup): makensis is preinstalled on the GitHub
; windows-latest runner image, and the TS reference pipeline
; Why NSIS (not Inno Setup): the TS reference pipeline
; (apps/desktop/electron-builder.yml) also targets `nsis`, so installer UX
; stays consistent between the Electron and Rust shells.
; stays consistent between the Electron and Rust shells. The release workflow
; installs NSIS explicitly before invoking makensis.
;
; Installs:
; openpencil-desktop.exe editor binary