| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254 |
- name: Build single-exe
- # Native builds for the release targets; see
- # .agents/notes/implemented/architecture/2026-07-10-single-file-executable-sdk-runtime-distribution.md.
- # A full target run retains one SDK wheel and three runtime wheels; subset
- # dispatch retains the SDK wheel and selected runtime wheels. Bare executables
- # and source closures are test inputs. Run manually or label a PR
- # `build-exe` (remove and reapply to rerun). Checkout uses the triggering ref,
- # so dispatch needs no separate ref input.
- on:
- workflow_dispatch:
- inputs:
- targets:
- description: >-
- Comma-separated pkg targets to build. Any subset of:
- node24-linux-x64, node24-linux-arm64, node24-macos-arm64.
- Empty builds all three.
- type: string
- required: false
- default: ''
- pull_request:
- types: [labeled]
- concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
- permissions:
- contents: read
- jobs:
- # Job-level conditions cannot inspect `matrix`, so validate target names and
- # construct the matrix before the dependent jobs.
- plan:
- name: plan targets
- if: github.event_name == 'workflow_dispatch' || github.event.label.name == 'build-exe'
- runs-on: ubuntu-latest
- timeout-minutes: 5
- outputs:
- matrix: ${{ steps.plan.outputs.matrix }}
- version: ${{ steps.version.outputs.version }}
- steps:
- - uses: actions/checkout@v6
- - name: Resolve repository version
- id: version
- run: |
- set -euo pipefail
- version="$(jq -r '.version // empty' package.json)"
- [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || {
- echo "::error::package.json version must be stable X.Y.Z, got '$version'"
- exit 1
- }
- echo "version=$version" >> "$GITHUB_OUTPUT"
- - name: Compute matrix from targets input
- id: plan
- env:
- # Label runs and blank dispatch inputs build all targets.
- TARGETS: ${{ inputs.targets || 'node24-linux-x64,node24-linux-arm64,node24-macos-arm64' }}
- run: |
- set -euo pipefail
- matrix='[]'
- IFS=',' read -r -a targets <<< "$TARGETS"
- for raw in "${targets[@]}"; do
- t="$(echo "$raw" | xargs)" # trim surrounding whitespace
- [ -z "$t" ] && continue
- # Native-only: hosted arm64 Linux uses ubuntu-24.04-arm, while
- # macos-latest is Apple Silicon.
- case "$t" in
- node24-linux-x64) runner=ubuntu-latest ;;
- node24-linux-arm64) runner=ubuntu-24.04-arm ;;
- node24-macos-arm64) runner=macos-latest ;;
- *)
- echo "::error::Unknown target '$t'. Supported: node24-linux-x64, node24-linux-arm64, node24-macos-arm64."
- exit 1
- ;;
- esac
- matrix="$(jq -c --arg target "$t" --arg runner "$runner" '. + [{target: $target, runner: $runner}]' <<< "$matrix")"
- done
- if [ "$matrix" = '[]' ]; then
- echo "::error::The targets input selected nothing to build."
- exit 1
- fi
- echo "Matrix: $matrix"
- echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
- sdk-wheel:
- needs: plan
- name: deepseek_harness-${{ needs.plan.outputs.version }}-py3-none-any.whl
- runs-on: ubuntu-latest
- timeout-minutes: 5
- steps:
- - uses: actions/checkout@v6
- - uses: actions/setup-python@v6
- with:
- python-version: '3.10'
- - name: Install Python build tooling
- run: python -m pip install uv==0.11.23
- - name: Build release-shaped SDK wheel
- run: >-
- python scripts/build-python-release.py
- --package sdk
- --output-dir dist-python
- - uses: actions/upload-artifact@v6
- with:
- name: deepseek_harness-${{ needs.plan.outputs.version }}-py3-none-any.whl
- path: dist-python/deepseek_harness-${{ needs.plan.outputs.version }}-py3-none-any.whl
- if-no-files-found: error
- build:
- needs: [plan, sdk-wheel]
- name: ${{ matrix.target }}
- runs-on: ${{ matrix.runner }}
- timeout-minutes: 45
- strategy:
- fail-fast: false
- matrix:
- include: ${{ fromJSON(needs.plan.outputs.matrix) }}
- steps:
- - uses: actions/checkout@v6
- - uses: actions/setup-node@v6
- with:
- node-version: 24
- - uses: actions/setup-python@v6
- with:
- python-version: '3.10'
- - name: Install Python build tooling
- run: python -m pip install uv==0.11.23
- - name: Enable corepack (pnpm)
- run: corepack enable
- - name: Resolve pnpm store path
- id: pnpm-store
- run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
- # Linux architectures share runner.os, so the cache key includes arch.
- - uses: actions/cache@v4
- with:
- path: ${{ steps.pnpm-store.outputs.path }}
- key: ${{ runner.os }}-${{ runner.arch }}-node-24-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
- restore-keys: |
- ${{ runner.os }}-${{ runner.arch }}-node-24-pnpm-
- # Cache pkg's target Node binary; lockfile changes roll the
- # exact key while the restore prefix can seed its replacement.
- - uses: actions/cache@v4
- with:
- path: ~/.pkg-cache
- key: pkg-fetch-${{ matrix.target }}-${{ hashFiles('pnpm-lock.yaml') }}
- restore-keys: |
- pkg-fetch-${{ matrix.target }}-
- - name: Install (immutable)
- run: pnpm install --frozen-lockfile
- - name: Build single-exe
- run: pnpm exec tsx scripts/build-exe-for-python-sdk.ts --targets=${{ matrix.target }}
- - name: Resolve platform outputs
- id: runtime
- env:
- TARGET: ${{ matrix.target }}
- VERSION: ${{ needs.plan.outputs.version }}
- run: |
- set -euo pipefail
- platform="${TARGET#node24-}"
- exe="$PWD/dist-exe/dsh-jsonrpc-agent-pkg-$platform"
- [ -x "$exe" ] || { echo "::error::$exe missing or not executable"; exit 1; }
- case "$platform" in
- linux-x64) wheel=deepseek_harness_runtime_bin-$VERSION-py3-none-manylinux_2_28_x86_64.whl ;;
- linux-arm64) wheel=deepseek_harness_runtime_bin-$VERSION-py3-none-manylinux_2_28_aarch64.whl ;;
- macos-arm64) wheel=deepseek_harness_runtime_bin-$VERSION-py3-none-macosx_11_0_arm64.whl ;;
- *) echo "::error::Unsupported runtime platform $platform"; exit 1 ;;
- esac
- echo "platform=$platform" >> "$GITHUB_OUTPUT"
- echo "exe=$exe" >> "$GITHUB_OUTPUT"
- echo "wheel=$wheel" >> "$GITHUB_OUTPUT"
- - name: Full-turn SDK, executable snapshot, and direct-binary smoke
- run: >-
- uv run --python 3.10 --group test --project python/sdk
- python scripts/smoke-python-runtime.py
- --scenario all
- --exe "${{ steps.runtime.outputs.exe }}"
- - name: Build release-shaped runtime wheel
- run: >-
- python scripts/build-python-release.py
- --package runtime
- --platform "${{ steps.runtime.outputs.platform }}"
- --runtime-exe "${{ steps.runtime.outputs.exe }}"
- --output-dir dist-python
- - uses: actions/download-artifact@v8
- with:
- name: deepseek_harness-${{ needs.plan.outputs.version }}-py3-none-any.whl
- path: dist-python
- - name: Install only the SDK into a clean venv and run zero-config
- env:
- VERSION: ${{ needs.plan.outputs.version }}
- run: |
- set -euo pipefail
- python -m venv "$RUNNER_TEMP/dsh-sdk-smoke"
- "$RUNNER_TEMP/dsh-sdk-smoke/bin/python" -m pip install \
- --find-links dist-python \
- deepseek-harness=="$VERSION"
- "$RUNNER_TEMP/dsh-sdk-smoke/bin/python" scripts/smoke-python-runtime.py \
- --scenario sdk-default
- - name: Check Linux GLIBC requirements
- if: runner.os == 'Linux'
- run: |
- set -euo pipefail
- readelf --version-info "${{ steps.runtime.outputs.exe }}" | tee glibc-versions.txt
- maximum="$(sed -n 's/.*Name: GLIBC_\([0-9.]*\).*/\1/p' glibc-versions.txt | sort -V | tail -1)"
- [ -n "$maximum" ] || { echo "::error::No GLIBC requirements found"; exit 1; }
- dpkg --compare-versions "$maximum" le 2.28 || {
- echo "::error::Executable requires GLIBC_$maximum but wheel claims manylinux_2_28"
- exit 1
- }
- - name: Run wheel in a manylinux 2.28 container
- if: runner.os == 'Linux'
- env:
- RUNNER_ARCH: ${{ runner.arch }}
- VERSION: ${{ needs.plan.outputs.version }}
- run: |
- set -euo pipefail
- case "$RUNNER_ARCH" in
- X64) image=quay.io/pypa/manylinux_2_28_x86_64 ;;
- ARM64) image=quay.io/pypa/manylinux_2_28_aarch64 ;;
- *) echo "::error::Unsupported Linux runner architecture $RUNNER_ARCH"; exit 1 ;;
- esac
- docker run --rm -e VERSION -v "$PWD:/work" -w /work "$image" bash -euxo pipefail -c '
- /opt/python/cp310-cp310/bin/python -m venv /tmp/dsh-sdk
- /tmp/dsh-sdk/bin/python -m pip install --find-links /work/dist-python deepseek-harness=="$VERSION"
- /tmp/dsh-sdk/bin/python /work/scripts/smoke-python-runtime.py --scenario sdk-default
- '
- - uses: actions/upload-artifact@v6
- with:
- name: ${{ steps.runtime.outputs.wheel }}
- path: dist-python/${{ steps.runtime.outputs.wheel }}
- if-no-files-found: error
|