build: derive package versions from Cargo
This commit is contained in:
parent
983b2a3573
commit
d3fc4fed6d
13
.github/workflows/rust-release.yml
vendored
13
.github/workflows/rust-release.yml
vendored
|
|
@ -155,14 +155,15 @@ jobs:
|
|||
- 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.
|
||||
cargo_version="$(scripts/workspace-version.sh)"
|
||||
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"
|
||||
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 "OP_VERSION=$cargo_version" >> "$GITHUB_ENV"
|
||||
- name: Build (host)
|
||||
run: cargo build -p op-host-desktop -p op-cli --target ${{ matrix.target }} --release
|
||||
- name: Package archives (unix)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@
|
|||
# first).
|
||||
#
|
||||
# CI / cross-build controls (all optional, default = local behavior):
|
||||
# OPENPENCIL_VERSION CFBundleShortVersionString (default 0.8.1)
|
||||
# OPENPENCIL_VERSION CFBundleShortVersionString (default: root Cargo
|
||||
# workspace version; overrides must match it)
|
||||
# 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
|
||||
|
|
@ -49,7 +50,13 @@
|
|||
set -euo pipefail
|
||||
|
||||
WS_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
APP_VERSION="${OPENPENCIL_VERSION:-0.8.1}"
|
||||
CANONICAL_VERSION="$("$WS_ROOT/scripts/workspace-version.sh")"
|
||||
APP_VERSION="${OPENPENCIL_VERSION:-$CANONICAL_VERSION}"
|
||||
if [[ "$APP_VERSION" != "$CANONICAL_VERSION" ]]; then
|
||||
printf 'bundle-macos: error: OPENPENCIL_VERSION (%s) must match Cargo workspace version (%s)\n' \
|
||||
"$APP_VERSION" "$CANONICAL_VERSION" >&2
|
||||
exit 1
|
||||
fi
|
||||
TARGET_TRIPLE="${OPENPENCIL_TARGET:-}"
|
||||
|
||||
# Locate cargo-bundle. Tries PATH first, then a workspace-local
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
#
|
||||
# Usage:
|
||||
# ./install-op.sh # install the latest stable release
|
||||
# OP_VERSION=0.8.1 ./install-op.sh # pin a specific version
|
||||
# OP_VERSION=X.Y.Z ./install-op.sh # pin a specific version
|
||||
# OP_PRERELEASE=1 ./install-op.sh # allow the newest pre-release
|
||||
# INSTALL_DIR=$HOME/.local/bin ./install-op.sh
|
||||
#
|
||||
|
|
@ -68,7 +68,7 @@ resolve_version() {
|
|||
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.1 $0" >&2
|
||||
echo " set OP_VERSION explicitly, e.g. OP_VERSION=X.Y.Z $0" >&2
|
||||
echo " or set OP_PRERELEASE=1 to allow pre-release tags" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -25,10 +25,10 @@
|
|||
;
|
||||
; Compile (relative paths resolve against this script's directory, so the
|
||||
; workflow passes absolute /D defines):
|
||||
; makensis "/DVERSION=0.8.1" "/DARCH=x64" ^
|
||||
; makensis "/DVERSION=X.Y.Z" "/DARCH=x64" ^
|
||||
; "/DBIN_DIR=D:\w\target\x86_64-pc-windows-msvc\release" ^
|
||||
; "/DICON_FILE=D:\w\crates\op-host-desktop\assets\icon.ico" ^
|
||||
; "/DOUT_FILE=D:\w\OpenPencil-0.8.1-x64-win-setup.exe" ^
|
||||
; "/DOUT_FILE=D:\w\OpenPencil-X.Y.Z-x64-win-setup.exe" ^
|
||||
; scripts\package-windows.nsi
|
||||
;
|
||||
; NOT compiled locally (no makensis on the macOS dev machine) — first real
|
||||
|
|
|
|||
|
|
@ -24,6 +24,13 @@
|
|||
set -e
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
CANONICAL_VERSION="$("$ROOT/scripts/workspace-version.sh")"
|
||||
APP_VERSION="${OPENPENCIL_VERSION:-$CANONICAL_VERSION}"
|
||||
if [ "$APP_VERSION" != "$CANONICAL_VERSION" ]; then
|
||||
printf 'bundle-macos: error: OPENPENCIL_VERSION (%s) must match Cargo workspace version (%s)\n' \
|
||||
"$APP_VERSION" "$CANONICAL_VERSION" >&2
|
||||
exit 1
|
||||
fi
|
||||
BIN="$ROOT/target/release/openpencil-desktop"
|
||||
ICON="$ROOT/crates/op-host-desktop/assets/icon.icns"
|
||||
ENTITLEMENTS="$ROOT/crates/op-host-desktop/entitlements.plist"
|
||||
|
|
@ -75,7 +82,7 @@ cat > "$APP/Contents/Info.plist" <<PLIST
|
|||
<key>CFBundleIconFile</key><string>icon</string>
|
||||
<key>CFBundlePackageType</key><string>APPL</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key><string>6.0</string>
|
||||
<key>CFBundleShortVersionString</key><string>0.8.1</string>
|
||||
<key>CFBundleShortVersionString</key><string>${APP_VERSION}</string>
|
||||
<key>NSHighResolutionCapable</key><true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -7,16 +7,72 @@ repo_root=$(CDPATH= cd "$script_dir/.." && pwd)
|
|||
fixture_version=1.0.0
|
||||
current_version=$(bash "$repo_root/scripts/workspace-version.sh")
|
||||
|
||||
cd "$repo_root"
|
||||
|
||||
errors=0
|
||||
fixture_scan_skipped=0
|
||||
|
||||
report_missing() {
|
||||
file=$1
|
||||
message=$2
|
||||
printf '%s:1: error: %s\n' "$file" "$message" >&2
|
||||
errors=1
|
||||
}
|
||||
|
||||
require_fixed() {
|
||||
file=$1
|
||||
needle=$2
|
||||
message=$3
|
||||
if [[ ! -f "$file" ]] || ! rg --fixed-strings --quiet -- "$needle" "$file"; then
|
||||
report_missing "$file" "$message"
|
||||
fi
|
||||
}
|
||||
|
||||
require_regex() {
|
||||
file=$1
|
||||
pattern=$2
|
||||
message=$3
|
||||
if [[ ! -f "$file" ]] || ! rg --quiet -- "$pattern" "$file"; then
|
||||
report_missing "$file" "$message"
|
||||
fi
|
||||
}
|
||||
|
||||
reject_matches() {
|
||||
mode=$1
|
||||
file=$2
|
||||
pattern=$3
|
||||
message=$4
|
||||
rg_status=0
|
||||
if [[ "$mode" == fixed ]]; then
|
||||
matches=$(rg --fixed-strings --line-number --with-filename --color never -- \
|
||||
"$pattern" "$file") || rg_status=$?
|
||||
else
|
||||
matches=$(rg --line-number --with-filename --color never -- \
|
||||
"$pattern" "$file") || rg_status=$?
|
||||
fi
|
||||
|
||||
if [[ "$rg_status" -gt 1 ]]; then
|
||||
printf '%s:1: error: failed to scan version policy (rg status %s)\n' \
|
||||
"$file" "$rg_status" >&2
|
||||
errors=1
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -n "$matches" ]]; then
|
||||
while IFS=: read -r match_file match_line _; do
|
||||
printf '%s:%s: error: %s\n' "$match_file" "$match_line" "$message" >&2
|
||||
done <<< "$matches"
|
||||
errors=1
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ "$current_version" == "$fixture_version" ]]; then
|
||||
printf 'version-fixtures: current product version %s equals stable fixture version %s; skipping literal fixture drift scan because stable fixtures and product-version literals are indistinguishable\n' \
|
||||
"$current_version" "$fixture_version"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cd "$repo_root"
|
||||
|
||||
rg_status=0
|
||||
matches=$(rg \
|
||||
fixture_scan_skipped=1
|
||||
else
|
||||
rg_status=0
|
||||
matches=$(rg \
|
||||
--fixed-strings \
|
||||
--line-number \
|
||||
--with-filename \
|
||||
|
|
@ -26,19 +82,80 @@ matches=$(rg \
|
|||
"$current_version" \
|
||||
crates) || rg_status=$?
|
||||
|
||||
if [[ "$rg_status" -gt 1 ]]; then
|
||||
if [[ "$rg_status" -gt 1 ]]; then
|
||||
printf 'version-fixtures: failed to scan Rust sources with rg (status %s)\n' "$rg_status" >&2
|
||||
exit "$rg_status"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n "$matches" ]]; then
|
||||
if [[ -n "$matches" ]]; then
|
||||
printf 'version-fixtures: ordinary Rust fixtures copy current product version %s:\n' \
|
||||
"$current_version" >&2
|
||||
printf '%s\n' "$matches" >&2
|
||||
printf 'version-fixtures: use stable %s test data unless a test explicitly covers compatibility, migration, or updates\n' \
|
||||
"$fixture_version" >&2
|
||||
errors=1
|
||||
fi
|
||||
fi
|
||||
|
||||
for macos_script in scripts/bundle-macos.sh tools/bundle-macos.sh; do
|
||||
if [[ "$macos_script" == scripts/bundle-macos.sh ]]; then
|
||||
require_fixed "$macos_script" \
|
||||
'CANONICAL_VERSION="$("$WS_ROOT/scripts/workspace-version.sh")"' \
|
||||
'macOS packaging must assign CANONICAL_VERSION from scripts/workspace-version.sh'
|
||||
require_fixed "$macos_script" 'CFBundleShortVersionString $APP_VERSION' \
|
||||
'CFBundleShortVersionString must use APP_VERSION'
|
||||
else
|
||||
require_fixed "$macos_script" \
|
||||
'CANONICAL_VERSION="$("$ROOT/scripts/workspace-version.sh")"' \
|
||||
'macOS packaging must assign CANONICAL_VERSION from scripts/workspace-version.sh'
|
||||
require_fixed "$macos_script" \
|
||||
'CFBundleShortVersionString</key><string>${APP_VERSION}' \
|
||||
'CFBundleShortVersionString must use APP_VERSION'
|
||||
fi
|
||||
require_fixed "$macos_script" 'APP_VERSION="${OPENPENCIL_VERSION:-$CANONICAL_VERSION}"' \
|
||||
'OPENPENCIL_VERSION must default to the Cargo workspace version'
|
||||
require_fixed "$macos_script" '"$APP_VERSION" != "$CANONICAL_VERSION"' \
|
||||
'OPENPENCIL_VERSION overrides must be rejected when they differ from Cargo'
|
||||
reject_matches regex "$macos_script" \
|
||||
'OPENPENCIL_VERSION:-[0-9]+[.][0-9]+[.][0-9]+' \
|
||||
'OPENPENCIL_VERSION must fall back to the Cargo workspace version'
|
||||
reject_matches regex "$macos_script" \
|
||||
'CFBundleShortVersionString[^[:cntrl:]]*[0-9]+[.][0-9]+[.][0-9]+' \
|
||||
'CFBundleShortVersionString must use the resolved Cargo workspace version'
|
||||
done
|
||||
|
||||
for example_file in scripts/package-windows.nsi scripts/install-op.sh; do
|
||||
require_regex "$example_file" 'X[.]Y[.]Z|<version>' \
|
||||
'version examples must use X.Y.Z or <version>'
|
||||
reject_matches fixed "$example_file" '0.8.1' \
|
||||
'version examples must use X.Y.Z or <version>, not a product release'
|
||||
if [[ "$current_version" != 0.8.1 ]]; then
|
||||
reject_matches fixed "$example_file" "$current_version" \
|
||||
'version examples must use X.Y.Z or <version>, not the current Cargo version'
|
||||
fi
|
||||
done
|
||||
|
||||
release_workflow=.github/workflows/rust-release.yml
|
||||
require_fixed "$release_workflow" 'cargo_version="$(scripts/workspace-version.sh)"' \
|
||||
'release version computation must invoke scripts/workspace-version.sh'
|
||||
require_fixed "$release_workflow" 'tag_version="${GITHUB_REF_NAME#v}"' \
|
||||
'release version computation must derive the version from v* tags'
|
||||
require_fixed "$release_workflow" '"$tag_version" != "$cargo_version"' \
|
||||
'release tags must be compared with the Cargo workspace version'
|
||||
require_fixed "$release_workflow" 'echo "OP_VERSION=$cargo_version" >> "$GITHUB_ENV"' \
|
||||
'OP_VERSION must always be written from the Cargo workspace version'
|
||||
reject_matches fixed "$release_workflow" 'echo "OP_VERSION=${GITHUB_REF_NAME#v}"' \
|
||||
'OP_VERSION must not be written directly from the release tag'
|
||||
reject_matches fixed "$release_workflow" 'echo "OP_VERSION=${ver:-0.0.0}"' \
|
||||
'OP_VERSION must not use an independent manifest parser or fallback'
|
||||
|
||||
if [[ "$errors" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf 'version-fixtures: no ordinary Rust fixtures copy current product version %s\n' \
|
||||
if [[ "$fixture_scan_skipped" -eq 0 ]]; then
|
||||
printf 'version-fixtures: no ordinary Rust fixtures copy current product version %s\n' \
|
||||
"$current_version"
|
||||
fi
|
||||
printf 'version-fixtures: packaging and release versions derive from Cargo workspace version %s\n' \
|
||||
"$current_version"
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ new_repo() {
|
|||
repo="$temp_root/$name"
|
||||
|
||||
mkdir -p \
|
||||
"$repo/.github/workflows" \
|
||||
"$repo/tools" \
|
||||
"$repo/scripts" \
|
||||
"$repo/crates/example/src" \
|
||||
|
|
@ -83,6 +84,51 @@ new_repo() {
|
|||
"version = \"$version\"" \
|
||||
'edition = "2024"' > "$repo/Cargo.toml"
|
||||
|
||||
cat > "$repo/scripts/bundle-macos.sh" <<'SCRIPT'
|
||||
#!/usr/bin/env bash
|
||||
WS_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CANONICAL_VERSION="$("$WS_ROOT/scripts/workspace-version.sh")"
|
||||
APP_VERSION="${OPENPENCIL_VERSION:-$CANONICAL_VERSION}"
|
||||
if [[ "$APP_VERSION" != "$CANONICAL_VERSION" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $APP_VERSION" "$PLIST"
|
||||
SCRIPT
|
||||
|
||||
cat > "$repo/tools/bundle-macos.sh" <<'SCRIPT'
|
||||
#!/bin/sh
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
CANONICAL_VERSION="$("$ROOT/scripts/workspace-version.sh")"
|
||||
APP_VERSION="${OPENPENCIL_VERSION:-$CANONICAL_VERSION}"
|
||||
if [ "$APP_VERSION" != "$CANONICAL_VERSION" ]; then
|
||||
exit 1
|
||||
fi
|
||||
<key>CFBundleShortVersionString</key><string>${APP_VERSION}</string>
|
||||
SCRIPT
|
||||
|
||||
cat > "$repo/scripts/package-windows.nsi" <<'SCRIPT'
|
||||
; makensis "/DVERSION=X.Y.Z" "/DOUT_FILE=OpenPencil-X.Y.Z-x64-win-setup.exe"
|
||||
SCRIPT
|
||||
|
||||
cat > "$repo/scripts/install-op.sh" <<'SCRIPT'
|
||||
# OP_VERSION=X.Y.Z ./install-op.sh
|
||||
# set OP_VERSION explicitly, e.g. OP_VERSION=X.Y.Z ./install-op.sh
|
||||
SCRIPT
|
||||
|
||||
cat > "$repo/.github/workflows/rust-release.yml" <<'SCRIPT'
|
||||
- name: Compute release version
|
||||
shell: bash
|
||||
run: |
|
||||
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
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
echo "OP_VERSION=$cargo_version" >> "$GITHUB_ENV"
|
||||
SCRIPT
|
||||
|
||||
printf '%s\n' "$repo"
|
||||
}
|
||||
|
||||
|
|
@ -146,4 +192,68 @@ assert_not_contains 'no ordinary Rust fixtures copy current product version' \
|
|||
'fixture-version collision'
|
||||
pass 'fixture-version collision is documented and skipped'
|
||||
|
||||
repo=$(new_repo hardcoded_macos_version 0.8.1)
|
||||
printf '%s\n' 'APP_VERSION="${OPENPENCIL_VERSION:-0.8.1}"' \
|
||||
>> "$repo/scripts/bundle-macos.sh"
|
||||
run_guard "$repo"
|
||||
assert_status 1 'hardcoded macOS version'
|
||||
assert_contains 'scripts/bundle-macos.sh:' 'hardcoded macOS version'
|
||||
assert_contains 'error: OPENPENCIL_VERSION must fall back to the Cargo workspace version' \
|
||||
'hardcoded macOS version'
|
||||
assert_no_success_output 'hardcoded macOS version'
|
||||
pass 'hardcoded macOS package version is rejected with file and line guidance'
|
||||
|
||||
repo=$(new_repo macos_reader_comment_only 0.8.1)
|
||||
cat > "$repo/scripts/bundle-macos.sh" <<'SCRIPT'
|
||||
#!/usr/bin/env bash
|
||||
# scripts/workspace-version.sh
|
||||
APP_VERSION="${OPENPENCIL_VERSION:-$CANONICAL_VERSION}"
|
||||
if [[ "$APP_VERSION" != "$CANONICAL_VERSION" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $OTHER_VERSION" "$PLIST"
|
||||
SCRIPT
|
||||
run_guard "$repo"
|
||||
assert_status 1 'macOS reader comment only'
|
||||
assert_contains 'scripts/bundle-macos.sh:1:' 'macOS reader comment only'
|
||||
assert_contains 'error: macOS packaging must assign CANONICAL_VERSION from scripts/workspace-version.sh' \
|
||||
'macOS reader comment only'
|
||||
assert_contains 'error: CFBundleShortVersionString must use APP_VERSION' \
|
||||
'macOS reader comment only'
|
||||
assert_no_success_output 'macOS reader comment only'
|
||||
pass 'macOS packaging must invoke the reader and use the resolved version'
|
||||
|
||||
repo=$(new_repo release_missing_tag_equality 0.8.1)
|
||||
cat > "$repo/.github/workflows/rust-release.yml" <<'SCRIPT'
|
||||
- name: Compute release version
|
||||
shell: bash
|
||||
run: |
|
||||
cargo_version="$(scripts/workspace-version.sh)"
|
||||
if [[ "$GITHUB_REF" == refs/tags/v* ]]; then
|
||||
tag_version="${GITHUB_REF_NAME#v}"
|
||||
fi
|
||||
echo "OP_VERSION=$cargo_version" >> "$GITHUB_ENV"
|
||||
SCRIPT
|
||||
run_guard "$repo"
|
||||
assert_status 1 'release missing tag equality'
|
||||
assert_contains '.github/workflows/rust-release.yml:1:' 'release missing tag equality'
|
||||
assert_contains 'error: release tags must be compared with the Cargo workspace version' \
|
||||
'release missing tag equality'
|
||||
assert_no_success_output 'release missing tag equality'
|
||||
pass 'release workflow must reject tags that differ from Cargo'
|
||||
|
||||
repo=$(new_repo collision_still_checks_packaging 1.0.0)
|
||||
printf '%s\n' 'APP_VERSION="${OPENPENCIL_VERSION:-0.8.1}"' \
|
||||
>> "$repo/tools/bundle-macos.sh"
|
||||
run_guard "$repo"
|
||||
assert_status 1 'fixture-version collision packaging check'
|
||||
assert_contains 'skipping literal fixture drift scan' \
|
||||
'fixture-version collision packaging check'
|
||||
assert_contains 'tools/bundle-macos.sh:' 'fixture-version collision packaging check'
|
||||
assert_contains 'error: OPENPENCIL_VERSION must fall back to the Cargo workspace version' \
|
||||
'fixture-version collision packaging check'
|
||||
assert_not_contains 'no ordinary Rust fixtures copy current product version' \
|
||||
'fixture-version collision packaging check'
|
||||
pass 'fixture-version collision still runs packaging checks'
|
||||
|
||||
printf '1..%s\n' "$tests_run"
|
||||
|
|
|
|||
Loading…
Reference in a new issue