| # 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/**' |
| - 'native/runtime-host-windows-task-launcher/**' |
| - '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-package-deployment.ts' |
| - 'packages/cli/src/runtime-host-peer-*' |
| - 'packages/cli/src/runtime-host-service-*' |
| - 'packages/cli/src/runtime-host-windows-*' |
| - '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.state-root-qualification.outputs.release_predecessor_version }} |
| release_predecessor_tarball_url: |
| description: Exact npm Nightly tarball qualified against this candidate |
| value: ${{ jobs.state-root-qualification.outputs.release_predecessor_tarball_url }} |
| release_predecessor_integrity: |
| description: npm SHA-512 integrity of the Nightly tarball qualified against this candidate |
| value: ${{ jobs.state-root-qualification.outputs.release_predecessor_integrity }} |
| workflow_dispatch: |
| |
| permissions: |
| contents: read |
| |
| concurrency: |
| group: cli-package-validation-${{ github.workflow }}-${{ github.ref }} |
| # Four build targets and four install environments fan out to about fourteen |
| # jobs per run, which makes this the largest holder of runner slots in the |
| # repository. Without this, a run a newer push had already invalidated was not |
| # cancelled — it ran to completion while the replacement queued behind it in |
| # this same group. Release callers arrive through `workflow_call`, where |
| # `github.event_name` is the caller's, so publication runs never cancel. |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} |
| |
| jobs: |
| 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: Build the Windows task launcher |
| if: matrix.target == 'win32-x64' |
| run: | |
| cargo fmt --manifest-path native/runtime-host-windows-task-launcher/Cargo.toml --check |
| node native/runtime-host-windows-task-launcher/build.mjs |
| - name: Report Rust build cache |
| shell: bash |
| run: | |
| set -o pipefail |
| kache report --format github | tee -a "$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 native artifacts |
| env: |
| NATIVE_TARGET: ${{ matrix.target }} |
| run: node -e "const fs=require('node:fs'),p=require('node:path'),t=process.env.NATIVE_TARGET,d=p.join('native-prebuilds',t); 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')); if(t==='win32-x64') fs.copyFileSync(p.join('native','runtime-host-windows-task-launcher','target','release','maka-runtime-host-task-launcher.exe'),p.join(d,'maka-runtime-host-task-launcher.exe'))" |
| - name: Upload the platform native artifacts |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 |
| with: |
| name: runtime-host-native-${{ matrix.target }} |
| path: native-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 Runtime Host native artifacts |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 |
| with: |
| pattern: runtime-host-native-* |
| path: ${{ runner.temp }}/runtime-host-native-prebuilds |
| merge-multiple: true |
| - name: Build the release tarball once |
| env: |
| MAKA_CLI_NIGHTLY_VERSION: ${{ inputs.package_version }} |
| MAKA_RUNTIME_HOST_NATIVE_PREBUILDS: ${{ runner.temp }}/runtime-host-native-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: |
| # The two supported Node versions share a machine and a tarball, so |
| # they are two runs of the same smoke rather than two runners. |
| - name: Linux x64 / Node 22.19 and 24 |
| runner: ubuntu-24.04 |
| node: '22.19.0' |
| second_node: '24' |
| platform: linux |
| arch: x64 |
| - name: Linux arm64 / Node 24 |
| runner: ubuntu-24.04-arm |
| node: '24' |
| second_node: '' |
| platform: linux |
| arch: arm64 |
| - name: macOS arm64 / Node 24 |
| runner: macos-15 |
| node: '24' |
| second_node: '' |
| platform: darwin |
| arch: arm64 |
| - name: Windows x64 / Node 24 |
| runner: windows-2025 |
| node: '24' |
| second_node: '' |
| 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 |
| # The two supported Node versions share a runner, so a failure on the |
| # first would otherwise mean the second never runs — which is the |
| # independence the matrix these replaced bought with `fail-fast: false`. |
| # The outcome is re-raised below, once both have had their turn. |
| - name: Validate the installed tarball |
| id: first-node-smoke |
| continue-on-error: true |
| run: node scripts/smoke-release-cli-package.mjs |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 |
| if: matrix.second_node != '' |
| with: |
| node-version: ${{ matrix.second_node }} |
| - name: Select the release npm toolchain for the second Node |
| if: matrix.second_node != '' |
| run: npm install --global --no-audit --no-fund "$(node -p 'require("./package.json").packageManager')" |
| - name: Validate the installed tarball on the second Node |
| if: matrix.second_node != '' |
| run: node scripts/smoke-release-cli-package.mjs |
| # `always()` because the second Node failing must not swallow the first. |
| - name: Report the first Node result |
| if: always() && steps.first-node-smoke.outcome != 'success' |
| env: |
| NODE_VERSION: ${{ matrix.node }} |
| run: | |
| echo "The installed tarball failed on Node $NODE_VERSION" |
| exit 1 |
| |
| state-root-qualification: |
| name: Qualify released State Roots |
| needs: build |
| runs-on: ubuntu-24.04 |
| timeout-minutes: 45 |
| outputs: |
| release_predecessor_version: ${{ steps.predecessor.outputs.version }} |
| release_predecessor_tarball_url: ${{ steps.predecessor.outputs.tarball_url }} |
| release_predecessor_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: 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 |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 |
| with: |
| artifact-ids: ${{ needs.build.outputs.release_candidate_artifact_id }} |
| path: packages/cli/release |
| # Resolved here rather than on `build`, which is the most expensive job in |
| # the workflow: a registry blip used to forfeit the tarball build and |
| # every job downstream of it, and this is the only job that reads the |
| # answer. It still costs no runner of its own, and the exported identity |
| # is now this job's output. |
| - name: Resolve the current npm Nightly as immutable evidence |
| id: predecessor |
| run: node scripts/release-cli-publication.mjs resolve-nightly-predecessor "$GITHUB_OUTPUT" |
| # Three runs of one script against one sandbox, not three runners. Two of |
| # these transitions are between tarballs that were published and frozen, |
| # so nothing in a pull request can change their outcome except the |
| # qualifier itself, and the third reads the candidate this run built — |
| # which is why it goes first. Sharing one `set -e` with the frozen pair |
| # meant a flaky `curl` on either of them left the only transition a pull |
| # request can influence unexecuted. |
| - name: Qualify the released State Root transitions |
| env: |
| MAKA_QUALIFICATION_BWRAP_USE_SUDO: '1' |
| PREDECESSOR_TARBALL_URL: ${{ steps.predecessor.outputs.tarball_url }} |
| PREDECESSOR_INTEGRITY: ${{ steps.predecessor.outputs.integrity }} |
| run: | |
| set -euo pipefail |
| evidence_root="$RUNNER_TEMP/released-state-root" |
| mkdir -p "$evidence_root" |
| |
| qualify() { |
| local slug="$1" source_url="$2" source_sha256="$3" source_integrity="$4" |
| local target_kind="$5" target_url="$6" target_sha256="$7" epoch_relation="$8" |
| local source_path target_path |
| echo "::group::Qualify $slug" |
| source_path="$evidence_root/$slug-source.tgz" |
| curl --fail --location --max-filesize 67108864 --proto '=https' --tlsv1.2 "$source_url" --output "$source_path" |
| 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/$slug-target.tgz" |
| curl --fail --location --max-filesize 67108864 --proto '=https' --tlsv1.2 "$target_url" --output "$target_path" |
| test -n "$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 |
| 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 "$epoch_relation" \ |
| | tee "$evidence_root/$slug-report.json" |
| echo "::endgroup::" |
| } |
| |
| qualify current-nightly-predecessor-to-candidate \ |
| "$PREDECESSOR_TARBALL_URL" '' "$PREDECESSOR_INTEGRITY" \ |
| candidate '' '' any |
| |
| qualify cross-epoch-74-to-76 \ |
| https://registry.npmjs.org/maka-agent/-/maka-agent-0.2.0-dev.3.20260830.tgz \ |
| 66b1ce9307c9d5c06eaa7a6cbf533d4747d02caf71c1776c69c7dbfa12c3f414 '' \ |
| published https://registry.npmjs.org/maka-agent/-/maka-agent-0.2.0-dev.4.20260830.tgz \ |
| b7d48adb466e16be7ffefbda3a0fcd833cc4108ea502b27778d0f4da680e1fc0 different |
| |
| qualify same-epoch-76 \ |
| https://registry.npmjs.org/maka-agent/-/maka-agent-0.2.0-dev.4.20260830.tgz \ |
| b7d48adb466e16be7ffefbda3a0fcd833cc4108ea502b27778d0f4da680e1fc0 '' \ |
| published https://registry.npmjs.org/maka-agent/-/maka-agent-0.2.0-dev.5.20260830.tgz \ |
| e7a682157c6899fc7f1be86a2d7b0bd0696195a5771d8cc97bd1389a5b74989f same |
| # The three transitions used to be three matrix jobs, so one failing left |
| # the others to upload their own reports. Folded into one step they share |
| # a `set -e`, and `tee` has already written the failing transition's own |
| # output by then. `if-no-files-found` stays `error` on a green run, where |
| # an empty directory means a broken path; on a red one it drops to `warn`, |
| # because a `curl` that failed before any `tee` would otherwise add a |
| # second, unrelated red to a job that already reported the real one. |
| - name: Preserve the qualification reports |
| if: always() |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 |
| with: |
| name: released-state-root |
| path: ${{ runner.temp }}/released-state-root/*-report.json |
| if-no-files-found: ${{ job.status == 'success' && 'error' || 'warn' }} |
| retention-days: 7 |
| - name: Require the qualified Nightly predecessor to remain current |
| env: |
| PREDECESSOR_VERSION: ${{ steps.predecessor.outputs.version }} |
| PREDECESSOR_TARBALL_URL: ${{ steps.predecessor.outputs.tarball_url }} |
| PREDECESSOR_INTEGRITY: ${{ steps.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 |