The released extension was a shell without its runtime. The build matrix
now also produces op-host-web-server per target; the vsix release job
packages six platform vsix (vsce --target) embedding bin/<daemon> plus
web/{pkg,canvaskit}; the extension prefers the bundled runtime at spawn
(binary probe with exec-bit restore, env pointing the daemon at the
bundled web assets) with the workspace debug build as the dev fallback.
1141 lines
48 KiB
YAML
1141 lines
48 KiB
YAML
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, publishes the web SDK packages to npm, publishes a GitHub
|
||
# prerelease, then updates the Homebrew tap and Scoop bucket.
|
||
#
|
||
# Artifact matrix (per platform; <v> = tag version, arch token = x64/arm64 to
|
||
# match electron-builder's `${productName}-${version}-${arch}-…` convention
|
||
# from apps/desktop/electron-builder.yml):
|
||
# macOS OpenPencil-<v>-<arch>-mac.dmg notarized final DMG containing
|
||
# a notarized .app from
|
||
# scripts/bundle-macos.sh
|
||
# (.op/.pen/.fig associations in
|
||
# Info.plist, op CLI embedded in
|
||
# Contents/MacOS; Developer ID
|
||
# signed/notarized when secrets
|
||
# exist, ad-hoc signed locally)
|
||
# op-cli-<label>.tar.gz standalone CLI
|
||
# Windows OpenPencil-<v>-<arch>-win-setup.exe NSIS installer (Start-Menu +
|
||
# desktop shortcuts, HKCR
|
||
# .op/.pen ProgID, uninstaller,
|
||
# bundles op.exe)
|
||
# openpencil-desktop-<label>.zip portable (kept)
|
||
# op-cli-<label>.zip standalone CLI
|
||
# Linux OpenPencil-<v>-<arch>-linux.AppImage both arches (appimagetool
|
||
# 1.9.1 ships native x86_64 AND
|
||
# aarch64 builds)
|
||
# OpenPencil-<v>-<arch>-linux.deb .desktop + shared-mime-info
|
||
# registration of .op/.pen,
|
||
# bundles usr/bin/op
|
||
# openpencil-desktop-<label>.tar.gz raw binary (kept)
|
||
# op-cli-<label>.tar.gz standalone CLI
|
||
# all openpencil-vscode-<v>-<t>.vsix VS Code extension, one per
|
||
# vsce target (darwin/linux/
|
||
# win32 × x64/arm64), each
|
||
# bundling the platform's
|
||
# op-host-web-server + the
|
||
# wasm/CanvasKit web assets
|
||
#
|
||
# Branding: productName "OpenPencil" everywhere (parity with
|
||
# electron-builder.yml). The macOS bundle id stays com.zseven-w.openpencil
|
||
# (the Rust shell identity already registered with LaunchServices via
|
||
# scripts/bundle-macos.sh + op-host-desktop bundle metadata) — intentionally
|
||
# NOT electron's dev.openpencil.app, so the Electron and Rust apps can
|
||
# coexist without fighting over file-association ownership.
|
||
#
|
||
# ─── Local-verification caveats (2026-06-10, macOS dev machine) ────────────
|
||
# Verified locally:
|
||
# * bash -n on scripts/bundle-macos.sh, package-deb.sh, package-appimage.sh,
|
||
# package-linux-common.sh
|
||
# * YAML parse of this file
|
||
# * package-deb.sh --layout-only and package-appimage.sh layout dry-runs
|
||
# with stub binaries (payload tree, control file, desktop entry, mime XML)
|
||
# * appimagetool 1.9.1 pinned release assets (x86_64 + aarch64) exist
|
||
# (checked via GitHub API); cargo-bundle `--target` flag confirmed
|
||
# 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;
|
||
# 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)
|
||
# * cargo-bundle bootstrap + `--target` bundling + hdiutil DMG creation on
|
||
# the GitHub macOS runner
|
||
# * installer smoke tests (NSIS install/uninstall + HKCR assoc, deb
|
||
# postinst mime refresh, AppImage launch, DMG mount + Gatekeeper)
|
||
#
|
||
# The `build` job builds the real `op-host-desktop` crate (winit + skia-safe
|
||
# runner that drives the Rust editor); its shipped executable keeps the
|
||
# stable `openpencil-desktop` name. The `op` CLI (crates/op-cli, binary name
|
||
# `op`) is built in the same cargo invocation. The web Docker job builds the
|
||
# Rust web host from `Dockerfile.web-rust`, and the SDK job packages the
|
||
# wasm-backed `packages/op-web-sdk*` workspace tarballs.
|
||
#
|
||
# The standalone `.github/workflows/wasm-bundle-build.yml` still gates the
|
||
# CanvasKit web bundle on push/PR. This release workflow only assembles the
|
||
# tagged, publishable artifacts.
|
||
|
||
on:
|
||
push:
|
||
tags: ['v*']
|
||
workflow_dispatch:
|
||
|
||
permissions:
|
||
contents: write
|
||
packages: write
|
||
|
||
jobs:
|
||
version:
|
||
name: Validate release version
|
||
runs-on: ubuntu-latest
|
||
outputs:
|
||
version: ${{ steps.version.outputs.version }}
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- name: Validate Cargo and tag versions
|
||
id: version
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
cargo_version="$(scripts/workspace-version.sh)"
|
||
if [[ "$GITHUB_REF" == refs/tags/v* ]]; then
|
||
tag_version="${GITHUB_REF_NAME#v}"
|
||
if [[ "$tag_version" != "$cargo_version" ]]; then
|
||
echo "::error::release tag version $tag_version does not match Cargo workspace version $cargo_version"
|
||
exit 1
|
||
fi
|
||
fi
|
||
echo "version=$cargo_version" >> "$GITHUB_OUTPUT"
|
||
|
||
build:
|
||
name: ${{ matrix.label }}
|
||
needs: version
|
||
runs-on: ${{ matrix.runner }}
|
||
if: github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v')
|
||
env:
|
||
OP_VERSION: ${{ needs.version.outputs.version }}
|
||
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:
|
||
include:
|
||
- label: macos-aarch64
|
||
runner: macos-latest
|
||
target: aarch64-apple-darwin
|
||
archive: tar.gz
|
||
arch: arm64
|
||
# macos-13 (Intel) deprecated; cross-compile x86_64-apple-darwin
|
||
# from Apple Silicon (cargo supports cross-compile to host's other
|
||
# arch out of the box, no `cross` needed).
|
||
- label: macos-x86_64
|
||
runner: macos-latest
|
||
target: x86_64-apple-darwin
|
||
archive: tar.gz
|
||
arch: x64
|
||
- label: linux-x86_64
|
||
runner: ubuntu-latest
|
||
target: x86_64-unknown-linux-gnu
|
||
archive: tar.gz
|
||
arch: x64
|
||
# Native ARM64 hosted runner — no `cross`. The cross 0.2.x aarch64
|
||
# image (Ubuntu 16.04 / GCC 5 / FreeType 2.6) is too old to link the
|
||
# modern skia that skia-bindings builds.
|
||
- label: linux-aarch64
|
||
runner: ubuntu-24.04-arm
|
||
target: aarch64-unknown-linux-gnu
|
||
archive: tar.gz
|
||
arch: arm64
|
||
- label: windows-x86_64
|
||
runner: windows-latest
|
||
target: x86_64-pc-windows-msvc
|
||
archive: zip
|
||
arch: x64
|
||
# Windows ARM64 — cargo cross-compile from x86_64 windows runner.
|
||
# The NSIS installer stub stays x86 (runs under emulation on
|
||
# Windows-on-ARM); the installed payload is native aarch64.
|
||
- label: windows-aarch64
|
||
runner: windows-latest
|
||
target: aarch64-pc-windows-msvc
|
||
archive: zip
|
||
arch: arm64
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
with:
|
||
submodules: recursive
|
||
- uses: dtolnay/rust-toolchain@stable
|
||
with:
|
||
toolchain: '1.94'
|
||
targets: ${{ matrix.target }}
|
||
- uses: Swatinem/rust-cache@v2
|
||
with:
|
||
key: release-${{ matrix.target }}
|
||
- name: Install Linux GL/EGL prereqs
|
||
if: runner.os == 'Linux'
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
packages=(
|
||
libxkbcommon-dev libxkbcommon-x11-dev \
|
||
libwayland-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev \
|
||
libegl1-mesa-dev libgles2-mesa-dev libgbm-dev \
|
||
libfreetype-dev libfontconfig1-dev
|
||
)
|
||
for attempt in 1 2 3 4; do
|
||
if sudo apt-get \
|
||
-o Acquire::ForceIPv4=true \
|
||
-o Acquire::Retries=3 \
|
||
-o Acquire::http::Timeout=30 \
|
||
update && \
|
||
sudo apt-get \
|
||
-o Acquire::ForceIPv4=true \
|
||
-o Acquire::Retries=3 \
|
||
-o Acquire::http::Timeout=30 \
|
||
install --yes "${packages[@]}"; then
|
||
break
|
||
fi
|
||
if [[ "$attempt" == 4 ]]; then
|
||
echo "::error::apt prerequisites failed after $attempt attempts"
|
||
exit 1
|
||
fi
|
||
delay=$((attempt * 20))
|
||
echo "::warning::apt prerequisites attempt $attempt failed; retrying in ${delay}s"
|
||
sleep "$delay"
|
||
done
|
||
- name: Build (host)
|
||
run: cargo build -p op-host-desktop -p op-cli -p op-host-web-server --target ${{ matrix.target }} --release
|
||
- name: Package archives (unix)
|
||
if: matrix.archive == 'tar.gz'
|
||
shell: bash
|
||
run: |
|
||
cd target/${{ matrix.target }}/release
|
||
if [ "$RUNNER_OS" != "macOS" ]; then
|
||
tar czf ../../../openpencil-desktop-${{ matrix.label }}.tar.gz openpencil-desktop
|
||
fi
|
||
tar czf ../../../op-cli-${{ matrix.label }}.tar.gz op
|
||
- name: Package archives (windows)
|
||
if: matrix.archive == 'zip'
|
||
shell: pwsh
|
||
run: |
|
||
Compress-Archive `
|
||
-Path target\${{ matrix.target }}\release\openpencil-desktop.exe `
|
||
-DestinationPath openpencil-desktop-${{ matrix.label }}.zip
|
||
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: 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: Bundle macOS app
|
||
if: runner.os == 'macOS'
|
||
shell: bash
|
||
env:
|
||
OPENPENCIL_VERSION: ${{ env.OP_VERSION }}
|
||
OPENPENCIL_TARGET: ${{ matrix.target }}
|
||
# Hand the already-built binaries to the bundle script: it skips its
|
||
# own cargo build and overwrites the bundled executable with exactly
|
||
# 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
|
||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||
run: |
|
||
if [ "$HAS_APPLE_CERTIFICATE" = "true" ]; then
|
||
: "${MACOS_SIGN_IDENTITY:?MACOS_SIGN_IDENTITY was not resolved}"
|
||
export MACOS_SIGN_IDENTITY
|
||
fi
|
||
bash scripts/bundle-macos.sh
|
||
- name: Notarize macOS app
|
||
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_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||
run: |
|
||
set -euo pipefail
|
||
APP="target/${{ matrix.target }}/release/bundle/osx/OpenPencil.app"
|
||
APP_ZIP="OpenPencil-$OP_VERSION-${{ matrix.arch }}-mac-app.zip"
|
||
ditto -c -k --keepParent "$APP" "$APP_ZIP"
|
||
notary_log="$(mktemp)"
|
||
set +e
|
||
xcrun notarytool submit "$APP_ZIP" \
|
||
--apple-id "$APPLE_ID" \
|
||
--team-id "$APPLE_TEAM_ID" \
|
||
--password "$APPLE_APP_SPECIFIC_PASSWORD" \
|
||
--wait 2>&1 | tee "$notary_log"
|
||
notary_status="${PIPESTATUS[0]}"
|
||
set -e
|
||
submission_id="$(awk '/id: / { print $2; exit }' "$notary_log")"
|
||
if [ -n "$submission_id" ]; then
|
||
xcrun notarytool log "$submission_id" \
|
||
--apple-id "$APPLE_ID" \
|
||
--team-id "$APPLE_TEAM_ID" \
|
||
--password "$APPLE_APP_SPECIFIC_PASSWORD" || true
|
||
fi
|
||
if [ "$notary_status" -ne 0 ] || grep -q "status: Invalid" "$notary_log"; then
|
||
echo "::error::macOS app notarization failed"
|
||
exit 1
|
||
fi
|
||
xcrun stapler staple "$APP"
|
||
xcrun stapler validate "$APP"
|
||
- name: Package DMG (macos)
|
||
if: runner.os == 'macOS'
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
APP="target/${{ matrix.target }}/release/bundle/osx/OpenPencil.app"
|
||
DMG="OpenPencil-$OP_VERSION-${{ matrix.arch }}-mac.dmg"
|
||
STAGE="$(mktemp -d)"
|
||
ditto "$APP" "$STAGE/OpenPencil.app"
|
||
if [ "$HAS_APPLE_CERTIFICATE" = "true" ]; then
|
||
xcrun stapler validate "$STAGE/OpenPencil.app"
|
||
fi
|
||
ln -s /Applications "$STAGE/Applications"
|
||
# HFS+ instead of the APFS default: mountable on every macOS the
|
||
# binary itself supports.
|
||
hdiutil create \
|
||
-volname "OpenPencil $OP_VERSION" \
|
||
-srcfolder "$STAGE" \
|
||
-fs HFS+ -format UDZO -ov \
|
||
"$DMG"
|
||
if [ "$HAS_APPLE_CERTIFICATE" = "true" ]; then
|
||
codesign --force --timestamp --sign "$MACOS_SIGN_IDENTITY" "$DMG"
|
||
codesign --verify --verbose "$DMG"
|
||
fi
|
||
- name: 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_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||
run: |
|
||
set -euo pipefail
|
||
DMG="OpenPencil-$OP_VERSION-${{ matrix.arch }}-mac.dmg"
|
||
notary_log="$(mktemp)"
|
||
set +e
|
||
xcrun notarytool submit "$DMG" \
|
||
--apple-id "$APPLE_ID" \
|
||
--team-id "$APPLE_TEAM_ID" \
|
||
--password "$APPLE_APP_SPECIFIC_PASSWORD" \
|
||
--wait 2>&1 | tee "$notary_log"
|
||
notary_status="${PIPESTATUS[0]}"
|
||
set -e
|
||
submission_id="$(awk '/id: / { print $2; exit }' "$notary_log")"
|
||
if [ -n "$submission_id" ]; then
|
||
xcrun notarytool log "$submission_id" \
|
||
--apple-id "$APPLE_ID" \
|
||
--team-id "$APPLE_TEAM_ID" \
|
||
--password "$APPLE_APP_SPECIFIC_PASSWORD" || true
|
||
fi
|
||
if [ "$notary_status" -ne 0 ] || grep -q "status: Invalid" "$notary_log"; then
|
||
echo "::error::macOS DMG notarization failed"
|
||
exit 1
|
||
fi
|
||
xcrun stapler staple "$DMG"
|
||
xcrun stapler validate "$DMG"
|
||
# ─── Code-signing secret reference ─────────────────────────────────
|
||
# Each platform signing path is gated on its required secrets. Without
|
||
# them, releases ship ad-hoc-signed (macOS, via scripts/bundle-macos.sh)
|
||
# / unsigned (Windows); with them, CI performs Developer ID
|
||
# signing/notarization for macOS and Authenticode signing for Windows.
|
||
#
|
||
# Required macOS secrets (Developer ID + notarization):
|
||
# CSC_LINK base64 of the Developer ID Application
|
||
# .p12 export
|
||
# 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_SPECIFIC_PASSWORD app-specific password for notarytool
|
||
#
|
||
# Required Windows secrets (Authenticode):
|
||
# 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: 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: |
|
||
# Defines are absolute because NSIS resolves relative paths against
|
||
# the .nsi's own directory (scripts/).
|
||
makensis `
|
||
"/DVERSION=$env:OP_VERSION" `
|
||
"/DARCH=${{ matrix.arch }}" `
|
||
"/DBIN_DIR=$env:GITHUB_WORKSPACE\target\${{ matrix.target }}\release" `
|
||
"/DICON_FILE=$env:GITHUB_WORKSPACE\crates\op-host-desktop\assets\icon.ico" `
|
||
"/DOUT_FILE=$env:GITHUB_WORKSPACE\OpenPencil-$env:OP_VERSION-${{ matrix.arch }}-win-setup.exe" `
|
||
scripts\package-windows.nsi
|
||
# Authenticode signing scaffolding (NO-OP until WINDOWS_CERT_BASE64
|
||
# exists — see the secrets comment block in the macOS section above).
|
||
# Until then the NSIS installer ships unsigned (current behavior).
|
||
- name: Sign NSIS installer (windows)
|
||
if: runner.os == 'Windows' && env.HAS_WINDOWS_CERTIFICATE == 'true'
|
||
shell: pwsh
|
||
env:
|
||
WIN_CSC_LINK: ${{ secrets.WIN_CSC_LINK }}
|
||
WIN_CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }}
|
||
run: |
|
||
# Materialize the .pfx from a base64/data-url/url secret into a temp file.
|
||
$pfx = Join-Path $env:RUNNER_TEMP "codesign.pfx"
|
||
$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
|
||
# cert's validity window.
|
||
$signtool = (Get-ChildItem "C:\Program Files (x86)\Windows Kits\10\bin\*\x64\signtool.exe" |
|
||
Sort-Object FullName -Descending | Select-Object -First 1).FullName
|
||
& $signtool sign `
|
||
/f $pfx `
|
||
/p $env:WIN_CSC_KEY_PASSWORD `
|
||
/fd SHA256 `
|
||
/tr http://timestamp.digicert.com `
|
||
/td SHA256 `
|
||
$exe
|
||
Remove-Item $pfx -Force
|
||
- name: Package .deb (linux)
|
||
if: runner.os == 'Linux'
|
||
shell: bash
|
||
run: |
|
||
case "${{ matrix.target }}" in
|
||
x86_64-*) DEB_ARCH=amd64 ;;
|
||
aarch64-*) DEB_ARCH=arm64 ;;
|
||
*) echo "unmapped deb arch for ${{ matrix.target }}" >&2; exit 1 ;;
|
||
esac
|
||
bash scripts/package-deb.sh \
|
||
--desktop-bin "target/${{ matrix.target }}/release/openpencil-desktop" \
|
||
--cli-bin "target/${{ matrix.target }}/release/op" \
|
||
--icon crates/op-host-desktop/assets/icon.png \
|
||
--version "$OP_VERSION" \
|
||
--arch "$DEB_ARCH" \
|
||
--out-dir .
|
||
- name: Package AppImage (linux)
|
||
if: runner.os == 'Linux'
|
||
shell: bash
|
||
run: |
|
||
case "${{ matrix.target }}" in
|
||
x86_64-*) AI_ARCH=x86_64 ;;
|
||
aarch64-*) AI_ARCH=aarch64 ;;
|
||
*) echo "unmapped AppImage arch for ${{ matrix.target }}" >&2; exit 1 ;;
|
||
esac
|
||
# appimagetool pinned by URL (release 1.9.1 ships native builds for
|
||
# both runner arches, so each matrix leg packs natively). NOTE:
|
||
# appimagetool 1.9.x downloads the matching static type2-runtime
|
||
# from GitHub at pack time — the runner needs network access.
|
||
curl -fsSL --retry 3 -o /tmp/appimagetool \
|
||
"https://github.com/AppImage/appimagetool/releases/download/1.9.1/appimagetool-${AI_ARCH}.AppImage"
|
||
chmod +x /tmp/appimagetool
|
||
bash scripts/package-appimage.sh \
|
||
--desktop-bin "target/${{ matrix.target }}/release/openpencil-desktop" \
|
||
--cli-bin "target/${{ matrix.target }}/release/op" \
|
||
--icon crates/op-host-desktop/assets/icon.png \
|
||
--version "$OP_VERSION" \
|
||
--arch "$AI_ARCH" \
|
||
--tool /tmp/appimagetool \
|
||
--out-dir .
|
||
- name: Upload artifact
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: openpencil-desktop-${{ matrix.label }}
|
||
path: |
|
||
openpencil-desktop-${{ matrix.label }}.tar.gz
|
||
openpencil-desktop-${{ matrix.label }}.zip
|
||
op-cli-${{ matrix.label }}.tar.gz
|
||
op-cli-${{ matrix.label }}.zip
|
||
# Raw daemon binary for the platform vsix (the extension's bundled
|
||
# runtime). No archive extension, so release-draft's flatten step
|
||
# never picks it up as a release file on its own.
|
||
- name: Upload daemon binary for the VS Code extension
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: openpencil-daemon-${{ matrix.label }}
|
||
path: |
|
||
target/${{ matrix.target }}/release/op-host-web-server
|
||
target/${{ matrix.target }}/release/op-host-web-server.exe
|
||
if-no-files-found: error
|
||
OpenPencil-*-mac.dmg
|
||
OpenPencil-*-win-setup.exe
|
||
OpenPencil-*-linux.AppImage
|
||
OpenPencil-*-linux.deb
|
||
if-no-files-found: ignore
|
||
|
||
web-docker:
|
||
name: Build and push web Docker image
|
||
needs: version
|
||
runs-on: ubuntu-latest
|
||
if: startsWith(github.ref, 'refs/tags/v')
|
||
env:
|
||
OP_VERSION: ${{ needs.version.outputs.version }}
|
||
outputs:
|
||
image: ${{ steps.meta.outputs.image }}
|
||
tag: ${{ steps.meta.outputs.tag }}
|
||
digest: ${{ steps.build.outputs.digest }}
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
with:
|
||
submodules: recursive
|
||
- uses: docker/setup-buildx-action@v3
|
||
- name: Log in to GHCR
|
||
uses: docker/login-action@v3
|
||
with:
|
||
registry: ghcr.io
|
||
username: ${{ github.actor }}
|
||
password: ${{ github.token }}
|
||
- name: Compute image metadata
|
||
id: meta
|
||
shell: bash
|
||
run: |
|
||
version="$OP_VERSION"
|
||
image="ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/openpencil-web"
|
||
tag="${image}:v${version}"
|
||
echo "image=${image}" >> "$GITHUB_OUTPUT"
|
||
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
|
||
- name: Build and push
|
||
id: build
|
||
uses: docker/build-push-action@v6
|
||
with:
|
||
context: .
|
||
file: Dockerfile.web-rust
|
||
push: true
|
||
tags: ${{ steps.meta.outputs.tag }}
|
||
|
||
sdk-packages:
|
||
name: Build and publish web SDK npm packages
|
||
needs: version
|
||
runs-on: ubuntu-latest
|
||
if: startsWith(github.ref, 'refs/tags/v')
|
||
env:
|
||
OP_VERSION: ${{ needs.version.outputs.version }}
|
||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
with:
|
||
submodules: recursive
|
||
- uses: dtolnay/rust-toolchain@stable
|
||
with:
|
||
toolchain: '1.94'
|
||
targets: wasm32-unknown-unknown
|
||
- uses: Swatinem/rust-cache@v2
|
||
with:
|
||
shared-key: web-sdk-release
|
||
- uses: oven-sh/setup-bun@v2
|
||
- name: Install wasm-bindgen-cli (pinned to Cargo.lock)
|
||
shell: bash
|
||
run: |
|
||
version="$(awk '/^name = "wasm-bindgen"$/{found=1; next} found && /^version = /{gsub(/[" ]/,"",$3); print $3; exit}' Cargo.lock)"
|
||
if [ -z "$version" ]; then
|
||
echo "::error::could not resolve wasm-bindgen version from Cargo.lock"
|
||
exit 1
|
||
fi
|
||
cargo install wasm-bindgen-cli --version "$version" --locked
|
||
- name: Install binaryen
|
||
run: |
|
||
BINARYEN_VERSION=version_123
|
||
curl -fsSL \
|
||
"https://github.com/WebAssembly/binaryen/releases/download/${BINARYEN_VERSION}/binaryen-${BINARYEN_VERSION}-x86_64-linux.tar.gz" \
|
||
| tar -xz
|
||
echo "${GITHUB_WORKSPACE}/binaryen-${BINARYEN_VERSION}/bin" >> "$GITHUB_PATH"
|
||
- name: Install package workspace dependencies
|
||
working-directory: packages
|
||
run: bun install --frozen-lockfile
|
||
- name: Verify package versions
|
||
working-directory: packages
|
||
run: bun run sync-version:check
|
||
- 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
|
||
echo "::notice::NPM_TOKEN must be scoped for @zseven-w packages and have bypass 2FA enabled"
|
||
printf '//registry.npmjs.org/:_authToken=%s\n' "$NPM_TOKEN" > ~/.npmrc
|
||
npm whoami
|
||
- name: Build wasm-backed SDK packages
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
cd packages/op-web-sdk
|
||
bun run sync-wasm
|
||
bun run build
|
||
cd ../op-web-sdk-react
|
||
bun run build
|
||
cd ../op-web-sdk-vue
|
||
bun run build
|
||
- name: Pack npm tarballs
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
mkdir -p sdk-packages
|
||
for pkg in op-web-sdk op-web-sdk-react op-web-sdk-vue; do
|
||
(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="$OP_VERSION"
|
||
publish_pkg() {
|
||
local name="$1"
|
||
local tarball="$2"
|
||
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"
|
||
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:
|
||
name: openpencil-web-sdk-packages
|
||
path: sdk-packages/*.tgz
|
||
if-no-files-found: error
|
||
|
||
vsix:
|
||
name: Package VS Code extension (platform vsix with bundled runtime)
|
||
needs: [version, build]
|
||
runs-on: ubuntu-latest
|
||
if: startsWith(github.ref, 'refs/tags/v')
|
||
env:
|
||
OP_VERSION: ${{ needs.version.outputs.version }}
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
with:
|
||
submodules: recursive
|
||
- uses: dtolnay/rust-toolchain@stable
|
||
with:
|
||
toolchain: '1.94'
|
||
targets: wasm32-unknown-unknown
|
||
- uses: Swatinem/rust-cache@v2
|
||
with:
|
||
shared-key: web-sdk-release
|
||
- uses: oven-sh/setup-bun@v2
|
||
- uses: actions/setup-node@v4
|
||
with:
|
||
node-version: '20'
|
||
- name: Install wasm-bindgen-cli (pinned to Cargo.lock)
|
||
shell: bash
|
||
run: |
|
||
version="$(awk '/^name = "wasm-bindgen"$/{found=1; next} found && /^version = /{gsub(/[" ]/,"",$3); print $3; exit}' Cargo.lock)"
|
||
if [ -z "$version" ]; then
|
||
echo "::error::could not resolve wasm-bindgen version from Cargo.lock"
|
||
exit 1
|
||
fi
|
||
cargo install wasm-bindgen-cli --version "$version" --locked
|
||
- name: Install binaryen
|
||
run: |
|
||
BINARYEN_VERSION=version_123
|
||
curl -fsSL \
|
||
"https://github.com/WebAssembly/binaryen/releases/download/${BINARYEN_VERSION}/binaryen-${BINARYEN_VERSION}-x86_64-linux.tar.gz" \
|
||
| tar -xz
|
||
echo "${GITHUB_WORKSPACE}/binaryen-${BINARYEN_VERSION}/bin" >> "$GITHUB_PATH"
|
||
- name: Build web bundle (served by the bundled daemon)
|
||
run: bash tools/check-wasm-bundle.sh
|
||
- name: Download daemon binaries
|
||
uses: actions/download-artifact@v4
|
||
with:
|
||
path: daemons
|
||
pattern: openpencil-daemon-*
|
||
- name: Install package workspace dependencies
|
||
working-directory: packages
|
||
run: bun install --frozen-lockfile
|
||
- name: Check and build extension
|
||
working-directory: packages/op-vscode
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
bunx tsc -p tsconfig.json --noEmit
|
||
bun test src
|
||
node build.mjs
|
||
# Stamp the release version into the manifest (runtime-only — the
|
||
# extension is not in the sync-version managed set yet).
|
||
node -e "const fs=require('fs');const p=JSON.parse(fs.readFileSync('package.json','utf8'));p.version=process.env.OP_VERSION;fs.writeFileSync('package.json',JSON.stringify(p,null,2)+'\n')"
|
||
cp ../../LICENSE LICENSE
|
||
- name: Package platform vsix (daemon + web assets bundled)
|
||
working-directory: packages/op-vscode
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
# build-matrix label -> vsce --target. The extension resolves
|
||
# bin/op-host-web-server[.exe] and web/{pkg,canvaskit} at runtime
|
||
# (restart-source.ts bundledDaemonPath / bundledDaemonEnv).
|
||
pairs=(
|
||
"macos-aarch64 darwin-arm64"
|
||
"macos-x86_64 darwin-x64"
|
||
"linux-x86_64 linux-x64"
|
||
"linux-aarch64 linux-arm64"
|
||
"windows-x86_64 win32-x64"
|
||
"windows-aarch64 win32-arm64"
|
||
)
|
||
for pair in "${pairs[@]}"; do
|
||
label="${pair% *}"
|
||
vs_target="${pair#* }"
|
||
exe="op-host-web-server"
|
||
case "$label" in windows-*) exe="op-host-web-server.exe";; esac
|
||
src="../../daemons/openpencil-daemon-${label}/${exe}"
|
||
if [ ! -f "$src" ]; then
|
||
echo "::error::missing daemon binary for ${label}: ${src}"
|
||
exit 1
|
||
fi
|
||
rm -rf bin web
|
||
mkdir -p bin web
|
||
cp "$src" "bin/${exe}"
|
||
chmod +x "bin/${exe}" || true
|
||
cp -R ../../crates/op-host-web/pkg web/pkg
|
||
# The .opt.wasm is a byte-identical intermediate of the served
|
||
# op_host_web_bg.wasm — shipping both doubles the vsix.
|
||
rm -f web/pkg/*.opt.wasm web/pkg/*.d.ts
|
||
cp -R ../../crates/op-host-web/assets/canvaskit web/canvaskit
|
||
bunx @vscode/vsce package --no-dependencies --allow-missing-repository \
|
||
--target "$vs_target" \
|
||
--out "openpencil-vscode-${OP_VERSION}-${vs_target}.vsix"
|
||
done
|
||
ls -la ./*.vsix
|
||
- name: Upload .vsix artifacts
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: openpencil-vscode-vsix
|
||
path: packages/op-vscode/*.vsix
|
||
if-no-files-found: error
|
||
|
||
release-draft:
|
||
name: Create / update GitHub prerelease
|
||
needs: [version, build, web-docker, sdk-packages, vsix]
|
||
runs-on: ubuntu-latest
|
||
if: startsWith(github.ref, 'refs/tags/v')
|
||
env:
|
||
OP_VERSION: ${{ needs.version.outputs.version }}
|
||
TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- uses: actions/download-artifact@v4
|
||
with:
|
||
path: dist
|
||
pattern: openpencil-*
|
||
- name: Flatten artifacts
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
mkdir -p release-files
|
||
find dist -type f \
|
||
\( -name "*.tar.gz" -o -name "*.zip" -o -name "*.dmg" \
|
||
-o -name "*.exe" -o -name "*.AppImage" -o -name "*.deb" \
|
||
-o -name "*.tgz" -o -name "*.vsix" \) \
|
||
-exec cp {} release-files/ \;
|
||
ls -la release-files
|
||
shopt -s nullglob
|
||
native=(release-files/OpenPencil-* release-files/openpencil-desktop-*)
|
||
cli=(release-files/op-cli-*)
|
||
sdk=(release-files/*.tgz)
|
||
vsix=(release-files/*.vsix)
|
||
if [ "${#native[@]}" -eq 0 ]; then
|
||
echo "::error::missing native desktop artifacts"
|
||
exit 1
|
||
fi
|
||
if [ "${#cli[@]}" -eq 0 ]; then
|
||
echo "::error::missing op-cli artifacts"
|
||
exit 1
|
||
fi
|
||
if [ "${#sdk[@]}" -ne 3 ]; then
|
||
echo "::error::expected 3 SDK tarballs, found ${#sdk[@]}"
|
||
exit 1
|
||
fi
|
||
if [ "${#vsix[@]}" -ne 6 ]; then
|
||
echo "::error::expected 6 platform VS Code extension .vsix, found ${#vsix[@]}"
|
||
exit 1
|
||
fi
|
||
- name: Generate CLI install scripts
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
version="$OP_VERSION"
|
||
|
||
sha_file() {
|
||
sha256sum "release-files/$1" | awk '{print $1}'
|
||
}
|
||
|
||
cli_mac_arm_sha="$(sha_file "op-cli-macos-aarch64.tar.gz")"
|
||
cli_mac_x64_sha="$(sha_file "op-cli-macos-x86_64.tar.gz")"
|
||
cli_linux_arm_sha="$(sha_file "op-cli-linux-aarch64.tar.gz")"
|
||
cli_linux_x64_sha="$(sha_file "op-cli-linux-x86_64.tar.gz")"
|
||
cli_win_arm_sha="$(sha_file "op-cli-windows-aarch64.zip")"
|
||
cli_win_x64_sha="$(sha_file "op-cli-windows-x86_64.zip")"
|
||
|
||
cp scripts/install-op.sh release-files/install-op.sh
|
||
cp scripts/install-op.ps1 release-files/install-op.ps1
|
||
OP_INSTALLER_VERSION="$version" \
|
||
OP_SHA_MACOS_AARCH64="$cli_mac_arm_sha" \
|
||
OP_SHA_MACOS_X86_64="$cli_mac_x64_sha" \
|
||
OP_SHA_LINUX_AARCH64="$cli_linux_arm_sha" \
|
||
OP_SHA_LINUX_X86_64="$cli_linux_x64_sha" \
|
||
perl -0pi -e '
|
||
s/^DEFAULT_OP_VERSION=.*/DEFAULT_OP_VERSION="$ENV{OP_INSTALLER_VERSION}"/m;
|
||
s/^DEFAULT_SHA_MACOS_AARCH64=.*/DEFAULT_SHA_MACOS_AARCH64="$ENV{OP_SHA_MACOS_AARCH64}"/m;
|
||
s/^DEFAULT_SHA_MACOS_X86_64=.*/DEFAULT_SHA_MACOS_X86_64="$ENV{OP_SHA_MACOS_X86_64}"/m;
|
||
s/^DEFAULT_SHA_LINUX_AARCH64=.*/DEFAULT_SHA_LINUX_AARCH64="$ENV{OP_SHA_LINUX_AARCH64}"/m;
|
||
s/^DEFAULT_SHA_LINUX_X86_64=.*/DEFAULT_SHA_LINUX_X86_64="$ENV{OP_SHA_LINUX_X86_64}"/m;
|
||
' release-files/install-op.sh
|
||
OP_INSTALLER_VERSION="$version" \
|
||
OP_SHA_WINDOWS_AARCH64="$cli_win_arm_sha" \
|
||
OP_SHA_WINDOWS_X86_64="$cli_win_x64_sha" \
|
||
perl -0pi -e '
|
||
s/^\$DefaultOpVersion = .*/\$DefaultOpVersion = "$ENV{OP_INSTALLER_VERSION}"/m;
|
||
s/^\$DefaultShaWindowsAarch64 = .*/\$DefaultShaWindowsAarch64 = "$ENV{OP_SHA_WINDOWS_AARCH64}"/m;
|
||
s/^\$DefaultShaWindowsX86_64 = .*/\$DefaultShaWindowsX86_64 = "$ENV{OP_SHA_WINDOWS_X86_64}"/m;
|
||
' release-files/install-op.ps1
|
||
chmod +x release-files/install-op.sh
|
||
if grep -E 'DEFAULT_OP_VERSION=""|DEFAULT_SHA_(MACOS|LINUX)_[A-Z0-9_]+=""|\$Default(OpVersion|ShaWindows[A-Za-z0-9_]+) = ""' \
|
||
release-files/install-op.sh release-files/install-op.ps1; then
|
||
echo "::error::CLI install scripts still contain unstamped release metadata"
|
||
exit 1
|
||
fi
|
||
grep -q "$version" release-files/install-op.sh
|
||
grep -q "$version" release-files/install-op.ps1
|
||
- name: Prepare release body
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
version="$OP_VERSION"
|
||
note="RELEASE_NOTES/v${version}.md"
|
||
if [ ! -f "$note" ]; then
|
||
echo "::error::missing release note: $note"
|
||
exit 1
|
||
fi
|
||
cp "$note" release-body.md
|
||
{
|
||
printf '\n## Web Docker Image\n\n'
|
||
printf -- '- Image: `%s`\n' '${{ needs.web-docker.outputs.tag }}'
|
||
printf -- '- Digest: `%s`\n' '${{ needs.web-docker.outputs.digest }}'
|
||
} >> release-body.md
|
||
- name: Create / update GitHub prerelease
|
||
uses: softprops/action-gh-release@v2
|
||
with:
|
||
draft: false
|
||
prerelease: true
|
||
make_latest: false
|
||
body_path: release-body.md
|
||
files: release-files/*
|
||
|
||
package-managers:
|
||
name: Update Homebrew tap and Scoop bucket
|
||
needs: [version, release-draft]
|
||
runs-on: ubuntu-latest
|
||
if: startsWith(github.ref, 'refs/tags/v')
|
||
env:
|
||
OP_VERSION: ${{ needs.version.outputs.version }}
|
||
TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
|
||
HOMEBREW_TAP_REPOSITORY: ${{ vars.HOMEBREW_TAP_REPOSITORY || 'ZSeven-W/homebrew-openpencil' }}
|
||
SCOOP_BUCKET_REPOSITORY: ${{ vars.SCOOP_BUCKET_REPOSITORY || 'ZSeven-W/scoop-openpencil' }}
|
||
steps:
|
||
- name: Validate package-manager token
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
if [ -z "${TAP_GITHUB_TOKEN:-}" ]; then
|
||
echo "::error::TAP_GITHUB_TOKEN is required to update Homebrew and Scoop repositories"
|
||
exit 1
|
||
fi
|
||
|
||
- name: Download release assets and compute hashes
|
||
shell: bash
|
||
env:
|
||
GH_TOKEN: ${{ github.token }}
|
||
run: |
|
||
set -euo pipefail
|
||
version="$OP_VERSION"
|
||
mkdir -p release-assets
|
||
gh release download "$GITHUB_REF_NAME" \
|
||
--repo "$GITHUB_REPOSITORY" \
|
||
--dir release-assets \
|
||
--clobber
|
||
|
||
required=(
|
||
"OpenPencil-${version}-arm64-mac.dmg"
|
||
"OpenPencil-${version}-x64-mac.dmg"
|
||
"openpencil-desktop-windows-x86_64.zip"
|
||
"openpencil-desktop-windows-aarch64.zip"
|
||
"op-cli-macos-aarch64.tar.gz"
|
||
"op-cli-macos-x86_64.tar.gz"
|
||
"op-cli-linux-aarch64.tar.gz"
|
||
"op-cli-linux-x86_64.tar.gz"
|
||
"op-cli-windows-aarch64.zip"
|
||
"op-cli-windows-x86_64.zip"
|
||
)
|
||
for file in "${required[@]}"; do
|
||
if [ ! -f "release-assets/$file" ]; then
|
||
echo "::error::missing release asset for package-manager update: $file"
|
||
exit 1
|
||
fi
|
||
done
|
||
|
||
sha_file() {
|
||
sha256sum "release-assets/$1" | awk '{print $1}'
|
||
}
|
||
|
||
{
|
||
echo "MAC_ARM_DMG_SHA=$(sha_file "OpenPencil-${version}-arm64-mac.dmg")"
|
||
echo "MAC_X64_DMG_SHA=$(sha_file "OpenPencil-${version}-x64-mac.dmg")"
|
||
echo "CLI_MAC_ARM_SHA=$(sha_file "op-cli-macos-aarch64.tar.gz")"
|
||
echo "CLI_MAC_X64_SHA=$(sha_file "op-cli-macos-x86_64.tar.gz")"
|
||
echo "CLI_LINUX_ARM_SHA=$(sha_file "op-cli-linux-aarch64.tar.gz")"
|
||
echo "CLI_LINUX_X64_SHA=$(sha_file "op-cli-linux-x86_64.tar.gz")"
|
||
echo "DESKTOP_WIN_ARM_SHA=$(sha_file "openpencil-desktop-windows-aarch64.zip")"
|
||
echo "DESKTOP_WIN_X64_SHA=$(sha_file "openpencil-desktop-windows-x86_64.zip")"
|
||
echo "CLI_WIN_ARM_SHA=$(sha_file "op-cli-windows-aarch64.zip")"
|
||
echo "CLI_WIN_X64_SHA=$(sha_file "op-cli-windows-x86_64.zip")"
|
||
} >> "$GITHUB_ENV"
|
||
|
||
- name: Checkout Homebrew tap
|
||
uses: actions/checkout@v4
|
||
with:
|
||
repository: ${{ env.HOMEBREW_TAP_REPOSITORY }}
|
||
token: ${{ env.TAP_GITHUB_TOKEN }}
|
||
path: homebrew-tap
|
||
|
||
- name: Update Homebrew cask and formula
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
mkdir -p homebrew-tap/Casks homebrew-tap/Formula
|
||
cat > homebrew-tap/Casks/openpencil.rb <<EOF
|
||
cask "openpencil" do
|
||
version "${OP_VERSION}"
|
||
|
||
on_arm do
|
||
sha256 "${MAC_ARM_DMG_SHA}"
|
||
url "https://github.com/zseven-w/openpencil/releases/download/v#{version}/OpenPencil-#{version}-arm64-mac.dmg"
|
||
end
|
||
on_intel do
|
||
sha256 "${MAC_X64_DMG_SHA}"
|
||
url "https://github.com/zseven-w/openpencil/releases/download/v#{version}/OpenPencil-#{version}-x64-mac.dmg"
|
||
end
|
||
|
||
name "OpenPencil"
|
||
desc "Open-source AI-native vector design tool"
|
||
homepage "https://github.com/zseven-w/openpencil"
|
||
|
||
app "OpenPencil.app"
|
||
|
||
zap trash: [
|
||
"~/Library/Application Support/OpenPencil",
|
||
"~/Library/Preferences/com.zseven-w.openpencil.plist",
|
||
"~/Library/Preferences/dev.openpencil.app.plist",
|
||
"~/Library/Caches/com.zseven-w.openpencil",
|
||
"~/Library/Caches/dev.openpencil.app",
|
||
]
|
||
end
|
||
EOF
|
||
|
||
cat > homebrew-tap/Formula/op.rb <<EOF
|
||
class Op < Formula
|
||
desc "OpenPencil command-line tool"
|
||
homepage "https://github.com/zseven-w/openpencil"
|
||
version "${OP_VERSION}"
|
||
license "MIT"
|
||
|
||
on_macos do
|
||
if Hardware::CPU.arm?
|
||
url "https://github.com/zseven-w/openpencil/releases/download/v#{version}/op-cli-macos-aarch64.tar.gz"
|
||
sha256 "${CLI_MAC_ARM_SHA}"
|
||
else
|
||
url "https://github.com/zseven-w/openpencil/releases/download/v#{version}/op-cli-macos-x86_64.tar.gz"
|
||
sha256 "${CLI_MAC_X64_SHA}"
|
||
end
|
||
end
|
||
|
||
on_linux do
|
||
if Hardware::CPU.arm?
|
||
url "https://github.com/zseven-w/openpencil/releases/download/v#{version}/op-cli-linux-aarch64.tar.gz"
|
||
sha256 "${CLI_LINUX_ARM_SHA}"
|
||
else
|
||
url "https://github.com/zseven-w/openpencil/releases/download/v#{version}/op-cli-linux-x86_64.tar.gz"
|
||
sha256 "${CLI_LINUX_X64_SHA}"
|
||
end
|
||
end
|
||
|
||
def install
|
||
bin.install "op"
|
||
end
|
||
|
||
test do
|
||
assert_match version.to_s, shell_output("#{bin}/op --version")
|
||
end
|
||
end
|
||
EOF
|
||
|
||
ruby -c homebrew-tap/Casks/openpencil.rb
|
||
ruby -c homebrew-tap/Formula/op.rb
|
||
|
||
- name: Commit Homebrew updates
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
cd homebrew-tap
|
||
git config user.name "github-actions[bot]"
|
||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||
git add Casks/openpencil.rb Formula/op.rb
|
||
if git diff --cached --quiet; then
|
||
echo "::notice::Homebrew tap already matches v${OP_VERSION}"
|
||
else
|
||
git commit -m "Update OpenPencil to v${OP_VERSION}"
|
||
git push
|
||
fi
|
||
|
||
- name: Checkout Scoop bucket
|
||
uses: actions/checkout@v4
|
||
with:
|
||
repository: ${{ env.SCOOP_BUCKET_REPOSITORY }}
|
||
token: ${{ env.TAP_GITHUB_TOKEN }}
|
||
path: scoop-bucket
|
||
|
||
- name: Update Scoop manifests
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
mkdir -p scoop-bucket/bucket
|
||
cat > scoop-bucket/bucket/openpencil.json <<EOF
|
||
{
|
||
"version": "${OP_VERSION}",
|
||
"description": "Open-source AI-native vector design tool",
|
||
"homepage": "https://github.com/zseven-w/openpencil",
|
||
"license": "MIT",
|
||
"architecture": {
|
||
"64bit": {
|
||
"url": "https://github.com/zseven-w/openpencil/releases/download/v${OP_VERSION}/openpencil-desktop-windows-x86_64.zip",
|
||
"hash": "${DESKTOP_WIN_X64_SHA}"
|
||
},
|
||
"arm64": {
|
||
"url": "https://github.com/zseven-w/openpencil/releases/download/v${OP_VERSION}/openpencil-desktop-windows-aarch64.zip",
|
||
"hash": "${DESKTOP_WIN_ARM_SHA}"
|
||
}
|
||
},
|
||
"bin": "openpencil-desktop.exe",
|
||
"shortcuts": [["openpencil-desktop.exe", "OpenPencil"]],
|
||
"checkver": {
|
||
"github": "https://github.com/zseven-w/openpencil"
|
||
},
|
||
"autoupdate": {
|
||
"architecture": {
|
||
"64bit": {
|
||
"url": "https://github.com/zseven-w/openpencil/releases/download/v\$version/openpencil-desktop-windows-x86_64.zip"
|
||
},
|
||
"arm64": {
|
||
"url": "https://github.com/zseven-w/openpencil/releases/download/v\$version/openpencil-desktop-windows-aarch64.zip"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
EOF
|
||
|
||
cat > scoop-bucket/bucket/op.json <<EOF
|
||
{
|
||
"version": "${OP_VERSION}",
|
||
"description": "OpenPencil command-line tool",
|
||
"homepage": "https://github.com/zseven-w/openpencil",
|
||
"license": "MIT",
|
||
"architecture": {
|
||
"64bit": {
|
||
"url": "https://github.com/zseven-w/openpencil/releases/download/v${OP_VERSION}/op-cli-windows-x86_64.zip",
|
||
"hash": "${CLI_WIN_X64_SHA}"
|
||
},
|
||
"arm64": {
|
||
"url": "https://github.com/zseven-w/openpencil/releases/download/v${OP_VERSION}/op-cli-windows-aarch64.zip",
|
||
"hash": "${CLI_WIN_ARM_SHA}"
|
||
}
|
||
},
|
||
"bin": "op.exe",
|
||
"checkver": {
|
||
"github": "https://github.com/zseven-w/openpencil"
|
||
},
|
||
"autoupdate": {
|
||
"architecture": {
|
||
"64bit": {
|
||
"url": "https://github.com/zseven-w/openpencil/releases/download/v\$version/op-cli-windows-x86_64.zip"
|
||
},
|
||
"arm64": {
|
||
"url": "https://github.com/zseven-w/openpencil/releases/download/v\$version/op-cli-windows-aarch64.zip"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
EOF
|
||
|
||
jq empty scoop-bucket/bucket/openpencil.json scoop-bucket/bucket/op.json
|
||
|
||
- name: Commit Scoop updates
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
cd scoop-bucket
|
||
git config user.name "github-actions[bot]"
|
||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||
git add bucket/openpencil.json bucket/op.json
|
||
if git diff --cached --quiet; then
|
||
echo "::notice::Scoop bucket already matches v${OP_VERSION}"
|
||
else
|
||
git commit -m "Update OpenPencil to v${OP_VERSION}"
|
||
git push
|
||
fi
|