Skip to content

Adopt vscode-common-python-lsp as a git submodule, synced via repository_dispatch, and stop Dependabot duplicates - #575

Merged
Eduardo Villalpando Mello (edvilme) merged 28 commits into
mainfrom
shared-package-sync
Jul 22, 2026
Merged

Adopt vscode-common-python-lsp as a git submodule, synced via repository_dispatch, and stop Dependabot duplicates#575
Eduardo Villalpando Mello (edvilme) merged 28 commits into
mainfrom
shared-package-sync

Conversation

@edvilme

@edvilme Eduardo Villalpando Mello (edvilme) commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Consumes the shared LSP package (vscode-common-python-lsp) as a git submodule pinned to a release tag, instead of the published npm/PyPI packages, and keeps that submodule updated via a repository_dispatch event from vscode-common-python-lsp rather than Dependabot.

1. Git submodule

  • Adds external/vscode-common-python-lsp (.gitmodules) pinned to a shared-package release tag.
  • TypeScript: package.json sources it via "@vscode/common-python-lsp": "file:external/vscode-common-python-lsp/typescript"; build/postinstall.js builds the submodule's TS output on install (skips when already built or when the submodule toolchain is absent).
  • Python: noxfile.py installs ./external/vscode-common-python-lsp/python into ./bundled/libs with --no-deps --upgrade, so the bundled copy matches the pinned submodule commit.

2. New .github/workflows/shared-package-submodule-sync.yml

Handles the shared-package-release repository_dispatch: branches off main, advances the submodule pointer to the released tag, refreshes the npm lockfile, verifies the pinned tag and the Python requires-python floor, then pushes the branch and opens a tracking issue with a manual compare/PR link (org settings prevent the workflow from opening PRs automatically).

3. .github/dependabot.yml

Moves the shared package into the npm and pip ignore lists so Dependabot no longer opens duplicate update PRs for it.

…icates

Adds .github/workflows/shared-package-release.yml to handle
repository_dispatch (shared-package-release) events from
vscode-common-python-lsp: it bumps the npm dep, recompiles the pip pin via
uv, pushes a branch, and opens a tracking issue with a manual-PR link
(org settings block auto-created PRs).

Removes @vscode/common-python-lsp (npm) and vscode-common-python-lsp (pip)
from Dependabot via the ignore lists so Dependabot no longer opens
duplicate update PRs for the shared package.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@edvilme Eduardo Villalpando Mello (edvilme) added the debt Technical debt or repo cleanup label Jun 30, 2026
Replace the inline Python rewrite of requirements.in with a sed one-liner,
keeping uv pip compile --generate-hashes --upgrade to regenerate the
hash-locked requirements.txt (matching the command documented in
requirements.in).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Pin uv pip compile to the Python version documented in requirements.in
  (--python-version) so markers like exceptiongroup are not dropped
- Scope the recompile with --upgrade-package so unrelated deps don't move
- Assert the pip pin actually changed after sed; fail fast otherwise
- Validate release_tag is present and version-shaped before any work
- Add concurrency group, --force-with-lease, tracking-issue dedup, and
  tolerate Issues being disabled (surface compare URL via job summary)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread .github/workflows/shared-package-submodule-sync.yml Outdated
Comment thread .github/workflows/shared-package-release.yml Outdated
Comment thread .github/workflows/shared-package-release.yml Outdated
Comment thread .github/workflows/shared-package-release.yml Outdated
Comment thread .github/workflows/shared-package-release.yml Outdated
Comment thread .github/workflows/shared-package-submodule-sync.yml
@rchiodo

Copy link
Copy Markdown

Overall the change is sound. The main theme of the feedback is a few robustness edge cases in the requirements.in pin rewrite (regex-as-literal in the sed/grep steps and unvalidated payload deps) plus a concurrency-group vs. branch-name keying mismatch. None are blocking.

@rchiodo Rich Chiodo (rchiodo) left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Review Center.

* Integrate shared package as a git submodule

Replaces the external @vscode/common-python-lsp / vscode-common-python-lsp
dependency with a git submodule at external/vscode-common-python-lsp (pinned
to v0.8.0). npm consumes it via a file: reference, the Python lib is bundled
from the submodule, and the release pipeline now bumps the submodule commit
instead of a version pin.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix CI for git submodule integration

- Build the shared package TypeScript dist via a postinstall hook so the
  bundler and type-checker can resolve the file: dependency.
- Regenerate package-lock.json to record the file: link and the submodule
  dependency tree.
- Exclude external/ from the extension tsconfig so the submodule sources are
  not type-checked against the extension rootDir.
- Check out submodules (recursive) in PR and push CI so the submodule and its
  Python library are present for npm and nox.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix VSIX packaging and type-checking for submodule integration

- Exclude external/ from the VSIX (.vscodeignore) since webpack already
  bundles the shared package; avoids case-insensitive path collisions from
  the submodule node_modules.
- Add skipLibCheck to tsconfig where missing so hoisted submodule devDep
  type definitions do not fail the extension type-check.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Checkout submodules in Azure DevOps pipelines

The 1ES/DevDiv checkout template does not initialize submodules, so add an
explicit git submodule update --init --recursive step before npm ci and nox
in the validation, stable, and pre-release pipelines.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Consolidate the sync steps into a single script, add a retry wrapper for
network operations, fail when the released tag is missing instead of
silently falling back to main, and surface failures as workflow annotations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Pin an explicit --force-with-lease baseline so concurrent branch
  updates are not silently overwritten
- Regenerate the lockfile in the sync workflow so produced branches
  stay npm ci-mergeable
- Require a real semver release_tag in the dispatch payload
- Guard the postinstall build so a clone without the submodule warns
  instead of failing npm install

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread .github/workflows/shared-package-submodule-sync.yml Outdated
Comment thread .github/workflows/shared-package-submodule-sync.yml Outdated
Comment thread build/postinstall.js
@rchiodo

Copy link
Copy Markdown

Overall looks good to merge. A few non-blocking robustness notes on the new submodule-sync workflow and the postinstall build step are worth a look, but nothing blocking. Note: several first-pass comments referenced shared-package-release.yml, which isn't part of this PR's diff, so they were dropped.

@rchiodo Rich Chiodo (rchiodo) left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Review Center.

- Split sync workflow into prepare (validate/normalize tag) and sync jobs;
  create-once branch semantics (no force-push), normalized concurrency key,
  pin actions/checkout@v4
- Fix dependabot.yml comments to reference shared-package-submodule-sync
- Drop dangling '# via vscode-common-python-lsp' annotations from requirements.txt
- Document the shared-package runtime-dep contract in requirements.in

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread .github/workflows/shared-package-submodule-sync.yml
Comment thread .github/workflows/shared-package-submodule-sync.yml Outdated
Comment thread build/postinstall.js
Comment thread .github/workflows/shared-package-submodule-sync.yml

@rchiodo Rich Chiodo (rchiodo) left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Review Center.

@rchiodo

Rich Chiodo (rchiodo) commented Jul 6, 2026

Copy link
Copy Markdown

🔒 Automated review in progress — Rich Chiodo (@rchiodo) is auto-reviewing this PR.

Add refs/tags/${VERSION} as a third tag candidate so a shared-package
release tagged as a bare 1.2.3 (not v1.2.3) is found regardless of
whether the dispatch payload was v-prefixed or bare.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: edbe35d6-256c-40ad-9254-88df49980dba
The old sed grabbed the first major.minor after requires-python regardless
of operator, so an upper-bound-only spec ("<3.14", "==3.11.*") was misread
as a floor and could spuriously fail a legitimate sync. Extract the quoted
requires-python value and read only a >=/~= lower bound, leaving the floor
empty (warn-and-skip) when none is present.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: edbe35d6-256c-40ad-9254-88df49980dba
The semver regex matches per line, so a multi-line release_tag (or a "+build"
segment containing "..") could slip through and produce an invalid branch/ref
name that fails later git operations loudly. Run git check-ref-format on the
computed refs/heads/<branch> and hard-fail early if it is malformed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: edbe35d6-256c-40ad-9254-88df49980dba
Quote the title in the gh issue search so the bracketed "[Shared Package]"
prefix is matched as a phrase instead of being tokenized (which could miss an
existing issue and create a duplicate), and stop swallowing gh errors with
2>/dev/null/|| echo so a real lookup/create/comment failure surfaces instead
of leaving the job green with no issue.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: edbe35d6-256c-40ad-9254-88df49980dba
Skip the build when dist/ is already present (avoids redundant rebuilds), and
skip with guidance when the submodule's TypeScript toolchain is not installed
instead of hard-failing. A dev-pruned install (npm ci --omit=dev,
NODE_ENV=production, or a pruning VSIX packager) drops the shared package's
typescript devDependency, which previously made the whole install fail at
postinstall; build/packaging jobs run a full install and still produce dist/.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: edbe35d6-256c-40ad-9254-88df49980dba
The @vscode/common-python-lsp (now a file: dep, never bumped by Dependabot) and
pip vscode-common-python-lsp (pin removed with the submodule move) ignore
entries no longer guard anything. Annotate them as defensive no-ops so readers
do not assume Dependabot would otherwise touch these.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: edbe35d6-256c-40ad-9254-88df49980dba
@edvilme

Copy link
Copy Markdown
Contributor Author

Review fixes applied (6 commits)

Pushed 6 identical commits across all 5 consumer PRs (one per fix). The workflow/postinstall.js/dependabot.yml changes are byte-identical in every repo:

  1. sync.yml tag resolution — added refs/tags/${VERSION} (bare) as a third candidate so v-prefixed and bare release tags both resolve symmetrically.
  2. sync.yml requires-python floor — rewrote the parse to extract the quoted value and read only the >=/~= lower bound (empty → warn-skip) instead of greedily grabbing the first major.minor.
  3. sync.yml ref hardening — added git check-ref-format "refs/heads/${BRANCH}" to reject multiline / .. injection before emitting outputs.
  4. sync.yml tracking-issue step — quoted the in:title search and stopped swallowing gh failures (removed 2>/dev/null || true) so errors surface under set -e.
  5. postinstall.js — skip the build when dist/index.js already exists, and skip-with-guidance (instead of hard-failing) when the submodule TS toolchain is absent (dev-pruned installs).
  6. dependabot.yml — annotated the two now-inert shared-package ignore entries as intentional defensive no-ops.

Deferred (need human/CI judgement — left unresolved intentionally)

Design/verification-level threads are not auto-fixed: lockfile drift from npm install --package-lock-only, the compat-guard "validates the wrong minimum" redesign (runtime.txt/.nvmrc vs constants), the noxfile --no-deps cross-repo coupling guard, project-wide tsconfig skipLibCheck, the file: vendoring/.vscodeignore bundling verification, and the sync-notification/branch-exists robustness gap. These warrant maintainer review rather than a mechanical change.

@edvilme Eduardo Villalpando Mello (edvilme) changed the title Sync shared package via repository_dispatch and stop Dependabot duplicates Adopt vscode-common-python-lsp as a git submodule, synced via repository_dispatch, and stop Dependabot duplicates Jul 21, 2026
…ady exists

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: edbe35d6-256c-40ad-9254-88df49980dba
…compat gate

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: edbe35d6-256c-40ad-9254-88df49980dba
…issing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: edbe35d6-256c-40ad-9254-88df49980dba
@edvilme

Copy link
Copy Markdown
Contributor Author

Follow-up review fixes (3 more commits)

Three additional identical commits address further review feedback:

  1. sync.yml — re-notify on re-dispatch. The "Open or update the tracking issue" step now also runs when the sync branch already exists (exists == 'true'). If a prior run pushed the branch but died before notifying (or the tracking issue was closed), a re-dispatch now re-ensures the tracking issue and re-emits the compare URL to the job summary instead of silently exit 0-ing. The step is idempotent (it reuses any open issue). Combined with the earlier removal of the swallowed gh failure, the notification can no longer be permanently lost.
  2. sync.yml — resolve symbolic Python floor. The Python-compat gate now falls back to resolving MINIMUM_PYTHON_MAJOR/MINIMUM_PYTHON_MINOR when minimumPythonVersion is declared via named constants (as in vscode-pylint), so the gate is no longer a silent no-op there.
  3. noxfile.py — submodule guard. Added a friendly missing-submodule check (git submodule update --init --recursive) before installing the shared Python lib, symmetric with the build/postinstall.js guard.

Already addressed in the current workflow (earlier-round threads resolved)

Several earlier comments referenced code that in-place hardening already fixed: the ls-remote branch-exists check now retries and treats only exit code 2 as "absent"; the Node floor is read from engines.node (not .nvmrc); the Python floor is compared against src/common/constants.ts (not runtime.txt); and an unparseable floor now warns-and-skips while the compat steps are gated on an actual submodule change.

Still open for maintainer decision (not auto-changed)

Lockfile drift from npm install --package-lock-only, whether the sync should build/validate (npm ci + nox install_bundled_libs) before declaring the branch "ready" vs. wording the issue as "needs validation", the noxfile --no-deps runtime-dep mirroring strategy, preventing nox -s update_packages from rewriting the file: dependency, project-wide tsconfig skipLibCheck, and the .vscodeignore/pr-check verification items. These are design/verification calls I've left for review.

…ips under pipefail

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: edbe35d6-256c-40ad-9254-88df49980dba
…nstall

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: edbe35d6-256c-40ad-9254-88df49980dba
…le: link

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: edbe35d6-256c-40ad-9254-88df49980dba
…dated

Use v${VERSION} in the tracking-issue title so re-dispatching the same release as 1.2.3 and �1.2.3 reuses one issue instead of creating a duplicate, and note in the issue body that the pushed branch must pass the normal PR checks before merging.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: edbe35d6-256c-40ad-9254-88df49980dba
@edvilme

Copy link
Copy Markdown
Contributor Author

Review follow-up complete

All review threads on this PR are now resolved. Latest round of identical fixes across all five consumer PRs:

  • Floor-parse hardening — Node/Python compatibility gates now warn-and-skip an unparseable floor instead of aborting the step under set -euo pipefail.
  • nox -s update_packages safety@vscode/common-python-lsp is pinned so the updater can't rewrite the file: link back to a registry range.
  • Honest postinstall comment — clarified that a non-recursive clone fails at file: resolution during install, not at postinstall.
  • Deterministic tracking issue — the tracking-issue title now uses the canonical v${VERSION}, so re-dispatching the same release as 1.2.3 and v1.2.3 reuses one issue instead of duplicating it; the issue body now states the pushed branch is unvalidated and must pass PR checks before merge.

Remaining review notes (lockfile --package-lock-only re-resolution, Python --no-deps dependency mirroring, project-wide skipLibCheck, and the dispatch-only compatibility gate) were acknowledged inline as documented, intentional trade-offs of the submodule consumption model.

Comment thread build/postinstall.js

// Already built (e.g. by a prior install or the packaging pipeline); nothing to do.
if (existsSync(`${pkgDir}/dist/index.js`)) {
process.exit(0);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This skip condition only checks whether dist/index.js exists. After updating the submodule in an existing checkout, previously built (and typically untracked) dist/ output can remain, causing npm install to reuse JavaScript built from the old submodule commit. Record the built submodule SHA or rebuild when the source revision changes.

@rchiodo Rich Chiodo (rchiodo) left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Review Center.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Review Center.

@edvilme
Eduardo Villalpando Mello (edvilme) merged commit d321435 into main Jul 22, 2026
22 checks passed
@edvilme
Eduardo Villalpando Mello (edvilme) deleted the shared-package-sync branch July 22, 2026 17:15

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Review Center.

Comment thread build/postinstall.js
}

// Already built (e.g. by a prior install or the packaging pipeline); nothing to do.
if (existsSync(`${pkgDir}/dist/index.js`)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only checks whether dist/index.js exists. Because dist/ can survive a git submodule update, a local install after advancing the submodule can retain and package the previous shared-library build. Stamp the built submodule SHA and rebuild when it differs from the current gitlink, or always rebuild after a submodule change.

Comment thread noxfile.py
"--no-deps",
"--upgrade",
"vscode-common-python-lsp==0.6.0",
"./external/vscode-common-python-lsp/python",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Installing the shared Python library with --no-deps means future runtime dependencies added by the pinned submodule will not be installed unless they are manually mirrored in this repository. Add a sync/build-time check that the submodule's declared runtime dependencies are present and compatible in requirements.txt, so a new dependency cannot become a bundled runtime import error.

@rchiodo Rich Chiodo (rchiodo) left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Review Center.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

debt Technical debt or repo cleanup

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants