name: PM Bundle # Stages the self-contained agent payload for each (os, arch) target with # `hermes pm bundle`: repo snapshot + tool store + facts + a relocatable # venv built on the staged python-build-standalone interpreter. Each # payload is smoke-tested on its native runner — the staged interpreter # boots hermes_cli out of the payload with no network and no PYTHONPATH — # as a pre-merge check for the pm tooling itself. # # Staging is native per target (no cross-target wheel tables anywhere): # the runner IS the target. pm's arch guard fails the job if any staged # mismatches, before anything ships. on: workflow_dispatch: inputs: ref: description: 'Git ref to bundle (default: the triggering commit, github.sha)' required: false type: string workflow_call: inputs: release: description: 'Stable-release candidate run (ignored by the jobs; uniform callable surface).' required: false type: boolean default: false ref: description: 'Git ref to bundle (default: the triggering commit, github.sha)' required: false type: string default: '' pull_request: paths: - 'pm/**' - '!pm/**/*.md' - 'scripts/bundles/**' - 'scripts/ci/desktop_build_cache.py' - '.github/actions/desktop-build-cache/**' - 'scripts/windows-build-deps.ps1' - 'scripts/build/windows-deps.ps1' - '.github/actions/setup-windows-build-deps/**' - 'uv.lock' - 'pyproject.toml' - '.github/workflows/pm-bundle.yml' permissions: contents: read # A workflow_call run (stable release) is never cancelled: its group uses # github.run_id so a parent rerun cannot kill this child mid-flight. concurrency: group: pm-bundle-${{ inputs.release == true && github.run_id || github.ref }} cancel-in-progress: ${{ inputs.release != true }} jobs: bundle: name: bundle ${{ matrix.target.label }} runs-on: ${{ matrix.target.runner }} # Leave time for setup, cache saves and smoke tests around the wheel build. timeout-minutes: 180 strategy: fail-fast: false matrix: target: # TODO(ethie): restore the linux legs once the ubuntu-latest-32-* # larger runners are re-imaged to Ubuntu 24.04. They currently boot # ubuntu-22.04 (glibc 2.35), and the prebuilt llama-server staged by # the native payload requires GLIBC_2.38, so verification fails. # - label: linux-x64 # runner: ubuntu-latest-32-core # - label: linux-arm64 # runner: ubuntu-latest-32-arm-core - label: darwin-arm64 runner: macos-15 - label: darwin-x64 runner: macos-15-intel - label: win32-x64 runner: windows-latest-32-core - label: win32-arm64 runner: windows-latest-32-arm-core steps: - uses: actions/checkout@f548e57e544e1ff5a4c46bf1e1b8685f8e4a348a # v7.0.1 with: # Default to the exact candidate commit (github.sha); inputs.ref # remains an escape hatch for a focused manual reproduction. ref: ${{ inputs.ref || github.sha }} fetch-tags: true # The bootstrap scripts need Python >= 3.11 (tomllib). Larger runners # (ubuntu-latest-32-*) are provisioned with ubuntu-22.04 images whose # system python3 is 3.10, so pin the bootstrap interpreter instead of # trusting the image. - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.13" - name: Restore native dependency candidates id: native-cache uses: ./.github/actions/desktop-build-cache with: phase: restore source: ${{ github.workspace }} work: ${{ runner.temp }}/payload-job cache: ${{ runner.temp }}/payload-inputs producer: payload-test - name: Prepare native payload dependencies id: prepare shell: bash env: PAYLOAD_WORK: ${{ runner.temp }}/payload-job PAYLOAD_CACHE: ${{ runner.temp }}/payload-inputs PYTHONUTF8: '1' run: | if [ "$RUNNER_OS" = Windows ]; then bootstrap=python; else bootstrap=python3; fi "$bootstrap" -S -B scripts/bundles/native_build.py --prepare-only \ --source "$GITHUB_WORKSPACE" --work "$PAYLOAD_WORK" --cache "$PAYLOAD_CACHE" \ --out "$GITHUB_WORKSPACE/build/agent-payload" --ref HEAD # PRs can restore candidates but never gain a cache-writing lane here. - name: Save native dependency candidates before assembly if: ${{ !cancelled() && steps.prepare.outcome == 'success' && github.event_name != 'pull_request' && github.ref == 'refs/heads/main' && (inputs.ref == '' || inputs.ref == github.sha) }} uses: ./.github/actions/desktop-build-cache with: phase: save source: ${{ github.workspace }} work: ${{ runner.temp }}/payload-job cache: ${{ runner.temp }}/payload-inputs producer: payload-test key: ${{ steps.native-cache.outputs.cache-key }} - name: Assemble the prepared payload without dependency acquisition shell: bash run: | if [ "$RUNNER_OS" = Windows ]; then bootstrap=python; else bootstrap=python3; fi "$bootstrap" -S -B scripts/bundles/native_build.py \ --prepared "$GITHUB_WORKSPACE/build/agent-payload.prepared.json" # The generated command must survive relocation without checkout env/cwd. - name: Smoke test the relocated payload (network disabled) if: startsWith(matrix.target.label, 'linux-') shell: bash run: sudo --preserve-env=PATH unshare --net bash scripts/smoke-payload.sh build/agent-payload - name: Smoke test the relocated payload (native launchers) if: ${{ !startsWith(matrix.target.label, 'linux-') }} shell: bash run: bash scripts/smoke-payload.sh build/agent-payload