openpencil/scripts/install-op.sh
Kayshen-X 0104c8bd61 feat(editor): a11y + single-instance + layer-panel Rust↔TS parity
Close the non-web-productionization gaps from the 2026-06-18 recheck:

- a11y (#67/#57): assemble each widget's access_node() into an
  accesskit::TreeUpdate (op-editor-ui/accessibility.rs); publish on
  desktop via accesskit_macos/_windows/_unix SubclassingAdapter off the
  raw window handle (op-host-desktop/a11y.rs, NOT accesskit_winit — the
  casement winit fork), and on web via a hidden ARIA DOM mirror
  (op-host-web/a11y_dom.rs); native/web region enumeration + action
  routing in op-host-{native,web}.
- single-instance (#51): fixed-loopback-port guard + second-launch file
  forwarding to the running window (op-host-desktop/single_instance.rs).
- layer panel: drop-into-container inserts at index 0 (#12a); container
  predicate widened to frame/group/rectangle/ref (#12b); Escape closes
  the context menu on both hosts (#14).
- distribution: Homebrew cask fix + op formula + install-op.sh, README
  CLI install, CI codesign/notarize/signtool scaffolding gated on secrets.

Codex-reviewed (1 BLOCKER + 3 CONCERNs resolved). main.rs, app_handler.rs,
canvaskit.rs and Cargo.lock are staged whole and carry some unrelated
in-progress WIP they're interleaved with.
2026-06-18 22:18:07 +08:00

106 lines
3.4 KiB
Bash
Executable file

#!/usr/bin/env bash
# Install the OpenPencil `op` CLI from a GitHub Release.
#
# Detects the host OS + architecture, downloads the matching standalone
# `op-cli-<label>` tarball published by .github/workflows/rust-release.yml,
# and installs the `op` binary into a bin directory on PATH.
#
# Usage:
# ./install-op.sh # install the latest release
# OP_VERSION=0.8.0 ./install-op.sh # pin a specific version
# INSTALL_DIR=$HOME/.local/bin ./install-op.sh # custom install dir
#
# Environment overrides:
# OP_VERSION release version WITHOUT the leading "v" (default: latest)
# INSTALL_DIR install target directory (default: /usr/local/bin)
set -euo pipefail
OWNER="ZSeven-W"
REPO="openpencil"
INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}"
# Resolve the asset "label" token rust-release.yml uses in
# op-cli-<label>.tar.gz: "<os>-<arch>" where os is macos/linux and arch is
# x86_64/aarch64 (the cargo target arch, NOT the cask's x64/arm64 token).
detect_label() {
local os arch
case "$(uname -s)" in
Darwin) os="macos" ;;
Linux) os="linux" ;;
*)
echo "error: unsupported OS '$(uname -s)' (only macOS and Linux are packaged)" >&2
exit 1
;;
esac
case "$(uname -m)" in
x86_64 | amd64) arch="x86_64" ;;
arm64 | aarch64) arch="aarch64" ;;
*)
echo "error: unsupported architecture '$(uname -m)'" >&2
exit 1
;;
esac
printf '%s-%s' "$os" "$arch"
}
# Resolve the release version. Honors OP_VERSION; otherwise queries the
# GitHub API for the latest release tag and strips the leading "v".
resolve_version() {
if [ -n "${OP_VERSION:-}" ]; then
printf '%s' "$OP_VERSION"
return
fi
local api tag
api="https://api.github.com/repos/${OWNER}/${REPO}/releases/latest"
# Pull "tag_name": "vX.Y.Z" out of the JSON without a JSON parser.
tag="$(curl -fsSL "$api" | grep -o '"tag_name"[[:space:]]*:[[:space:]]*"[^"]*"' | head -n1 | sed 's/.*"\(v\{0,1\}[^"]*\)"$/\1/')"
if [ -z "$tag" ]; then
echo "error: could not resolve the latest release tag from GitHub" >&2
echo " set OP_VERSION explicitly, e.g. OP_VERSION=0.8.0 $0" >&2
exit 1
fi
printf '%s' "${tag#v}"
}
main() {
local label version asset url tmp
label="$(detect_label)"
version="$(resolve_version)"
asset="op-cli-${label}.tar.gz"
url="https://github.com/${OWNER}/${REPO}/releases/download/v${version}/${asset}"
echo "==> Installing op ${version} (${label})"
echo " from ${url}"
tmp="$(mktemp -d)"
# Always clean up the scratch dir, even on early exit.
trap 'rm -rf "$tmp"' EXIT
# Download and unpack. The tarball contains a single bare `op` binary.
curl -fsSL --retry 3 -o "$tmp/${asset}" "$url"
tar -xzf "$tmp/${asset}" -C "$tmp"
if [ ! -f "$tmp/op" ]; then
echo "error: ${asset} did not contain an 'op' binary" >&2
exit 1
fi
chmod +x "$tmp/op"
# Install — use sudo automatically only when the target dir is not
# writable by the current user (e.g. the default /usr/local/bin).
echo "==> Installing to ${INSTALL_DIR}/op"
# Create the dir first (best effort) so the writability test below is
# meaningful for a not-yet-existing custom INSTALL_DIR.
mkdir -p "$INSTALL_DIR" 2>/dev/null || true
if [ -w "$INSTALL_DIR" ]; then
install -m 0755 "$tmp/op" "$INSTALL_DIR/op"
else
echo " (need elevated permissions for ${INSTALL_DIR})"
sudo install -m 0755 "$tmp/op" "$INSTALL_DIR/op"
fi
echo "==> Done. Run 'op --version' to verify."
}
main "$@"