Skip to content

Commit 8ce4fb5

Browse files
authored
Ensure magics package version is consistent in future releases (#1280)
* implement and use new bump_version.py script * pre-commit * install tomlkit in releaser workflow * update lerna run script * set --skip-if-dirty * remove irrelevant comment * skip check-python step * add jupyter_releaser and tomlkit to dev deps
1 parent d4dcef9 commit 8ce4fb5

File tree

4 files changed

+132
-22
lines changed

4 files changed

+132
-22
lines changed

.jupyter-releaser.toml

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,24 @@
1+
# TODO: do not skip the check-python step.
2+
#
3+
# We have to skip this for now since Jupyter Releaser doesn't support monorepos
4+
# with interdependent packages that require the exact same version unless this
5+
# step is disabled.
6+
#
7+
# Upstream issue: https://github.com/jupyter-server/jupyter_releaser/issues/499
8+
skip = ["check-python"]
9+
110
[hooks]
211
before-build-npm = [
312
"python -m pip install jupyterlab~=4.0",
413
"jlpm",
5-
"jlpm build:prod"
6-
]
7-
before-build-python = [
8-
"jlpm clean:all"
14+
"jlpm build:prod",
915
]
16+
before-build-python = ["jlpm clean:all"]
17+
before-bump-version = ["python -m pip install tomlkit"]
1018

1119
[options]
12-
version-cmd = "../../scripts/bump-version.sh"
20+
version-cmd = "python ../../scripts/bump-version.py --skip-if-dirty"
1321
python_packages = [
1422
"packages/jupyter-ai:jupyter-ai",
15-
"packages/jupyter-ai-magics:jupyter-ai-magics"
23+
"packages/jupyter-ai-magics:jupyter-ai-magics",
1624
]

packages/jupyter-ai-magics/pyproject.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,13 @@ dependencies = [
3434
]
3535

3636
[project.optional-dependencies]
37-
dev = ["pre-commit>=3.3.3,<4"]
37+
dev = [
38+
# used to run our linters and formatters locally
39+
"pre-commit>=3.3.3,<4",
40+
# used to test release scripts
41+
"jupyter_releaser",
42+
"tomlkit",
43+
]
3844

3945
test = ["coverage", "pytest", "pytest-asyncio", "pytest-cov"]
4046

scripts/bump-version.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Copyright (c) Jupyter Development Team.
2+
# Distributed under the terms of the Modified BSD License.
3+
4+
"""
5+
The "bump version" script used by Jupyter Releaser to increment the version of
6+
Jupyter AI on manual release workflow runs. This script:
7+
8+
- Accepts a *specified version* `spec_version` as the first positional argument.
9+
`spec_version` must be either a PEP-440 version string or "minor" literally.
10+
11+
- Normal release version examples: "0.1.0", "1.2.3", "3.0.0"
12+
13+
- Pre-release version examples: "3.0.0a0", "3.0.0b1", "3.0.0rc2"
14+
15+
- **NOTE**: This script was not designed to support dev & post-releases for
16+
simplicity. By convention, our repo prefers patch releases over
17+
post-releases and pre-releases over dev releases.
18+
19+
- Bumps `jupyter-ai` and `jupyter-ai-magics` to `spec_version`. If
20+
`spec_version` is "minor", then this script bumps the minor version of each
21+
package.
22+
23+
- Updates `jupyter-ai`'s required version of `jupyter-ai-magics` to exactly
24+
match the specified version. In other words, this script ensures
25+
`jupyter-ai==x.y.z` always depends on `jupyter-ai-magics==x.y.z` exactly.
26+
27+
- If `--skip-if-dirty` is passed, successive calls do nothing. This is a
28+
temporary workaround for
29+
https://github.com/jupyter-server/jupyter_releaser/issues/567.
30+
"""
31+
32+
from pathlib import Path
33+
34+
import click
35+
import tomlkit
36+
from jupyter_releaser.util import get_version, run
37+
from packaging.version import Version
38+
from pkg_resources import parse_version
39+
40+
MONOREPO_ROOT = Path(__file__).parent.parent.resolve()
41+
LERNA_CMD = "npx -p [email protected] -y lerna version --no-push --force-publish --no-git-tag-version -y"
42+
43+
44+
@click.command()
45+
@click.option("--ignore-dirty", default=False, is_flag=True)
46+
@click.option("--skip-if-dirty", default=False, is_flag=True)
47+
@click.argument("spec", nargs=1)
48+
def bump_version(ignore_dirty: bool, skip_if_dirty: bool, spec: str):
49+
is_dirty = len(run("git status --porcelain").strip()) > 0
50+
if is_dirty and not ignore_dirty:
51+
if skip_if_dirty:
52+
print(
53+
"Skipping this call as the repo is in a dirty state with untracked files."
54+
)
55+
return
56+
raise Exception("Must be in a clean git state with no untracked files")
57+
58+
next_version: Version = compute_next_version(spec)
59+
60+
# convert the PyPI version string to a NPM version string
61+
next_version_npm = f"{next_version.major}.{next_version.minor}.{next_version.micro}"
62+
if next_version.pre:
63+
pre_type, pre_number = next_version.pre
64+
if pre_type == "a":
65+
pre_type = "alpha"
66+
elif pre_type == "b":
67+
pre_type = "beta"
68+
elif pre_type == "rc":
69+
pre_type = "rc"
70+
else:
71+
raise Exception(f"Unrecognized pre-release type: '{pre_type}'.")
72+
next_version_npm += f"-{pre_type}.{pre_number}"
73+
74+
# bump the versions in NPM packages
75+
#
76+
# Note: `_version.py` files do not need to be updated manually.
77+
# `hatch-nodejs-version` updates those files automatically on build, setting
78+
# them to the version specified in the corresponding package.json file.
79+
lerna_cmd = f"{LERNA_CMD} {next_version_npm}"
80+
run(lerna_cmd)
81+
82+
# bump the version of `jupyter-ai-magics` required by `jupyter-ai`
83+
jai_pyproject_path = MONOREPO_ROOT / "packages" / "jupyter-ai" / "pyproject.toml"
84+
jai_pyproject = tomlkit.parse(jai_pyproject_path.read_text())
85+
jai_deps = jai_pyproject.get("project").get("dependencies")
86+
for i, dep in enumerate(jai_deps):
87+
if str(dep).startswith("jupyter_ai_magics"):
88+
next_major_version = f"{next_version.major + 1}.0.0"
89+
jai_deps[i] = (
90+
f"jupyter_ai_magics>={str(next_version)},<{next_major_version}"
91+
)
92+
break
93+
94+
# write updated pyproject.toml file
95+
jai_pyproject_path.write_text(tomlkit.dumps(jai_pyproject))
96+
97+
98+
def compute_next_version(spec: str) -> Version:
99+
if spec == "minor":
100+
curr_version = parse_version(get_version())
101+
next_version = parse_version(
102+
f"{curr_version.major}.{curr_version.minor + 1}.{curr_version.micro}"
103+
)
104+
else:
105+
next_version = parse_version(spec)
106+
107+
return next_version
108+
109+
110+
if __name__ == "__main__":
111+
bump_version()

scripts/bump-version.sh

Lines changed: 0 additions & 15 deletions
This file was deleted.

0 commit comments

Comments
 (0)