Skip to content

Commit 6e230ea

Browse files
committed
build: rework version scheme to carry .dev0 on main
- `main` always carries the version planned for the next release with a `.dev0` suffix, e.g. `0.2.2.dev0`; per PEP 440 that sorts above the last official release (`0.2.1`), so a dev build never looks older than what's already published, and `.dev0` never ships in a wheel - `make build` strips the suffix for a clean release; `make build-dev` (new) appends a unique `.dev<timestamp>+<sha>` instead, so two local wheel builds can be compared to see which is newer - both targets route through `build.py`, which writes the resolved version into `_about.py` before `uv build`, then restores the file - `make version` now reads `_about.py` as plain text instead of importing the package, so it no longer needs the project venv - adds `COREAI_OPT_VERSION_EXTENSION` so a project that builds this repo as part of its own package can insert one extra release segment (e.g. `0.2.2.dev0` -> `0.2.2.1.dev0`) and keep its own, independent version numbering — `make build`/`make build-dev`/`make version` all work unchanged - adds `tests/test_release_utils.py` covering the new helpers and the extension seam
1 parent 04acaee commit 6e230ea

8 files changed

Lines changed: 482 additions & 77 deletions

File tree

Makefile

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -217,18 +217,23 @@ env-all: _maybe_patch_pyproject
217217
# Build
218218
# =============================================================================
219219

220-
# Build the canonical, publishable distribution (wheel + sdist) via the uv build
221-
# frontend. `--no-sources` ignores [tool.uv.sources], so the artifact doesn't
222-
# depend on uv-specific index overrides — the recommended way to build for
223-
# publication. This is what the release workflow runs.
220+
# Build the canonical, publishable distribution (wheel + sdist): the on-tree
221+
# version with any `.dev` suffix stripped (e.g. 0.2.2.dev0 -> 0.2.2), via
222+
# `uv build --no-sources`. `--no-sources` ignores [tool.uv.sources], so the
223+
# artifact doesn't depend on uv-specific index overrides — the recommended way
224+
# to build for publication. This is what the release workflow runs. Routed
225+
# through build.py (like build-dev) so both targets share one code path; set
226+
# COREAI_OPT_VERSION_EXTENSION to insert an extra release segment (see
227+
# RELEASE.md).
224228
build:
225-
@uv build --no-sources
229+
@$(call use_env,VENV) && uv run --no-sync --active python $(SCRIPTS)/make/build.py --no-sources
226230

227-
# Build the development distribution with build.py (standard version; build.py
228-
# also supports a PEP 440 .dev version via --dev). Used by contributors and the
229-
# smoke tests.
231+
# Build a development distribution with build.py: the release base with a
232+
# unique, timestamped PEP 440 dev suffix (e.g. 0.2.2.dev202607231430+abc1234).
233+
# Used by contributors, the smoke tests, and the nightly pipeline. Set
234+
# DEV_VERSION=... to use an exact version instead.
230235
build-dev:
231-
@$(call use_env,VENV) && uv run --no-sync --active python $(SCRIPTS)/make/build.py
236+
@$(call use_env,VENV) && uv run --no-sync --active python $(SCRIPTS)/make/build.py --dev
232237

233238
# =============================================================================
234239
# Code Quality
@@ -326,9 +331,12 @@ distclean-all:
326331
set-auto-venv:
327332
@$(SCRIPTS)/make/set_auto_venv.sh $(DEFAULT_VENV) $(SHELL_RC)
328333

329-
# Show current version
334+
# Show the development version carried on the tree (e.g. 0.2.2.dev0). Reads
335+
# _about.py as plain text (no torch import needed), so no venv is required —
336+
# stdlib-only, like _maybe_patch_pyproject. Works for a repo that vendors this
337+
# one via COREAI_OPT_VERSION_EXTENSION (e.g. printing 0.2.2.1.dev0).
330338
version:
331-
@python -c "exec(open('$(MAKEFILE_DIR)src/coreai_opt/_about.py').read()); print(__version__)"
339+
@python3 $(SCRIPTS)/make/print_version.py
332340

333341
# =============================================================================
334342
# Documentation

RELEASE.md

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,53 @@ The OSS release process for Core AI Optimization is being defined. This page wil
55
Available locally:
66

77
```bash
8-
make build # build the package wheel
9-
make version # show current version
8+
make build # build the canonical, publishable wheel + sdist (uv build --no-sources)
9+
make build-dev # build a timestamped dev wheel (e.g. 0.2.2.dev202607231430+abc1234)
10+
make version # show the development version carried on the tree (e.g. 0.2.2.dev0)
1011
make clean # remove build artifacts
1112
```
1213

14+
## Version scheme
15+
16+
`main` always carries the version planned for the *next* release, so ongoing
17+
development is never mistaken for an already-published version and a release
18+
can be stabilized, tested, and published on its own branch independently of
19+
later changes on `main`. (The release-branch workflow itself — branch naming,
20+
tagging, backporting fixes to `main` — will be documented separately in the
21+
release schedule doc; this section covers only the version string mechanics.)
22+
23+
Concretely, `main` carries the next planned release with a `.dev0` suffix in
24+
`src/coreai_opt/_about.py` (e.g. `0.2.2.dev0`). `.dev0` is only ever an on-tree
25+
marker — it never appears in a built wheel; both `make build` and
26+
`make build-dev` remove it before building.
27+
28+
- `make build` strips the `.dev` suffix, e.g. `0.2.2.dev0` -> `0.2.2`. A
29+
release is cut on a release branch by dropping the `.dev0` suffix in
30+
`_about.py` and tagging `v0.2.2`; the release workflow verifies the tag
31+
matches `_about.py`.
32+
- `make build-dev` replaces the suffix with a unique
33+
`.dev<UTC-timestamp>+<short-sha>` — used by contributors, smoke tests, and
34+
the nightly pipeline. `DEV_VERSION=<version>` uses that version exactly
35+
instead.
36+
37+
Sorting is preserved: `0.2.2.dev0 < 0.2.2.dev202607231430+abc1234 < 0.2.2`.
38+
39+
### Extending the scheme downstream
40+
41+
A repo that vendors this one under `external/` and includes this `Makefile`
42+
building a single combined wheel from both trees — can insert one extra
43+
release segment by setting `COREAI_OPT_VERSION_EXTENSION=<segment>` (e.g.
44+
`"1"`) before calling `make build` / `make build-dev` / `make version`, all
45+
unchanged:
46+
47+
- on tree: `0.2.2.dev0` + extension `1` -> `0.2.2.1.dev0`
48+
- `make build` -> `0.2.2.1`
49+
- `make build-dev` -> `0.2.2.1.dev<UTC-timestamp>+<short-sha>`
50+
51+
There is still exactly one `_about.py` (this package's own, found at
52+
`src/coreai_opt/_about.py` or `external/src/coreai_opt/_about.py`); the
53+
extension is a plain string composed entirely in
54+
`scripts/release/release_utils.apply_version_extension` — no downstream file,
55+
package, or version-composition logic is involved.
56+
1357
<!-- TODO: Document the chosen OSS release workflow (PyPI trusted publishing, twine upload, or uv publish). -->

changelog.d/53.changed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Carry the next planned release with a `.dev0` suffix on `main` (e.g. `0.2.2.dev0`). `make build` strips the suffix for a clean release and `make build-dev` builds a unique, timestamped dev wheel (`0.2.2.dev<timestamp>+<shortsha>`). A repo that vendors this one can insert one extra release segment via the `COREAI_OPT_VERSION_EXTENSION` environment variable to extend the version scheme.

scripts/make/build.py

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,20 @@
66
"""Build the coreai-opt package.
77
88
Usage:
9-
build.py Build a standard wheel from the current version
10-
build.py --dev Build a dev wheel with a PEP 440 .dev version
9+
build.py --no-sources Build a release: the `.dev` suffix stripped, via
10+
`uv build --no-sources` (ignores [tool.uv.sources];
11+
the recommended way to build the publishable
12+
artifact). Called by `make build`.
13+
build.py --dev Build a dev wheel with a timestamped PEP 440 .dev
14+
version. Called by `make build-dev`.
15+
16+
``main`` carries the next planned release with a ``.dev0`` suffix in ``_about.py``
17+
(e.g. ``0.2.2.dev0``); that suffix is only ever an on-tree marker and must never
18+
end up in a built wheel. This script computes the version to build, writes it
19+
into ``_about.py``, builds, then restores the file. A repo that vendors this one
20+
(building a single combined wheel from both trees) can insert one extra release
21+
segment via ``COREAI_OPT_VERSION_EXTENSION``; see
22+
``scripts/release/release_utils.apply_version_extension``.
1123
"""
1224

1325
from __future__ import annotations
@@ -24,43 +36,66 @@
2436
# internal-only scripts use the explicit `external.scripts.*` form instead.
2537
from scripts._utils import find_repo_root as _find_repo_root
2638
from scripts.release.release_utils import (
27-
get_dev_release_version,
28-
get_package_version,
39+
ENV_VERSION_EXTENSION,
40+
apply_version_extension,
41+
read_version,
42+
resolve_about_path,
43+
resolve_build_version,
44+
strip_dev_suffix,
2945
write_version,
3046
)
3147

3248

33-
def run_build() -> None:
34-
"""Run ``uv build`` to produce the wheel and sdist."""
49+
def run_build(*, no_sources: bool) -> None:
50+
"""Run ``uv build`` to produce the wheel and sdist.
51+
52+
Args:
53+
no_sources: Pass ``--no-sources`` to ``uv build``, ignoring
54+
``[tool.uv.sources]`` so the artifact doesn't depend on uv-specific
55+
index overrides — used for the publishable release build.
56+
"""
3557
print(f"Building package with python (version: {sys.version})...")
36-
subprocess.run(["uv", "build"], check=True)
58+
command = ["uv", "build", *(["--no-sources"] if no_sources else [])]
59+
subprocess.run(command, check=True)
3760
print("Build complete! Check dist/ directory")
3861

3962

4063
def _build_parser() -> argparse.ArgumentParser:
4164
parser = argparse.ArgumentParser(description="Build the coreai-opt package.")
42-
parser.add_argument(
65+
mode = parser.add_mutually_exclusive_group(required=True)
66+
mode.add_argument(
4367
"--dev",
4468
action="store_true",
45-
help="Build a dev wheel with a PEP 440 .dev version",
69+
help="Build a dev wheel with a timestamped PEP 440 .dev version",
70+
)
71+
mode.add_argument(
72+
"--no-sources",
73+
action="store_true",
74+
help="Build a release via `uv build --no-sources`",
4675
)
4776
return parser
4877

4978

5079
def main() -> None:
5180
args = _build_parser().parse_args()
52-
if not args.dev:
53-
run_build()
54-
return
5581
repo_root = _find_repo_root(Path(__file__))
56-
original_version = get_package_version(repo_root)
57-
dev_version = os.environ.get("DEV_VERSION") or get_dev_release_version(original_version)
82+
83+
about = resolve_about_path(repo_root)
84+
original = about.read_text(encoding="utf-8") # exact bytes to restore afterwards
85+
on_tree_version = read_version(original) # e.g. "0.2.2.dev0"
86+
extended = apply_version_extension(on_tree_version, os.environ.get(ENV_VERSION_EXTENSION))
87+
release_base = strip_dev_suffix(extended)
88+
build_version = resolve_build_version(
89+
release_base,
90+
dev=args.dev,
91+
dev_version_override=os.environ.get("DEV_VERSION"),
92+
)
5893
try:
59-
write_version(repo_root, dev_version)
60-
print(f"Dev version: {dev_version}")
61-
run_build()
94+
write_version(about, build_version)
95+
print(f"Version: {build_version}")
96+
run_build(no_sources=args.no_sources)
6297
finally:
63-
write_version(repo_root, original_version)
98+
about.write_text(original, encoding="utf-8", newline="\n") # restore on-tree version
6499

65100

66101
if __name__ == "__main__":

scripts/make/print_version.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/env python3
2+
3+
# Copyright 2026 Apple Inc.
4+
#
5+
# Use of this source code is governed by a BSD-3-Clause license that can
6+
# be found in the LICENSE file or at https://opensource.org/licenses/BSD-3-Clause
7+
8+
"""Print the development version carried on the tree (e.g. 0.2.2.dev0).
9+
10+
Reads ``_about.py`` as plain text (no import, so no torch dependency). A repo
11+
that vendors this one can insert one extra release segment via
12+
``COREAI_OPT_VERSION_EXTENSION`` (e.g. printing ``0.2.2.1.dev0``); see
13+
``scripts/release/release_utils.apply_version_extension``.
14+
"""
15+
16+
import os
17+
from pathlib import Path
18+
19+
from scripts._utils import find_repo_root
20+
from scripts.release.release_utils import (
21+
ENV_VERSION_EXTENSION,
22+
apply_version_extension,
23+
read_version,
24+
resolve_about_path,
25+
)
26+
27+
28+
def main() -> None:
29+
about = resolve_about_path(find_repo_root(Path(__file__)))
30+
on_tree_version = read_version(about.read_text(encoding="utf-8"))
31+
print(apply_version_extension(on_tree_version, os.environ.get(ENV_VERSION_EXTENSION)))
32+
33+
34+
if __name__ == "__main__":
35+
main()

0 commit comments

Comments
 (0)