#!/usr/bin/env bash set -euo pipefail script_dir=$(CDPATH= cd "$(dirname "$0")" && pwd) guard_source="$script_dir/check-version-sync.sh" reader_source="$script_dir/../scripts/workspace-version.sh" temp_root=$(mktemp -d "${TMPDIR:-/tmp}/check-version-sync.XXXXXX") trap 'rm -rf "$temp_root"' EXIT HUP INT TERM tests_run=0 case_status=0 case_output= fail() { printf 'not ok - %s\n' "$1" >&2 exit 1 } pass() { tests_run=$((tests_run + 1)) printf 'ok %s - %s\n' "$tests_run" "$1" } assert_status() { expected=$1 label=$2 if [[ "$case_status" -ne "$expected" ]]; then printf '%s\n' "$case_output" >&2 fail "$label: expected status $expected, got $case_status" fi } assert_contains() { needle=$1 label=$2 case "$case_output" in *"$needle"*) ;; *) printf '%s\n' "$case_output" >&2 fail "$label: missing output: $needle" ;; esac } assert_not_contains() { needle=$1 label=$2 case "$case_output" in *"$needle"*) printf '%s\n' "$case_output" >&2 fail "$label: unexpected output: $needle" ;; *) ;; esac } assert_no_success_output() { label=$1 assert_not_contains 'no ordinary Rust fixtures copy current product version' "$label" assert_not_contains 'skipping literal fixture drift scan' "$label" } cargo() { if [[ "$*" != 'metadata --no-deps --format-version 1 --locked' ]]; then printf 'unexpected cargo arguments: %s\n' "$*" >&2 return 41 fi repo_root=$(pwd -P) if [[ -e "$repo_root/.fake-cargo-empty" ]]; then printf '%s\n' '{"packages":[]}' return fi canonical=$("$repo_root/scripts/workspace-version.sh") package_version=$canonical if [[ -f "$repo_root/.fake-cargo-version" ]]; then package_version=$(sed -n '1p' "$repo_root/.fake-cargo-version") fi if [[ -e "$repo_root/.fake-cargo-warning" ]]; then printf 'warning: fake Cargo metadata warning\n' >&2 fi printf '{"packages":[{"name":"op-example","version":"%s","manifest_path":"%s/crates/example/Cargo.toml"}]}\n' \ "$package_version" "$repo_root" } bun() { repo_root=$(CDPATH= cd "$PWD/.." && pwd) if [[ "$PWD" != "$repo_root/packages" || "$*" != 'run sync-version:check' ]]; then printf 'unexpected bun invocation: cwd=%s args=%s\n' "$PWD" "$*" >&2 return 42 fi if [[ -e "$repo_root/.fake-bun-fail" ]]; then printf 'package versions are stale\n' >&2 return 43 fi } export -f cargo bun write_workflow_fixture() { repo=$1 dependency_mode=$2 publish_version_mode=$3 template="$repo/.github/workflows/rust-release.yml.in" cat > "$template" <<'SCRIPT' jobs: version: runs-on: ubuntu-latest outputs: version: ${{ steps.version.outputs.version }} steps: - uses: actions/checkout@v4 - id: 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 "version=$cargo_version" >> "$GITHUB_OUTPUT" build: needs: version runs-on: ubuntu-latest env: OP_VERSION: ${{ needs.version.outputs.version }} steps: - run: echo build web-docker: __WEB_NEEDS__ runs-on: ubuntu-latest env: OP_VERSION: ${{ needs.version.outputs.version }} steps: - run: | version="__PUBLISH_VERSION__" echo "$version" sdk-packages: __SDK_NEEDS__ runs-on: ubuntu-latest env: OP_VERSION: ${{ needs.version.outputs.version }} steps: - run: bun run sync-version:check - run: | version="__PUBLISH_VERSION__" echo "$version" release-draft: needs: [version, build, web-docker, sdk-packages, vsix] runs-on: ubuntu-latest env: OP_VERSION: ${{ needs.version.outputs.version }} steps: - run: version="$OP_VERSION" package-managers: needs: [version, release-draft] runs-on: ubuntu-latest env: OP_VERSION: ${{ needs.version.outputs.version }} steps: - run: version="$OP_VERSION" SCRIPT if [[ "$publish_version_mode" == independent ]]; then publish_version='${GITHUB_REF_NAME#v}' else publish_version='$OP_VERSION' fi awk -v dependency_mode="$dependency_mode" -v publish_version="$publish_version" ' $0 == "__WEB_NEEDS__" { if (dependency_mode == "required") print " needs: version" next } $0 == "__SDK_NEEDS__" { if (dependency_mode == "required") print " needs: version" next } { gsub(/__PUBLISH_VERSION__/, publish_version) print } ' "$template" > "$repo/.github/workflows/rust-release.yml" rm "$template" } new_repo() { name=$1 version=$2 repo="$temp_root/$name" mkdir -p \ "$repo/.github/workflows" \ "$repo/packages" \ "$repo/tools" \ "$repo/scripts" \ "$repo/crates/op-cli/assets" \ "$repo/crates/op-cli/src" \ "$repo/crates/op-editor-core/src" \ "$repo/crates/op-host-desktop" \ "$repo/crates/example/src" \ "$repo/crates/op-host-desktop/src" git -C "$repo" init -q cp "$guard_source" "$repo/tools/check-version-sync.sh" cp "$reader_source" "$repo/scripts/workspace-version.sh" chmod +x "$repo/tools/check-version-sync.sh" "$repo/scripts/workspace-version.sh" printf '%s\n' \ '[workspace]' \ 'members = []' \ '' \ '[workspace.package]' \ "version = \"$version\"" \ 'edition = "2024"' > "$repo/Cargo.toml" printf '%s\n' 'version = 4' > "$repo/Cargo.lock" printf '%s\n' '{"name":"fixture-packages"}' > "$repo/packages/package.json" mkdir -p "$repo/packages/op-chrome-extension" printf '{"manifest_version":3,"name":"fixture-extension","version":"%s"}\n' "$version" \ > "$repo/packages/op-chrome-extension/manifest.json" cat > "$repo/crates/op-cli/assets/skill-bundle.json" <<'JSON' {"one":"__OPENPENCIL_VERSION__","two":"__OPENPENCIL_VERSION__","three":"__OPENPENCIL_VERSION__","four":"__OPENPENCIL_VERSION__","five":"__OPENPENCIL_VERSION__"} JSON cat > "$repo/crates/op-editor-core/src/state.rs" <<'RUST' version: env!("CARGO_PKG_VERSION").to_owned(), RUST cat > "$repo/crates/op-editor-core/src/host_support.rs" <<'RUST' pub fn sample() { let src = src.replace("__OPENPENCIL_VERSION__", env!("CARGO_PKG_VERSION")); } pub fn starter() { let src = src.replace("__OPENPENCIL_VERSION__", env!("CARGO_PKG_VERSION")); } #[cfg(test)] mod tests { } RUST cat > "$repo/crates/op-cli/src/app_control_cli.rs" <<'RUST' const MINIMAL_DOCUMENT: &str = concat!(env!("CARGO_PKG_VERSION")); RUST cat > "$repo/crates/op-host-desktop/Cargo.toml" <<'TOML' op-host-native = { path = "../op-host-native", features = ["gl-host"] } TOML cat > "$repo/crates/example/Cargo.toml" <<'TOML' [package] name = "op-example" version.workspace = true edition.workspace = true TOML cat > "$repo/README.md" <<'MARKDOWN' | Image | Includes | | --- | --- | | `ghcr.io/zseven-w/openpencil-web:vX.Y.Z` | Rust web host | ```bash VERSION="$(scripts/workspace-version.sh)" docker run -d -p 3100:3100 "ghcr.io/zseven-w/openpencil-web:v${VERSION}" ``` The TypeScript editor was retired at `v0.7.5`. MARKDOWN cat > "$repo/.github/workflows/version-sync.yml" <<'YAML' name: Version consistency on: pull_request: paths: - "README*.md" push: paths: - "README*.md" YAML 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 printf 'bundle-macos: error: OPENPENCIL_VERSION (%s) must match Cargo workspace version (%s)\n' \ "$APP_VERSION" "$CANONICAL_VERSION" >&2 exit 1 fi if [[ "${OPENPENCIL_VALIDATE_VERSION_ONLY:-}" == 1 ]]; then printf '%s\n' "$APP_VERSION" exit 0 fi /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $APP_VERSION" "$PLIST" touch "$WS_ROOT/packaging-side-effect" 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 printf 'bundle-macos: error: OPENPENCIL_VERSION (%s) must match Cargo workspace version (%s)\n' \ "$APP_VERSION" "$CANONICAL_VERSION" >&2 exit 1 fi if [ "${OPENPENCIL_VALIDATE_VERSION_ONLY:-}" = 1 ]; then printf '%s\n' "$APP_VERSION" exit 0 fi CFBundleShortVersionString${APP_VERSION} touch "$ROOT/packaging-side-effect" SCRIPT cat > "$repo/scripts/package-windows.nsi" <<'SCRIPT' ; makensis "/DVERSION=X.Y.Z" "/DOUT_FILE=OpenPencil-X.Y.Z-x64-win-setup.exe" !ifndef VERSION !define VERSION "0.0.0" !endif 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 write_workflow_fixture "$repo" required canonical printf '%s\n' "$repo" } run_guard() { repo=$1 shift case_status=0 case_output=$(cd "$repo" && env GITHUB_REF= GITHUB_REF_NAME= "$@" \ bash tools/check-version-sync.sh 2>&1) || case_status=$? } repo_snapshot() { repo=$1 find "$repo" -type f ! -path "$repo/.git/*" -exec shasum {} + | LC_ALL=C sort | shasum | awk '{print $1}' } repo=$(new_repo v_prefixed_stale_version 0.8.1) printf '%s\n' '# pin v0.8.2' >> "$repo/scripts/install-op.sh" run_guard "$repo" assert_status 1 'v-prefixed stale version' assert_contains 'scripts/install-op.sh:' 'v-prefixed stale version' assert_contains 'error: version examples must use X.Y.Z or , not a SemVer release' \ 'v-prefixed stale version' assert_no_success_output 'v-prefixed stale version' pass 'v-prefixed stale SemVer examples are rejected' repo=$(new_repo sentence_final_stale_version 0.8.1) printf '%s\n' '# pin 0.8.2.' >> "$repo/scripts/install-op.sh" run_guard "$repo" assert_status 1 'sentence-final stale version' assert_contains 'scripts/install-op.sh:' 'sentence-final stale version' assert_contains 'error: version examples must use X.Y.Z or , not a SemVer release' \ 'sentence-final stale version' assert_no_success_output 'sentence-final stale version' pass 'sentence-final stale SemVer examples are rejected' repo=$(new_repo prerelease_build_stale_version 0.8.1) printf '%s\n' '# pin v0.8.2-beta.1+build.5.' >> "$repo/scripts/install-op.sh" run_guard "$repo" assert_status 1 'pre-release/build stale version' assert_contains 'scripts/install-op.sh:' 'pre-release/build stale version' assert_contains 'error: version examples must use X.Y.Z or , not a SemVer release' \ 'pre-release/build stale version' assert_no_success_output 'pre-release/build stale version' pass 'pre-release and build metadata remain part of the rejected token' repo=$(new_repo ordinary_current 0.8.1) printf '%s\n' 'const DOC: &str = r#"{"version":"0.8.1","children":[]}"#;' \ > "$repo/crates/example/src/lib.rs" run_guard "$repo" assert_status 1 'ordinary current-version fixture' assert_contains 'crates/example/src/lib.rs:1:' 'ordinary current-version fixture' assert_contains 'use stable 1.0.0 test data' 'ordinary current-version fixture' assert_no_success_output 'ordinary current-version fixture' pass 'ordinary current-version fixture is rejected with guidance' repo=$(new_repo updater_compatibility 0.8.1) printf '%s\n' 'assert!(is_newer("0.8.1", "0.8.0"));' \ > "$repo/crates/op-host-desktop/src/update_check.rs" run_guard "$repo" assert_status 0 'updater compatibility literal' assert_contains 'no ordinary Rust fixtures copy current product version 0.8.1' \ 'updater compatibility literal' pass 'updater compatibility literal is allowed' repo=$(new_repo legitimate_stable_constant 0.8.1) printf '%s\n' 'pub const FORMAT_VERSION: &str = "1.0.0";' \ > "$repo/crates/example/src/lib.rs" run_guard "$repo" assert_status 0 'legitimate stable production constant' assert_contains 'no ordinary Rust fixtures copy current product version 0.8.1' \ 'legitimate stable production constant' pass 'unrelated stable production constant is allowed' repo=$(new_repo reader_failure 0.8.1) printf '%s\n' \ '[workspace.package]' \ 'version = "not-semver"' > "$repo/Cargo.toml" run_guard "$repo" assert_status 1 'canonical reader failure' assert_contains 'workspace-version: invalid version' 'canonical reader failure' assert_no_success_output 'canonical reader failure' pass 'canonical reader failure has no misleading success output' repo=$(new_repo inherited_tag_environment 1.0.0) GITHUB_REF=refs/tags/v0.8.1 GITHUB_REF_NAME=v0.8.1 run_guard "$repo" assert_status 0 'inherited tag environment' assert_not_contains 'release tag v0.8.1 does not match Cargo workspace version 1.0.0' \ 'inherited tag environment' pass 'test cases do not inherit the workflow tag environment' repo=$(new_repo fixture_version_collision 1.0.0) printf '%s\n' \ 'const DOC: &str = r#"{"version":"1.0.0","children":[]}"#;' \ 'pub const FORMAT_VERSION: &str = "1.0.0";' \ > "$repo/crates/example/src/lib.rs" run_guard "$repo" assert_status 0 'fixture-version collision' assert_contains 'current product version 1.0.0 equals stable fixture version 1.0.0' \ 'fixture-version collision' assert_contains 'skipping literal fixture drift scan' 'fixture-version collision' assert_contains 'stable fixtures and product-version literals are indistinguishable' \ 'fixture-version collision' 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 macos_readers_commented_out 0.8.1) cat > "$repo/scripts/bundle-macos.sh" <<'SCRIPT' #!/usr/bin/env bash WS_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" CANONICAL_VERSION="0.0.0" # 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="0.0.0" # CANONICAL_VERSION="$("$ROOT/scripts/workspace-version.sh")" APP_VERSION="${OPENPENCIL_VERSION:-$CANONICAL_VERSION}" if [ "$APP_VERSION" != "$CANONICAL_VERSION" ]; then exit 1 fi CFBundleShortVersionString${APP_VERSION} SCRIPT run_guard "$repo" assert_status 1 'commented macOS readers' assert_contains 'scripts/bundle-macos.sh:1:' 'commented macOS readers' assert_contains 'tools/bundle-macos.sh:1:' 'commented macOS readers' assert_contains 'error: macOS packaging must assign CANONICAL_VERSION from scripts/workspace-version.sh' \ 'commented macOS readers' assert_no_success_output 'commented macOS readers' pass 'commented macOS reader assignments do not satisfy the guard' repo=$(new_repo release_checks_commented_out 0.8.1) cat > "$repo/.github/workflows/rust-release.yml" <<'SCRIPT' - name: Compute release version shell: bash run: | cargo_version="0.0.0" # 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 : fi # if [[ "$tag_version" != "$cargo_version" ]]; then fi echo "OP_VERSION=$cargo_version" >> "$GITHUB_ENV" SCRIPT run_guard "$repo" assert_status 1 'commented release checks' assert_contains '.github/workflows/rust-release.yml:1:' 'commented release checks' assert_contains 'error: release version computation must invoke scripts/workspace-version.sh' \ 'commented release checks' assert_contains 'error: release tags must be compared with the Cargo workspace version' \ 'commented release checks' assert_no_success_output 'commented release checks' pass 'commented release derivation and comparison do not satisfy the guard' repo=$(new_repo macos_noop_mismatch_and_reassignment 0.8.1) 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 : fi if [[ "${OPENPENCIL_VALIDATE_VERSION_ONLY:-}" == 1 ]]; then printf '%s\n' "$APP_VERSION" exit 0 fi APP_VERSION="0.8.2" /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $APP_VERSION" "$PLIST" touch "$WS_ROOT/packaging-side-effect" SCRIPT run_guard "$repo" assert_status 1 'macOS no-op mismatch and reassignment' assert_contains 'scripts/bundle-macos.sh:1:' 'macOS no-op mismatch and reassignment' assert_contains 'error: expected exactly one active APP_VERSION assignment' \ 'macOS no-op mismatch and reassignment' assert_contains 'error: mismatched OPENPENCIL_VERSION must fail validation with actionable error' \ 'macOS no-op mismatch and reassignment' if [[ -e "$repo/packaging-side-effect" ]]; then fail 'macOS no-op mismatch and reassignment: validation executed packaging side effects' fi assert_no_success_output 'macOS no-op mismatch and reassignment' pass 'macOS validation rejects no-op mismatch bodies and APP_VERSION reassignment' repo=$(new_repo arbitrary_stale_example_version 0.8.1) printf '%s\n' '# stale example: OP_VERSION=0.8.2' >> "$repo/scripts/install-op.sh" run_guard "$repo" assert_status 1 'arbitrary stale example version' assert_contains 'scripts/install-op.sh:' 'arbitrary stale example version' assert_contains 'error: version examples must use X.Y.Z or , not a SemVer release' \ 'arbitrary stale example version' assert_no_success_output 'arbitrary stale example version' pass 'arbitrary stale SemVer examples are rejected' repo=$(new_repo embedded_version_like_substring 0.8.1) printf '%s\n' '# identifier build0.8.2candidate is not a version token' \ >> "$repo/scripts/install-op.sh" run_guard "$repo" assert_status 0 'embedded version-like substring' assert_contains 'all managed versions derive from Cargo workspace version 0.8.1' \ 'embedded version-like substring' pass 'version-like substrings inside larger identifiers are allowed' repo=$(new_repo nsis_defensive_fallback 0.8.1) run_guard "$repo" assert_status 0 'NSIS defensive fallback' assert_contains 'all managed versions derive from Cargo workspace version 0.8.1' \ 'NSIS defensive fallback' pass 'NSIS 0.0.0 defensive fallback remains allowed' repo=$(new_repo publish_jobs_without_version_dependency 0.8.1) write_workflow_fixture "$repo" missing canonical run_guard "$repo" assert_status 1 'publish jobs without version dependency' assert_contains '.github/workflows/rust-release.yml:1:' \ 'publish jobs without version dependency' assert_contains 'error: web-docker must depend on the version preflight job' \ 'publish jobs without version dependency' assert_contains 'error: sdk-packages must depend on the version preflight job' \ 'publish jobs without version dependency' assert_no_success_output 'publish jobs without version dependency' pass 'web Docker and SDK publishing cannot start before version preflight' repo=$(new_repo independent_publish_tag_versions 0.8.1) write_workflow_fixture "$repo" required independent run_guard "$repo" assert_status 1 'independent publish tag versions' assert_contains '.github/workflows/rust-release.yml:' 'independent publish tag versions' assert_contains 'error: publish paths must consume the canonical version job output' \ 'independent publish tag versions' assert_no_success_output 'independent publish tag versions' pass 'publish paths cannot derive independent versions from the tag' 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' repo=$(new_repo cargo_metadata_version_mismatch 0.8.1) printf '%s\n' '0.8.0' > "$repo/.fake-cargo-version" run_guard "$repo" assert_status 1 'Cargo metadata version mismatch' assert_contains 'crates/example/Cargo.toml:1: error: workspace package op-example has version 0.8.0; expected 0.8.1' \ 'Cargo metadata version mismatch' assert_no_success_output 'Cargo metadata version mismatch' pass 'workspace op-* packages under crates must match the canonical Cargo version' repo=$(new_repo cargo_metadata_without_local_packages 0.8.1) touch "$repo/.fake-cargo-empty" run_guard "$repo" assert_status 1 'Cargo metadata without local packages' assert_contains 'Cargo.toml:1: error: cargo metadata found no local op-* workspace packages under crates' \ 'Cargo metadata without local packages' assert_no_success_output 'Cargo metadata without local packages' pass 'zero local op-* packages is an actionable metadata failure' repo=$(new_repo symlinked_repo_package_mismatch 0.8.1) printf '%s\n' '0.8.0' > "$repo/.fake-cargo-version" logical_repo="$temp_root/symlinked_repo_package_mismatch_logical" ln -s "$repo" "$logical_repo" run_guard "$logical_repo" assert_status 1 'symlinked repository package mismatch' assert_contains 'crates/example/Cargo.toml:1: error: workspace package op-example has version 0.8.0; expected 0.8.1' \ 'symlinked repository package mismatch' assert_no_success_output 'symlinked repository package mismatch' pass 'symlinked repository invocation still validates physical Cargo manifest paths' repo=$(new_repo cargo_metadata_warning 0.8.1) touch "$repo/.fake-cargo-warning" run_guard "$repo" assert_status 0 'Cargo metadata warning' assert_contains 'warning: fake Cargo metadata warning' 'Cargo metadata warning' pass 'Cargo warnings do not corrupt the metadata JSON passed to jq' repo=$(new_repo package_version_drift 0.8.1) touch "$repo/.fake-bun-fail" run_guard "$repo" assert_status 1 'package version drift' assert_contains 'packages:1: error: bun run sync-version:check failed' 'package version drift' assert_no_success_output 'package version drift' pass 'web SDK package drift is reported by the read-only guard' repo=$(new_repo matching_release_tag 0.8.1) run_guard "$repo" GITHUB_REF=refs/tags/v0.8.1 GITHUB_REF_NAME=v0.8.1 assert_status 0 'matching release tag' pass 'matching v* release tags pass the repository guard' repo=$(new_repo mismatched_release_tag 0.8.1) run_guard "$repo" GITHUB_REF=refs/tags/v0.8.2 GITHUB_REF_NAME=v0.8.2 assert_status 1 'mismatched release tag' assert_contains 'environment:1: error: release tag v0.8.2 does not match Cargo workspace version 0.8.1' \ 'mismatched release tag' assert_no_success_output 'mismatched release tag' pass 'mismatched v* release tags are rejected outside the release workflow too' repo=$(new_repo cli_bundle_wrong_sentinel_count 0.8.1) sed -i.bak 's/,"five":"__OPENPENCIL_VERSION__"//' \ "$repo/crates/op-cli/assets/skill-bundle.json" rm "$repo/crates/op-cli/assets/skill-bundle.json.bak" run_guard "$repo" assert_status 1 'CLI bundle sentinel count' assert_contains 'crates/op-cli/assets/skill-bundle.json:1: error: expected exactly 5 version sentinels' \ 'CLI bundle sentinel count' assert_no_success_output 'CLI bundle sentinel count' pass 'embedded CLI bundle retains exactly five version sentinels' repo=$(new_repo cli_bundle_without_sentinels 0.8.1) sed -i.bak 's/__OPENPENCIL_VERSION__/__MISSING_VERSION__/g' \ "$repo/crates/op-cli/assets/skill-bundle.json" rm "$repo/crates/op-cli/assets/skill-bundle.json.bak" run_guard "$repo" assert_status 1 'CLI bundle missing sentinels' assert_contains 'crates/op-cli/assets/skill-bundle.json:1: error: expected exactly 5 version sentinels' \ 'CLI bundle missing sentinels' assert_contains '(found 0)' 'CLI bundle missing sentinels' assert_no_success_output 'CLI bundle missing sentinels' pass 'embedded CLI bundle reports zero missing version sentinels actionably' repo=$(new_repo cli_bundle_numeric_product_version 0.8.1) printf '%s\n' '{"stale":"0.8.1"}' >> "$repo/crates/op-cli/assets/skill-bundle.json" run_guard "$repo" assert_status 1 'CLI bundle numeric product version' assert_contains 'crates/op-cli/assets/skill-bundle.json:' 'CLI bundle numeric product version' assert_contains 'error: embedded CLI bundle must not contain the canonical version literal' \ 'CLI bundle numeric product version' assert_no_success_output 'CLI bundle numeric product version' pass 'embedded CLI bundle cannot duplicate the numeric product version' repo=$(new_repo hardcoded_rust_product_version 0.8.1) printf '%s\n' 'version: "0.8.1".to_owned(),' > "$repo/crates/op-editor-core/src/state.rs" run_guard "$repo" assert_status 1 'hardcoded Rust product version' assert_contains 'crates/op-editor-core/src/state.rs:1: error: empty documents must derive their version from CARGO_PKG_VERSION' \ 'hardcoded Rust product version' assert_no_success_output 'hardcoded Rust product version' pass 'Rust product-version producers remain derived from Cargo metadata' repo=$(new_repo versioned_local_product_dependency 0.8.1) printf '%s\n' \ 'op-host-native = { path = "../op-host-native", version = "0.8.1", features = ["gl-host"] }' \ > "$repo/crates/op-host-desktop/Cargo.toml" run_guard "$repo" assert_status 1 'versioned local product dependency' assert_contains 'crates/op-host-desktop/Cargo.toml:1: error: local op-host-native dependency must not duplicate the product version' \ 'versioned local product dependency' assert_no_success_output 'versioned local product dependency' pass 'local product dependencies do not repeat the workspace version' . "$script_dir/check-version-sync-policy.test-cases.sh" repo=$(new_repo guard_is_read_only 0.8.1) before_snapshot=$(repo_snapshot "$repo") run_guard "$repo" assert_status 0 'read-only guard' after_snapshot=$(repo_snapshot "$repo") if [[ "$before_snapshot" != "$after_snapshot" ]]; then fail 'read-only guard changed repository file contents' fi pass 'repository-wide version guard does not write files' printf '1..%s\n' "$tests_run"