Files
deepseek-harness/.github/workflows/build-exe-for-python-sdk.yml
T
imccyu 129c915498 build: split build:src and build:test, and schedule build in parallel
pnpm run build ran every step in one serial chain, including the two
noEmit aggregate programs (tests, scripts, benchmarks, website) that no
artifact step consumes, so the Host aggregate check alone delayed tsdown by
about 66 s.

- build:src (scripts/build.ts --scope src) builds every artifact in the
  existing order and writes the client build record; it replaces the
  removed --artifacts-only flag and the libraries CLI mode.
- build:test (--scope test) type-checks only the Host and Client aggregate
  programs with tsc -p, concurrently, without compiling references or
  checking prerequisites.
- build runs both as one dependency graph through run-gates' new
  runAggregate: each aggregate check starts once its face's packages are
  compiled, and Client compilation stays after the Host bundle that writes
  the Typert /remote declarations. DSH_GATE_CONCURRENCY=1 serializes it;
  DSH_GATE_FAIL_FAST=1 stops the remaining steps at the first failure.
  Passing steps print one PASS line (DSH_GATE_VERBOSE=1 shows their output).
- PR artifact builds (e2e, release, preview, Node 22 compatibility) and the
  Python builder (--build-src) use build:src; dev:web and dev:desktop use
  build:src. test:*, bench, typecheck and the required consumer/Windows
  lanes keep the complete build.
- lint, lint:fix, doc-typecheck and the Typert contracts gate prepare with
  host-libraries (Host package compilation + Host bundle) instead of
  build:lib:host; their own checks never read the aggregate root or the
  Desktop bundle.

Parallel and serial build produce byte-identical lib/, Web dist and client
build record; a test-only type error fails build and build:test while
build:src passes.

Refs #5852
2026-10-11 18:19:23 +08:00

513 lines
22 KiB
YAML

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 five runtime wheels; subset
# dispatch retains the SDK wheel and selected runtime wheels. Bare executables
# and source closures are test inputs. Run manually or call it from the Python
# release workflow. There is no `pull_request` trigger: a label trigger would
# list gray skipped checks on every unrelated PR label event. Checkout uses the
# triggering ref, so dispatch needs no separate ref input.
on:
workflow_call:
inputs:
targets:
description: Comma-separated pkg targets to build; empty builds all five.
type: string
required: false
default: ''
release:
description: Run as the native builder for the Python release workflow.
type: boolean
required: false
default: false
ci:
description: Run Python runtime CI validation for the selected targets.
type: boolean
required: false
default: false
secrets:
DEEPSEEK_API_KEY_EXTERNAL:
description: Real DeepSeek API key for trusted installed-wheel pull-request tests.
required: false
workflow_dispatch:
inputs:
targets:
description: >-
Comma-separated pkg targets to build. Any subset of:
node24-linux-x64, node24-linux-arm64, node24-macos-arm64,
node24-macos-x64, node24-win-x64. Empty builds all five.
type: string
required: false
default: ''
concurrency:
# Keep the called workflow distinct from its caller's concurrency group;
# github.workflow identifies the caller inside a reusable workflow and keeps
# an ordinary CI run from cancelling a full release validation on the same ref.
group: build-single-exe-${{ github.workflow }}-${{ github.ref }}
# Release-owned builds are part of an intentional publication transaction.
cancel-in-progress: ${{ !inputs.release }}
permissions:
contents: read
env:
# CI runs must never report to the production telemetry endpoint baked
# into apps/cli/cordis.yml (AppCLIEntry disables the row when set).
DSH_TELEMETRY_DISABLED: '1'
jobs:
# Job-level conditions cannot inspect `matrix`, so validate target names and
# construct the matrix before the dependent jobs.
plan:
name: plan targets
if: inputs.ci || inputs.release || github.event_name == 'workflow_dispatch'
runs-on: >-
${{ inputs.ci && !inputs.release && vars.DSH_CI_FAILOVER_LINUX == 'blacksmith'
&& 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-latest' }}
timeout-minutes: 5
outputs:
matrix: ${{ steps.plan.outputs.matrix }}
version: ${{ steps.version.outputs.version }}
repository-version: ${{ steps.version.outputs.repository-version }}
steps:
- uses: actions/[email protected]
- name: Resolve repository version
id: version
run: |
set -euo pipefail
python3 - <<'PY' >> "$GITHUB_OUTPUT"
import runpy
release = runpy.run_path("scripts/build-python-release.py")
repository_version = release["repository_version"]()
wheel_version = release["pep440_version"](repository_version)
print(f"repository-version={repository_version}")
print(f"version={wheel_version}")
PY
- name: Compute matrix from targets input
id: plan
env:
# Blank dispatch inputs build all targets.
TARGETS: ${{ inputs.targets || 'node24-linux-x64,node24-linux-arm64,node24-macos-arm64,node24-macos-x64,node24-win-x64' }}
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; macos-15-intel is native x64.
case "$t" in
node24-linux-x64) runner=ubuntu-latest ;;
node24-linux-arm64) runner=ubuntu-24.04-arm ;;
node24-macos-arm64) runner=macos-latest ;;
node24-macos-x64) runner=macos-15-intel ;;
node24-win-x64) runner=windows-2025 ;;
*)
echo "::error::Unknown target '$t'. Supported: node24-linux-x64, node24-linux-arm64, node24-macos-arm64, node24-macos-x64, node24-win-x64."
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_sdk-${{ needs.plan.outputs.version }}-py3-none-any.whl
runs-on: >-
${{ inputs.ci && !inputs.release && vars.DSH_CI_FAILOVER_LINUX == 'blacksmith'
&& 'blacksmith-4vcpu-ubuntu-2404' || 'ubuntu-latest' }}
timeout-minutes: 5
steps:
- uses: actions/[email protected]
- uses: actions/[email protected]
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/[email protected]
with:
name: deepseek_harness_sdk-${{ needs.plan.outputs.version }}-py3-none-any.whl
path: dist-python/deepseek_harness_sdk-${{ needs.plan.outputs.version }}-py3-none-any.whl
if-no-files-found: error
retention-days: 7
# Blacksmith x64 CI builds use 2 vCPU for compilation, bundling, and wheel smokes.
# Release macOS builds use larger GitHub runners; ordinary CI and direct
# builder dispatches retain the standard native target matrix.
build:
needs: [plan, sdk-wheel]
name: ${{ matrix.target }}
runs-on: >-
${{ inputs.release && matrix.target == 'node24-macos-x64' && 'macos-15-large'
|| inputs.release && matrix.target == 'node24-macos-arm64' && 'macos-15-xlarge'
|| inputs.ci && !inputs.release && matrix.target == 'node24-linux-x64'
&& vars.DSH_CI_FAILOVER_LINUX == 'blacksmith' && 'blacksmith-2vcpu-ubuntu-2404'
|| inputs.ci && !inputs.release && matrix.target == 'node24-win-x64'
&& vars.DSH_CI_FAILOVER_WINDOWS == 'blacksmith' && 'blacksmith-2vcpu-windows-2025'
|| matrix.runner }}
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
include: ${{ fromJSON(needs.plan.outputs.matrix) }}
steps:
- uses: actions/[email protected]
with:
persist-credentials: false
- uses: pnpm/[email protected]
with:
dest: ${{ runner.temp }}/setup-pnpm-js-${{ github.run_id }}-${{ github.run_attempt }}-${{ github.job }}
- name: Enable Windows Developer Mode (symlink support)
if: runner.os == 'Windows'
shell: pwsh
run: >-
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock"
/t REG_DWORD /f /v "AllowDevelopmentWithoutDevLicense" /d "1"
# setup-node's built-in pnpm store cache keys on platform AND arch, so
# the Linux architectures sharing runner.os stay on separate caches.
- uses: actions/[email protected]
with:
node-version: 24
cache: pnpm
- uses: actions/[email protected]
with:
python-version: '3.10'
- name: Install Python build tooling
run: python -m pip install uv==0.11.23
# Cache pkg's target Node binary; lockfile changes roll the
# exact key while the restore prefix can seed its replacement.
- uses: actions/[email protected]
with:
path: ~/.pkg-cache
key: pkg-fetch-${{ matrix.target }}-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
pkg-fetch-${{ matrix.target }}-
- name: Install (immutable)
# node-gyp fetches Node headers from nodejs.org during install; that
# endpoint stalls intermittently (observed 10 s connect timeouts).
# Bounded retries mirror the wine lane's transfer policy without a
# mirror, since these installs also resolve native addons.
shell: bash
run: |
for attempt in 1 2 3; do
if pnpm install --frozen-lockfile; then exit 0; fi
[ "$attempt" -lt 3 ] && sleep 10
done
exit 1
- name: Rebuild Linux node-pty against manylinux 2.28
if: runner.os == 'Linux'
env:
RUNNER_ARCH: ${{ runner.arch }}
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
addon_dir="$(realpath packages/subprocess/subprocess-local/node_modules/node-pty)"
pnpm_setup_root="$(realpath "$(dirname "$(dirname "$PNPM_HOME")")")"
# Verification installs would install node-gyp inside the package and package it into the exe.
(cd "$addon_dir" && npm_config_build_from_source=true pnpm --config.verifyDepsBeforeRun=false run install)
addon="$addon_dir/build/Release/pty.node"
[ -f "$addon_dir/build/Makefile" ] || {
echo "::error::node-pty install did not generate $addon_dir/build/Makefile"
exit 1
}
docker run --rm \
--user "$(id -u):$(id -g)" \
-v "$PWD:$PWD" \
-v "$HOME/.cache/node-gyp:$HOME/.cache/node-gyp:ro" \
-v "$pnpm_setup_root:$pnpm_setup_root:ro" \
-w "$addon_dir" \
"$image" \
bash -euxo pipefail -c \
'rm -rf build/Release && make -C build -j2 BUILDTYPE=Release'
[ -f "$addon" ] || { echo "::error::$addon missing after manylinux rebuild"; exit 1; }
readelf --version-info "$addon" | tee node-pty-glibc-versions.txt
maximum="$(sed -n 's/.*Name: GLIBC_\([0-9.]*\).*/\1/p' node-pty-glibc-versions.txt | sort -V | tail -1)"
[ -n "$maximum" ] || { echo "::error::No GLIBC requirements found in $addon"; exit 1; }
dpkg --compare-versions "$maximum" le 2.28 || {
echo "::error::node-pty addon requires GLIBC_$maximum but wheel claims manylinux_2_28"
exit 1
}
- name: Build single-exe
env:
DSH_BUILD_CLIENT_PROFILE: official
run: >-
pnpm exec tsx scripts/build-exe-for-python-sdk.ts --targets=${{ matrix.target }}
${{ inputs.ci && github.event_name == 'pull_request' && '--build-src' || '' }}
- name: Resolve platform outputs (POSIX)
id: runtime-posix
if: runner.os != 'Windows'
env:
TARGET: ${{ matrix.target }}
VERSION: ${{ needs.plan.outputs.version }}
run: |
set -euo pipefail
platform="${TARGET#node24-}"
exe="$PWD/dist-exe/deepseek-harness-sdk-runtime-$platform"
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_14_0_arm64.whl ;;
macos-x64) wheel=deepseek_harness_runtime_bin-$VERSION-py3-none-macosx_14_0_x86_64.whl ;;
*) echo "::error::Unsupported runtime platform $platform"; exit 1 ;;
esac
[ -x "$exe" ] || { echo "::error::$exe missing or not executable"; exit 1; }
echo "platform=$platform" >> "$GITHUB_OUTPUT"
echo "exe=$exe" >> "$GITHUB_OUTPUT"
echo "wheel=$wheel" >> "$GITHUB_OUTPUT"
- name: Resolve platform outputs (Windows)
id: runtime-windows
if: runner.os == 'Windows'
shell: pwsh
env:
TARGET: ${{ matrix.target }}
VERSION: ${{ needs.plan.outputs.version }}
run: |
if ($env:TARGET -ne 'node24-win-x64') { throw "Unsupported runtime target $env:TARGET" }
$platform = 'win-x64'
$exe = Join-Path $PWD 'dist-exe\deepseek-harness-sdk-runtime-win-x64.exe'
$wheel = "deepseek_harness_runtime_bin-$env:VERSION-py3-none-win_amd64.whl"
if (-not (Test-Path -LiteralPath $exe -PathType Leaf)) { throw "Runtime executable is missing at $exe" }
"platform=$platform" >> $env:GITHUB_OUTPUT
"exe=$exe" >> $env:GITHUB_OUTPUT
"wheel=$wheel" >> $env:GITHUB_OUTPUT
- name: Build release-shaped runtime wheel
run: >-
python scripts/build-python-release.py
--package runtime
--platform "${{ steps.runtime-posix.outputs.platform || steps.runtime-windows.outputs.platform }}"
--runtime-exe "${{ steps.runtime-posix.outputs.exe || steps.runtime-windows.outputs.exe }}"
--output-dir dist-python
- uses: actions/[email protected]
with:
name: deepseek_harness_sdk-${{ needs.plan.outputs.version }}-py3-none-any.whl
path: dist-python
- name: Install local SDK and runtime wheels into a clean venv (POSIX)
id: smoke-venv-posix
if: runner.os != 'Windows'
env:
RUNTIME_WHEEL: ${{ steps.runtime-posix.outputs.wheel }}
SDK_WHEEL: deepseek_harness_sdk-${{ needs.plan.outputs.version }}-py3-none-any.whl
run: |
set -euo pipefail
venv="$(python -c 'import tempfile; print(tempfile.mkdtemp(prefix="dsh-sdk-smoke-"))')"
python -m venv "$venv"
smoke_python="$venv/bin/python"
"$smoke_python" -m pip install \
"dist-python/$SDK_WHEEL" \
"dist-python/$RUNTIME_WHEEL"
echo "python=$smoke_python" >> "$GITHUB_OUTPUT"
- name: Install local SDK and runtime wheels into a clean venv (Windows)
id: smoke-venv-windows
if: runner.os == 'Windows'
shell: pwsh
env:
RUNTIME_WHEEL: ${{ steps.runtime-windows.outputs.wheel }}
SDK_WHEEL: deepseek_harness_sdk-${{ needs.plan.outputs.version }}-py3-none-any.whl
run: |
$venv = (& python -c 'import tempfile; print(tempfile.mkdtemp(prefix="dsh-sdk-smoke-"))').Trim()
python -m venv $venv
$smokePython = Join-Path $venv 'Scripts\python.exe'
& $smokePython -m pip install "dist-python/$env:SDK_WHEEL" "dist-python/$env:RUNTIME_WHEEL"
if ($LASTEXITCODE -ne 0) { throw "Wheel installation failed with exit code $LASTEXITCODE" }
"python=$smokePython" >> $env:GITHUB_OUTPUT
- name: Run installed-wheel keyless black-box tests (POSIX)
if: runner.os != 'Windows'
run: |
set -euo pipefail
blackbox_root="$(python -c 'import tempfile; print(tempfile.mkdtemp(prefix="dsh-sdk-blackbox-"))')"
cd "$blackbox_root"
env -u PYTHONPATH -u DSH_RUNTIME_MODE \
"${{ steps.smoke-venv-posix.outputs.python }}" \
"$GITHUB_WORKSPACE/scripts/smoke-python-runtime.py" \
--scenario all \
--installed-wheel
- name: Run installed-wheel keyless black-box tests (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$blackboxRoot = (& python -c 'import tempfile; print(tempfile.mkdtemp(prefix="dsh-sdk-blackbox-"))').Trim()
Remove-Item Env:PYTHONPATH -ErrorAction SilentlyContinue
Remove-Item Env:DSH_RUNTIME_MODE -ErrorAction SilentlyContinue
Push-Location $blackboxRoot
try {
& "${{ steps.smoke-venv-windows.outputs.python }}" "$env:GITHUB_WORKSPACE\scripts\smoke-python-runtime.py" --scenario all --installed-wheel
if ($LASTEXITCODE -ne 0) { throw "Installed-wheel black-box failed with exit code $LASTEXITCODE" }
} finally {
Pop-Location
}
- name: Preflight installed-wheel real API test (POSIX)
if: >-
inputs.ci
&& runner.os != 'Windows'
&& (github.event_name != 'pull_request'
|| !(github.event.pull_request.head.repo.fork
|| github.event.pull_request.user.login == 'dependabot[bot]'))
env:
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY_EXTERNAL }}
run: |
set -euo pipefail
if [ -z "${DEEPSEEK_API_KEY:-}" ]; then
echo "::error::DEEPSEEK_API_KEY_EXTERNAL is empty; the installed-wheel real API test cannot self-skip."
exit 1
fi
- name: Preflight installed-wheel real API test (Windows)
if: >-
inputs.ci
&& runner.os == 'Windows'
&& (github.event_name != 'pull_request'
|| !(github.event.pull_request.head.repo.fork
|| github.event.pull_request.user.login == 'dependabot[bot]'))
shell: pwsh
env:
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY_EXTERNAL }}
run: |
if ([string]::IsNullOrWhiteSpace($env:DEEPSEEK_API_KEY)) {
throw 'DEEPSEEK_API_KEY_EXTERNAL is empty; the installed-wheel real API test cannot self-skip.'
}
- name: Run installed-wheel real API black-box test (POSIX)
if: >-
inputs.ci
&& runner.os != 'Windows'
&& (github.event_name != 'pull_request'
|| !(github.event.pull_request.head.repo.fork
|| github.event.pull_request.user.login == 'dependabot[bot]'))
env:
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY_EXTERNAL }}
DEEPSEEK_BASE_URL: https://api.deepseek.com/anthropic
run: |
set -euo pipefail
blackbox_root="$(python -c 'import tempfile; print(tempfile.mkdtemp(prefix="dsh-sdk-blackbox-live-"))')"
cd "$blackbox_root"
env -u PYTHONPATH -u DSH_RUNTIME_MODE \
"${{ steps.smoke-venv-posix.outputs.python }}" \
"$GITHUB_WORKSPACE/scripts/smoke-python-runtime.py" \
--scenario sdk-live \
--installed-wheel
- name: Run installed-wheel real API black-box test (Windows)
if: >-
inputs.ci
&& runner.os == 'Windows'
&& (github.event_name != 'pull_request'
|| !(github.event.pull_request.head.repo.fork
|| github.event.pull_request.user.login == 'dependabot[bot]'))
shell: pwsh
env:
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY_EXTERNAL }}
DEEPSEEK_BASE_URL: https://api.deepseek.com/anthropic
run: |
$blackboxRoot = (& python -c 'import tempfile; print(tempfile.mkdtemp(prefix="dsh-sdk-blackbox-live-"))').Trim()
Remove-Item Env:PYTHONPATH -ErrorAction SilentlyContinue
Remove-Item Env:DSH_RUNTIME_MODE -ErrorAction SilentlyContinue
Push-Location $blackboxRoot
try {
& "${{ steps.smoke-venv-windows.outputs.python }}" "$env:GITHUB_WORKSPACE\scripts\smoke-python-runtime.py" --scenario sdk-live --installed-wheel
if ($LASTEXITCODE -ne 0) { throw "Installed-wheel live API smoke failed with exit code $LASTEXITCODE" }
} finally {
Pop-Location
}
- name: Check Linux GLIBC requirements
if: runner.os == 'Linux'
run: |
set -euo pipefail
readelf --version-info "${{ steps.runtime-posix.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: Check macOS payload architecture and deployment target
if: runner.os == 'macOS'
env:
EXE: ${{ steps.runtime-posix.outputs.exe }}
PLATFORM: ${{ steps.runtime-posix.outputs.platform }}
run: |
set -euo pipefail
case "$PLATFORM" in
macos-arm64) macho_arch=arm64 ;;
macos-x64) macho_arch=x86_64 ;;
*) echo "::error::Unsupported macOS platform $PLATFORM"; exit 1 ;;
esac
for payload in "$EXE" "$EXE-rg" "$EXE-spawn-helper"; do
lipo "$payload" -verify_arch "$macho_arch"
done
python3 scripts/check-macos-deployment-target.py \
--platform "$PLATFORM" "$EXE" "$EXE-rg" "$EXE-spawn-helper"
- name: Run wheel in a manylinux 2.28 container
if: runner.os == 'Linux'
env:
RUNNER_ARCH: ${{ runner.arch }}
RUNTIME_WHEEL: ${{ steps.runtime-posix.outputs.wheel }}
SDK_WHEEL: deepseek_harness_sdk-${{ needs.plan.outputs.version }}-py3-none-any.whl
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 RUNTIME_WHEEL -e SDK_WHEEL -e DSH_TELEMETRY_DISABLED -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 "/work/dist-python/$SDK_WHEEL" "/work/dist-python/$RUNTIME_WHEEL"
mkdir -p /tmp/dsh-sdk-manylinux-smoke
cd /tmp/dsh-sdk-manylinux-smoke
env -u PYTHONPATH -u DSH_RUNTIME_MODE /tmp/dsh-sdk/bin/python /work/scripts/smoke-python-runtime.py --scenario sdk-default --installed-wheel
env -u PYTHONPATH -u DSH_RUNTIME_MODE /tmp/dsh-sdk/bin/python /work/scripts/smoke-python-runtime.py --scenario sdk-mcp --installed-wheel
'
- uses: actions/[email protected]
with:
name: ${{ steps.runtime-posix.outputs.wheel || steps.runtime-windows.outputs.wheel }}
path: dist-python/${{ steps.runtime-posix.outputs.wheel || steps.runtime-windows.outputs.wheel }}
if-no-files-found: error
retention-days: 7