11name : Release
2+ run-name : Release (package=${{ inputs.package || 'all' }} ref=${{ github.ref_name }})
23
34on :
45 push :
5- tags :
6- - " v*"
6+ tags : ["v*"]
77 workflow_dispatch :
88 inputs :
99 package :
2424
2525jobs :
2626 build :
27- name : Build + test + package (monorepo)
2827 runs-on : ubuntu-latest
29-
3028 steps :
3129 - uses : actions/checkout@v4
3230 with :
3331 fetch-depth : 0
3432 persist-credentials : false
3533
36- - name : Install uv (Python 3.12)
37- uses : astral-sh/setup-uv@v7
34+ - uses : astral-sh/setup-uv@v7
3835 with :
3936 python-version : " 3.12"
4037 enable-cache : true
4138
42- - name : Sync (workspace + dev + all extras)
43- run : uv sync --all-packages --all-extras --dev --locked
44-
45- - name : Run tests
46- run : uv run pytest
47-
48- - name : Install build backend
49- run : uv run python -m pip install --upgrade build
39+ - run : uv sync --all-packages --all-extras --dev --locked
40+ - run : uv run pytest
41+ - run : uv run python -m pip install --upgrade build
5042
5143 - name : Verify tag matches all package versions (tag runs only)
52- if : startsWith(github.ref, 'refs/tags/v')
44+ if : ${{ startsWith(github.ref, 'refs/tags/v') }}
5345 run : |
5446 uv run python - << 'PY'
5547 import os, sys, pathlib, tomllib
56-
57- tag = os.environ["GITHUB_REF_NAME"] # v0.1.0
48+ tag = os.environ["GITHUB_REF_NAME"]
5849 ver = tag[1:] if tag.startswith("v") else tag
59-
60- pyprojects = [pathlib.Path("pyproject.toml")]
61- pyprojects += sorted(pathlib.Path("packages").glob("*/pyproject.toml"))
62-
63- mismatches = []
50+ pyprojects = [pathlib.Path("pyproject.toml")] + sorted(pathlib.Path("packages").glob("*/pyproject.toml"))
51+ bad = []
6452 for p in pyprojects:
65- data = tomllib.loads(p.read_text(encoding="utf-8"))
66- v = data.get("project", {}).get("version")
53+ v = tomllib.loads(p.read_text(encoding="utf-8")).get("project", {}).get("version")
6754 if v != ver:
68- mismatches.append((str(p), v))
69-
70- if mismatches:
71- for p, v in mismatches:
55+ bad.append((str(p), v))
56+ if bad:
57+ for p, v in bad:
7258 print(f"Version mismatch: {p}: {v!r} != {ver!r}")
7359 sys.exit(1)
74-
75- print("OK: all versions match", ver)
60+ print("OK:", ver)
7661 PY
7762
7863 - name : Build all distributions into per-package dirs
7964 run : |
8065 uv run python - << 'PY'
8166 import pathlib, tomllib, subprocess, sys
82-
8367 root = pathlib.Path(".").resolve()
84- dist_root = root / "dist"
85- dist_root.mkdir(exist_ok=True)
86-
87- def project_name(pyproject: pathlib.Path) -> str:
88- data = tomllib.loads(pyproject.read_text(encoding="utf-8"))
89- name = data.get("project", {}).get("name")
90- if not name:
91- raise RuntimeError(f"Missing [project].name in {pyproject}")
92- return name
93-
94- # root package
95- root_name = project_name(root / "pyproject.toml")
96- (dist_root / root_name).mkdir(parents=True, exist_ok=True)
97- subprocess.check_call([sys.executable, "-m", "build", "--outdir", str(dist_root / root_name), str(root)])
98-
99- # workspace members
100- pkgs_dir = root / "packages"
101- if pkgs_dir.exists():
102- for pyproject in sorted(pkgs_dir.glob("*/pyproject.toml")):
103- pkg_dir = pyproject.parent
104- name = project_name(pyproject)
105- out = dist_root / name
106- out.mkdir(parents=True, exist_ok=True)
107- print(f"Building {name} from {pkg_dir}")
108- subprocess.check_call([sys.executable, "-m", "build", "--outdir", str(out), str(pkg_dir)])
109-
110- print("Built dists into:", dist_root)
68+ dist = root / "dist"
69+ dist.mkdir(exist_ok=True)
70+
71+ def name(pp):
72+ return tomllib.loads(pp.read_text(encoding="utf-8"))["project"]["name"]
73+
74+ # root
75+ root_name = name(root / "pyproject.toml")
76+ (dist / root_name).mkdir(parents=True, exist_ok=True)
77+ subprocess.check_call([sys.executable, "-m", "build", "--outdir", str(dist / root_name), str(root)])
78+
79+ # members
80+ for pp in sorted((root / "packages").glob("*/pyproject.toml")):
81+ pkg = pp.parent
82+ n = name(pp)
83+ (dist / n).mkdir(parents=True, exist_ok=True)
84+ subprocess.check_call([sys.executable, "-m", "build", "--outdir", str(dist / n), str(pkg)])
11185 PY
11286
113- - name : Upload dists artifact
114- uses : actions/upload-artifact@v4
87+ - uses : actions/upload-artifact@v4
11588 with :
11689 name : python-package-distributions
11790 path : dist/
11891
11992 publish :
120- name : Publish to PyPI (Trusted Publishing)
12193 needs : [build]
12294 runs-on : ubuntu-latest
123-
12495 permissions :
12596 id-token : write
12697 contents : read
12798
128- # One job per package (recommended; invoking pypi-publish multiple times in one job is not supported)
12999 strategy :
130100 fail-fast : false
131101 matrix :
@@ -139,25 +109,19 @@ jobs:
139109 - devqubit-cirq
140110 - devqubit-braket
141111
142- # On tag push: publish all packages.
143- # On manual run: publish only the selected one (or all).
144- if : |
145- github.event_name == 'push' ||
146- (github.event_name == 'workflow_dispatch' && (inputs.package == 'all' || inputs.package == matrix.package))
112+ if : ${{ github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && (inputs.package == 'all' || inputs.package == matrix.package)) }}
147113
148114 environment :
149115 name : pypi
150- url : https://pypi.org/p /${{ matrix.package }}
116+ url : https://pypi.org/project /${{ matrix.package }}/
151117
152118 steps :
153- - name : Download dists
154- uses : actions/download-artifact@v4
119+ - uses : actions/download-artifact@v4
155120 with :
156121 name : python-package-distributions
157122 path : dist/
158123
159- - name : Publish ${{ matrix.package }}
160- uses : pypa/gh-action-pypi-publish@release/v1
124+ - uses : pypa/gh-action-pypi-publish@release/v1
161125 with :
162126 packages-dir : dist/${{ matrix.package }}
163127 skip-existing : true
0 commit comments