# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. name: CLI package validation on: pull_request: paths: - '.github/workflows/cli-package-validation.yml' - '.github/workflows/npm-publication.yml' - '.github/workflows/runtime-host-peer-admission.yml' - 'deny.toml' - 'native/runtime-host-peer/**' - 'package-lock.json' - 'packages/cli/RUNTIME_HOST_PEER_*' - 'packages/cli/src/cli-core.ts' - 'packages/cli/src/runtime-host-cli.ts' - 'packages/cli/src/runtime-host-peer-*' - 'packages/cli/src/runtime-host-service-*' - 'packages/runtime-host/package.json' - 'packages/runtime-host/src/client/peer-client.ts' - 'packages/runtime-host/src/peer-mesh/**' - 'packages/runtime-host/src/server/peer-listener.ts' - 'packages/runtime-host/src/transport/peer-native.ts' - 'packages/storage/package.json' - 'packages/storage/src/file-lifetime-owner.ts' - 'packages/storage/src/native-file-lock.ts' - 'scripts/generate-runtime-host-peer-*' - 'scripts/release-cli-package.mjs' - 'scripts/qualify-released-cli-state-root.mjs' - 'scripts/qualify-released-cli-state-root.test.mjs' - 'scripts/released-cli-state-root-fixture.mjs' - 'scripts/smoke-release-cli-package.mjs' workflow_call: inputs: source_commit: description: Exact product release commit to build; defaults to the triggering commit required: false type: string default: '' package_version: description: Exact Product Nightly version; empty builds the checked-in formal version required: false type: string default: '' outputs: release_candidate_artifact_id: description: Immutable artifact produced by the build job value: ${{ jobs.build.outputs.release_candidate_artifact_id }} release_candidate_run_attempt: description: Workflow attempt that built the immutable artifact value: ${{ jobs.build.outputs.release_candidate_run_attempt }} release_predecessor_version: description: Exact npm Nightly version qualified against this candidate value: ${{ jobs.release-predecessor.outputs.version }} release_predecessor_tarball_url: description: Exact npm Nightly tarball qualified against this candidate value: ${{ jobs.release-predecessor.outputs.tarball_url }} release_predecessor_integrity: description: npm SHA-512 integrity of the Nightly tarball qualified against this candidate value: ${{ jobs.release-predecessor.outputs.integrity }} workflow_dispatch: permissions: contents: read concurrency: group: cli-package-validation-${{ github.workflow }}-${{ github.ref }} jobs: release-predecessor: name: Resolve immutable release predecessor runs-on: ubuntu-24.04 timeout-minutes: 45 outputs: version: ${{ steps.predecessor.outputs.version }} tarball_url: ${{ steps.predecessor.outputs.tarball_url }} integrity: ${{ steps.predecessor.outputs.integrity }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ inputs.source_commit || github.sha }} persist-credentials: false - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: '24' - name: Resolve the current npm Nightly as immutable evidence id: predecessor run: node scripts/release-cli-publication.mjs resolve-nightly-predecessor "$GITHUB_OUTPUT" peer-native: name: Build direct-peer addon (${{ matrix.target }}) runs-on: ${{ matrix.runner }} timeout-minutes: 45 strategy: fail-fast: false matrix: include: - runner: macos-15 target: darwin-arm64 rust_target: '' - runner: ubuntu-24.04-arm target: linux-arm64 rust_target: aarch64-unknown-linux-gnu.2.28 - runner: ubuntu-24.04 target: linux-x64 rust_target: x86_64-unknown-linux-gnu.2.28 - runner: windows-2025 target: win32-x64 rust_target: '' steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ inputs.source_commit || github.sha }} persist-credentials: false - name: Update stable Rust run: rustup update stable --no-self-update - id: rustc name: Resolve Rust cache version shell: bash run: | echo "version=$(rustc --version | cut -d ' ' -f 2)" >> "$GITHUB_OUTPUT" echo "revision=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" { echo "KACHE_CACHE_DIR=${{ runner.temp }}/kache" echo "KACHE_RUNTIME_DIR=${{ runner.temp }}/kache-runtime" echo "RUSTC_WRAPPER=kache" } >> "$GITHUB_ENV" - name: Install Kache uses: taiki-e/install-action@1ed6d7be6168f6c9046541087ff549b6bc581fdf # v2 with: tool: kache@0.16.0 - id: kache-cache name: Restore Rust build cache uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ${{ runner.temp }}/kache key: kache-runtime-host-peer-release-${{ matrix.target }}-v0.16.0-${{ runner.os }}-${{ runner.arch }}-rust-${{ steps.rustc.outputs.version }}-${{ steps.rustc.outputs.revision }} restore-keys: | kache-runtime-host-peer-release-${{ matrix.target }}-v0.16.0-${{ runner.os }}-${{ runner.arch }}-rust-${{ steps.rustc.outputs.version }}- - name: Install the Linux baseline linker if: startsWith(matrix.target, 'linux-') uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2 with: version: 0.16.0 - name: Install cargo-zigbuild if: startsWith(matrix.target, 'linux-') uses: taiki-e/install-action@1ed6d7be6168f6c9046541087ff549b6bc581fdf # v2 with: tool: cargo-zigbuild@0.23.2 - name: Build the release addon env: MAKA_RUNTIME_HOST_PEER_CARGO_SUBCOMMAND: ${{ matrix.rust_target && 'zigbuild' || '' }} MAKA_RUNTIME_HOST_PEER_CARGO_TARGET: ${{ matrix.rust_target }} run: node native/runtime-host-peer/build.mjs - name: Report Rust build cache shell: bash run: kache report --format github >> "$GITHUB_STEP_SUMMARY" - name: Save Rust build cache if: github.ref_name == github.event.repository.default_branch uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ${{ runner.temp }}/kache key: ${{ steps.kache-cache.outputs.cache-primary-key }} - name: Enforce the Linux glibc baseline if: startsWith(matrix.target, 'linux-') run: | node - <<'NODE' const { execFileSync } = require('node:child_process'); const output = execFileSync('readelf', [ '--version-info', 'native/runtime-host-peer/target/release/maka_runtime_host_peer.node', ], { encoding: 'utf8' }); const versions = [...output.matchAll(/GLIBC_(\d+)\.(\d+)/g)].map((match) => [ Number(match[1]), Number(match[2]), ]); const newer = versions.find(([major, minor]) => major > 2 || (major === 2 && minor > 28)); if (newer) throw new Error(`Direct-peer addon requires GLIBC_${newer.join('.')}`); NODE - name: Stage the platform addon env: PEER_TARGET: ${{ matrix.target }} run: node -e "const fs=require('node:fs'),p=require('node:path'); const d=p.join('peer-prebuilds',process.env.PEER_TARGET); fs.mkdirSync(d,{recursive:true}); fs.copyFileSync(p.join('native','runtime-host-peer','target','release','maka_runtime_host_peer.node'),p.join(d,'maka_runtime_host_peer.node'))" - name: Upload the platform addon uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: runtime-host-peer-${{ matrix.target }} path: peer-prebuilds if-no-files-found: error retention-days: 1 build: name: Build immutable tarball needs: peer-native runs-on: ubuntu-24.04 timeout-minutes: 45 outputs: release_candidate_artifact_id: ${{ steps.release-candidate.outputs.artifact-id }} release_candidate_run_attempt: ${{ github.run_attempt }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ inputs.source_commit || github.sha }} persist-credentials: false - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: '22.19.0' cache: npm - name: Select the release npm toolchain run: npm install --global --no-audit --no-fund "$(node -p 'require("./package.json").packageManager')" - name: Install cargo-deny uses: taiki-e/install-action@1ed6d7be6168f6c9046541087ff549b6bc581fdf # v2 with: tool: cargo-deny@0.20.2 - name: Download direct-peer addons uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: pattern: runtime-host-peer-* path: ${{ runner.temp }}/runtime-host-peer-prebuilds merge-multiple: true - name: Build the release tarball once env: MAKA_CLI_NIGHTLY_VERSION: ${{ inputs.package_version }} MAKA_RUNTIME_HOST_PEER_PREBUILDS: ${{ runner.temp }}/runtime-host-peer-prebuilds run: npm run release:cli:pack - name: Upload the immutable release candidate id: release-candidate uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: cli-release-candidate-${{ github.run_attempt }} path: | packages/cli/release/*.tgz packages/cli/release/*.tgz.sha256 packages/cli/release/*.tgz.files.json if-no-files-found: error retention-days: 7 smoke: name: Validate installed CLI ${{ matrix.name }} needs: build runs-on: ${{ matrix.runner }} timeout-minutes: 45 strategy: fail-fast: false matrix: include: - name: Linux x64 / Node 22.19 runner: ubuntu-24.04 node: '22.19.0' platform: linux arch: x64 - name: Linux x64 / Node 24 runner: ubuntu-24.04 node: '24' platform: linux arch: x64 - name: Linux arm64 / Node 24 runner: ubuntu-24.04-arm node: '24' platform: linux arch: arm64 - name: macOS arm64 / Node 24 runner: macos-15 node: '24' platform: darwin arch: arm64 - name: Windows x64 / Node 24 runner: windows-2025 node: '24' platform: win32 arch: x64 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ inputs.source_commit || github.sha }} persist-credentials: false - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{ matrix.node }} - name: Select the release npm toolchain run: npm install --global --no-audit --no-fund "$(node -p 'require("./package.json").packageManager')" - name: Assert the runner architecture env: EXPECTED_PLATFORM: ${{ matrix.platform }} EXPECTED_ARCH: ${{ matrix.arch }} run: | node -e "if (process.platform !== process.env.EXPECTED_PLATFORM || process.arch !== process.env.EXPECTED_ARCH) throw new Error('Expected ' + process.env.EXPECTED_PLATFORM + '/' + process.env.EXPECTED_ARCH + ', found ' + process.platform + '/' + process.arch)" - name: Download the release candidate uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: artifact-ids: ${{ needs.build.outputs.release_candidate_artifact_id }} path: packages/cli/release - name: Validate the installed tarball run: node scripts/smoke-release-cli-package.mjs state-root-qualification: name: Qualify released State Root (${{ matrix.name }}) needs: [build, release-predecessor] runs-on: ubuntu-24.04 timeout-minutes: 45 strategy: fail-fast: false matrix: include: - name: cross epoch 74 to 76 source_url: https://registry.npmjs.org/maka-agent/-/maka-agent-0.2.0-dev.3.20260830.tgz source_sha256: 66b1ce9307c9d5c06eaa7a6cbf533d4747d02caf71c1776c69c7dbfa12c3f414 target_kind: published target_url: https://registry.npmjs.org/maka-agent/-/maka-agent-0.2.0-dev.4.20260830.tgz target_sha256: b7d48adb466e16be7ffefbda3a0fcd833cc4108ea502b27778d0f4da680e1fc0 epoch_relation: different - name: same epoch 76 source_url: https://registry.npmjs.org/maka-agent/-/maka-agent-0.2.0-dev.4.20260830.tgz source_sha256: b7d48adb466e16be7ffefbda3a0fcd833cc4108ea502b27778d0f4da680e1fc0 target_kind: published target_url: https://registry.npmjs.org/maka-agent/-/maka-agent-0.2.0-dev.5.20260830.tgz target_sha256: e7a682157c6899fc7f1be86a2d7b0bd0696195a5771d8cc97bd1389a5b74989f epoch_relation: same - name: current Nightly predecessor to candidate source_url: ${{ needs.release-predecessor.outputs.tarball_url }} source_sha256: '' source_integrity: ${{ needs.release-predecessor.outputs.integrity }} target_kind: candidate target_url: '' target_sha256: '' epoch_relation: any steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ inputs.source_commit || github.sha }} persist-credentials: false - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: '24' - name: Select the release npm toolchain run: npm install --global --no-audit --no-fund "$(node -p 'require("./package.json").packageManager')" - name: Require the account-isolation sandbox run: | sudo apt-get update sudo apt-get install --yes bubblewrap bwrap --version - name: Download the release candidate if: matrix.target_kind == 'candidate' uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: artifact-ids: ${{ needs.build.outputs.release_candidate_artifact_id }} path: packages/cli/release - name: Prepare exact source and target artifacts env: SOURCE_URL: ${{ matrix.source_url }} SOURCE_SHA256: ${{ matrix.source_sha256 }} SOURCE_INTEGRITY: ${{ matrix.source_integrity }} TARGET_KIND: ${{ matrix.target_kind }} TARGET_URL: ${{ matrix.target_url }} TARGET_SHA256: ${{ matrix.target_sha256 }} run: | set -euo pipefail evidence_root="$RUNNER_TEMP/released-state-root" mkdir -p "$evidence_root" source_path="$evidence_root/source.tgz" curl --fail --location --max-filesize 67108864 --proto '=https' --tlsv1.2 "$SOURCE_URL" --output "$source_path" source_sha256="$SOURCE_SHA256" if [[ -n "$SOURCE_INTEGRITY" ]]; then node - "$source_path" "$SOURCE_INTEGRITY" <<'NODE' const { createHash } = require('node:crypto'); const { readFileSync } = require('node:fs'); const bytes = readFileSync(process.argv[2]); const actual = `sha512-${createHash('sha512').update(bytes).digest('base64')}`; if (actual !== process.argv[3]) throw new Error('Source tarball integrity mismatch'); NODE source_sha256="$(sha256sum "$source_path" | cut -d ' ' -f 1)" else test -n "$source_sha256" fi if [[ "$TARGET_KIND" == 'published' ]]; then target_path="$evidence_root/target.tgz" curl --fail --location --max-filesize 67108864 --proto '=https' --tlsv1.2 "$TARGET_URL" --output "$target_path" target_sha256="$TARGET_SHA256" else target_path="$(find packages/cli/release -maxdepth 1 -name '*.tgz' -print -quit)" test -n "$target_path" target_path="$(realpath "$target_path")" target_sha256="$(sha256sum "$target_path" | cut -d ' ' -f 1)" fi { echo "SOURCE_PATH=$source_path" echo "SOURCE_SHA256=$source_sha256" echo "TARGET_PATH=$target_path" echo "TARGET_SHA256=$target_sha256" } >> "$GITHUB_ENV" - name: Qualify the released State Root transition env: EXPECTED_EPOCH_RELATION: ${{ matrix.epoch_relation }} MAKA_QUALIFICATION_BWRAP_USE_SUDO: '1' run: | set -o pipefail npm run --silent release:cli:qualify-state-root -- \ --source "$SOURCE_PATH" \ --source-sha256 "$SOURCE_SHA256" \ --target "$TARGET_PATH" \ --target-sha256 "$TARGET_SHA256" \ --expect-epoch-relation "$EXPECTED_EPOCH_RELATION" \ | tee "$RUNNER_TEMP/released-state-root-report.json" - name: Preserve the qualification report uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: released-state-root-${{ strategy.job-index }} path: ${{ runner.temp }}/released-state-root-report.json if-no-files-found: error retention-days: 7 - name: Require the qualified Nightly predecessor to remain current if: matrix.target_kind == 'candidate' env: PREDECESSOR_VERSION: ${{ needs.release-predecessor.outputs.version }} PREDECESSOR_TARBALL_URL: ${{ needs.release-predecessor.outputs.tarball_url }} PREDECESSOR_INTEGRITY: ${{ needs.release-predecessor.outputs.integrity }} run: | node scripts/release-cli-publication.mjs assert-nightly-predecessor \ "$PREDECESSOR_VERSION" \ "$PREDECESSOR_TARBALL_URL" \ "$PREDECESSOR_INTEGRITY" eval: name: Validate installed CLI Eval if: github.event_name != 'pull_request' needs: build runs-on: ubuntu-24.04 timeout-minutes: 45 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ inputs.source_commit || github.sha }} persist-credentials: false - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: '24' - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: '3.12' - name: Select the release npm toolchain run: npm install --global --no-audit --no-fund "$(node -p 'require("./package.json").packageManager')" - name: Install pinned Eval frameworks run: | python -m venv "$RUNNER_TEMP/maka-harbor" "$RUNNER_TEMP/maka-harbor/bin/python" -m pip install --disable-pip-version-check 'harbor==0.20.0' python -m venv "$RUNNER_TEMP/maka-pier" "$RUNNER_TEMP/maka-pier/bin/python" -m pip install --disable-pip-version-check 'datacurve-pier==0.3.0' echo "MAKA_RELEASE_HARBOR_PYTHON=$RUNNER_TEMP/maka-harbor/bin/python" >> "$GITHUB_ENV" echo "MAKA_RELEASE_PIER_PYTHON=$RUNNER_TEMP/maka-pier/bin/python" >> "$GITHUB_ENV" - name: Download the release candidate uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: artifact-ids: ${{ needs.build.outputs.release_candidate_artifact_id }} path: packages/cli/release - name: Validate real Harbor and Pier cells run: npm run release:cli:eval