Skip to content

Fix Windows release preflight for versioned installations #1213

Fix Windows release preflight for versioned installations

Fix Windows release preflight for versioned installations #1213

Workflow file for this run

name: Maple Agent CI
on:
push:
branches: [master]
pull_request:
# Run unprivileged validation for stacked PRs as well as master targets.
workflow_dispatch:
# PR code, including forks, executes only on fresh managed runners with a
# read-only token. No signing, release publication, environment, or deployment
# credentials belong in this workflow.
permissions:
contents: read
concurrency:
group: maple-agent-ci-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: 0
CARGO_PROFILE_DEV_DEBUG: 0
RUSTFLAGS: -D warnings
defaults:
run:
shell: bash
working-directory: apps/maple-agent
jobs:
changes:
name: Select Agent checks
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
actions: read
outputs:
agent: ${{ steps.classify.outputs.agent }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
persist-credentials: false
- name: Retain Agent changes since the last validated master run
id: master
if: ${{ github.repository == 'MaplePrivacyLabs/Maple' && github.ref == 'refs/heads/master' && github.event_name == 'push' }}
working-directory: .
env:
GH_TOKEN: ${{ github.token }}
run: python3 scripts/ci/agent_master_selection.py --lane ci >> "$GITHUB_OUTPUT"
- name: Classify changed paths
id: classify
working-directory: .
env:
BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }}
HEAD_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
MASTER_AGENT: ${{ steps.master.outputs.agent }}
run: |
set -euo pipefail
enable_agent() { echo 'agent=true' >> "$GITHUB_OUTPUT"; }
case "$GITHUB_EVENT_NAME" in
pull_request) separator='...' ;;
push)
# A later unrelated push must not consume Agent work cancelled
# before every workspace, provider and release check completed.
if [ "$GITHUB_REPOSITORY" = MaplePrivacyLabs/Maple ] && [ "$GITHUB_REF" = refs/heads/master ]; then
if [ "${MASTER_AGENT:-}" = false ]; then
echo 'agent=false' >> "$GITHUB_OUTPUT"
else
enable_agent
fi
exit 0
fi
separator='..'
if [[ "$BASE_SHA" =~ ^0+$ ]]; then
enable_agent
exit 0
fi
;;
*) enable_agent; exit 0 ;;
esac
if ! git cat-file -e "${BASE_SHA}^{commit}" || ! git cat-file -e "${HEAD_SHA}^{commit}"; then
enable_agent
exit 0
fi
changed_paths="$(mktemp)"
trap 'rm -f "$changed_paths"' EXIT
if ! git diff --no-renames --name-only -z "${BASE_SHA}${separator}${HEAD_SHA}" > "$changed_paths"; then
enable_agent
elif ! python3 scripts/ci/agent_change_detection.py < "$changed_paths" >> "$GITHUB_OUTPUT"; then
enable_agent
fi
desktop:
name: Agent (${{ matrix.os }})
needs: changes
# A failed selector must never silently omit validation.
if: ${{ always() && !cancelled() && (needs.changes.result != 'success' || needs.changes.outputs.agent != 'false') }}
runs-on: ${{ matrix.os }}
permissions:
contents: read
actions: read
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
os: [blacksmith-8vcpu-ubuntu-2404, macos-26, windows-latest]
env:
DEPENDENCY_CACHE_JOB: Agent (${{ matrix.os }})
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- name: Install pinned Nix environment
if: runner.os != 'Windows'
uses: DeterminateSystems/determinate-nix-action@96c5ee93a9dddf2830d31e1da6ec719077336c28 # v3.23.3
with:
source-tag: v3.23.3
github-token: ""
- name: Verify pinned Nix runtime
if: runner.os != 'Windows'
working-directory: .
run: bash scripts/ci/verify-nix-runtime.sh
- name: Install pinned Windows Rust
if: runner.os == 'Windows'
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable action, exact toolchain below
with:
# Matches the component's frozen rust-overlay input.
toolchain: 1.98.0
- name: Select Windows MSVC and SDK
if: runner.os == 'Windows'
uses: ilammy/msvc-dev-cmd@a102174a2b586eec2ea151a69e6fd14404a8ce7c # v1.13.0
with:
arch: x64
- name: Select native Windows Perl for the static crypto build
if: runner.os == 'Windows'
run: |
native_perl=/c/Strawberry/perl/bin/perl.exe
if [ ! -x "$native_perl" ]; then
echo 'Native Windows Perl is missing from the runner' >&2
exit 1
fi
"$native_perl" -MLocale::Maketext::Simple -MIPC::Cmd -e 'die "OpenSSL requires native Windows Perl\n" unless $^O eq "MSWin32"; print "$^V\n"'
# Pin the native executable across later Git Bash steps and Rust's
# process resolver instead of depending on their PATH ordering.
native_perl_windows="$(cygpath -m "$native_perl")"
printf 'OPENSSL_SRC_PERL=%s\n' "$native_perl_windows" >> "$GITHUB_ENV"
# openssl-src uses NASM if available and otherwise selects no-asm.
- name: Pin and smoke-test the selected Windows linker
if: runner.os == 'Windows'
run: python3 ../../scripts/ci/agent_windows.py
- name: Fingerprint the actual build environment
id: environment
run: |
if [ "$RUNNER_OS" = Windows ]; then
python3 ../../scripts/ci/agent_cache_scope.py --profile ci --output "$RUNNER_TEMP/agent-environment.json"
else
nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_cache_scope.py --profile ci --output "$RUNNER_TEMP/agent-environment.json"
fi
- name: Restore validated master dependency archive
id: dependency-archive
env:
GH_TOKEN: ${{ github.token }}
run: |
python3 ../../scripts/ci/agent_dependency_archive.py restore \
--environment "$RUNNER_TEMP/agent-environment.json" --target "target" \
--job-name "$DEPENDENCY_CACHE_JOB" --workflow agent-ci.yml \
--receipt "$RUNNER_TEMP/agent-dependency-receipt.json"
- name: Cache Agent Rust dependencies
id: rust-cache
if: ${{ steps.dependency-archive.outputs.restored != 'true' }}
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: apps/maple-agent -> target
add-rust-environment-hash-key: "true"
# Dependency hashes belong in the action's exact suffix, not its restore prefix.
key: maple-agent-ci-v2-${{ steps.environment.outputs.compatibility }}
cmd-format: python3 "${{ github.workspace }}/scripts/ci/agent_cache_command.py" {0}
save-if: "false"
- name: Check whether Cargo targets were restored
id: restored-target
run: |
python3 - <<'PYTHON'
import os
from pathlib import Path
target = Path(os.environ["GITHUB_WORKSPACE"]) / "apps/maple-agent/target"
present = any(
any(fingerprint.iterdir()) and any(
output.is_file() and output.stat().st_size > 0
for pattern in ("*.rlib", "*.rmeta")
for output in (fingerprint.parent / "deps").glob(pattern)
)
for fingerprint in target.rglob(".fingerprint") if fingerprint.is_dir()
)
with Path(os.environ["GITHUB_OUTPUT"]).open("a") as output:
output.write(f"present={str(present).lower()}\n")
PYTHON
- name: Restore compatible legacy Cargo targets on a cache miss
if: ${{ steps.dependency-archive.outputs.restored != 'true' && steps.restored-target.outputs.present != 'true' }}
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: apps/maple-agent -> target
# Read only within the existing actual compiler/native ABI prefix.
# Native bootstrap is read-only; durable artifacts own cache writes.
add-rust-environment-hash-key: "false"
key: maple-agent-ci-v2-${{ steps.environment.outputs.legacy_compatibility }}
cmd-format: python3 "${{ github.workspace }}/scripts/ci/agent_cache_command.py" {0}
save-if: "false"
- name: Record the current build environment after restore
run: |
mkdir -p target/ci-metrics
cp "$RUNNER_TEMP/agent-environment.json" target/ci-metrics/environment.json
- name: Verify Git metadata freshness on native workspace builds
if: runner.os != 'Linux'
run: |
if [ "$RUNNER_OS" = Windows ]; then
python3 scripts/test_git_metadata.py
else
nix develop --no-update-lock-file . -c python3 scripts/test_git_metadata.py
fi
- name: Verify the canonical SDK and proxy dependency graph
run: |
metadata="$RUNNER_TEMP/agent-workspace-metadata.json"
if [ "$RUNNER_OS" = Windows ]; then
cargo metadata --locked --format-version 1 > "$metadata"
else
# shellcheck disable=SC2016 # The child shell expands its own positional argument.
nix develop --no-update-lock-file . -c bash -c 'cargo metadata --locked --format-version 1 > "$1"' -- "$metadata"
fi
python3 ../../scripts/ci/verify-agent-rust-deps.py "$metadata"
if [ "$RUNNER_OS" = Windows ]; then
python3 scripts/verify-sqlcipher-build.py "$metadata"
else
nix develop --no-update-lock-file . -c python3 scripts/verify-sqlcipher-build.py "$metadata"
fi
- name: Format, lint, build, and test Linux feature matrix
if: runner.os == 'Linux'
run: nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_measure.py --phase linux-ci --target target -- just ci
- name: Build macOS workspace and test targets
if: runner.os == 'macOS'
run: nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_measure.py --phase macos-build --target target -- cargo build --workspace --all-targets --locked --timings
- name: Package and smoke macOS debug app
if: runner.os == 'macOS'
run: nix develop --no-update-lock-file . -c ./scripts/macos-debug-app.sh
- name: Test macOS workspace
if: runner.os == 'macOS'
run: nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_measure.py --phase macos-test --target target -- cargo test --workspace --locked --timings
- name: Build Windows workspace and test targets
if: runner.os == 'Windows'
run: python3 ../../scripts/ci/agent_measure.py --phase windows-build --target target -- cargo build --workspace --all-targets --locked --timings
- name: Test Windows workspace
if: runner.os == 'Windows'
run: python3 ../../scripts/ci/agent_measure.py --phase windows-test --target target -- cargo test --workspace --locked --timings
- name: Audit Agent dependencies
if: runner.os == 'Linux'
run: nix develop --no-update-lock-file . -c python3 scripts/audit-sqlcipher-dependencies.py
- name: Regenerate the signed SQLCipher source
if: runner.os == 'Linux'
run: |
metadata="$(mktemp)"
trap 'rm -f "$metadata"' EXIT
# shellcheck disable=SC2016 # The child shell expands its own positional argument.
nix develop --no-update-lock-file . -c bash -c 'cargo metadata --locked --format-version 1 > "$1"' -- "$metadata"
nix develop --no-update-lock-file . -c python3 dependencies/sqlcipher/verify_sqlcipher.py --metadata "$metadata"
- name: Prepare trusted dependency archive after validation
id: dependency-seed
if: ${{ success() && github.repository == 'MaplePrivacyLabs/Maple' && github.event_name == 'push' && github.ref == 'refs/heads/master' }}
run: |
python3 ../../scripts/ci/agent_dependency_archive.py pack \
--environment "$RUNNER_TEMP/agent-environment.json" --metadata "$RUNNER_TEMP/agent-workspace-metadata.json" \
--target "target" --job-name "$DEPENDENCY_CACHE_JOB" --workflow agent-ci.yml \
--output-dir "$RUNNER_TEMP/agent-dependency-seed" \
--receipt "$RUNNER_TEMP/agent-dependency-receipt.json"
- name: Upload durable dependency archive
if: ${{ success() && github.repository == 'MaplePrivacyLabs/Maple' && github.event_name == 'push' && github.ref == 'refs/heads/master' && steps.dependency-seed.outputs.needs-upload == 'true' }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: ${{ steps.dependency-seed.outputs.artifact-name }}
path: |
${{ runner.temp }}/agent-dependency-seed/manifest.json
${{ runner.temp }}/agent-dependency-seed/dependencies.tar.gz
if-no-files-found: error
compression-level: 0
overwrite: true
retention-days: 3
- name: Summarize phase timings and target size
if: always()
run: python3 ../../scripts/ci/agent_measure.py --summarize target
- name: Upload bounded CI measurements
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: maple-agent-ci-metrics-${{ runner.os }}-${{ runner.arch }}-${{ github.run_id }}-${{ github.run_attempt }}
path: apps/maple-agent/target/ci-metrics/
if-no-files-found: warn
retention-days: 7
provider:
name: Agent provider (${{ matrix.os }})
needs: changes
if: ${{ always() && !cancelled() && (needs.changes.result != 'success' || needs.changes.outputs.agent != 'false') }}
runs-on: ${{ matrix.os }}
permissions:
contents: read
actions: read
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
os: [blacksmith-8vcpu-ubuntu-2404, macos-26, windows-latest]
env:
DEPENDENCY_CACHE_JOB: Agent provider (${{ matrix.os }})
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- name: Install pinned Nix environment
if: runner.os != 'Windows'
uses: DeterminateSystems/determinate-nix-action@96c5ee93a9dddf2830d31e1da6ec719077336c28 # v3.23.3
with:
source-tag: v3.23.3
github-token: ""
- name: Verify pinned Nix runtime
if: runner.os != 'Windows'
working-directory: .
run: bash scripts/ci/verify-nix-runtime.sh
- name: Install pinned Windows Rust
if: runner.os == 'Windows'
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable action, exact toolchain below
with:
# Matches the component's frozen rust-overlay input.
toolchain: 1.98.0
- name: Select Windows MSVC and SDK
if: runner.os == 'Windows'
uses: ilammy/msvc-dev-cmd@a102174a2b586eec2ea151a69e6fd14404a8ce7c # v1.13.0
with:
arch: x64
- name: Select native Windows Perl for the static crypto build
if: runner.os == 'Windows'
run: |
native_perl=/c/Strawberry/perl/bin/perl.exe
if [ ! -x "$native_perl" ]; then
echo 'Native Windows Perl is missing from the runner' >&2
exit 1
fi
"$native_perl" -MLocale::Maketext::Simple -MIPC::Cmd -e 'die "OpenSSL requires native Windows Perl\n" unless $^O eq "MSWin32"; print "$^V\n"'
# Pin the native executable across later Git Bash steps and Rust's
# process resolver instead of depending on their PATH ordering.
native_perl_windows="$(cygpath -m "$native_perl")"
printf 'OPENSSL_SRC_PERL=%s\n' "$native_perl_windows" >> "$GITHUB_ENV"
# openssl-src uses NASM if available and otherwise selects no-asm.
- name: Pin and smoke-test the selected Windows linker
if: runner.os == 'Windows'
run: python3 ../../scripts/ci/agent_windows.py
- name: Select isolated provider paths
run: |
printf 'PROVIDER_WORK=%s/rusqlite-provider\nPROVIDER_TARGET=%s/rusqlite-provider-target\n' "$RUNNER_TEMP" "$RUNNER_TEMP" >> "$GITHUB_ENV"
- name: Prepare and verify the resolved upstream provider test graph
id: provider
run: |
metadata="$RUNNER_TEMP/provider-workspace-metadata.json"
if [ "$RUNNER_OS" = Windows ]; then
cargo metadata --locked --format-version 1 > "$metadata"
python3 scripts/test-rusqlite-provider.py "$metadata" --phase prepare --work-dir "$PROVIDER_WORK" --target-dir "$PROVIDER_TARGET" --github-output "$GITHUB_OUTPUT"
else
# shellcheck disable=SC2016
nix develop --no-update-lock-file . -c bash -c 'cargo metadata --locked --format-version 1 > "$1"' -- "$metadata"
nix develop --no-update-lock-file . -c python3 scripts/test-rusqlite-provider.py "$metadata" --phase prepare --work-dir "$PROVIDER_WORK" --target-dir "$PROVIDER_TARGET" --github-output "$GITHUB_OUTPUT"
fi
- name: Fingerprint the actual build environment
id: environment
run: |
if [ "$RUNNER_OS" = Windows ]; then
python3 ../../scripts/ci/agent_cache_scope.py --profile provider --cargo-config-dir "$PROVIDER_WORK" --output target/ci-metrics/environment.json
else
nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_cache_scope.py --profile provider --cargo-config-dir "$PROVIDER_WORK" --output target/ci-metrics/environment.json
fi
- name: Restore validated master dependency archive
id: dependency-archive
env:
GH_TOKEN: ${{ github.token }}
PROVIDER_DEPENDENCY_FINGERPRINT: ${{ steps.provider.outputs.dependency-fingerprint }}
run: |
python3 ../../scripts/ci/agent_dependency_archive.py restore \
--environment "target/ci-metrics/environment.json" --target "$PROVIDER_TARGET" \
--job-name "$DEPENDENCY_CACHE_JOB" --workflow agent-ci.yml \
--receipt "$RUNNER_TEMP/agent-dependency-receipt.json" \
--dependency-fingerprint "$PROVIDER_DEPENDENCY_FINGERPRINT"
- name: Restore only matching verified provider artifacts
id: provider-cache
if: ${{ steps.dependency-archive.outputs.restored != 'true' }}
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: ${{ env.PROVIDER_TARGET }}
key: maple-agent-provider-v2-${{ steps.environment.outputs.legacy_compatibility }}-${{ steps.provider.outputs.dependency-fingerprint }}
- name: Test released rusqlite against the static provider
run: |
metadata="$RUNNER_TEMP/provider-workspace-metadata.json"
if [ "$RUNNER_OS" = Windows ]; then
python3 ../../scripts/ci/agent_measure.py --phase provider-test --target "$PROVIDER_TARGET" -- python3 scripts/test-rusqlite-provider.py "$metadata" --phase run --work-dir "$PROVIDER_WORK" --target-dir "$PROVIDER_TARGET"
else
nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_measure.py --phase provider-test --target "$PROVIDER_TARGET" -- python3 scripts/test-rusqlite-provider.py "$metadata" --phase run --work-dir "$PROVIDER_WORK" --target-dir "$PROVIDER_TARGET"
fi
- name: Prepare trusted dependency archive after validation
id: dependency-seed
if: ${{ success() && github.repository == 'MaplePrivacyLabs/Maple' && github.event_name == 'push' && github.ref == 'refs/heads/master' }}
env:
PROVIDER_DEPENDENCY_FINGERPRINT: ${{ steps.provider.outputs.dependency-fingerprint }}
run: |
python3 ../../scripts/ci/agent_dependency_archive.py pack \
--environment "target/ci-metrics/environment.json" --metadata "$PROVIDER_WORK/maple-provider-metadata.json" \
--target "$PROVIDER_TARGET" --job-name "$DEPENDENCY_CACHE_JOB" --workflow agent-ci.yml \
--output-dir "$RUNNER_TEMP/agent-dependency-seed" \
--receipt "$RUNNER_TEMP/agent-dependency-receipt.json" \
--dependency-fingerprint "$PROVIDER_DEPENDENCY_FINGERPRINT"
- name: Upload durable dependency archive
if: ${{ success() && github.repository == 'MaplePrivacyLabs/Maple' && github.event_name == 'push' && github.ref == 'refs/heads/master' && steps.dependency-seed.outputs.needs-upload == 'true' }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: ${{ steps.dependency-seed.outputs.artifact-name }}
path: |
${{ runner.temp }}/agent-dependency-seed/manifest.json
${{ runner.temp }}/agent-dependency-seed/dependencies.tar.gz
if-no-files-found: error
compression-level: 0
overwrite: true
retention-days: 3
- name: Summarize phase timings and target size
if: always()
run: python3 ../../scripts/ci/agent_measure.py --summarize "$PROVIDER_TARGET"
- name: Upload bounded CI measurements
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: maple-agent-provider-metrics-${{ runner.os }}-${{ runner.arch }}-${{ github.run_id }}-${{ github.run_attempt }}
path: apps/maple-agent/target/ci-metrics/
if-no-files-found: warn
retention-days: 7
linux-release:
name: Agent Linux release binary
needs: changes
if: ${{ always() && !cancelled() && (needs.changes.result != 'success' || needs.changes.outputs.agent != 'false') }}
runs-on: blacksmith-8vcpu-ubuntu-2404
permissions:
contents: read
actions: read
timeout-minutes: 90
env:
DEPENDENCY_CACHE_JOB: Agent Linux release binary
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- name: Install pinned Nix environment
if: runner.os != 'Windows'
uses: DeterminateSystems/determinate-nix-action@96c5ee93a9dddf2830d31e1da6ec719077336c28 # v3.23.3
with:
source-tag: v3.23.3
github-token: ""
- name: Verify pinned Nix runtime
if: runner.os != 'Windows'
working-directory: .
run: bash scripts/ci/verify-nix-runtime.sh
- name: Fingerprint the actual build environment
id: environment
run: |
if [ "$RUNNER_OS" = Windows ]; then
python3 ../../scripts/ci/agent_cache_scope.py --profile release --output "$RUNNER_TEMP/agent-environment.json"
else
nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_cache_scope.py --profile release --output "$RUNNER_TEMP/agent-environment.json"
fi
- name: Restore validated master dependency archive
id: dependency-archive
env:
GH_TOKEN: ${{ github.token }}
run: |
python3 ../../scripts/ci/agent_dependency_archive.py restore \
--environment "$RUNNER_TEMP/agent-environment.json" --target "target" \
--job-name "$DEPENDENCY_CACHE_JOB" --workflow agent-ci.yml \
--receipt "$RUNNER_TEMP/agent-dependency-receipt.json"
- name: Cache Agent release dependencies
id: rust-cache
if: ${{ steps.dependency-archive.outputs.restored != 'true' }}
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: apps/maple-agent -> target
add-rust-environment-hash-key: "true"
# Dependency hashes belong in the action's exact suffix, not its restore prefix.
key: maple-agent-release-v2-${{ steps.environment.outputs.compatibility }}
cmd-format: python3 "${{ github.workspace }}/scripts/ci/agent_cache_command.py" {0}
save-if: "false"
- name: Check whether Cargo targets were restored
id: restored-target
run: |
python3 - <<'PYTHON'
import os
from pathlib import Path
target = Path(os.environ["GITHUB_WORKSPACE"]) / "apps/maple-agent/target"
present = any(
any(fingerprint.iterdir()) and any(
output.is_file() and output.stat().st_size > 0
for pattern in ("*.rlib", "*.rmeta")
for output in (fingerprint.parent / "deps").glob(pattern)
)
for fingerprint in target.rglob(".fingerprint") if fingerprint.is_dir()
)
with Path(os.environ["GITHUB_OUTPUT"]).open("a") as output:
output.write(f"present={str(present).lower()}\n")
PYTHON
- name: Restore compatible legacy Cargo targets on a cache miss
if: ${{ steps.dependency-archive.outputs.restored != 'true' && steps.restored-target.outputs.present != 'true' }}
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
workspaces: apps/maple-agent -> target
add-rust-environment-hash-key: "false"
key: maple-agent-release-v2-${{ steps.environment.outputs.legacy_compatibility }}
cmd-format: python3 "${{ github.workspace }}/scripts/ci/agent_cache_command.py" {0}
save-if: "false"
- name: Record the current build environment after restore
run: |
mkdir -p target/ci-metrics
cp "$RUNNER_TEMP/agent-environment.json" target/ci-metrics/environment.json
- name: Verify the canonical SDK and proxy dependency graph
run: |
metadata="$RUNNER_TEMP/agent-workspace-metadata.json"
if [ "$RUNNER_OS" = Windows ]; then
cargo metadata --locked --format-version 1 > "$metadata"
else
# shellcheck disable=SC2016 # The child shell expands its own positional argument.
nix develop --no-update-lock-file . -c bash -c 'cargo metadata --locked --format-version 1 > "$1"' -- "$metadata"
fi
python3 ../../scripts/ci/verify-agent-rust-deps.py "$metadata"
if [ "$RUNNER_OS" = Windows ]; then
python3 scripts/verify-sqlcipher-build.py "$metadata"
else
nix develop --no-update-lock-file . -c python3 scripts/verify-sqlcipher-build.py "$metadata"
fi
- name: Generate and validate Linux third-party notices
if: runner.os == 'Linux'
run: |
nix develop --no-update-lock-file . -c python3 scripts/generate-third-party-notices.py
nix develop --no-update-lock-file . -c python3 scripts/generate-third-party-notices.py --check
- name: Build Linux release binary
if: runner.os == 'Linux'
run: nix develop --no-update-lock-file . -c python3 ../../scripts/ci/agent_measure.py --phase linux-release --target target -- cargo build --release -p maple-agent-app --locked --timings
# This is an unsigned CI build, never a GitHub Release or an updater feed.
- name: Upload Linux CI binary
if: runner.os == 'Linux'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: maple-agent-linux-x86_64-ci-${{ github.run_id }}-${{ github.run_attempt }}
path: |
apps/maple-agent/target/release/maple-agent
apps/maple-agent/target/third-party-notices
if-no-files-found: error
retention-days: 5
- name: Prepare trusted dependency archive after validation
id: dependency-seed
if: ${{ success() && github.repository == 'MaplePrivacyLabs/Maple' && github.event_name == 'push' && github.ref == 'refs/heads/master' }}
run: |
python3 ../../scripts/ci/agent_dependency_archive.py pack \
--environment "$RUNNER_TEMP/agent-environment.json" --metadata "$RUNNER_TEMP/agent-workspace-metadata.json" \
--target "target" --job-name "$DEPENDENCY_CACHE_JOB" --workflow agent-ci.yml \
--output-dir "$RUNNER_TEMP/agent-dependency-seed" \
--receipt "$RUNNER_TEMP/agent-dependency-receipt.json"
- name: Upload durable dependency archive
if: ${{ success() && github.repository == 'MaplePrivacyLabs/Maple' && github.event_name == 'push' && github.ref == 'refs/heads/master' && steps.dependency-seed.outputs.needs-upload == 'true' }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: ${{ steps.dependency-seed.outputs.artifact-name }}
path: |
${{ runner.temp }}/agent-dependency-seed/manifest.json
${{ runner.temp }}/agent-dependency-seed/dependencies.tar.gz
if-no-files-found: error
compression-level: 0
overwrite: true
retention-days: 3
- name: Summarize phase timings and target size
if: always()
run: python3 ../../scripts/ci/agent_measure.py --summarize target
- name: Upload bounded CI measurements
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: maple-agent-release-metrics-${{ runner.os }}-${{ runner.arch }}-${{ github.run_id }}-${{ github.run_attempt }}
path: apps/maple-agent/target/ci-metrics/
if-no-files-found: warn
retention-days: 7
required:
name: Agent CI required jobs
needs: [changes, desktop, provider, linux-release]
if: ${{ always() && !cancelled() }}
runs-on: ubuntu-latest
timeout-minutes: 5
defaults:
run:
working-directory: .
steps:
- name: Require every selected platform and independent check
env:
SELECTOR_RESULT: ${{ needs.changes.result }}
AGENT_SELECTED: ${{ needs.changes.outputs.agent }}
DESKTOP_RESULT: ${{ needs.desktop.result }}
PROVIDER_RESULT: ${{ needs.provider.result }}
RELEASE_RESULT: ${{ needs.linux-release.result }}
run: |
set -euo pipefail
if [ "$SELECTOR_RESULT" = success ] && [ "$AGENT_SELECTED" = false ]; then
test "$DESKTOP_RESULT" = skipped
test "$PROVIDER_RESULT" = skipped
test "$RELEASE_RESULT" = skipped
else
test "$DESKTOP_RESULT" = success
test "$PROVIDER_RESULT" = success
test "$RELEASE_RESULT" = success
fi