Dyro 0.7.1 #22
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Publish to PyPI | |
| on: | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| inputs: | |
| release_tag: | |
| description: "Existing vX.Y.Z tag to build and publish" | |
| required: true | |
| type: string | |
| permissions: | |
| actions: read | |
| contents: read | |
| jobs: | |
| build: | |
| name: Build and validate distributions | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Check out release source | |
| uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | |
| with: | |
| ref: ${{ github.event.release.tag_name || inputs.release_tag }} | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Set up Python | |
| uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 | |
| with: | |
| python-version: "3.11" | |
| - name: Set up uv | |
| uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 | |
| with: | |
| enable-cache: true | |
| cache-dependency-glob: uv.lock | |
| - name: Verify trusted release source | |
| env: | |
| RELEASE_TAG: ${{ github.event.release.tag_name || inputs.release_tag }} | |
| run: | | |
| git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main | |
| python tools/verify_release_source.py --release-tag "$RELEASE_TAG" --trusted-ref origin/main | |
| - name: Require successful exact-SHA CI | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| release_sha="$(git rev-parse HEAD)" | |
| for attempt in $(seq 1 30); do | |
| row="$(gh api --method GET \ | |
| "repos/${GITHUB_REPOSITORY}/actions/workflows/ci.yml/runs" \ | |
| -f "head_sha=${release_sha}" -f event=push -f per_page=100 \ | |
| --jq '.workflow_runs | sort_by(.created_at) | last | if . == null then ["missing","missing","missing","missing","missing"] else [.status, (.conclusion // "pending"), .head_sha, (.id | tostring), .html_url] end | @tsv')" | |
| IFS=$'\t' read -r status conclusion observed_sha run_id run_url <<<"${row}" | |
| if [[ "${observed_sha}" != "missing" && "${observed_sha}" != "${release_sha}" ]]; then | |
| echo "CI run SHA mismatch: expected ${release_sha}, got ${observed_sha}" >&2 | |
| exit 1 | |
| fi | |
| if [[ "${status}" == "completed" ]]; then | |
| if [[ "${conclusion}" != "success" ]]; then | |
| echo "Exact-SHA CI did not succeed: ${conclusion} ${run_url}" >&2 | |
| exit 1 | |
| fi | |
| printf '%s\t%s\t%s\n' \ | |
| "${release_sha}" "${run_id}" "${run_url}" \ | |
| > ci-gate-run.tsv | |
| break | |
| fi | |
| if [[ "${attempt}" -eq 30 ]]; then | |
| echo "No completed successful ci.yml push run for ${release_sha}" >&2 | |
| exit 1 | |
| fi | |
| sleep 10 | |
| done | |
| - name: Verify locked release environment | |
| run: | | |
| uv lock --check | |
| uv sync --locked --all-extras --dev | |
| - name: Run tests | |
| env: | |
| DYRO_RELEASE_TAG: ${{ github.event.release.tag_name || inputs.release_tag }} | |
| run: uv run python -m unittest discover -s tests -t . -v | |
| - name: Check release version | |
| env: | |
| RELEASE_TAG: ${{ github.event.release.tag_name || inputs.release_tag }} | |
| run: | | |
| uv run python - <<'PY' | |
| import os | |
| import tomllib | |
| from pathlib import Path | |
| metadata = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8")) | |
| expected = f"v{metadata['project']['version']}" | |
| actual = os.environ["RELEASE_TAG"] | |
| if actual != expected: | |
| raise SystemExit(f"release tag {actual!r} must equal {expected!r}") | |
| PY | |
| - name: Refuse 1.0.0 without delivery-physics gates | |
| env: | |
| RELEASE_TAG: ${{ github.event.release.tag_name || inputs.release_tag }} | |
| run: uv run python tools/verify_release_gates.py --release-tag "$RELEASE_TAG" | |
| - name: Build distributions | |
| run: uv run python -m build | |
| - name: Reject generated bytecode in distributions | |
| run: | | |
| uv run python - <<'PY' | |
| from pathlib import Path, PurePosixPath | |
| import tarfile | |
| import zipfile | |
| def forbidden(name: str) -> bool: | |
| path = PurePosixPath(name) | |
| return "__pycache__" in path.parts or path.suffix in {".pyc", ".pyo"} | |
| for artifact in Path("dist").glob("dyro-*"): | |
| if artifact.suffix == ".whl": | |
| with zipfile.ZipFile(artifact) as archive: | |
| names = archive.namelist() | |
| elif artifact.name.endswith(".tar.gz"): | |
| with tarfile.open(artifact, "r:gz") as archive: | |
| names = archive.getnames() | |
| else: | |
| continue | |
| leaked = [name for name in names if forbidden(name)] | |
| if leaked: | |
| raise SystemExit( | |
| f"generated bytecode found in {artifact.name}: {leaked[:5]}" | |
| ) | |
| PY | |
| - name: Verify installed wheel and sdist outside checkout | |
| run: | | |
| set -euo pipefail | |
| smoke_root="$(mktemp -d)" | |
| cd "$smoke_root" | |
| uv run python -m venv "$smoke_root/wheel-venv" | |
| "$smoke_root/wheel-venv/bin/pip" install "$GITHUB_WORKSPACE"/dist/dyro-*.whl | |
| "$smoke_root/wheel-venv/bin/python" -c "import experiments.local_agent_dispatch" | |
| "$smoke_root/wheel-venv/bin/python" -I -c "from dyro.console.assets import validate_assets; validate_assets()" | |
| "$smoke_root/wheel-venv/bin/python" -I -c "from importlib.resources import files; root=files('dyro.integrations').joinpath('assets'); assert root.joinpath('dyro-control-plane','SKILL.md').is_file(); assert root.joinpath('dyro-control-plane','agents','openai.yaml').is_file(); assert root.joinpath('dyro-dispatch','SKILL.md').is_file(); assert root.joinpath('dyro-dispatch','agents','openai.yaml').is_file()" | |
| "$smoke_root/wheel-venv/bin/python" -I -c "import importlib.util; assert importlib.util.find_spec('dyro.bridge') is None" | |
| test -x "$smoke_root/wheel-venv/bin/dyro" | |
| test ! -e "$smoke_root/wheel-venv/bin/dyro-bridge" | |
| test ! -e "$smoke_root/wheel-venv/bin/dyro-mcp" | |
| DYRO_LOCAL_AGENT_DISPATCH_HOME="$smoke_root/wheel-dispatch-home" "$smoke_root/wheel-venv/bin/dyro" dispatch doctor >"$smoke_root/wheel-doctor.json" | |
| uv run python -c "import json; json.load(open('$smoke_root/wheel-doctor.json'))" | |
| uv run python -m venv "$smoke_root/sdist-venv" | |
| "$smoke_root/sdist-venv/bin/pip" install "$GITHUB_WORKSPACE"/dist/dyro-*.tar.gz | |
| "$smoke_root/sdist-venv/bin/python" -c "import experiments.local_agent_dispatch" | |
| "$smoke_root/sdist-venv/bin/python" -I -c "from dyro.console.assets import validate_assets; validate_assets()" | |
| "$smoke_root/sdist-venv/bin/python" -I -c "from importlib.resources import files; root=files('dyro.integrations').joinpath('assets'); assert root.joinpath('dyro-control-plane','SKILL.md').is_file(); assert root.joinpath('dyro-control-plane','agents','openai.yaml').is_file(); assert root.joinpath('dyro-dispatch','SKILL.md').is_file(); assert root.joinpath('dyro-dispatch','agents','openai.yaml').is_file()" | |
| "$smoke_root/sdist-venv/bin/python" -I -c "import importlib.util; assert importlib.util.find_spec('dyro.bridge') is None" | |
| test -x "$smoke_root/sdist-venv/bin/dyro" | |
| test ! -e "$smoke_root/sdist-venv/bin/dyro-bridge" | |
| test ! -e "$smoke_root/sdist-venv/bin/dyro-mcp" | |
| DYRO_LOCAL_AGENT_DISPATCH_HOME="$smoke_root/sdist-dispatch-home" "$smoke_root/sdist-venv/bin/dyro" dispatch doctor >"$smoke_root/sdist-doctor.json" | |
| uv run python -c "import json; json.load(open('$smoke_root/sdist-doctor.json'))" | |
| "$smoke_root/sdist-venv/bin/python" "$GITHUB_WORKSPACE/tools/verify_bundle_stranger.py" "$smoke_root/sdist-venv/bin/dyro" | |
| - name: Validate distribution metadata | |
| run: uv run python -m twine check --strict dist/dyro-*.whl dist/dyro-*.tar.gz | |
| - name: Record locked build inputs and distribution digests | |
| run: | | |
| uv export --locked --all-extras --dev --format requirements-txt > release-build-requirements.txt | |
| sha256sum dist/dyro-*.whl dist/dyro-*.tar.gz > distribution-sha256sums.txt | |
| - name: Upload distributions for publishing | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: python-package-distributions | |
| path: | | |
| dist/dyro-*.whl | |
| dist/dyro-*.tar.gz | |
| retention-days: 7 | |
| - name: Upload release build record | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: release-build-record | |
| path: | | |
| release-build-requirements.txt | |
| distribution-sha256sums.txt | |
| ci-gate-run.tsv | |
| retention-days: 90 | |
| publish-to-pypi: | |
| name: Publish to PyPI | |
| needs: build | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| environment: | |
| name: pypi | |
| url: https://pypi.org/p/dyro | |
| permissions: | |
| id-token: write | |
| steps: | |
| - name: Download distributions | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: python-package-distributions | |
| path: dist/ | |
| - name: Publish distributions to PyPI | |
| uses: pypa/gh-action-pypi-publish@ba38be9e461d3875417946c167d0b5f3d385a247 # v1.14.1 |