# solstone-linux Makefile # Standalone Linux desktop observer for solstone .PHONY: all bootstrap agent-setup install format brand-sync test check-observer-contract check-rust-release-manifest check-transparency-minisign check-audit-signed-packet ci audit update-deps shellcheck install-service uninstall-service service-restart service-status service-logs versions clean clean-install release release-images release-candidate release-candidate-prove release-candidate-recover sign-release-manifest verify-release-signature publish-origin publish-release publish-transparency resign-transparency-pointer check-toolchain-env establish-toolchain rust-preflight check-cargo-deny APP := solstone-linux UNIT := solstone-linux.service CARGO ?= cargo RUSTUP ?= rustup CARGO_HOME ?= $(HOME)/.cargo CARGO_BIN_DIR := $(CARGO_HOME)/bin RUST_VERSION := $(shell sed -n 's/^channel = "\([^"]*\)"/\1/p' rust-toolchain.toml 2>/dev/null) AMBIENT_RUSTUP_TOOLCHAIN := $(RUSTUP_TOOLCHAIN) export RUSTUP_TOOLCHAIN := $(RUST_VERSION) export PATH := $(CARGO_BIN_DIR):$(PATH) RUST_TARGET := x86_64-unknown-linux-gnu CARGO_LOCKED := --locked UPDATE_DEPS_ARGS ?= CARGO_DENY_VERSION := 0.20.2 CARGO_DEB_VERSION := 3.7.0 CARGO_GENERATE_RPM_VERSION := 0.21.0 # The in-repo v1 publisher is retired. Each changed app release gets a v2 record # produced outside this repository, followed by an independent audit. Records are # published at trust.solstone.app. Keep the v1 targets dormant. # This flag alone does not authorize a v1 publish. TRANSPARENCY_ACTIVATED ?= 0 # The release rail builds and proves natively for the host architecture only. RELEASE_ARCH ?= $(shell uname -m) # Proof roles are provisioned images now, so keep their immutable stock bases explicit. # Each architecture's pin is the image ID of `podman pull --platform # linux/` from one manifest list: docker.io/library/ubuntu:jammy-20260901.2 # (index sha256:b8b6ee6aa931ecd9d0d952abc34dc0e5f7c6a30c6bb71b079fe399fde0329c02) # and registry.fedoraproject.org/fedora:42 # (index sha256:e78cd1a688cd079c23864f289a89a49a3f4ad66d817864e325e1d058310ee95c). UBUNTU_STOCK_BASE_x86_64 := sha256:bf7f4568d95723d2148bb19c688526d1404ef3302ef024bc1513ad8f533d46c8 FEDORA_STOCK_BASE_x86_64 := sha256:1eea7f82474ec19ef359ee5a5896014df434cd44c0d6ba2b937ffbe0697dec56 UBUNTU_STOCK_BASE_aarch64 := sha256:80d8dd83c66bb85511514da010a61b49af58a6cd9ce4541f5e964acdcd7aea5b FEDORA_STOCK_BASE_aarch64 := sha256:36c733c50e07ae9840ae6982fbbc032ecb48f2f68f4316942fef9026bf78e0d5 UBUNTU_STOCK_BASE := $(UBUNTU_STOCK_BASE_$(RELEASE_ARCH)) FEDORA_STOCK_BASE := $(FEDORA_STOCK_BASE_$(RELEASE_ARCH)) SHELLCHECK_SCRIPTS := scripts/build-release.sh scripts/extract_changelog.sh scripts/install.sh scripts/publish-origin.sh scripts/publish-release.sh all: install check-toolchain-env: @test -n "$(RUST_VERSION)" || { echo "error: Rust toolchain declaration mismatch: expected a channel in rust-toolchain.toml, actual missing or malformed" >&2; echo "repair: git restore rust-toolchain.toml" >&2; exit 1; } @if [ -n "$(AMBIENT_RUSTUP_TOOLCHAIN)" ] && [ "$(AMBIENT_RUSTUP_TOOLCHAIN)" != "$(RUST_VERSION)" ]; then \ echo "error: Rust toolchain mismatch: expected $(RUST_VERSION), RUSTUP_TOOLCHAIN is '$(AMBIENT_RUSTUP_TOOLCHAIN)'" >&2; \ echo "repair: unset RUSTUP_TOOLCHAIN" >&2; \ echo "repair: rustup toolchain install $(RUST_VERSION) --component rustfmt --component clippy" >&2; \ exit 1; \ fi establish-toolchain: check-toolchain-env @command -v $(RUSTUP) >/dev/null 2>&1 || { echo "error: Rust installer mismatch: expected rustup on PATH, actual not found" >&2; echo "repair: make bootstrap" >&2; exit 1; } $(RUSTUP) toolchain install $(RUST_VERSION) --profile minimal --component rustfmt --component clippy --target $(RUST_TARGET) rust-preflight: check-toolchain-env @actual=$$($(CARGO) --version >/dev/null 2>&1 && rustc --version --verbose | sed -n 's/^release: //p'); \ if [ "$$actual" != "$(RUST_VERSION)" ]; then \ echo "error: Rust toolchain mismatch: expected $(RUST_VERSION), actual $${actual:-unavailable}" >&2; \ echo "repair: rustup toolchain install $(RUST_VERSION) --component rustfmt --component clippy" >&2; \ exit 1; \ fi check-cargo-deny: @actual=$$(cargo deny --version 2>/dev/null || true); \ if [ -z "$$actual" ]; then \ echo "error: cargo-deny not found; expected 'cargo-deny $(CARGO_DENY_VERSION)'; run 'make install'" >&2; exit 1; \ elif [ "$$actual" != "cargo-deny $(CARGO_DENY_VERSION)" ]; then \ echo "error: cargo-deny version mismatch: expected 'cargo-deny $(CARGO_DENY_VERSION)', got '$$actual'; run 'make install'" >&2; exit 1; \ fi agent-setup: establish-toolchain rust-preflight @actual=$$(cargo deny --version 2>/dev/null || true); \ if [ "$$actual" != "cargo-deny $(CARGO_DENY_VERSION)" ]; then \ $(CARGO) install cargo-deny --version $(CARGO_DENY_VERSION) $(CARGO_LOCKED) || { actual=$$(cargo deny --version 2>/dev/null || echo unavailable); echo "error: cargo-deny installation mismatch: expected 'cargo-deny $(CARGO_DENY_VERSION)', actual '$$actual'" >&2; echo "repair: cargo install cargo-deny --version $(CARGO_DENY_VERSION) --locked" >&2; exit 1; }; \ fi @actual=$$(cargo deny --version 2>/dev/null || true); \ [ "$$actual" = "cargo-deny $(CARGO_DENY_VERSION)" ] || { echo "error: cargo-deny verification mismatch: expected 'cargo-deny $(CARGO_DENY_VERSION)', actual '$${actual:-unavailable}'" >&2; echo "repair: cargo install cargo-deny --version $(CARGO_DENY_VERSION) --locked" >&2; exit 1; } $(CARGO) metadata $(CARGO_LOCKED) --format-version 1 > /dev/null # Build both binaries before installing either, so a build failure (such as a # missing GTK development package) leaves the installed app untouched. install: agent-setup @$(CARGO) build --release $(CARGO_LOCKED) -p solstone-linux -p solstone-linux-window || { echo "error: build failed; nothing was installed and the installed app is unchanged" >&2; exit 1; } @$(CARGO) install --path crates/solstone-linux $(CARGO_LOCKED) || { echo "error: observer installation mismatch: expected $(CARGO_BIN_DIR)/$(APP), actual installation failed" >&2; echo "repair: cargo install --path crates/solstone-linux --locked" >&2; exit 1; } @$(CARGO) install --path crates/solstone-linux-window $(CARGO_LOCKED) @if systemctl --user is-active --quiet $(UNIT) 2>/dev/null; then \ systemctl --user restart $(UNIT) && echo "restarted the running solstone app on the new version"; \ fi bootstrap: @if ! command -v rustup >/dev/null 2>&1; then curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --no-modify-path; fi @$(MAKE) install format: rust-preflight $(CARGO) fmt # Re-vendor brand assets from the canonical brand source. CI verifies the # committed output (it does not run brand-sync) — run this locally when the # brand spec updates, then commit the diff. # # The scalable sources are copies. The hicolor PNG ladder has no committed # raster in the brand source: each size is rendered straight from the vendored # app SVG at its exact pixel size (never downsampled from one raster), which # needs rsvg-convert (librsvg). # apt: librsvg2-bin dnf: librsvg2-tools # # The status names are this theme's, not the brand source's — the mapping below # is the contract crates/solstone-linux/build.rs embeds by constant. BRAND_ICON_DIR = contrib/icons/hicolor BRAND_STATUS_SYNC = solstone-recording:mark solstone-paused:mark-paused solstone-syncing:mark-connecting solstone-error:mark-error solstone-attention:mark-attention solstone-offline:mark-offline BRAND_ICON_SIZES = 16 24 32 48 64 128 256 512 brand-sync: @test -n "$(BRAND_DIR)" || { echo "brand: BRAND_DIR is required — point it at your brand asset directory (BRAND_DIR=/path/to/brand make brand-sync)"; exit 1; } @test -d "$(BRAND_DIR)" || { echo "brand: BRAND_DIR=$(BRAND_DIR) not found"; exit 1; } @command -v rsvg-convert >/dev/null 2>&1 || { echo "brand: rsvg-convert (librsvg) not found — apt install librsvg2-bin, or dnf install librsvg2-tools"; exit 1; } @set -e; for pair in $(BRAND_STATUS_SYNC); do \ cp "$(BRAND_DIR)/$${pair#*:}.svg" "$(BRAND_ICON_DIR)/scalable/status/$${pair%%:*}.svg"; \ done cp "$(BRAND_DIR)/app-icon/app-icon-transparent.svg" $(BRAND_ICON_DIR)/scalable/apps/solstone-observer.svg @set -e; for size in $(BRAND_ICON_SIZES); do \ rsvg-convert -w $$size -h $$size $(BRAND_ICON_DIR)/scalable/apps/solstone-observer.svg \ -o $(BRAND_ICON_DIR)/$${size}x$${size}/apps/solstone-observer.png; \ done @echo "brand: synced from $(BRAND_DIR)" test: rust-preflight $(CARGO) test $(CARGO_LOCKED) -p solstone-linux -p solstone-linux-window check-observer-contract: rust-preflight @echo "Client-ingest contract bundle: 14.0.0" @echo "Client-ingest contract manifest SHA-256: 86d4358916a0303c29a8e61c6d1e48ef1939d0b5a2f042ae617958b9d0c1a5a8" @inventory=$$(CARGO_NET_OFFLINE=true $(CARGO) test $(CARGO_LOCKED) -p solstone-linux observer_contract_tests:: -- --list); \ printf '%s\n' "$$inventory"; \ printf '%s\n' "$$inventory" | grep -Fx 'observer_contract_tests::observer_contract_conformance: test' >/dev/null || { echo "error: observer contract test inventory mismatch" >&2; exit 1; }; \ actual=$$(printf '%s\n' "$$inventory" | grep -c '^observer_contract_tests::'); \ [ "$$actual" -eq 1 ] || { echo "error: observer contract test inventory mismatch: expected 1, actual $$actual" >&2; exit 1; } @output=$$(mktemp); \ trap 'rm -f "$$output"' EXIT; \ CARGO_NET_OFFLINE=true $(CARGO) test $(CARGO_LOCKED) -p solstone-linux observer_contract_tests::observer_contract_conformance -- --exact >"$$output" 2>&1 || { status=$$?; tail -50 "$$output"; exit $$status; }; \ tail -50 "$$output"; \ grep -Eq 'test result: ok\. 1 passed; 0 failed' "$$output" || { echo "error: observer contract named test did not execute" >&2; exit 1; } check-package-audit: rust-preflight @test -n "$(strip $(TAR))" || { echo "error: TAR is required" >&2; exit 1; } @test -n "$(strip $(DEB))" || { echo "error: DEB is required" >&2; exit 1; } @test -n "$(strip $(RPM))" || { echo "error: RPM is required" >&2; exit 1; } @test -n "$(strip $(EXPECTED_EXECUTABLE_SHA256))" || { echo "error: EXPECTED_EXECUTABLE_SHA256 is required" >&2; exit 1; } CARGO_NET_OFFLINE=true $(CARGO) run $(CARGO_LOCKED) -p rust-release-manifest -- audit-packages \ --tar "$(TAR)" \ --deb "$(DEB)" \ --rpm "$(RPM)" \ --expected-executable-sha256 "$(EXPECTED_EXECUTABLE_SHA256)" check-rust-release-manifest: rust-preflight @echo "Rust release manifest schema: 1" @echo "Rust release manifest schema SHA-256: 45e12a73062eededbe4d47b1101425eeca7be03f55dbc75ce087f477ae3a8a72" @echo "Rust release candidate ledger schema: 1" @echo "Rust release candidate ledger schema SHA-256: 258eb46b54469c0fe8d30d7ecd0e2d1d41af89fd6afc863c60154693c16da41b" @echo "Rust release candidate proof schema: 1" @echo "Rust release candidate proof schema SHA-256: 5f1242240e6b0dac176134dc8976f5ef2570453e03073af6ce3303c1a4552350" @manifest_set=$(if $(filter environment%,$(origin MANIFEST)),1,$(if $(findstring command line,$(origin MANIFEST)),1,0)); \ release_dir_set=$(if $(filter environment%,$(origin RELEASE_DIR)),1,$(if $(findstring command line,$(origin RELEASE_DIR)),1,0)); \ if { [ "$$manifest_set" -eq 1 ] && [ -z "$(strip $(MANIFEST))" ]; } || { [ "$$release_dir_set" -eq 1 ] && [ -z "$(strip $(RELEASE_DIR))" ]; }; then echo "error: release manifest selector empty" >&2; exit 1; \ elif [ "$$manifest_set" -eq 1 ] && [ "$$release_dir_set" -eq 1 ]; then echo "error: release manifest mode mismatch: expected one selector, actual two" >&2; exit 1; \ elif [ "$$manifest_set" -eq 1 ]; then \ CARGO_NET_OFFLINE=true $(CARGO) run $(CARGO_LOCKED) -p rust-release-manifest -- validate --manifest "$(MANIFEST)"; \ elif [ "$$release_dir_set" -eq 1 ]; then \ CARGO_NET_OFFLINE=true $(CARGO) run $(CARGO_LOCKED) -p rust-release-manifest -- validate --release-dir "$(RELEASE_DIR)"; \ else \ inventory=$$(CARGO_NET_OFFLINE=true $(CARGO) test $(CARGO_LOCKED) -p rust-release-manifest tests::rust_release_manifest_conformance -- --list); \ printf '%s\n' "$$inventory"; \ printf '%s\n' "$$inventory" | grep -Fx 'tests::rust_release_manifest_conformance: test' >/dev/null || { echo "error: release manifest test inventory mismatch" >&2; exit 1; }; \ actual=$$(printf '%s\n' "$$inventory" | grep -c '^tests::rust_release_manifest_conformance:'); \ [ "$$actual" -eq 1 ] || { echo "error: release manifest test inventory mismatch: expected 1, actual $$actual" >&2; exit 1; }; \ output=$$(mktemp); \ trap 'rm -f "$$output"' EXIT; \ CARGO_NET_OFFLINE=true $(CARGO) test $(CARGO_LOCKED) -p rust-release-manifest -- --skip transparency >"$$output" 2>&1 || { status=$$?; tail -50 "$$output"; exit $$status; }; \ tail -50 "$$output"; \ results=$$(grep '^test result:' "$$output" || true); \ [ -n "$$results" ] && ! printf '%s\n' "$$results" | grep -Ev '^test result: ok\..*0 failed' >/dev/null && ! grep -F 'FAILED' "$$output" >/dev/null || { echo "error: release manifest test suite did not pass" >&2; exit 1; }; \ fi shellcheck: shellcheck $(SHELLCHECK_SCRIPTS) ci: rust-preflight check-cargo-deny check-observer-contract check-rust-release-manifest check-audit-signed-packet @echo "Evidence class: host evidence (format, lint, tests, and offline dependency policy)." @echo "This gate does not run target-package validation or the release FLAC soak." $(CARGO) fmt --check $(CARGO) clippy $(CARGO_LOCKED) --all-targets -- -D warnings $(CARGO) test $(CARGO_LOCKED) -p solstone-linux -p solstone-linux-window $(MAKE) shellcheck cargo deny $(CARGO_LOCKED) --offline check licenses bans sources check-transparency-minisign: @test "$(TRANSPARENCY_ACTIVATED)" = 1 || { echo "the in-repo v1 transparency publisher is retired; v2 app records are produced outside this repository (see trust.solstone.app); TRANSPARENCY_ACTIVATED=1 does not by itself authorize a publish" >&2; exit 2; } @$(MAKE) rust-preflight @command -v minisign >/dev/null 2>&1 || { echo "error: minisign prerequisite mismatch: expected minisign on PATH, actual missing" >&2; echo "repair: sudo zypper install minisign" >&2; exit 1; } CARGO_NET_OFFLINE=true $(CARGO) test $(CARGO_LOCKED) -p rust-release-manifest transparency_tests::real_minisign_sign_verify_and_reject_tamper -- --exact --ignored check-audit-signed-packet: rust-preflight check-cargo-deny @command -v minisign >/dev/null 2>&1 || { echo "error: minisign prerequisite mismatch: expected minisign on PATH, actual missing" >&2; echo "repair: sudo zypper install minisign" >&2; exit 1; } CARGO_NET_OFFLINE=true $(CARGO) test $(CARGO_LOCKED) -p rust-release-manifest audit_tests::real_signed_packet_local_audit -- --exact --ignored publish-transparency: @test "$(TRANSPARENCY_ACTIVATED)" = 1 || { echo "the in-repo v1 transparency publisher is retired; v2 app records are produced outside this repository (see trust.solstone.app); TRANSPARENCY_ACTIVATED=1 does not by itself authorize a publish" >&2; exit 2; } @$(MAKE) rust-preflight @test -n "$(strip $(RELEASE_DIR))" || { echo "error: transparency release directory mismatch: expected RELEASE_DIR, actual missing" >&2; echo "repair: make publish-transparency RELEASE_DIR=" >&2; exit 1; } CARGO_NET_OFFLINE=true $(CARGO) run $(CARGO_LOCKED) -p rust-release-manifest -- transparency publish --release-dir "$(RELEASE_DIR)" # The release origin. This is the publish owners fetch from; it never contacts # GitHub and deliberately does not require the Rust toolchain on the staging and # dev proof lanes. The release lane adds the source binding and the repository's # own release-model validation, which do use cargo. publish-origin: @test -n "$(strip $(LANE))" || { echo "error: publish lane mismatch: expected LANE, actual missing" >&2; echo "repair: make publish-origin LANE=release RELEASE_DIR=dist/rust" >&2; exit 1; } @test -n "$(strip $(RELEASE_DIR))" || { echo "error: release directory mismatch: expected RELEASE_DIR, actual missing" >&2; echo "repair: make publish-origin LANE=release RELEASE_DIR=dist/rust" >&2; exit 1; } bash scripts/publish-origin.sh --lane "$(LANE)" --release-dir "$(RELEASE_DIR)" # The optional GitHub mirror. Run it after the origin publish; a failure here # leaves the origin publish intact, because the origin publish already happened. publish-release: rust-preflight @test -n "$(strip $(RELEASE_DIR))" || { echo "error: release directory mismatch: expected RELEASE_DIR, actual missing" >&2; echo "repair: make publish-release RELEASE_DIR=dist/rust" >&2; exit 1; } bash scripts/publish-release.sh "$(RELEASE_DIR)" resign-transparency-pointer: @test "$(TRANSPARENCY_ACTIVATED)" = 1 || { echo "the in-repo v1 transparency publisher is retired; v2 app records are produced outside this repository (see trust.solstone.app); TRANSPARENCY_ACTIVATED=1 does not by itself authorize a publish" >&2; exit 2; } @$(MAKE) rust-preflight CARGO_NET_OFFLINE=true $(CARGO) run $(CARGO_LOCKED) -p rust-release-manifest -- transparency resign-pointer audit: rust-preflight check-cargo-deny @test -n "$(strip $(BUNDLE))" || { echo "error: audit bundle mismatch: expected BUNDLE, actual missing" >&2; echo "repair: make audit BUNDLE= RECEIPT= PUBKEY= LOCATOR=" >&2; exit 1; } @test -n "$(strip $(RECEIPT))" || { echo "error: audit receipt mismatch: expected RECEIPT, actual missing" >&2; echo "repair: make audit BUNDLE= RECEIPT= PUBKEY= LOCATOR=" >&2; exit 1; } @test -n "$(strip $(PUBKEY))" || { echo "error: audit public key mismatch: expected PUBKEY, actual missing" >&2; echo "repair: make audit BUNDLE= RECEIPT= PUBKEY= LOCATOR=" >&2; exit 1; } @test -n "$(strip $(LOCATOR))" || { echo "error: audit locator mismatch: expected LOCATOR, actual missing" >&2; echo "repair: make audit BUNDLE= RECEIPT= PUBKEY= LOCATOR=" >&2; exit 1; } @CARGO_NET_OFFLINE=true $(CARGO) run $(CARGO_LOCKED) -p rust-release-manifest -- audit --bundle "$(BUNDLE)" --receipt "$(RECEIPT)" --pubkey "$(PUBKEY)" --locator "$(LOCATOR)" update-deps: rust-preflight $(CARGO) update $(UPDATE_DEPS_ARGS) install-service: install $(CARGO_BIN_DIR)/$(APP) install-service uninstall-service: rust-preflight $(CARGO) run $(CARGO_LOCKED) -p solstone-linux -- uninstall-service service-restart: systemctl --user restart $(UNIT) service-status: systemctl --user --no-pager status $(UNIT) service-logs: journalctl --user -u $(UNIT) -n 100 --no-pager -f versions: rust-preflight check-cargo-deny rustc --version --verbose $(CARGO) --version cargo deny --version @command -v $(APP) >/dev/null 2>&1 && $(APP) --version || true release: release-candidate release-images: @test -n "$(UBUNTU_STOCK_BASE)" && test -n "$(FEDORA_STOCK_BASE)" || { echo "error: release architecture mismatch: expected x86_64 or aarch64, actual '$(RELEASE_ARCH)'" >&2; exit 1; } @test "$(RELEASE_ARCH)" = "$$(uname -m)" || { echo "error: release architecture mismatch: expected native host architecture $$(uname -m), actual requested $(RELEASE_ARCH)" >&2; echo "repair: run make release-images on a native $(RELEASE_ARCH) host" >&2; exit 1; } @# Build and proof images share the same explicitly pinned stock bases. podman build --pull=never --no-cache --file packaging/Containerfile.tools --target ubuntu-tools --tag localhost/solstone-linux-build-ubuntu \ --build-arg "UBUNTU_STOCK_BASE=$(UBUNTU_STOCK_BASE)" --build-arg "FEDORA_STOCK_BASE=$(FEDORA_STOCK_BASE)" \ --build-arg "RUST_VERSION=$(RUST_VERSION)" --build-arg "CARGO_DEB_VERSION=$(CARGO_DEB_VERSION)" \ --build-arg "RELEASE_TARGET=$(RELEASE_ARCH)-unknown-linux-gnu" . podman build --pull=never --no-cache --file packaging/Containerfile.tools --target fedora-tools --tag localhost/solstone-linux-build-fedora \ --build-arg "UBUNTU_STOCK_BASE=$(UBUNTU_STOCK_BASE)" --build-arg "FEDORA_STOCK_BASE=$(FEDORA_STOCK_BASE)" \ --build-arg "RUST_VERSION=$(RUST_VERSION)" --build-arg "CARGO_GENERATE_RPM_VERSION=$(CARGO_GENERATE_RPM_VERSION)" \ --build-arg "RELEASE_TARGET=$(RELEASE_ARCH)-unknown-linux-gnu" . podman build --pull=never --no-cache --file packaging/Containerfile.tools --target ubuntu-proof --tag localhost/solstone-linux-proof-ubuntu \ --build-arg "UBUNTU_STOCK_BASE=$(UBUNTU_STOCK_BASE)" --build-arg "FEDORA_STOCK_BASE=$(FEDORA_STOCK_BASE)" . podman build --pull=never --no-cache --file packaging/Containerfile.tools --target fedora-proof --tag localhost/solstone-linux-proof-fedora \ --build-arg "UBUNTU_STOCK_BASE=$(UBUNTU_STOCK_BASE)" --build-arg "FEDORA_STOCK_BASE=$(FEDORA_STOCK_BASE)" . release-candidate: rust-preflight @test -n "$(strip $(EXPECTED_RELEASE_COMMIT))" || { echo "error: expected release commit mismatch: expected EXPECTED_RELEASE_COMMIT, actual missing" >&2; echo "repair: make release-candidate EXPECTED_RELEASE_COMMIT= ADVISORY_DESCRIPTOR=" >&2; exit 1; } @test -n "$(strip $(ADVISORY_DESCRIPTOR))" || { echo "error: advisory descriptor mismatch: expected ADVISORY_DESCRIPTOR, actual missing" >&2; echo "repair: make release-candidate EXPECTED_RELEASE_COMMIT=$(EXPECTED_RELEASE_COMMIT) ADVISORY_DESCRIPTOR=" >&2; exit 1; } CARGO_NET_OFFLINE=true $(CARGO) run $(CARGO_LOCKED) -p rust-release-manifest -- candidate create --expected-release-commit "$(EXPECTED_RELEASE_COMMIT)" --advisory-descriptor "$(ADVISORY_DESCRIPTOR)" --arch "$(RELEASE_ARCH)" release-candidate-prove: rust-preflight @test -n "$(strip $(VERSION))" || { echo "error: candidate version mismatch: expected VERSION, actual missing" >&2; echo "repair: make release-candidate-prove VERSION= ADVISORY_DESCRIPTOR=" >&2; exit 1; } @test -n "$(strip $(ADVISORY_DESCRIPTOR))" || { echo "error: advisory descriptor mismatch: expected ADVISORY_DESCRIPTOR, actual missing" >&2; echo "repair: make release-candidate-prove VERSION=$(VERSION) ADVISORY_DESCRIPTOR=" >&2; exit 1; } CARGO_NET_OFFLINE=true $(CARGO) run $(CARGO_LOCKED) -p rust-release-manifest -- candidate prove --version "$(VERSION)" --advisory-descriptor "$(ADVISORY_DESCRIPTOR)" release-candidate-recover: rust-preflight @test -n "$(strip $(VERSION))" || { echo "error: candidate version mismatch: expected VERSION, actual missing" >&2; echo "repair: make release-candidate-recover VERSION=" >&2; exit 1; } CARGO_NET_OFFLINE=true $(CARGO) run $(CARGO_LOCKED) -p rust-release-manifest -- candidate recover --version "$(VERSION)" sign-release-manifest: rust-preflight @test -n "$(strip $(RELEASE_DIR))" || { echo "error: release signature mismatch: expected RELEASE_DIR, actual missing" >&2; exit 1; } @test -n "$(strip $(RELEASE_MINISIGN_KEY))" || { echo "error: release signature mismatch: expected RELEASE_MINISIGN_KEY, actual missing" >&2; exit 1; } @test -n "$(strip $(RELEASE_MINISIGN_PASS))" || { echo "error: release signature mismatch: expected RELEASE_MINISIGN_PASS, actual missing" >&2; exit 1; } CARGO_NET_OFFLINE=true $(CARGO) run $(CARGO_LOCKED) -p rust-release-manifest -- sign-release-manifest --release-dir "$(RELEASE_DIR)" --secret-key "$(RELEASE_MINISIGN_KEY)" --passphrase-file "$(RELEASE_MINISIGN_PASS)" verify-release-signature: rust-preflight @test -n "$(strip $(RELEASE_DIR))" || { echo "error: release signature mismatch: expected RELEASE_DIR, actual missing" >&2; exit 1; } CARGO_NET_OFFLINE=true $(CARGO) run $(CARGO_LOCKED) -p rust-release-manifest -- verify-release-signature --release-dir "$(RELEASE_DIR)" clean: @echo "Cleaning build artifacts and cache files..." rm -rf build/ dist/ rm -rf target/ clean-install: clean install