-
Notifications
You must be signed in to change notification settings - Fork 23
154 lines (147 loc) · 6.8 KB
/
Copy pathrelease.yml
File metadata and controls
154 lines (147 loc) · 6.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
name: Release
run-name: 'Release ${{ github.ref_name }} · @${{ github.actor }}'
# Publishing to PyPI uses Trusted Publishing (OIDC) — no API token is stored.
# A release manager pushes a `vMAJOR.MINOR.PATCH` tag; the `build` and
# `smoke-test` jobs run automatically, then the `publish` job pauses on the
# `pypi` GitHub environment until a reviewer approves it. That environment's
# protection rules (required reviewers, prevent-self-review so the tag pusher
# can't approve their own release, and a wait timer) live in the repo's
# Environment settings, not in this file.
on:
push:
tags:
# Strictly vMAJOR.MINOR.PATCH with numeric parts (e.g. v0.2.2). This is a
# glob, not a regex: `.` is a literal dot and `[0-9]` a digit range. The
# filter must match the entire tag, so pre-releases (v1.2.3rc1 — the
# trailing `rc1` is left unmatched) and other non-release tags never start
# the release run. The `pypi` environment tag rule and approval gate are
# secondary controls; the version guard below is the final backstop.
- 'v[0-9]+.[0-9]+.[0-9]+'
# Manual dry run: builds and smoke-tests the current ref but never publishes
# (the publish job is gated to tag pushes). Trigger from the Actions tab
# ("Release" -> "Run workflow") or `gh workflow run release.yml --ref <branch>`.
workflow_dispatch:
# Least privilege by default; the publish job opts into `id-token: write`.
permissions:
contents: read
concurrency:
# Serialize releases per tag and never cancel an in-flight publish.
group: release-${{ github.ref }}
cancel-in-progress: false
jobs:
# ── Build the exact wheel + sdist that will be smoke-tested and published. ──
build:
name: Build distributions
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
enable-cache: false
- name: Verify the tag matches the package version
# The published version comes from src/coreai_opt/_about.py, not the tag.
# Fail early if they disagree so we never publish a mismatched/duplicate
# version (PyPI uploads are immutable and cannot be overwritten).
# `print_version.py --release` computes the version exactly as the
# `make build` step below does, so this guard can't drift from what
# actually gets published.
# Skipped on manual dry runs, where the ref is a branch, not a vX.Y.Z tag.
# Run via uv (installed above) so the interpreter satisfies
# requires-python whatever the runner image ships; see the Makefile's
# `version` target.
if: github.event_name == 'push'
run: |
tag="${GITHUB_REF_NAME}"
version="$(uv run --no-config --no-project --python '>=3.11' scripts/make/print_version.py --release)"
echo "tag=${tag} package version=${version}"
if [ "${tag}" != "v${version}" ]; then
echo "::error::Tag ${tag} does not match package version v${version} (src/coreai_opt/_about.py). Update latest_released_version so the release it implies matches the tag."
exit 1
fi
- name: Build wheel and sdist
run: make build
- name: Upload distributions
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: dist
path: dist/
if-no-files-found: error
# ── Smoke test the exact built wheel and sdist via `make test-smoke`. ──
# Reuses the repo's smoke suite (ci/nox/noxfile.py → tests/test_smoke.py) but
# points it at the pre-built artifact instead of rebuilding, so we test the
# bytes we are about to publish. `make test-smoke` runs across every supported
# Python version internally.
smoke-test:
name: Smoke test (${{ matrix.format }}, ${{ matrix.torch_group }})
needs: build
runs-on: ubuntu-latest
timeout-minutes: 60
env:
INSTALL_PRECOMMIT: 'false'
strategy:
fail-fast: false
matrix:
# Test both distribution formats against every supported torch version,
# mirroring the PR CI smoke matrix (ci.yaml).
format: [wheel, sdist]
torch_group: [torch_2_8, torch_2_9, torch_2_10, torch_2_11]
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
enable-cache: false
- name: Download distributions
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: dist
path: dist/
- name: Smoke test the built ${{ matrix.format }} against ${{ matrix.torch_group }}
run: |
# Expand the glob to the single built artifact; the `test -f` below
# fails the job if it's missing or if more than one matched.
case "${{ matrix.format }}" in
wheel) dist="$(echo dist/*.whl)" ;;
sdist) dist="$(echo dist/*.tar.gz)" ;;
esac
test -f "${dist}" || { echo "::error::Expected exactly one ${{ matrix.format }} in dist/"; exit 1; }
echo "Smoke testing ${dist} against ${{ matrix.torch_group }}"
make test-smoke SMOKE_TEST_DIST="${dist}" TORCH_GROUP="${{ matrix.torch_group }}"
# ── Publish to PyPI via Trusted Publishing. Only this job holds `id-token`. ──
# It builds nothing and runs no project code: it just downloads the vetted
# artifact and uploads it, keeping build/test dependencies out of the
# OIDC-privileged job.
publish:
name: Publish to PyPI
needs: [build, smoke-test]
# Publish only on a tag push (never on a manual dry run) and never from forks.
if: github.event_name == 'push' && github.repository == 'apple/coreai-optimization'
runs-on: ubuntu-latest
timeout-minutes: 15
environment:
name: pypi
url: https://pypi.org/p/coreai-opt
permissions:
id-token: write # mint the OIDC token PyPI validates for Trusted Publishing
contents: read
steps:
- name: Install uv
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
enable-cache: false
- name: Download distributions
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: dist
path: dist/
- name: Publish to PyPI
# `always` requires Trusted Publishing (OIDC) — no fallback to tokens.
run: uv publish --trusted-publishing always