Skip to content

Commit 15431ee

Browse files
committed
merge main
Signed-off-by: Prathamesh Mandke <46148373+pkmandke@users.noreply.github.com>
2 parents 26f7def + 1813541 commit 15431ee

194 files changed

Lines changed: 16668 additions & 3872 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/dependabot.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: "github-actions"
4+
directory: "/"
5+
schedule:
6+
interval: "weekly"
7+
cooldown:
8+
default-days: 3
9+
commit-message:
10+
prefix: "ci"
11+
groups:
12+
actions:
13+
patterns:
14+
- "*"

.github/workflows/ci.yaml

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
name: CI
2+
3+
run-name: >-
4+
${{ github.event_name == 'pull_request'
5+
&& format('CI · PR #{0} · {1} · @{2}', github.event.pull_request.number, github.event.pull_request.head.ref, github.actor)
6+
|| format('CI · push {0} · @{1}', github.ref_name, github.actor) }}
7+
8+
on:
9+
pull_request:
10+
branches: [main]
11+
push:
12+
branches: [main]
13+
14+
permissions:
15+
contents: read
16+
17+
concurrency:
18+
group: ${{ github.workflow }}-${{ github.ref }}
19+
cancel-in-progress: true
20+
21+
jobs:
22+
23+
# ── Stage 1: cheap correctness gate (ubuntu-latest, ungated). ──
24+
check:
25+
name: Linux / make check
26+
runs-on: ubuntu-latest
27+
timeout-minutes: 30
28+
steps:
29+
- name: Check out repository
30+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
31+
with:
32+
fetch-depth: 0
33+
- name: Install uv
34+
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
35+
with:
36+
enable-cache: false
37+
- name: Run `make check`
38+
run: make check
39+
40+
test-smoke:
41+
name: Linux / make test-smoke / torch=${{ matrix.torch_version }}
42+
runs-on: ubuntu-latest
43+
timeout-minutes: 60
44+
env:
45+
INSTALL_PRECOMMIT: "false"
46+
strategy:
47+
fail-fast: false
48+
matrix:
49+
include:
50+
- torch_group: torch_2_8
51+
torch_version: "2.8"
52+
- torch_group: torch_2_9
53+
torch_version: "2.9"
54+
- torch_group: torch_2_10
55+
torch_version: "2.10"
56+
- torch_group: torch_2_11
57+
torch_version: "2.11"
58+
steps:
59+
- name: Check out repository
60+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
61+
- name: Install uv
62+
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
63+
with:
64+
enable-cache: false
65+
- name: Run `make test-smoke`
66+
run: make test-smoke TORCH_GROUP=${{ matrix.torch_group }}
67+
68+
test-tutorials:
69+
name: Linux / make test-tutorials
70+
runs-on: ubuntu-latest
71+
timeout-minutes: 60
72+
env:
73+
INSTALL_PRECOMMIT: "false"
74+
steps:
75+
- name: Check out repository
76+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
77+
- name: Install uv
78+
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
79+
with:
80+
enable-cache: false
81+
- name: Run `make test-tutorials`
82+
run: make test-tutorials
83+
84+
# ── Stage 2: full test matrix, gated on Stage 1. ──
85+
macos-tests:
86+
name: macOS / make test-highest-pytorch / markers=not-slow
87+
needs: [check, test-smoke, test-tutorials]
88+
if: github.repository == 'apple/coreai-optimization'
89+
runs-on: [self-hosted, macos, tahoe, ARM64]
90+
timeout-minutes: 60
91+
env:
92+
INSTALL_PRECOMMIT: "false"
93+
steps:
94+
- name: Check out repository
95+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
96+
- name: Install uv
97+
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
98+
with:
99+
enable-cache: false
100+
- name: Run `make test-highest-pytorch`
101+
run: make test-highest-pytorch PYTEST_ARGS="--marker 'not slow' --junit"
102+
- name: Upload test results
103+
if: always()
104+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
105+
with:
106+
name: test-results-macos-highest-fast
107+
path: test-results/
108+
109+
linux-tests:
110+
name: Linux / make test-${{ matrix.torch }}-pytorch / markers=${{ matrix.speed == 'slow' && 'slow' || 'not-slow' }}
111+
needs: [check, test-smoke, test-tutorials]
112+
runs-on: ubuntu-latest
113+
timeout-minutes: 120
114+
env:
115+
INSTALL_PRECOMMIT: "false"
116+
strategy:
117+
fail-fast: false
118+
matrix:
119+
torch: [highest, lowest]
120+
speed: [slow, fast]
121+
steps:
122+
- name: Check out repository
123+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
124+
- name: Install uv
125+
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
126+
with:
127+
enable-cache: false
128+
- name: Run `make test-${{ matrix.torch }}-pytorch`
129+
run: make test-${{ matrix.torch }}-pytorch PYTEST_ARGS="--marker '${{ matrix.speed == 'slow' && 'slow' || 'not slow' }}' --junit"
130+
- name: Upload test results
131+
if: always()
132+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
133+
with:
134+
name: test-results-linux-${{ matrix.torch }}-${{ matrix.speed }}
135+
path: test-results/

.github/workflows/release.yml

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

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ uv.lock
4747
*.nc
4848
.python-version
4949
/.tox
50-
deps/
5150
scratch/
5251
/local_stash
5352
/model_weights

.pre-commit-config.yaml

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ repos:
2323
language: system
2424
entry: scripts/pre_commit/add_license_header.py --license-file configs/BSD-3-LICENSE-HEADER-TEMPLATE --start-year 2026
2525
files: '\.(py|sh|js|css|html)$|(^|/)(GNUmakefile|[Mm]akefile)$'
26+
exclude: ^src/coreai_opt/deps/
2627

2728
# ----------------------------------------------------------------------------
2829
# 0.2 Update LICENSE year
@@ -62,7 +63,7 @@ repos:
6263
language: system
6364
entry: end-of-file-fixer
6465
types: [text]
65-
exclude: ^\.codegenius/learnings\.md$
66+
exclude: ^\.codegenius/
6667

6768
- id: check-yaml
6869
name: check yaml
@@ -174,7 +175,7 @@ repos:
174175
language: system
175176
entry: mdformat --number
176177
files: \.(md)$
177-
exclude: ^(CHANGELOG\.md|\.codegenius/learnings\.md)$
178+
exclude: ^(CHANGELOG\.md$|\.codegenius/)
178179
args: [
179180
--number, # Force 1., 2., 3. instead of all 1.
180181
--wrap=keep, # Preserve existing line breaks
@@ -255,7 +256,7 @@ repos:
255256
language: system
256257
entry: pymarkdown --disable-rules MD013,MD024 scan
257258
files: \.(md)$
258-
exclude: ^\.codegenius/learnings\.md$
259+
exclude: ^\.codegenius/
259260

260261
# ----------------------------------------------------------------------------
261262
# 3.5 Lint Shell Script (bashate)
@@ -304,6 +305,34 @@ repos:
304305
types: [python]
305306
exclude: ^tests/
306307

308+
- repo: local
309+
hooks:
310+
- id: check-internal-import-aliases
311+
name: Check `_`-prefix aliasing of internal imports
312+
description: |
313+
Enforce code_style_guide.md §3.3: public modules must alias symbols
314+
imported from private modules with a `_` prefix; private modules must
315+
not. Fails the commit and reports each violation with a suggested fix;
316+
it does not edit files — run the script with `--fix` to apply fixes.
317+
entry: python scripts/pre_commit/check_internal_import_aliases.py
318+
language: system
319+
types: [python]
320+
exclude: ^tests/
321+
322+
- repo: local
323+
hooks:
324+
- id: check-about-version
325+
name: Check _about.py version fields
326+
description: |
327+
Check that _about.py's latest_released_version matches the repo's
328+
latest release tag, and that __version__ is its last number plus
329+
one, plus .dev0. Catches a release candidate that looks like a
330+
release has already shipped when it hasn't.
331+
entry: python scripts/pre_commit/check_about_version.py
332+
language: system
333+
files: (^|/)_about\.py$
334+
pass_filenames: false
335+
307336
- repo: local
308337
hooks:
309338
- id: towncrier-check

AGENTS.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717
- `pytest -n auto path/to/test.py` — run a test file
1818
- `pytest path/to/test.py::test_name` — run a single test
1919

20+
## uv usage
21+
22+
Always pass `--no-sync` to `uv run`: `uv run --no-sync --active …`.
23+
24+
`uv run` implicitly syncs the active project to its default-groups before running, which re-resolves dependencies and can clobber a venv's group-pinned packages — e.g. the torch pin in `.venv-lowest-torch`/`.venv-highest-torch` gets re-anchored back to the default torch. Our Make targets always prepare the environment first via `use_env`/`setup_env.sh`, so by the time `uv run` executes the deps are already correct. A `uv run` invocation should be a read-only run of a command in that prepared env, never a dependency mutation — `--no-sync` enforces that.
25+
2026
## Editing Guidelines
2127

2228
- Use `@path` to reference small files (loaded into every session automatically).

0 commit comments

Comments
 (0)