Skip to content

Commit 9a16a1b

Browse files
edvilmeCopilot
andcommitted
Enforce matching extension and API package versions
Add scripts/compare_package_versions.py and a CI job to verify the extension (package.json) and published API package (api/package.json) share the same version. Bump api package to 1.37.0 to match. Add a check-for-changed-files gate requiring an api/package.json version bump when src/api.ts changes (bypass with the 'skip api version' label). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 86ff0da commit 9a16a1b

5 files changed

Lines changed: 104 additions & 3 deletions

File tree

.github/workflows/pr-file-check.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,28 @@ jobs:
4141
src/**/*.unit.test.ts
4242
skip-label: 'skip tests'
4343
failure-message: 'TypeScript code was edited without also editing a test file (the ${skip-label} label can be used to pass this check)'
44+
45+
- name: 'Public API changes require a version bump'
46+
uses: brettcannon/check-for-changed-files@871d7b8b5917a4f6f06662e2262e8ffc51dff6d1 # v1.2.1
47+
with:
48+
prereq-pattern: 'src/api.ts'
49+
file-pattern: 'api/package.json'
50+
skip-label: 'skip api version'
51+
failure-message: 'The public API (${prereq-pattern}) was changed without bumping the package version in ${file-pattern} (the ${skip-label} label can be used to pass this check)'
52+
53+
version-match:
54+
name: 'Extension and API package versions match'
55+
runs-on: ubuntu-latest
56+
permissions:
57+
contents: read
58+
steps:
59+
- name: Checkout
60+
uses: actions/checkout@v4
61+
62+
- name: Set up Python
63+
uses: actions/setup-python@v5
64+
with:
65+
python-version: '3.x'
66+
67+
- name: 'Compare package.json versions'
68+
run: python scripts/compare_package_versions.py

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ The npm package under [`api/`](./api) is the public API facade other extensions
9696
- Edit the API only in `src/api.ts`. This file contains the full public surface, including the runtime `PythonEnvironments.api()` helper and `EXTENSION_ID`. `api/src/main.ts` is a build artifact — never edit or commit it.
9797
- `api/src/main.ts` is produced by the publish pipeline ([`build/azure-pipeline.npm.yml`](./build/azure-pipeline.npm.yml)), which copies `src/api.ts` to `api/src/main.ts` before compiling. The api package is therefore built in CI only; to build it locally, copy the file first (e.g. `cp src/api.ts api/src/main.ts`).
9898
- `src/api.ts` itself is validated on every PR by the extension's own lint and TypeScript compile.
99+
- **Versioning:** the published package version in [`api/package.json`](./api/package.json) must always match the extension version in [`package.json`](./package.json). CI enforces this via [`scripts/compare_package_versions.py`](./scripts/compare_package_versions.py). Additionally, any PR that edits `src/api.ts` must bump `api/package.json` (use the `skip api version` label to bypass). When bumping, update both files so they stay in sync.
99100

100101
## Questions or Issues?
101102

api/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@vscode/python-environments",
33
"description": "An API facade for the Python Environments extension in VS Code",
4-
"version": "1.0.0",
4+
"version": "1.37.0",
55
"author": {
66
"name": "Microsoft Corporation"
77
},
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#!/usr/bin/env python3
2+
"""Verify that the extension and the public API npm package share the same version.
3+
4+
The root ``package.json`` (the VS Code extension) and ``api/package.json`` (the
5+
published ``@vscode/python-environments`` npm package) must always declare the same
6+
``version``. This keeps the published API package in lock-step with the extension
7+
that implements it.
8+
9+
Exits with status 0 when the versions match, and status 1 (printing an error) when
10+
they differ. Intended to be run from CI, but can also be run locally:
11+
12+
python scripts/compare_package_versions.py
13+
"""
14+
15+
from __future__ import annotations
16+
17+
import json
18+
import sys
19+
from pathlib import Path
20+
21+
REPO_ROOT = Path(__file__).resolve().parent.parent
22+
EXTENSION_PACKAGE = REPO_ROOT / "package.json"
23+
API_PACKAGE = REPO_ROOT / "api" / "package.json"
24+
25+
26+
def read_version(package_json: Path) -> str:
27+
"""Return the ``version`` field from the given package.json file.
28+
29+
Args:
30+
package_json: Path to a ``package.json`` file.
31+
32+
Returns:
33+
The declared version string.
34+
35+
Raises:
36+
SystemExit: If the file is missing, unparseable, or has no ``version``.
37+
"""
38+
try:
39+
data = json.loads(package_json.read_text(encoding="utf-8"))
40+
except FileNotFoundError:
41+
sys.exit(f"::error::{package_json} not found")
42+
except json.JSONDecodeError as exc:
43+
sys.exit(f"::error::Failed to parse {package_json}: {exc}")
44+
45+
version = data.get("version")
46+
if not isinstance(version, str) or not version:
47+
sys.exit(f"::error::{package_json} is missing a 'version' field")
48+
return version
49+
50+
51+
def main() -> int:
52+
"""Compare the extension and API package versions.
53+
54+
Returns:
55+
0 when the versions match, 1 when they differ.
56+
"""
57+
extension_version = read_version(EXTENSION_PACKAGE)
58+
api_version = read_version(API_PACKAGE)
59+
60+
print(f"Extension version (package.json): {extension_version}")
61+
print(f"API package version (api/package.json): {api_version}")
62+
63+
if extension_version != api_version:
64+
print(
65+
f"::error::Version mismatch: package.json is {extension_version} but "
66+
f"api/package.json is {api_version}. Update api/package.json to match."
67+
)
68+
return 1
69+
70+
print("Versions match.")
71+
return 0
72+
73+
74+
if __name__ == "__main__":
75+
raise SystemExit(main())

0 commit comments

Comments
 (0)