chore(release): include cli install scripts
This commit is contained in:
parent
a98458e146
commit
d49087027e
49
.github/workflows/rust-release.yml
vendored
49
.github/workflows/rust-release.yml
vendored
|
|
@ -444,6 +444,7 @@ jobs:
|
|||
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
|
||||
|
|
@ -529,6 +530,54 @@ jobs:
|
|||
echo "::error::expected 3 SDK tarballs, found ${#sdk[@]}"
|
||||
exit 1
|
||||
fi
|
||||
- name: Generate CLI install scripts
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
tag="$GITHUB_REF_NAME"
|
||||
version="${tag#v}"
|
||||
|
||||
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: |
|
||||
|
|
|
|||
|
|
@ -18,6 +18,21 @@
|
|||
- Linux: `OpenPencil-0.8.0-<arch>-linux.AppImage`, `OpenPencil-0.8.0-<arch>-linux.deb`, plus raw desktop and `op` CLI archives.
|
||||
- Web SDK: published npm packages and attached npm tarballs for the base SDK, React adapter, and Vue adapter.
|
||||
- Package managers: Homebrew cask/formula and Scoop desktop/CLI manifests.
|
||||
- CLI installers: `install-op.sh` and `install-op.ps1`.
|
||||
|
||||
## CLI Install
|
||||
|
||||
macOS / Linux:
|
||||
|
||||
```sh
|
||||
curl -fsSL https://raw.githubusercontent.com/ZSeven-W/openpencil/main/scripts/install-op.sh | bash
|
||||
```
|
||||
|
||||
Windows PowerShell:
|
||||
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/ZSeven-W/openpencil/main/scripts/install-op.ps1 | iex
|
||||
```
|
||||
|
||||
## Docker
|
||||
|
||||
|
|
|
|||
84
scripts/install-op.ps1
Normal file
84
scripts/install-op.ps1
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
param(
|
||||
[string]$OpVersion = $env:OP_VERSION,
|
||||
[string]$InstallDir = $(if ($env:INSTALL_DIR) { $env:INSTALL_DIR } else { Join-Path $env:USERPROFILE ".openpencil\bin" })
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$Owner = "ZSeven-W"
|
||||
$Repo = "openpencil"
|
||||
$DefaultOpVersion = ""
|
||||
$DefaultShaWindowsAarch64 = ""
|
||||
$DefaultShaWindowsX86_64 = ""
|
||||
|
||||
function Resolve-Version {
|
||||
if (-not [string]::IsNullOrWhiteSpace($OpVersion)) {
|
||||
return $OpVersion.TrimStart("v")
|
||||
}
|
||||
if (-not [string]::IsNullOrWhiteSpace($DefaultOpVersion)) {
|
||||
return $DefaultOpVersion.TrimStart("v")
|
||||
}
|
||||
|
||||
$Latest = Invoke-RestMethod -Uri "https://api.github.com/repos/$Owner/$Repo/releases/latest"
|
||||
if (-not $Latest.tag_name) {
|
||||
throw "install-op: could not resolve latest release tag; set OP_VERSION explicitly"
|
||||
}
|
||||
return $Latest.tag_name.TrimStart("v")
|
||||
}
|
||||
|
||||
switch ($env:PROCESSOR_ARCHITECTURE) {
|
||||
"AMD64" {
|
||||
$Label = "windows-x86_64"
|
||||
$ExpectedSha = $DefaultShaWindowsX86_64
|
||||
}
|
||||
"ARM64" {
|
||||
$Label = "windows-aarch64"
|
||||
$ExpectedSha = $DefaultShaWindowsAarch64
|
||||
}
|
||||
default {
|
||||
throw "install-op: unsupported Windows architecture $env:PROCESSOR_ARCHITECTURE"
|
||||
}
|
||||
}
|
||||
|
||||
$Version = Resolve-Version
|
||||
$Asset = "op-cli-$Label.zip"
|
||||
$Url = "https://github.com/$Owner/$Repo/releases/download/v$Version/$Asset"
|
||||
|
||||
Write-Host "==> Installing op $Version ($Label)"
|
||||
Write-Host " from $Url"
|
||||
|
||||
$Temp = Join-Path ([System.IO.Path]::GetTempPath()) ("openpencil-op-install-" + [System.Guid]::NewGuid().ToString("N"))
|
||||
New-Item -ItemType Directory -Path $Temp | Out-Null
|
||||
try {
|
||||
$Archive = Join-Path $Temp $Asset
|
||||
Invoke-WebRequest -Uri $Url -OutFile $Archive -UseBasicParsing
|
||||
|
||||
if (-not [string]::IsNullOrWhiteSpace($ExpectedSha)) {
|
||||
$ActualSha = (Get-FileHash -Algorithm SHA256 $Archive).Hash.ToLowerInvariant()
|
||||
if ($ActualSha -ne $ExpectedSha) {
|
||||
throw "install-op: checksum mismatch for $Asset. Expected $ExpectedSha, got $ActualSha"
|
||||
}
|
||||
}
|
||||
|
||||
Expand-Archive -Path $Archive -DestinationPath $Temp -Force
|
||||
$Source = Get-ChildItem -Path $Temp -Filter "op.exe" -Recurse | Select-Object -First 1
|
||||
if (-not $Source) {
|
||||
throw "install-op: op.exe was not found in $Asset"
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
|
||||
$Target = Join-Path $InstallDir "op.exe"
|
||||
Copy-Item -Path $Source.FullName -Destination $Target -Force
|
||||
|
||||
$UserPath = [Environment]::GetEnvironmentVariable("Path", "User")
|
||||
$PathEntries = @($UserPath -split ";" | Where-Object { $_ })
|
||||
if ($PathEntries -notcontains $InstallDir) {
|
||||
[Environment]::SetEnvironmentVariable("Path", (($PathEntries + $InstallDir) -join ";"), "User")
|
||||
Write-Host "Added $InstallDir to the user PATH. Restart the shell to use op globally."
|
||||
}
|
||||
|
||||
Write-Host "==> Done. Run 'op --version' to verify."
|
||||
& $Target --version
|
||||
} finally {
|
||||
Remove-Item -Path $Temp -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
|
@ -6,13 +6,13 @@
|
|||
# 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
|
||||
# ./install-op.sh # install the latest stable release
|
||||
# OP_VERSION=0.8.0 ./install-op.sh # pin a specific version
|
||||
# INSTALL_DIR=$HOME/.local/bin ./install-op.sh
|
||||
#
|
||||
# Environment overrides:
|
||||
# OP_VERSION release version WITHOUT the leading "v" (default: latest)
|
||||
# INSTALL_DIR install target directory (default: /usr/local/bin)
|
||||
# The release workflow stamps DEFAULT_OP_VERSION and DEFAULT_SHA_* in the copy
|
||||
# uploaded to GitHub Releases, so the release asset installs that exact tag and
|
||||
# verifies the CLI archive checksum.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
|
|
@ -20,22 +20,25 @@ 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).
|
||||
DEFAULT_OP_VERSION=""
|
||||
DEFAULT_SHA_MACOS_AARCH64=""
|
||||
DEFAULT_SHA_MACOS_X86_64=""
|
||||
DEFAULT_SHA_LINUX_AARCH64=""
|
||||
DEFAULT_SHA_LINUX_X86_64=""
|
||||
|
||||
detect_label() {
|
||||
local os arch
|
||||
case "$(uname -s)" in
|
||||
Darwin) os="macos" ;;
|
||||
Linux) os="linux" ;;
|
||||
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" ;;
|
||||
x86_64 | amd64) arch="x86_64" ;;
|
||||
arm64 | aarch64) arch="aarch64" ;;
|
||||
*)
|
||||
echo "error: unsupported architecture '$(uname -m)'" >&2
|
||||
exit 1
|
||||
|
|
@ -44,16 +47,18 @@ detect_label() {
|
|||
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
|
||||
if [ -n "$DEFAULT_OP_VERSION" ]; then
|
||||
printf '%s' "$DEFAULT_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
|
||||
|
|
@ -63,22 +68,52 @@ resolve_version() {
|
|||
printf '%s' "${tag#v}"
|
||||
}
|
||||
|
||||
expected_sha_for_label() {
|
||||
case "$1" in
|
||||
macos-aarch64) printf '%s' "$DEFAULT_SHA_MACOS_AARCH64" ;;
|
||||
macos-x86_64) printf '%s' "$DEFAULT_SHA_MACOS_X86_64" ;;
|
||||
linux-aarch64) printf '%s' "$DEFAULT_SHA_LINUX_AARCH64" ;;
|
||||
linux-x86_64) printf '%s' "$DEFAULT_SHA_LINUX_X86_64" ;;
|
||||
*) printf '' ;;
|
||||
esac
|
||||
}
|
||||
|
||||
sha256_file() {
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
sha256sum "$1" | awk '{print $1}'
|
||||
elif command -v shasum >/dev/null 2>&1; then
|
||||
shasum -a 256 "$1" | awk '{print $1}'
|
||||
else
|
||||
echo "error: sha256sum or shasum is required for checksum verification" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
local label version asset url tmp
|
||||
local label version asset url tmp expected_sha actual_sha
|
||||
label="$(detect_label)"
|
||||
version="$(resolve_version)"
|
||||
asset="op-cli-${label}.tar.gz"
|
||||
url="https://github.com/${OWNER}/${REPO}/releases/download/v${version}/${asset}"
|
||||
expected_sha="$(expected_sha_for_label "$label")"
|
||||
|
||||
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"
|
||||
if [ -n "$expected_sha" ]; then
|
||||
actual_sha="$(sha256_file "$tmp/${asset}")"
|
||||
if [ "$actual_sha" != "$expected_sha" ]; then
|
||||
echo "error: checksum mismatch for ${asset}" >&2
|
||||
echo " expected: $expected_sha" >&2
|
||||
echo " actual: $actual_sha" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
tar -xzf "$tmp/${asset}" -C "$tmp"
|
||||
if [ ! -f "$tmp/op" ]; then
|
||||
echo "error: ${asset} did not contain an 'op' binary" >&2
|
||||
|
|
@ -86,11 +121,7 @@ main() {
|
|||
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"
|
||||
|
|
|
|||
Loading…
Reference in a new issue