Skip to content

Dyro 0.6.6

Dyro 0.6.6 #17

Workflow file for this run

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: 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()"
"$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()"
"$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'))"
- 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