From 5324163407801ee71efb4059d1124e0669e28007 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Nov 2026 23:10:00 +0000 Subject: [PATCH 1/2] Initial plan From 1581f8cb8e8d0b10d3a1139d4b62a75c867b38b7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Nov 2026 23:13:16 +0000 Subject: [PATCH 2/2] Retire package version automation Co-authored-by: edvilme <5952839+edvilme@users.noreply.github.com> --- .github/workflows/auto-patch-bump.yml | 138 -------------------------- .github/workflows/pr-version.yml | 23 ----- .github/workflows/version-check.yml | 15 --- CONTRIBUTING.md | 76 ++++---------- VERSION | 1 - scripts/version/__init__.py | 47 --------- scripts/version/bump_patch.py | 45 --------- scripts/version/sync.py | 73 -------------- scripts/version/validate.py | 49 --------- 9 files changed, 22 insertions(+), 445 deletions(-) delete mode 100644 .github/workflows/auto-patch-bump.yml delete mode 100644 .github/workflows/pr-version.yml delete mode 100644 .github/workflows/version-check.yml delete mode 100644 VERSION delete mode 100644 scripts/version/__init__.py delete mode 100644 scripts/version/bump_patch.py delete mode 100644 scripts/version/sync.py delete mode 100644 scripts/version/validate.py diff --git a/.github/workflows/auto-patch-bump.yml b/.github/workflows/auto-patch-bump.yml deleted file mode 100644 index 5d487ea..0000000 --- a/.github/workflows/auto-patch-bump.yml +++ /dev/null @@ -1,138 +0,0 @@ -name: Auto Patch Bump - -on: - schedule: - - cron: '0 0 * * 1' # Every Monday at 00:00 UTC - workflow_dispatch: - -concurrency: - group: auto-patch-bump - cancel-in-progress: true - -permissions: - contents: write - pull-requests: write - -jobs: - bump-patch: - name: Bump patch version - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - fetch-depth: 0 - - - name: Get latest release tag - id: release - run: | - if ! TAG=$(gh release view --json tagName -q '.tagName'); then - echo "No releases found (or API error). Skipping." - echo "skip=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - if [ -z "$TAG" ]; then - echo "No releases found. Skipping." - echo "skip=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - echo "tag=$TAG" >> "$GITHUB_OUTPUT" - echo "skip=false" >> "$GITHUB_OUTPUT" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Check for new commits since release - if: steps.release.outputs.skip != 'true' - id: check - run: | - COMMITS=$(git log "${TAG}..HEAD" --oneline) - if [ -z "$COMMITS" ]; then - echo "No new commits since $TAG. Skipping." - echo "has_new_commits=false" >> "$GITHUB_OUTPUT" - else - echo "New commits found since $TAG:" - echo "$COMMITS" - echo "has_new_commits=true" >> "$GITHUB_OUTPUT" - fi - env: - TAG: ${{ steps.release.outputs.tag }} - - - name: Handle existing bump PR - if: steps.release.outputs.skip != 'true' && steps.check.outputs.has_new_commits == 'true' - id: existing_pr - run: | - echo "proceed=true" >> "$GITHUB_OUTPUT" - - # Find any open PR with the auto/bump-version- branch prefix - if ! EXISTING_PR=$(gh pr list --state open --json number,headRefName,headRefOid \ - -q '[.[] | select(.headRefName | startswith("auto/bump-version-"))] | .[0]'); then - echo "WARNING: Failed to list PRs; proceeding without stale-PR check." >&2 - exit 0 - fi - EXISTING_PR=${EXISTING_PR:-""} - if [ -n "$EXISTING_PR" ] && [ "$EXISTING_PR" != "null" ]; then - PR_NUMBER=$(echo "$EXISTING_PR" | jq -r '.number') - BRANCH_NAME=$(echo "$EXISTING_PR" | jq -r '.headRefName') - PR_HEAD_SHA=$(echo "$EXISTING_PR" | jq -r '.headRefOid') - echo "Found existing bump PR #$PR_NUMBER on branch $BRANCH_NAME at $PR_HEAD_SHA" - - git fetch origin main - MAIN_SHA=$(git rev-parse origin/main) - - # Check if main advanced past where the PR branched - FORK_POINT=$(git merge-base "$PR_HEAD_SHA" "$MAIN_SHA") - if [ "$FORK_POINT" != "$MAIN_SHA" ]; then - # main has advanced beyond the bump PR's fork point, so replace the stale PR - gh pr close "$PR_NUMBER" --comment "Closing: new commits have landed on main since this PR was created. A fresh bump PR will be opened." - git push origin --delete "$BRANCH_NAME" 2>/dev/null || true - echo "Closed stale PR #$PR_NUMBER and deleted branch $BRANCH_NAME" - else - echo "Existing bump PR #$PR_NUMBER already covers the current state of main. Skipping." - echo "proceed=false" >> "$GITHUB_OUTPUT" - fi - fi - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 - if: steps.release.outputs.skip != 'true' && steps.check.outputs.has_new_commits == 'true' && steps.existing_pr.outputs.proceed == 'true' - with: - python-version: '3.x' - - - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 - if: steps.release.outputs.skip != 'true' && steps.check.outputs.has_new_commits == 'true' && steps.existing_pr.outputs.proceed == 'true' - with: - node-version-file: '.nvmrc' - - - name: Bump version and sync manifests - if: steps.release.outputs.skip != 'true' && steps.check.outputs.has_new_commits == 'true' && steps.existing_pr.outputs.proceed == 'true' - id: version - run: | - python -m scripts.version.bump_patch - NEW_VERSION=$(cat VERSION | tr -d '[:space:]') - echo "new_version=$NEW_VERSION" >> "$GITHUB_OUTPUT" - - - name: Create branch, commit, and open PR - if: steps.release.outputs.skip != 'true' && steps.check.outputs.has_new_commits == 'true' && steps.existing_pr.outputs.proceed == 'true' - run: | - BRANCH="auto/bump-version-${NEW_VERSION}" - - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - # Delete remote branch if it exists from a prior failed run - git push origin --delete "$BRANCH" 2>/dev/null || true - - git checkout -b "$BRANCH" - git add -A - git commit -m "Bump version to ${NEW_VERSION}" - git push origin "$BRANCH" - - gh pr create \ - --base main \ - --head "$BRANCH" \ - --title "Bump version to ${NEW_VERSION}" \ - --body "Automated weekly patch bump. New commits were found on \`main\` since the last release." \ - --label "debt" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NEW_VERSION: ${{ steps.version.outputs.new_version }} diff --git a/.github/workflows/pr-version.yml b/.github/workflows/pr-version.yml deleted file mode 100644 index f7d9b7c..0000000 --- a/.github/workflows/pr-version.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Check version match -on: - pull_request: - types: - - 'opened' - - 'reopened' - - 'labeled' - - 'unlabeled' - - 'synchronize' -permissions: - pull-requests: read - -jobs: - check-version-match: - name: 'Check version match' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0 - with: - python-version: '3.x' - - name: Validate version consistency - run: python -m scripts.version.validate diff --git a/.github/workflows/version-check.yml b/.github/workflows/version-check.yml deleted file mode 100644 index f085398..0000000 --- a/.github/workflows/version-check.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Version Check - -on: - pull_request: - branches: [main] - -jobs: - version-consistency: - name: Verify version consistency - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - - name: Check version consistency - run: python -m scripts.version.validate diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9daae17..f713110 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,60 +18,28 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio 2. Make your changes and ensure they pass the existing tests and linting. 3. Submit a pull request with a clear description of the changes. -## Versioning - -This project adheres to [Semantic Versioning (SemVer)](https://semver.org/). -In short: - -- **MAJOR** (`X.0.0`) – incompatible API changes. -- **MINOR** (`0.X.0`) – new functionality that is backwards-compatible. -- **PATCH** (`0.0.X`) – backwards-compatible bug fixes. - -We cut a new **patch release every week** if there are any unreleased fixes on -`main`. If no fixes have landed since the last release, the weekly release is -skipped. All released versions are listed on the -[GitHub Releases](https://github.com/microsoft/vscode-common-python-lsp/releases) page. - -Both the TypeScript and Python packages share a single version number defined in -the `VERSION` file at the repository root. When bumping the version: - -```bash -# Set the version and propagate to package.json + pyproject.toml -python -m scripts.version.sync 0.2.0 -``` - -To verify all manifests are in sync (also runs automatically in CI): - -```bash -python -m scripts.version.validate -``` - -A PR will fail the **Version Check** workflow if the version in `VERSION`, -`typescript/package.json`, or `python/pyproject.toml` don't match. - -### Automated weekly patch bump - -A GitHub Actions workflow (`.github/workflows/auto-patch-bump.yml`) runs every -Monday at 00:00 UTC. If there are unreleased commits on `main` since the last -GitHub Release, the workflow automatically opens a PR that increments the patch -version and syncs all manifests. If a previous bump PR is still open and `main` -has advanced beyond it (new commits landed since the PR was created), the stale -PR is closed and replaced with a fresh one. If the existing PR still covers the -current state of `main`, no action is taken. Maintainers review and merge the -bump PR before creating the release. - -### Release process - -1. Bump the version: `python -m scripts.version.sync X.Y.Z` -2. Update `CHANGELOG.md` with the new version and release notes. -3. Commit, push, and merge the PR. -4. After GitHub Actions validation passes, manually create a GitHub Release - tagged `vX.Y.Z` at the merged commit, using the changelog for release notes. - -Azure DevOps release automation is no longer used. Extensions consume this -repository as a git submodule pinned to a commit; GitHub Releases are versioned -milestones, not a prerequisite for submodule updates. The npm and Python packages -are not published. +## Adoption and milestones + +Extensions consume this repository at a pinned Git commit and build both the +TypeScript and Python runtimes from that revision. The pinned source revision, +not the version strings in the package manifests, is the compatibility identity. + +The private npm manifest and Python package metadata retain fixed version values +needed for local builds and installation. Ordinary source changes do not require +changing or synchronizing those values. The default Python server reports its +installed package version for diagnostics, but that value is not a source +provenance guarantee. The npm and Python packages are not published. + +Maintainers may optionally tag a reviewed commit and create a +[GitHub Release](https://github.com/microsoft/vscode-common-python-lsp/releases) +with release notes as a human-readable milestone. A milestone does not require +package-version changes or publication and does not trigger or gate consumer +submodule updates. + +Consumers are responsible for building both runtimes from their intended pinned +revision. Automated artifact-provenance enforcement and reliable build +invalidation remain roadmap S4 follow-up work rather than a package-version +release requirement. ## Reporting Issues diff --git a/VERSION b/VERSION deleted file mode 100644 index ee94dd8..0000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.8.3 diff --git a/scripts/version/__init__.py b/scripts/version/__init__.py deleted file mode 100644 index c89f82c..0000000 --- a/scripts/version/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. -"""Shared version utilities for the vscode-common-python-lsp monorepo.""" - -from __future__ import annotations - -import json -import re -import sys -from pathlib import Path - -if sys.version_info >= (3, 11): - import tomllib -else: - tomllib = None # type: ignore[assignment] - -ROOT = Path(__file__).resolve().parent.parent.parent -VERSION_FILE = ROOT / "VERSION" -PACKAGE_JSON = ROOT / "typescript" / "package.json" -PYPROJECT_TOML = ROOT / "python" / "pyproject.toml" - -SEMVER_RE = re.compile(r"^(\d+)\.(\d+)\.(\d+)$") - - -def read_versions() -> dict[str, str]: - """Read version strings from all manifest files.""" - pkg = json.loads(PACKAGE_JSON.read_text(encoding="utf-8")) - - if tomllib is not None: - with PYPROJECT_TOML.open("rb") as f: - toml_data = tomllib.load(f) - toml_version = toml_data.get("project", {}).get("version") - if toml_version is None: - raise ValueError("pyproject.toml: [project].version key not found") - else: - # Fallback regex for Python < 3.11 without tomllib - toml_text = PYPROJECT_TOML.read_text(encoding="utf-8") - m = re.search(r'^version\s*=\s*"([^"]+)"', toml_text, re.MULTILINE) - if m is None: - raise ValueError("pyproject.toml: could not find version field") - toml_version = m.group(1) - - return { - "VERSION": VERSION_FILE.read_text(encoding="utf-8").strip(), - "package.json": pkg["version"], - "pyproject.toml": toml_version, - } diff --git a/scripts/version/bump_patch.py b/scripts/version/bump_patch.py deleted file mode 100644 index 361e781..0000000 --- a/scripts/version/bump_patch.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. -"""Bump the patch version and synchronize all package manifests. - -Usage: - python -m scripts.version.bump_patch -""" - -from __future__ import annotations - -import sys - -from . import SEMVER_RE, VERSION_FILE - -# NOTE: sync.main() reads the version from VERSION_FILE (the else branch in -# sync.py) when sys.argv has no extra arguments, which is the case here. -from .sync import main as sync_manifests - - -def main() -> None: - current = VERSION_FILE.read_text(encoding="utf-8").strip() - match = SEMVER_RE.fullmatch(current) - if not match: - print(f"ERROR: VERSION file contains invalid semver: '{current}'", file=sys.stderr) - sys.exit(1) - - major, minor, patch = match.group(1, 2, 3) - new_version = f"{major}.{minor}.{int(patch) + 1}" - - VERSION_FILE.write_text(new_version + "\n", encoding="utf-8") - print(f"VERSION bumped: {current} -> {new_version}") - - # Propagate to package.json, pyproject.toml, and package-lock.json - try: - sync_manifests() - except Exception: - # Restore original version to avoid inconsistent state - VERSION_FILE.write_text(current + "\n", encoding="utf-8") - print("ERROR: sync failed, restored original VERSION", file=sys.stderr) - raise - - -if __name__ == "__main__": - main() diff --git a/scripts/version/sync.py b/scripts/version/sync.py deleted file mode 100644 index 95d9786..0000000 --- a/scripts/version/sync.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. -"""Set and synchronize the version across all package manifests. - -Usage: - python -m scripts.version.sync # propagate VERSION → manifests - python -m scripts.version.sync 0.2.0 # set VERSION to 0.2.0, then propagate -""" - -from __future__ import annotations - -import re -import subprocess -import sys - -from . import PACKAGE_JSON, PYPROJECT_TOML, SEMVER_RE, VERSION_FILE - - -def main() -> None: - if len(sys.argv) > 1: - version = sys.argv[1] - if not SEMVER_RE.match(version): - print(f"ERROR: '{version}' is not valid semver (X.Y.Z)") - sys.exit(1) - VERSION_FILE.write_text(version + "\n", encoding="utf-8") - print(f"VERSION set to {version}") - else: - version = VERSION_FILE.read_text(encoding="utf-8").strip() - - changed = [] - - # package.json — regex replacement to preserve formatting - pkg_text = PACKAGE_JSON.read_text(encoding="utf-8") - new_pkg_text = re.sub( - r'("version"\s*:\s*")[^"]+(")', - rf"\g<1>{version}\2", - pkg_text, - count=1, - ) - if new_pkg_text != pkg_text: - PACKAGE_JSON.write_text(new_pkg_text, encoding="utf-8") - changed.append("package.json") - # Keep package-lock.json in sync - subprocess.run( - ["npm", "install", "--package-lock-only"], - cwd=PACKAGE_JSON.parent, - check=True, - shell=(sys.platform == "win32"), - ) - changed.append("package-lock.json") - - # pyproject.toml - content = PYPROJECT_TOML.read_text(encoding="utf-8") - new_content = re.sub( - r'^(version\s*=\s*")[^"]+(")', - rf"\g<1>{version}\2", - content, - count=1, - flags=re.MULTILINE, - ) - if new_content != content: - PYPROJECT_TOML.write_text(new_content, encoding="utf-8") - changed.append("pyproject.toml") - - if changed: - print(f"Updated: {', '.join(changed)}") - else: - print("All manifests already at the correct version.") - - -if __name__ == "__main__": - main() diff --git a/scripts/version/validate.py b/scripts/version/validate.py deleted file mode 100644 index 46289c5..0000000 --- a/scripts/version/validate.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. -"""Validate version consistency across VERSION, package.json, and pyproject.toml. - -Usage: - python -m scripts.version.validate # verify manifests match VERSION - python -m scripts.version.validate v0.2.0 # also verify against a git tag -""" - -from __future__ import annotations - -import sys - -from . import SEMVER_RE, read_versions - - -def main() -> None: - versions = read_versions() - tag = None - if len(sys.argv) > 1: - raw_tag = sys.argv[1] - tag = raw_tag.removeprefix("v") - if not SEMVER_RE.fullmatch(tag): - print(f"ERROR: Tag must be in the format vX.Y.Z; got: {raw_tag}") - sys.exit(1) - reference = tag or versions["VERSION"] - - for source, value in versions.items(): - status = "✅" if value == reference else "❌" - print(f" {status} {source}: {value}") - if tag: - print(f" 🏷️ tag: {tag}") - - mismatches = [s for s, v in versions.items() if v != reference] - if mismatches: - print(f"\nVersion mismatch in: {', '.join(mismatches)}") - print() - print("To fix, run:") - print(f" python -m scripts.version.sync {reference}") - print() - print("This updates VERSION, package.json, and pyproject.toml to match.") - sys.exit(1) - - print(f"\nAll versions consistent: {reference}") - - -if __name__ == "__main__": - main()