chore(build): release packaging scripts, wasm bundle gate with codegen, workspace docs + lockfile
This commit is contained in:
parent
036222645c
commit
4c7a2a52a1
193
.github/workflows/rust-release.yml
vendored
193
.github/workflows/rust-release.yml
vendored
|
|
@ -1,13 +1,67 @@
|
|||
name: Rust release artifacts
|
||||
|
||||
# Triggered on tag push (v*) — builds release binaries across desktop targets
|
||||
# and assembles a GitHub Release draft. 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. Full DMG / AppImage / EXE installer packaging lands in Step 1f.
|
||||
# The wasm web bundle is currently DEFERRED (see comment block below); when
|
||||
# the CI-side C-hard pipeline (emscripten install + EMSDK + symlink hack +
|
||||
# wasm-bindgen + wasm-opt) lands it will re-enter the matrix.
|
||||
# Triggered on tag push (v*) — builds release binaries across desktop targets,
|
||||
# packages real installers per platform, and assembles a GitHub Release draft.
|
||||
#
|
||||
# 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 .app via scripts/bundle-macos.sh
|
||||
# (.op/.pen/.fig associations in
|
||||
# Info.plist, op CLI embedded in
|
||||
# Contents/MacOS, ad-hoc signed)
|
||||
# openpencil-desktop-<label>.tar.gz raw binary (kept)
|
||||
# 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
|
||||
#
|
||||
# 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;
|
||||
# NSIS is preinstalled on windows-latest per the runner-images manifest)
|
||||
# * 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 wasm web bundle is
|
||||
# currently DEFERRED (see comment block below); when the CI-side C-hard
|
||||
# pipeline (emscripten install + EMSDK + symlink hack + wasm-bindgen +
|
||||
# wasm-opt) lands it will re-enter the matrix.
|
||||
|
||||
on:
|
||||
push:
|
||||
|
|
@ -26,6 +80,7 @@ jobs:
|
|||
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).
|
||||
|
|
@ -33,10 +88,12 @@ jobs:
|
|||
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.
|
||||
|
|
@ -44,15 +101,20 @@ jobs:
|
|||
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:
|
||||
|
|
@ -73,21 +135,119 @@ jobs:
|
|||
libwayland-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev \
|
||||
libegl1-mesa-dev libgles2-mesa-dev libgbm-dev \
|
||||
libfreetype-dev libfontconfig1-dev
|
||||
- name: Compute release version
|
||||
shell: bash
|
||||
run: |
|
||||
# Tag pushes (vX.Y.Z) drive the artifact version; workflow_dispatch
|
||||
# falls back to [workspace.package].version in the root Cargo.toml.
|
||||
if [[ "$GITHUB_REF" == refs/tags/v* ]]; then
|
||||
echo "OP_VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_ENV"
|
||||
else
|
||||
ver="$(awk -F'"' '/^\[workspace\.package\]/{f=1} f && /^version/{print $2; exit}' Cargo.toml)"
|
||||
echo "OP_VERSION=${ver:-0.0.0}" >> "$GITHUB_ENV"
|
||||
fi
|
||||
- name: Build (host)
|
||||
run: cargo build -p op-host-desktop --target ${{ matrix.target }} --release
|
||||
- name: Package archive (unix)
|
||||
run: cargo build -p op-host-desktop -p op-cli --target ${{ matrix.target }} --release
|
||||
- name: Package archives (unix)
|
||||
if: matrix.archive == 'tar.gz'
|
||||
shell: bash
|
||||
run: |
|
||||
cd target/${{ matrix.target }}/release
|
||||
tar czf ../../../openpencil-desktop-${{ matrix.label }}.tar.gz openpencil-desktop
|
||||
- name: Package archive (windows)
|
||||
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: Package DMG (macos)
|
||||
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
|
||||
# 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 }}
|
||||
run: |
|
||||
bash scripts/bundle-macos.sh
|
||||
APP="target/${{ matrix.target }}/release/bundle/osx/OpenPencil.app"
|
||||
STAGE="$(mktemp -d)"
|
||||
cp -R "$APP" "$STAGE/OpenPencil.app"
|
||||
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 \
|
||||
"OpenPencil-$OP_VERSION-${{ matrix.arch }}-mac.dmg"
|
||||
- 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/).
|
||||
makensis `
|
||||
"/DVERSION=$env:OP_VERSION" `
|
||||
"/DARCH=${{ matrix.arch }}" `
|
||||
"/DBIN_DIR=$env:GITHUB_WORKSPACE\target\${{ matrix.target }}\release" `
|
||||
"/DICON_FILE=$env:GITHUB_WORKSPACE\apps\desktop\build\icon.ico" `
|
||||
"/DOUT_FILE=$env:GITHUB_WORKSPACE\OpenPencil-$env:OP_VERSION-${{ matrix.arch }}-win-setup.exe" `
|
||||
scripts\package-windows.nsi
|
||||
- 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 apps/desktop/build/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 apps/desktop/build/icon.png \
|
||||
--version "$OP_VERSION" \
|
||||
--arch "$AI_ARCH" \
|
||||
--tool /tmp/appimagetool \
|
||||
--out-dir .
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
|
@ -95,6 +255,12 @@ jobs:
|
|||
path: |
|
||||
openpencil-desktop-${{ matrix.label }}.tar.gz
|
||||
openpencil-desktop-${{ matrix.label }}.zip
|
||||
op-cli-${{ matrix.label }}.tar.gz
|
||||
op-cli-${{ matrix.label }}.zip
|
||||
OpenPencil-*-mac.dmg
|
||||
OpenPencil-*-win-setup.exe
|
||||
OpenPencil-*-linux.AppImage
|
||||
OpenPencil-*-linux.deb
|
||||
if-no-files-found: ignore
|
||||
|
||||
# Phase 1b release wasm artifact: explicitly DEFERRED (not silently
|
||||
|
|
@ -132,7 +298,10 @@ jobs:
|
|||
- name: Flatten artifacts
|
||||
run: |
|
||||
mkdir -p release-files
|
||||
find dist -type f \( -name "*.tar.gz" -o -name "*.zip" \) -exec cp {} release-files/ \;
|
||||
find dist -type f \
|
||||
\( -name "*.tar.gz" -o -name "*.zip" -o -name "*.dmg" \
|
||||
-o -name "*.exe" -o -name "*.AppImage" -o -name "*.deb" \) \
|
||||
-exec cp {} release-files/ \;
|
||||
ls -la release-files
|
||||
- name: Create / update GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
|
|
|
|||
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -3001,6 +3001,7 @@ dependencies = [
|
|||
"op-editor-core",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3146,6 +3147,7 @@ dependencies = [
|
|||
"op-codegen",
|
||||
"op-editor-core",
|
||||
"op-editor-ui",
|
||||
"op-figma",
|
||||
"op-pen-loader",
|
||||
"serde_json",
|
||||
"skia-safe",
|
||||
|
|
|
|||
|
|
@ -489,8 +489,12 @@ real CLI agents, not a stub:
|
|||
|
||||
- `ChatState::begin_send` (shell-core `document/chat.rs`) — the
|
||||
native send path: pushes the user message + an empty assistant
|
||||
bubble, raises `chat.pending_send`. The web shell keeps the
|
||||
offline `send()` echo stub.
|
||||
bubble, raises `chat.pending_send`. The web shell (with the
|
||||
`codegen` feature — the production bundle) drains it into
|
||||
`web_chat.rs` and streams the turn through the daemon's
|
||||
`/api/ai/stream`; a transport-less build reports an honest
|
||||
per-send error (the old `send()` echo stub is retired from the
|
||||
web send path).
|
||||
- `ChatProvider` (shell-core `chat_provider.rs`) is the
|
||||
transport-free trait; real impls live desktop-side:
|
||||
`chat_runtime.rs` (`BuiltInProvider`, agent-rs), `chat_claude.rs`
|
||||
|
|
|
|||
|
|
@ -27,11 +27,30 @@
|
|||
# fields without compounding them (PlistBuddy `Set` replaces; `Add`
|
||||
# would duplicate, so this script clears the entries it rewrites
|
||||
# first).
|
||||
#
|
||||
# CI / cross-build controls (all optional, default = local behavior):
|
||||
# OPENPENCIL_VERSION CFBundleShortVersionString (default 0.8.0)
|
||||
# OPENPENCIL_TARGET cargo target triple (e.g. x86_64-apple-darwin);
|
||||
# builds + bundles for that triple and reads the
|
||||
# bundle from target/<triple>/release/bundle/osx
|
||||
# OPENPENCIL_BINARY path to a prebuilt release openpencil-desktop;
|
||||
# skips this script's own cargo build and is copied
|
||||
# over the bundled executable afterwards, so the
|
||||
# shipped binary is EXACTLY the one CI built
|
||||
# (cargo-bundle still runs `cargo build` internally,
|
||||
# but with a warm target dir that is a no-op)
|
||||
# OPENPENCIL_CLI_BINARY path to a prebuilt `op` CLI binary; embedded at
|
||||
# Contents/MacOS/op so the .app/DMG ships the CLI
|
||||
# MACOS_SIGN_IDENTITY codesign identity. Defaults to "-" (ad-hoc).
|
||||
# No signing secrets exist in CI today; when a
|
||||
# Developer ID cert lands, export this env (plus
|
||||
# keychain import + notarization in the workflow).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
WS_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
APP_VERSION="${OPENPENCIL_VERSION:-0.8.0}"
|
||||
TARGET_TRIPLE="${OPENPENCIL_TARGET:-}"
|
||||
|
||||
# Locate cargo-bundle. Tries PATH first, then a workspace-local
|
||||
# fallback under `target/cargo-bundle-host/bin`. When neither
|
||||
|
|
@ -56,14 +75,24 @@ else
|
|||
CARGO_BUNDLE="$CARGO_BUNDLE_HOME/bin/cargo-bundle"
|
||||
fi
|
||||
|
||||
echo "==> building release binary"
|
||||
( cd "$WS_ROOT" && cargo build --release --bin openpencil-desktop )
|
||||
if [ -n "${OPENPENCIL_BINARY:-}" ]; then
|
||||
echo "==> skipping cargo build (prebuilt binary: $OPENPENCIL_BINARY)"
|
||||
else
|
||||
echo "==> building release binary"
|
||||
( cd "$WS_ROOT" && cargo build --release --bin openpencil-desktop \
|
||||
${TARGET_TRIPLE:+--target "$TARGET_TRIPLE"} )
|
||||
fi
|
||||
|
||||
echo "==> running cargo-bundle"
|
||||
( cd "$WS_ROOT/crates/op-host-desktop" \
|
||||
&& "$CARGO_BUNDLE" bundle --bin openpencil-desktop --release --format osx )
|
||||
&& "$CARGO_BUNDLE" bundle --bin openpencil-desktop --release --format osx \
|
||||
${TARGET_TRIPLE:+--target "$TARGET_TRIPLE"} )
|
||||
|
||||
OUT_DIR="$WS_ROOT/target/release/bundle/osx"
|
||||
if [ -n "$TARGET_TRIPLE" ]; then
|
||||
OUT_DIR="$WS_ROOT/target/$TARGET_TRIPLE/release/bundle/osx"
|
||||
else
|
||||
OUT_DIR="$WS_ROOT/target/release/bundle/osx"
|
||||
fi
|
||||
RAW_APP="$OUT_DIR/op-host-desktop.app"
|
||||
APP="$OUT_DIR/OpenPencil.app"
|
||||
|
||||
|
|
@ -139,6 +168,30 @@ echo "==> copying icon into Resources/"
|
|||
mkdir -p "$APP/Contents/Resources"
|
||||
cp "$WS_ROOT/crates/op-host-desktop/assets/icon.icns" "$APP/Contents/Resources/icon.icns"
|
||||
|
||||
if [ -n "${OPENPENCIL_BINARY:-}" ]; then
|
||||
echo "==> installing prebuilt binary into Contents/MacOS/"
|
||||
cp "$OPENPENCIL_BINARY" "$APP/Contents/MacOS/openpencil-desktop"
|
||||
chmod 755 "$APP/Contents/MacOS/openpencil-desktop"
|
||||
fi
|
||||
|
||||
if [ -n "${OPENPENCIL_CLI_BINARY:-}" ]; then
|
||||
echo "==> embedding op CLI into Contents/MacOS/"
|
||||
cp "$OPENPENCIL_CLI_BINARY" "$APP/Contents/MacOS/op"
|
||||
chmod 755 "$APP/Contents/MacOS/op"
|
||||
fi
|
||||
|
||||
# Sign the bundle. Ad-hoc ("-") by default — enough for local launch and
|
||||
# for arm64 Macs which refuse fully unsigned code; Gatekeeper still warns
|
||||
# on downloaded DMGs until real Developer ID signing + notarization land
|
||||
# (set MACOS_SIGN_IDENTITY when the cert exists). Nested Mach-Os are
|
||||
# signed first so we don't need the deprecated `--deep`.
|
||||
SIGN_IDENTITY="${MACOS_SIGN_IDENTITY:--}"
|
||||
echo "==> codesigning (identity: $SIGN_IDENTITY)"
|
||||
if [ -f "$APP/Contents/MacOS/op" ]; then
|
||||
codesign --force --sign "$SIGN_IDENTITY" "$APP/Contents/MacOS/op"
|
||||
fi
|
||||
codesign --force --sign "$SIGN_IDENTITY" "$APP"
|
||||
|
||||
echo "==> registering with LaunchServices"
|
||||
LSREG=/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister
|
||||
"$LSREG" -u "$APP" 2>/dev/null || true
|
||||
|
|
|
|||
107
scripts/package-appimage.sh
Normal file
107
scripts/package-appimage.sh
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
#!/usr/bin/env bash
|
||||
# Assemble an AppImage for the OpenPencil Rust desktop shell + `op` CLI.
|
||||
#
|
||||
# AppDir layout (usr/ tree shared with the .deb via package-linux-common.sh):
|
||||
# AppRun -> usr/bin/openpencil-desktop (symlink entrypoint)
|
||||
# openpencil.desktop, openpencil.png, .DirIcon (AppImage root metadata)
|
||||
# usr/bin/{openpencil-desktop,op}
|
||||
# usr/share/{applications,mime/packages,icons,pixmaps}/...
|
||||
#
|
||||
# The actual packing tool (appimagetool) is downloaded pinned-by-URL by the
|
||||
# workflow and passed in via --tool; this script only assembles the AppDir
|
||||
# and invokes it. Without --tool (or with --layout-only) it stops after
|
||||
# assembly and prints the tree — that is the only mode runnable on macOS,
|
||||
# since appimagetool is a Linux ELF.
|
||||
#
|
||||
# NOTE: plain appimagetool does NOT bundle shared libraries (unlike
|
||||
# linuxdeploy). The produced AppImage links against the build host's glibc /
|
||||
# mesa / fontconfig, i.e. it runs on distros at least as new as the runner
|
||||
# image (ubuntu-24.04). Acceptable for now; switch to linuxdeploy if older
|
||||
# distro coverage becomes a requirement.
|
||||
#
|
||||
# Usage:
|
||||
# package-appimage.sh --desktop-bin PATH --cli-bin PATH --icon PATH \
|
||||
# --version X.Y.Z --arch x86_64|aarch64 \
|
||||
# --out-dir DIR [--tool PATH] [--layout-only]
|
||||
#
|
||||
# Output: <out-dir>/OpenPencil-<version>-<x64|arm64>-linux.AppImage
|
||||
# (matches electron-builder's `${productName}-${version}-${arch}-linux.${ext}`)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=package-linux-common.sh
|
||||
source "$SCRIPT_DIR/package-linux-common.sh"
|
||||
|
||||
DESKTOP_BIN="" CLI_BIN="" ICON_PNG="" VERSION="" AI_ARCH="" OUT_DIR="" TOOL=""
|
||||
LAYOUT_ONLY=0
|
||||
|
||||
usage() {
|
||||
sed -n '2,29p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
|
||||
exit "${1:-1}"
|
||||
}
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--desktop-bin) DESKTOP_BIN="$2"; shift 2 ;;
|
||||
--cli-bin) CLI_BIN="$2"; shift 2 ;;
|
||||
--icon) ICON_PNG="$2"; shift 2 ;;
|
||||
--version) VERSION="$2"; shift 2 ;;
|
||||
--arch) AI_ARCH="$2"; shift 2 ;;
|
||||
--out-dir) OUT_DIR="$2"; shift 2 ;;
|
||||
--tool) TOOL="$2"; shift 2 ;;
|
||||
--layout-only) LAYOUT_ONLY=1; shift ;;
|
||||
-h|--help) usage 0 ;;
|
||||
*) echo "error: unknown argument '$1'" >&2; usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
for req in DESKTOP_BIN CLI_BIN ICON_PNG VERSION AI_ARCH OUT_DIR; do
|
||||
if [ -z "${!req}" ]; then
|
||||
echo "error: --$(echo "$req" | tr '[:upper:]_' '[:lower:]-') is required" >&2
|
||||
usage
|
||||
fi
|
||||
done
|
||||
for f in "$DESKTOP_BIN" "$CLI_BIN" "$ICON_PNG"; do
|
||||
[ -f "$f" ] || { echo "error: input file not found: $f" >&2; exit 1; }
|
||||
done
|
||||
case "$AI_ARCH" in
|
||||
x86_64 | aarch64) ;;
|
||||
*) echo "error: --arch must be x86_64 or aarch64 (got '$AI_ARCH')" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
ARTIFACT_ARCH="$(op_artifact_arch "$AI_ARCH")"
|
||||
STAGE="$(mktemp -d)"
|
||||
trap 'rm -rf "$STAGE"' EXIT
|
||||
APPDIR="$STAGE/OpenPencil.AppDir"
|
||||
mkdir -p "$APPDIR"
|
||||
|
||||
echo "==> staging AppDir"
|
||||
op_install_usr_tree "$APPDIR" "$DESKTOP_BIN" "$CLI_BIN" "$ICON_PNG"
|
||||
|
||||
# AppImage root metadata: desktop entry + icon must sit at AppDir root;
|
||||
# .DirIcon is what most file managers display for the mounted image.
|
||||
cp "$APPDIR/usr/share/applications/openpencil.desktop" "$APPDIR/openpencil.desktop"
|
||||
cp "$ICON_PNG" "$APPDIR/openpencil.png"
|
||||
cp "$ICON_PNG" "$APPDIR/.DirIcon"
|
||||
ln -sf usr/bin/openpencil-desktop "$APPDIR/AppRun"
|
||||
|
||||
if [ "$LAYOUT_ONLY" = 1 ] || [ -z "$TOOL" ]; then
|
||||
echo "==> layout-only mode: assembled AppDir"
|
||||
(cd "$APPDIR" && find . \( -type f -o -type l \) | sort)
|
||||
if [ -z "$TOOL" ] && [ "$LAYOUT_ONLY" = 0 ]; then
|
||||
echo "note: no --tool given; skipped AppImage packing" >&2
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
[ -x "$TOOL" ] || { echo "error: appimagetool not executable: $TOOL" >&2; exit 1; }
|
||||
|
||||
mkdir -p "$OUT_DIR"
|
||||
OUT_PATH="$OUT_DIR/OpenPencil-$VERSION-$ARTIFACT_ARCH-linux.AppImage"
|
||||
echo "==> packing AppImage ($AI_ARCH)"
|
||||
# --appimage-extract-and-run: appimagetool itself ships as an AppImage and
|
||||
# CI runners have no FUSE; this flag self-extracts instead of mounting.
|
||||
# ARCH is required when the tool cannot infer the target architecture.
|
||||
ARCH="$AI_ARCH" "$TOOL" --appimage-extract-and-run "$APPDIR" "$OUT_PATH"
|
||||
echo "AppImage ready: $OUT_PATH"
|
||||
153
scripts/package-deb.sh
Normal file
153
scripts/package-deb.sh
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
#!/usr/bin/env bash
|
||||
# Build a Debian package for the OpenPencil Rust desktop shell + `op` CLI.
|
||||
#
|
||||
# Payload:
|
||||
# usr/bin/openpencil-desktop editor binary
|
||||
# usr/bin/op CLI binary
|
||||
# usr/share/applications/openpencil.desktop
|
||||
# Desktop entry with MimeType=application/x-openpencil so .op/.pen
|
||||
# double-clicks route to the editor after update-desktop-database.
|
||||
# usr/share/mime/packages/openpencil.xml
|
||||
# shared-mime-info definition registering the .op/.pen globs.
|
||||
# usr/share/icons/hicolor/1024x1024/apps/openpencil.png (+ pixmaps fallback)
|
||||
# DEBIAN/control, postinst, postrm
|
||||
# postinst refreshes the mime / desktop / icon caches (guarded — the
|
||||
# tools are Recommends-level, absent on minimal containers).
|
||||
#
|
||||
# Usage:
|
||||
# package-deb.sh --desktop-bin PATH --cli-bin PATH --icon PATH \
|
||||
# --version X.Y.Z --arch amd64|arm64 --out-dir DIR \
|
||||
# [--layout-only]
|
||||
#
|
||||
# --layout-only stage and print the tree without invoking dpkg-deb, so the
|
||||
# layout logic can be smoke-tested on machines without dpkg
|
||||
# (e.g. macOS dev boxes). CI always does the full build.
|
||||
#
|
||||
# Output: <out-dir>/OpenPencil-<version>-<x64|arm64>-linux.deb
|
||||
# (filename matches electron-builder's artifactName pattern
|
||||
# `${productName}-${version}-${arch}-linux.${ext}`; the Debian *control*
|
||||
# Architecture field still uses proper dpkg arch names amd64/arm64).
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=package-linux-common.sh
|
||||
source "$SCRIPT_DIR/package-linux-common.sh"
|
||||
|
||||
DESKTOP_BIN="" CLI_BIN="" ICON_PNG="" VERSION="" DEB_ARCH="" OUT_DIR=""
|
||||
LAYOUT_ONLY=0
|
||||
|
||||
usage() {
|
||||
sed -n '2,28p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
|
||||
exit "${1:-1}"
|
||||
}
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--desktop-bin) DESKTOP_BIN="$2"; shift 2 ;;
|
||||
--cli-bin) CLI_BIN="$2"; shift 2 ;;
|
||||
--icon) ICON_PNG="$2"; shift 2 ;;
|
||||
--version) VERSION="$2"; shift 2 ;;
|
||||
--arch) DEB_ARCH="$2"; shift 2 ;;
|
||||
--out-dir) OUT_DIR="$2"; shift 2 ;;
|
||||
--layout-only) LAYOUT_ONLY=1; shift ;;
|
||||
-h|--help) usage 0 ;;
|
||||
*) echo "error: unknown argument '$1'" >&2; usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
for req in DESKTOP_BIN CLI_BIN ICON_PNG VERSION DEB_ARCH OUT_DIR; do
|
||||
if [ -z "${!req}" ]; then
|
||||
echo "error: --$(echo "$req" | tr '[:upper:]_' '[:lower:]-') is required" >&2
|
||||
usage
|
||||
fi
|
||||
done
|
||||
for f in "$DESKTOP_BIN" "$CLI_BIN" "$ICON_PNG"; do
|
||||
[ -f "$f" ] || { echo "error: input file not found: $f" >&2; exit 1; }
|
||||
done
|
||||
case "$DEB_ARCH" in
|
||||
amd64 | arm64) ;;
|
||||
*) echo "error: --arch must be amd64 or arm64 (got '$DEB_ARCH')" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
ARTIFACT_ARCH="$(op_artifact_arch "$DEB_ARCH")"
|
||||
STAGE="$(mktemp -d)"
|
||||
trap 'rm -rf "$STAGE"' EXIT
|
||||
|
||||
echo "==> staging payload tree"
|
||||
op_install_usr_tree "$STAGE" "$DESKTOP_BIN" "$CLI_BIN" "$ICON_PNG"
|
||||
|
||||
echo "==> writing DEBIAN/ metadata"
|
||||
mkdir -p "$STAGE/DEBIAN"
|
||||
|
||||
# Installed-Size is in KiB of the payload (excludes DEBIAN/).
|
||||
INSTALLED_SIZE="$(du -sk "$STAGE/usr" | cut -f1)"
|
||||
|
||||
cat > "$STAGE/DEBIAN/control" <<EOF
|
||||
Package: openpencil
|
||||
Version: $VERSION
|
||||
Section: graphics
|
||||
Priority: optional
|
||||
Architecture: $DEB_ARCH
|
||||
Maintainer: ZSeven-W <fini.yang@gmail.com>
|
||||
Installed-Size: $INSTALLED_SIZE
|
||||
Depends: libc6, libfontconfig1, libfreetype6, libegl1, libgles2, libgbm1, libxkbcommon0, libxkbcommon-x11-0, libwayland-client0
|
||||
Recommends: shared-mime-info, desktop-file-utils
|
||||
Homepage: https://github.com/ZSeven-W/openpencil
|
||||
Description: OpenPencil — open-source AI-native vector design tool
|
||||
Native desktop editor (openpencil-desktop) built on the Rust shell, plus
|
||||
the op command-line client that drives the editor over MCP.
|
||||
Registers the application/x-openpencil MIME type for .op and .pen files.
|
||||
EOF
|
||||
|
||||
cat > "$STAGE/DEBIAN/postinst" <<'EOF'
|
||||
#!/bin/sh
|
||||
set -e
|
||||
# Refresh caches so the .op/.pen association is live immediately. Each tool
|
||||
# is optional (Recommends), so guard and never fail the install on them.
|
||||
if command -v update-mime-database >/dev/null 2>&1; then
|
||||
update-mime-database /usr/share/mime || true
|
||||
fi
|
||||
if command -v update-desktop-database >/dev/null 2>&1; then
|
||||
update-desktop-database /usr/share/applications || true
|
||||
fi
|
||||
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
|
||||
gtk-update-icon-cache -q /usr/share/icons/hicolor || true
|
||||
fi
|
||||
exit 0
|
||||
EOF
|
||||
|
||||
cat > "$STAGE/DEBIAN/postrm" <<'EOF'
|
||||
#!/bin/sh
|
||||
set -e
|
||||
if command -v update-mime-database >/dev/null 2>&1; then
|
||||
update-mime-database /usr/share/mime || true
|
||||
fi
|
||||
if command -v update-desktop-database >/dev/null 2>&1; then
|
||||
update-desktop-database /usr/share/applications || true
|
||||
fi
|
||||
exit 0
|
||||
EOF
|
||||
|
||||
chmod 755 "$STAGE/DEBIAN/postinst" "$STAGE/DEBIAN/postrm"
|
||||
|
||||
if [ "$LAYOUT_ONLY" = 1 ]; then
|
||||
echo "==> layout-only mode: staged tree"
|
||||
(cd "$STAGE" && find . \( -type f -o -type l \) | sort)
|
||||
echo "==> DEBIAN/control"
|
||||
cat "$STAGE/DEBIAN/control"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
command -v dpkg-deb >/dev/null 2>&1 || {
|
||||
echo "error: dpkg-deb not found — run on a Debian/Ubuntu host or pass --layout-only" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
mkdir -p "$OUT_DIR"
|
||||
DEB_PATH="$OUT_DIR/OpenPencil-$VERSION-$ARTIFACT_ARCH-linux.deb"
|
||||
echo "==> dpkg-deb build"
|
||||
# --root-owner-group: payload owned by root:root without needing fakeroot
|
||||
# (dpkg-deb >= 1.19; Ubuntu 24.04 runners ship 1.22).
|
||||
dpkg-deb --build --root-owner-group "$STAGE" "$DEB_PATH"
|
||||
echo "Package ready: $DEB_PATH"
|
||||
97
scripts/package-linux-common.sh
Normal file
97
scripts/package-linux-common.sh
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
# Shared helpers for Linux packaging (sourced by package-deb.sh and
|
||||
# package-appimage.sh — not executable on its own, so no shebang / `set`).
|
||||
#
|
||||
# Branding parity with the TS reference (apps/desktop/electron-builder.yml):
|
||||
# productName OpenPencil
|
||||
# mime type application/x-openpencil (covers .op and .pen)
|
||||
# categories Graphics;
|
||||
# Divergences (intentional):
|
||||
# * Exec is `openpencil-desktop %U`, not electron's `AppRun --no-sandbox %U`
|
||||
# — the Rust shell has no Chromium sandbox flag.
|
||||
# * Icon ships at its source size (1024px) into hicolor + pixmaps; older
|
||||
# hicolor index files may not list 1024x1024, so usr/share/pixmaps is the
|
||||
# universal fallback. No resize step: imagemagick is not guaranteed on
|
||||
# runners and the spec tolerates the fallback path.
|
||||
|
||||
OP_DESKTOP_BIN_NAME="openpencil-desktop"
|
||||
OP_CLI_BIN_NAME="op"
|
||||
|
||||
# op_write_desktop_file <dest-path>
|
||||
op_write_desktop_file() {
|
||||
cat > "$1" <<'EOF'
|
||||
[Desktop Entry]
|
||||
Name=OpenPencil
|
||||
Comment=Open-source AI-native vector design tool
|
||||
Exec=openpencil-desktop %U
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Icon=openpencil
|
||||
StartupWMClass=openpencil
|
||||
Categories=Graphics;
|
||||
MimeType=application/x-openpencil;
|
||||
EOF
|
||||
}
|
||||
|
||||
# op_write_mime_xml <dest-path>
|
||||
# shared-mime-info definition registering the .op / .pen extensions.
|
||||
op_write_mime_xml() {
|
||||
cat > "$1" <<'EOF'
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||
<mime-type type="application/x-openpencil">
|
||||
<comment>OpenPencil Document</comment>
|
||||
<glob pattern="*.op"/>
|
||||
<glob pattern="*.pen"/>
|
||||
<icon name="openpencil"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
EOF
|
||||
}
|
||||
|
||||
# op_install_usr_tree <root> <desktop-bin> <cli-bin> <icon-png>
|
||||
# Lays out the usr/ tree shared by the .deb payload and the AppImage AppDir:
|
||||
# usr/bin/openpencil-desktop, usr/bin/op
|
||||
# usr/share/applications/openpencil.desktop
|
||||
# usr/share/mime/packages/openpencil.xml
|
||||
# usr/share/icons/hicolor/1024x1024/apps/openpencil.png
|
||||
# usr/share/pixmaps/openpencil.png
|
||||
# Uses mkdir/cp/chmod (not `install -D`) so the layout can be dry-run on
|
||||
# macOS, whose BSD install(1) lacks -D.
|
||||
op_install_usr_tree() {
|
||||
local root="$1" desktop_bin="$2" cli_bin="$3" icon_png="$4"
|
||||
|
||||
mkdir -p \
|
||||
"$root/usr/bin" \
|
||||
"$root/usr/share/applications" \
|
||||
"$root/usr/share/mime/packages" \
|
||||
"$root/usr/share/icons/hicolor/1024x1024/apps" \
|
||||
"$root/usr/share/pixmaps"
|
||||
|
||||
cp "$desktop_bin" "$root/usr/bin/$OP_DESKTOP_BIN_NAME"
|
||||
cp "$cli_bin" "$root/usr/bin/$OP_CLI_BIN_NAME"
|
||||
chmod 755 "$root/usr/bin/$OP_DESKTOP_BIN_NAME" "$root/usr/bin/$OP_CLI_BIN_NAME"
|
||||
|
||||
op_write_desktop_file "$root/usr/share/applications/openpencil.desktop"
|
||||
op_write_mime_xml "$root/usr/share/mime/packages/openpencil.xml"
|
||||
|
||||
cp "$icon_png" "$root/usr/share/icons/hicolor/1024x1024/apps/openpencil.png"
|
||||
cp "$icon_png" "$root/usr/share/pixmaps/openpencil.png"
|
||||
chmod 644 \
|
||||
"$root/usr/share/icons/hicolor/1024x1024/apps/openpencil.png" \
|
||||
"$root/usr/share/pixmaps/openpencil.png"
|
||||
}
|
||||
|
||||
# op_artifact_arch <amd64|arm64|x86_64|aarch64>
|
||||
# Maps a deb/appimage arch onto the electron-builder artifact arch token
|
||||
# (x64 / arm64) so release filenames match the TS pipeline's
|
||||
# `${productName}-${version}-${arch}-linux.${ext}` convention.
|
||||
op_artifact_arch() {
|
||||
case "$1" in
|
||||
amd64 | x86_64) echo "x64" ;;
|
||||
arm64 | aarch64) echo "arm64" ;;
|
||||
*)
|
||||
echo "error: unmapped architecture '$1'" >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
161
scripts/package-windows.nsi
Normal file
161
scripts/package-windows.nsi
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
; OpenPencil Windows installer (NSIS).
|
||||
;
|
||||
; Why NSIS (not Inno Setup): makensis is preinstalled on the GitHub
|
||||
; windows-latest runner image, and the TS reference pipeline
|
||||
; (apps/desktop/electron-builder.yml) also targets `nsis`, so installer UX
|
||||
; stays consistent between the Electron and Rust shells.
|
||||
;
|
||||
; Installs:
|
||||
; openpencil-desktop.exe editor binary
|
||||
; op.exe CLI binary (also shipped standalone as
|
||||
; op-cli-<target>.zip in the release; $INSTDIR is
|
||||
; NOT added to PATH — doing so reliably needs the
|
||||
; non-stock EnVar plugin, deferred)
|
||||
; openpencil.ico icon used by shortcuts + the .op/.pen ProgID
|
||||
; Uninstall.exe uninstaller (registered in Add/Remove Programs)
|
||||
;
|
||||
; File association: ProgID "OpenPencil.Document" under HKCR with DefaultIcon
|
||||
; and an open command, claimed by .op and .pen. Writing the machine hive
|
||||
; requires elevation, hence RequestExecutionLevel admin + $PROGRAMFILES64 —
|
||||
; an intentional divergence from electron-builder's per-user install
|
||||
; (perMachine: false): a per-user HKCU\Software\Classes claim silently loses
|
||||
; to any pre-existing machine-level registration. `.fig` is deliberately not
|
||||
; claimed on Windows (macOS-only association, parity with electron-builder's
|
||||
; fileAssociations list which only covers .op).
|
||||
;
|
||||
; Compile (relative paths resolve against this script's directory, so the
|
||||
; workflow passes absolute /D defines):
|
||||
; makensis "/DVERSION=0.8.0" "/DARCH=x64" ^
|
||||
; "/DBIN_DIR=D:\w\target\x86_64-pc-windows-msvc\release" ^
|
||||
; "/DICON_FILE=D:\w\apps\desktop\build\icon.ico" ^
|
||||
; "/DOUT_FILE=D:\w\OpenPencil-0.8.0-x64-win-setup.exe" ^
|
||||
; scripts\package-windows.nsi
|
||||
;
|
||||
; NOT compiled locally (no makensis on the macOS dev machine) — first real
|
||||
; verification is the tag-push CI run. For ARCH=arm64 the installer stub is
|
||||
; x86 and runs under emulation on Windows-on-ARM; the payload binaries are
|
||||
; native aarch64.
|
||||
;
|
||||
; VIProductVersion is intentionally omitted: it requires a strict 4-part
|
||||
; numeric version and would break compiles for pre-release tags like
|
||||
; 0.9.0-beta.1.
|
||||
|
||||
Unicode true
|
||||
|
||||
!include "MUI2.nsh"
|
||||
|
||||
!ifndef VERSION
|
||||
!define VERSION "0.0.0"
|
||||
!endif
|
||||
!ifndef ARCH
|
||||
!define ARCH "x64"
|
||||
!endif
|
||||
!ifndef BIN_DIR
|
||||
!define BIN_DIR "..\target\release"
|
||||
!endif
|
||||
!ifndef ICON_FILE
|
||||
!define ICON_FILE "..\apps\desktop\build\icon.ico"
|
||||
!endif
|
||||
!ifndef OUT_FILE
|
||||
!define OUT_FILE "OpenPencil-${VERSION}-${ARCH}-win-setup.exe"
|
||||
!endif
|
||||
|
||||
!define PRODUCT_NAME "OpenPencil"
|
||||
!define EXE_NAME "openpencil-desktop.exe"
|
||||
!define CLI_NAME "op.exe"
|
||||
!define PROG_ID "OpenPencil.Document"
|
||||
!define REG_APP_KEY "Software\${PRODUCT_NAME}"
|
||||
!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
|
||||
|
||||
Name "${PRODUCT_NAME}"
|
||||
OutFile "${OUT_FILE}"
|
||||
InstallDir "$PROGRAMFILES64\${PRODUCT_NAME}"
|
||||
InstallDirRegKey HKLM "${REG_APP_KEY}" "InstallDir"
|
||||
RequestExecutionLevel admin
|
||||
SetCompressor /SOLID lzma
|
||||
|
||||
!define MUI_ICON "${ICON_FILE}"
|
||||
!define MUI_UNICON "${ICON_FILE}"
|
||||
!define MUI_ABORTWARNING
|
||||
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
; allowToChangeInstallationDirectory parity with electron-builder.yml
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
|
||||
Section "OpenPencil" SecMain
|
||||
SectionIn RO
|
||||
SetOutPath "$INSTDIR"
|
||||
|
||||
File "${BIN_DIR}\${EXE_NAME}"
|
||||
File "${BIN_DIR}\${CLI_NAME}"
|
||||
File "/oname=openpencil.ico" "${ICON_FILE}"
|
||||
|
||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||
WriteRegStr HKLM "${REG_APP_KEY}" "InstallDir" "$INSTDIR"
|
||||
|
||||
; Shortcuts — createDesktopShortcut / createStartMenuShortcut parity.
|
||||
CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}"
|
||||
CreateShortcut "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk" \
|
||||
"$INSTDIR\${EXE_NAME}" "" "$INSTDIR\openpencil.ico"
|
||||
CreateShortcut "$DESKTOP\${PRODUCT_NAME}.lnk" \
|
||||
"$INSTDIR\${EXE_NAME}" "" "$INSTDIR\openpencil.ico"
|
||||
|
||||
; Add/Remove Programs entry.
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "${PRODUCT_NAME}"
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "DisplayVersion" "${VERSION}"
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\openpencil.ico"
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "Publisher" "OpenPencil contributors"
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "URLInfoAbout" "https://github.com/ZSeven-W/openpencil"
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "InstallLocation" "$INSTDIR"
|
||||
WriteRegStr HKLM "${UNINST_KEY}" "UninstallString" '"$INSTDIR\Uninstall.exe"'
|
||||
WriteRegDWORD HKLM "${UNINST_KEY}" "NoModify" 1
|
||||
WriteRegDWORD HKLM "${UNINST_KEY}" "NoRepair" 1
|
||||
|
||||
; File association: one ProgID claimed by both OpenPencil extensions.
|
||||
WriteRegStr HKCR "${PROG_ID}" "" "OpenPencil Document"
|
||||
WriteRegStr HKCR "${PROG_ID}\DefaultIcon" "" "$INSTDIR\openpencil.ico"
|
||||
WriteRegStr HKCR "${PROG_ID}\shell" "" "open"
|
||||
WriteRegStr HKCR "${PROG_ID}\shell\open\command" "" '"$INSTDIR\${EXE_NAME}" "%1"'
|
||||
|
||||
WriteRegStr HKCR ".op" "" "${PROG_ID}"
|
||||
WriteRegStr HKCR ".op" "Content Type" "application/x-openpencil"
|
||||
WriteRegStr HKCR ".pen" "" "${PROG_ID}"
|
||||
WriteRegStr HKCR ".pen" "Content Type" "application/x-openpencil"
|
||||
|
||||
; SHCNE_ASSOCCHANGED — tell the shell to refresh icon/association caches.
|
||||
System::Call 'shell32::SHChangeNotify(i 0x08000000, i 0, i 0, i 0)'
|
||||
SectionEnd
|
||||
|
||||
Section "Uninstall"
|
||||
Delete "$INSTDIR\${EXE_NAME}"
|
||||
Delete "$INSTDIR\${CLI_NAME}"
|
||||
Delete "$INSTDIR\openpencil.ico"
|
||||
Delete "$INSTDIR\Uninstall.exe"
|
||||
RMDir "$INSTDIR"
|
||||
|
||||
Delete "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME}.lnk"
|
||||
RMDir "$SMPROGRAMS\${PRODUCT_NAME}"
|
||||
Delete "$DESKTOP\${PRODUCT_NAME}.lnk"
|
||||
|
||||
; Only unclaim the extensions if they still point at our ProgID — never
|
||||
; clobber an association another app took over after us.
|
||||
ReadRegStr $0 HKCR ".op" ""
|
||||
StrCmp $0 "${PROG_ID}" 0 +2
|
||||
DeleteRegKey HKCR ".op"
|
||||
ReadRegStr $0 HKCR ".pen" ""
|
||||
StrCmp $0 "${PROG_ID}" 0 +2
|
||||
DeleteRegKey HKCR ".pen"
|
||||
DeleteRegKey HKCR "${PROG_ID}"
|
||||
|
||||
DeleteRegKey HKLM "${UNINST_KEY}"
|
||||
DeleteRegKey HKLM "${REG_APP_KEY}"
|
||||
|
||||
System::Call 'shell32::SHChangeNotify(i 0x08000000, i 0, i 0, i 0)'
|
||||
SectionEnd
|
||||
23
tools/export-shadcn-kit.ts
Normal file
23
tools/export-shadcn-kit.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// Emit the TS shadcn UIKit (apps/web/src/uikit/kits/) as JSON for
|
||||
// embedding into the Rust editor core (GAP #23). The kit modules are
|
||||
// PenNode JSON literals with type-only `@/...` imports, so Bun can
|
||||
// execute them directly without the web app's path aliases.
|
||||
//
|
||||
// Run from the repo root after editing the TS kit:
|
||||
// bun tools/export-shadcn-kit.ts
|
||||
//
|
||||
// Output: crates/op-editor-core/assets/shadcn-kit.json — embedded via
|
||||
// `include_str!` in crates/op-editor-core/src/uikit_shadcn.rs.
|
||||
import { SHADCN_KIT_DOCUMENT } from '../apps/web/src/uikit/kits/shadcn-kit';
|
||||
import { SHADCN_KIT_META } from '../apps/web/src/uikit/kits/shadcn-kit-meta';
|
||||
|
||||
const out = {
|
||||
document: SHADCN_KIT_DOCUMENT,
|
||||
meta: SHADCN_KIT_META,
|
||||
};
|
||||
|
||||
const dest = new URL('../crates/op-editor-core/assets/shadcn-kit.json', import.meta.url);
|
||||
await Bun.write(dest, `${JSON.stringify(out, null, 2)}\n`);
|
||||
console.log(
|
||||
`wrote ${dest.pathname} (${SHADCN_KIT_DOCUMENT.children.length} kit document children)`,
|
||||
);
|
||||
Loading…
Reference in a new issue