diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 085093c..3dde42b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,14 +13,16 @@ updates: npm-minor-patch: patterns: - '*' - # Exclude @vscode/common-python-lsp so it gets its own standalone PR (weekly). - exclude-patterns: - - '@vscode/common-python-lsp' update-types: - 'minor' - 'patch' open-pull-requests-limit: 10 ignore: + # Defensive no-op: @vscode/common-python-lsp is a `file:` submodule dep, + # which Dependabot never bumps. It is synced by the + # shared-package-submodule-sync dispatch workflow instead. Kept here to + # document intent should the dependency form ever change. + - dependency-name: '@vscode/common-python-lsp' - dependency-name: '@types/vscode' - dependency-name: '@types/node' - dependency-name: 'vscode-languageclient' @@ -39,12 +41,14 @@ updates: pip-minor-patch: patterns: - '*' - # Exclude vscode-common-python-lsp so it gets its own standalone PR (weekly). - exclude-patterns: - - 'vscode-common-python-lsp' update-types: - 'minor' - 'patch' + ignore: + # Defensive no-op: the pip `vscode-common-python-lsp` pin was removed from + # requirements when the shared package moved to the git submodule, so + # Dependabot has nothing to bump. Kept to document intent. + - dependency-name: 'vscode-common-python-lsp' # Python test dependencies are updated weekly, minor updates are grouped (1 PR "pip-test-minor-patch"). - package-ecosystem: 'pip' diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 272fcc8..f032f19 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -18,6 +18,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + submodules: recursive - name: Build VSIX uses: ./.github/actions/build-vsix @@ -31,6 +33,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + submodules: recursive - name: Lint uses: ./.github/actions/lint @@ -54,6 +58,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: + submodules: recursive path: ${{ env.special-working-directory-relative }} # Install bundled libs using env.PYTHON_VERSION even though you test it on other versions. @@ -106,6 +111,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: + submodules: recursive path: ${{ env.special-working-directory-relative }} - name: Use Node.js ${{ env.NODE_VERSION }} diff --git a/.github/workflows/push-check.yml b/.github/workflows/push-check.yml index 60e7d18..c67cede 100644 --- a/.github/workflows/push-check.yml +++ b/.github/workflows/push-check.yml @@ -24,6 +24,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + submodules: recursive - name: Build VSIX uses: ./.github/actions/build-vsix @@ -38,6 +40,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + submodules: recursive - name: Lint uses: ./.github/actions/lint @@ -62,6 +66,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: + submodules: recursive path: ${{ env.special-working-directory-relative }} # Install bundled libs using env.PYTHON_VERSION even though you test it on other versions. @@ -115,6 +120,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: + submodules: recursive path: ${{ env.special-working-directory-relative }} - name: Install Node diff --git a/.github/workflows/shared-package-submodule-sync.yml b/.github/workflows/shared-package-submodule-sync.yml new file mode 100644 index 0000000..946445c --- /dev/null +++ b/.github/workflows/shared-package-submodule-sync.yml @@ -0,0 +1,281 @@ +name: Shared Package Submodule Sync + +on: + repository_dispatch: + types: [shared-package-release] + +permissions: + contents: write + issues: write + +env: + SUBMODULE_PATH: external/vscode-common-python-lsp + +jobs: + prepare: + name: Validate and normalize the release + runs-on: ubuntu-latest + outputs: + version: ${{ steps.normalize.outputs.version }} + branch: ${{ steps.normalize.outputs.branch }} + steps: + - name: Normalize release tag + id: normalize + env: + RELEASE_TAG: ${{ github.event.client_payload.release_tag }} + run: | + set -euo pipefail + # Require a real dotted release so a branch/ref name can never be malformed. + if ! printf '%s' "${RELEASE_TAG}" | grep -Eq '^v?[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.]+)*$'; then + echo "::error::release_tag '${RELEASE_TAG:-}' is missing or not a valid semver release." + exit 1 + fi + VERSION="${RELEASE_TAG#v}" + BRANCH="shared-package-v${VERSION}" + # The regex above matches per line, so a multi-line tag (or a "+build" + # segment containing "..") can slip through and taint the branch/ref + # name. Reject anything git itself considers a malformed ref before it + # is emitted as an output. + if ! git check-ref-format "refs/heads/${BRANCH}"; then + echo "::error::Computed branch name 'refs/heads/${BRANCH}' is not a valid git ref." + exit 1 + fi + { + echo "version=${VERSION}" + echo "branch=${BRANCH}" + } >>"$GITHUB_OUTPUT" + + sync: + name: Update shared package submodule + needs: prepare + runs-on: ubuntu-latest + # Key on the normalized branch so `v1.2.3` and `1.2.3` share one group. + concurrency: + group: shared-package-submodule-${{ needs.prepare.outputs.branch }} + cancel-in-progress: false + env: + VERSION: ${{ needs.prepare.outputs.version }} + BRANCH: ${{ needs.prepare.outputs.branch }} + RELEASE_TAG: ${{ github.event.client_payload.release_tag }} + RELEASE_URL: ${{ github.event.client_payload.release_url }} + REPO: ${{ github.repository }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + + # Expose a `retry` helper to every later bash step via BASH_ENV so the + # network operations below (fetch/push) stay readable one-liners. + - name: Set up the git retry helper + run: | + set -euo pipefail + HELPER="${RUNNER_TEMP}/helpers.sh" + cat >"${HELPER}" <<'SH' + retry() { + local attempt=1 + until "$@"; do + if [ "${attempt}" -ge 3 ]; then + echo "::error::Command failed after ${attempt} attempts: $*" + return 1 + fi + echo "Attempt ${attempt} failed: $* — retrying..." >&2 + attempt=$((attempt + 1)) + sleep $((attempt * 5)) + done + } + SH + echo "BASH_ENV=${HELPER}" >>"${GITHUB_ENV}" + + # Once the release branch exists a maintainer may be editing it, so the + # remaining steps are skipped to avoid clobbering their work. A transient + # ls-remote failure must not be mistaken for "branch absent", so retry and + # only treat a definitive "no such ref" (exit code 2) as missing. + - name: Check whether the release branch already exists + id: guard + run: | + set -euo pipefail + exists='' + for attempt in 1 2 3; do + set +e + git ls-remote --exit-code --heads origin "refs/heads/${BRANCH}" >/dev/null 2>&1 + rc=$? + set -e + if [ "${rc}" -eq 0 ]; then exists=true; break; fi + if [ "${rc}" -eq 2 ]; then exists=false; break; fi + echo "ls-remote failed (exit ${rc}); attempt ${attempt}/3..." >&2 + sleep $((attempt * 5)) + done + if [ -z "${exists}" ]; then + echo "::error::Could not reach origin to check for ${BRANCH} after 3 attempts." + exit 1 + fi + if [ "${exists}" = 'true' ]; then + echo "Branch ${BRANCH} already exists; leaving it untouched." + fi + echo "exists=${exists}" >>"${GITHUB_OUTPUT}" + + - name: Create the release branch from the latest main + if: steps.guard.outputs.exists == 'false' + run: | + set -euo pipefail + retry git fetch origin main + git checkout -B "${BRANCH}" FETCH_HEAD + git submodule update --init --recursive "${SUBMODULE_PATH}" + + - name: Point the submodule at the released commit + if: steps.guard.outputs.exists == 'false' + working-directory: ${{ env.SUBMODULE_PATH }} + run: | + set -euo pipefail + retry git fetch --tags --force origin + TARGET='' + for CANDIDATE in "refs/tags/${RELEASE_TAG}" "refs/tags/v${VERSION}" "refs/tags/${VERSION}"; do + if git rev-parse -q --verify "${CANDIDATE}^{commit}" >/dev/null; then + TARGET="${CANDIDATE}" + break + fi + done + if [ -z "${TARGET}" ]; then + echo "::error::Release tag '${RELEASE_TAG}' was not found in the shared package repository." + exit 1 + fi + echo "Checking out submodule at ${TARGET}." + git checkout --detach "${TARGET}" + + # Determine whether the release actually moves the submodule before running + # the compatibility guards, so a no-op re-dispatch exits cleanly instead of + # doing extra work or failing loudly with nothing to sync. + - name: Detect whether the submodule pointer moved + id: detect + if: steps.guard.outputs.exists == 'false' + run: | + set -euo pipefail + if git diff --quiet -- "${SUBMODULE_PATH}"; then + echo "Submodule already at ${RELEASE_TAG}; nothing to do." + echo 'changed=false' >>"${GITHUB_OUTPUT}" + else + echo 'changed=true' >>"${GITHUB_OUTPUT}" + fi + + # The extension must not claim support for an older Python than the shared + # package requires, or the bundled library would fail to import at runtime. + # Compare the package floor against the extension's *declared* minimum in + # src/common/constants.ts (not the bundling interpreter in runtime.txt); an + # unparseable floor only warns so upstream repackaging can't wedge the sync. + - name: Verify Python version compatibility + if: steps.guard.outputs.exists == 'false' && steps.detect.outputs.changed == 'true' + run: | + set -euo pipefail + EXT_MIN="$(sed -n 's/.*minimumPythonVersion:[[:space:]]*{[[:space:]]*major:[[:space:]]*\([0-9]\+\)[[:space:]]*,[[:space:]]*minor:[[:space:]]*\([0-9]\+\).*/\1.\2/p' src/common/constants.ts | head -n1)" + # Some extensions declare the floor with symbolic constants + # (minimumPythonVersion: { major: MINIMUM_PYTHON_MAJOR, minor: MINIMUM_PYTHON_MINOR }), + # so the inline-literal parse above finds nothing. Fall back to the + # numeric MINIMUM_PYTHON_MAJOR/MINOR definitions before giving up, or + # the gate silently passes on those repos. + if [ -z "${EXT_MIN}" ]; then + EXT_MAJOR="$(sed -n 's/.*MINIMUM_PYTHON_MAJOR[[:space:]]*=[[:space:]]*\([0-9]\+\).*/\1/p' src/common/constants.ts | head -n1)" + EXT_MINOR="$(sed -n 's/.*MINIMUM_PYTHON_MINOR[[:space:]]*=[[:space:]]*\([0-9]\+\).*/\1/p' src/common/constants.ts | head -n1)" + if [ -n "${EXT_MAJOR}" ] && [ -n "${EXT_MINOR}" ]; then + EXT_MIN="${EXT_MAJOR}.${EXT_MINOR}" + fi + fi + # Read only a >=/~= lower bound from requires-python; an upper-bound-only + # spec (e.g. "<3.14" or "==3.11.*") must not be misread as a floor, so + # leave PKG_FLOOR empty (→ warn-and-skip below) when no lower bound exists. + REQUIRES_PYTHON="$(sed -n 's/.*requires-python[[:space:]]*=[[:space:]]*["'\'']\([^"'\'']*\)["'\''].*/\1/p' "${SUBMODULE_PATH}/python/pyproject.toml" | head -n1)" + PKG_FLOOR="$(printf '%s' "${REQUIRES_PYTHON}" | grep -Eo '(>=|~=)[[:space:]]*[0-9]+\.[0-9]+' | grep -Eo '[0-9]+\.[0-9]+' | head -n1 || true)" + if [ -z "${PKG_FLOOR}" ]; then + echo "::warning::Could not parse requires-python from the shared package; skipping the Python compatibility check." + elif [ -z "${EXT_MIN}" ]; then + echo "::warning::Could not parse minimumPythonVersion from src/common/constants.ts; skipping the Python compatibility check." + elif [ "$(printf '%s\n%s\n' "${PKG_FLOOR}" "${EXT_MIN}" | sort -V | head -n1)" != "${PKG_FLOOR}" ]; then + echo "::error::Extension minimum Python (${EXT_MIN}, from src/common/constants.ts) is older than the shared package floor (>=${PKG_FLOOR}). Raise the minimum in src/common/constants.ts, runtime.txt and noxfile.py, or investigate the shared release before syncing." + exit 1 + else + echo "Python floors compatible: extension ${EXT_MIN} >= shared package ${PKG_FLOOR}." + fi + + # The extension builds the shared package's TypeScript during install, so its + # Node toolchain must satisfy the package's declared build floor + # (engines.node), not the exact dev pin in the submodule .nvmrc. An + # unparseable floor only warns rather than failing every future sync. + - name: Verify Node version compatibility + if: steps.guard.outputs.exists == 'false' && steps.detect.outputs.changed == 'true' + run: | + set -euo pipefail + EXT_NODE="$(grep -Eo '[0-9]+(\.[0-9]+)*' .nvmrc | head -n1 || true)" + PKG_RANGE="$(node -p "(require('./${SUBMODULE_PATH}/typescript/package.json').engines || {}).node || ''" 2>/dev/null || true)" + PKG_NODE="$(printf '%s' "${PKG_RANGE}" | grep -Eo '[0-9]+(\.[0-9]+)*' | head -n1 || true)" + if [ -z "${PKG_NODE}" ]; then + echo "::warning::Could not parse engines.node from the shared package; skipping the Node compatibility check." + elif [ -z "${EXT_NODE}" ]; then + echo "::warning::Could not parse a Node version from .nvmrc; skipping the Node compatibility check." + elif [ "$(printf '%s\n%s\n' "${PKG_NODE}" "${EXT_NODE}" | sort -V | head -n1)" != "${PKG_NODE}" ]; then + echo "::error::Extension Node version (${EXT_NODE}, from .nvmrc) is older than the shared package's required build floor (>=${PKG_NODE}, from engines.node). Update .nvmrc, or investigate the shared release before syncing." + exit 1 + else + echo "Node versions compatible: extension ${EXT_NODE} >= shared package build floor ${PKG_NODE}." + fi + + # Refresh the lockfile so the branch stays `npm ci`-mergeable, then commit + # both the submodule pointer and the regenerated lockfile. The branch is + # new (guarded above), so a plain push cannot overwrite anyone's work. + - name: Commit and push the update + if: steps.guard.outputs.exists == 'false' && steps.detect.outputs.changed == 'true' + run: | + set -euo pipefail + retry npm install --package-lock-only --ignore-scripts + git config user.name 'github-actions[bot]' + git config user.email 'github-actions[bot]@users.noreply.github.com' + git add "${SUBMODULE_PATH}" package-lock.json + git commit -m "Update shared package submodule to ${RELEASE_TAG}" + retry git push --set-upstream origin "${BRANCH}" + + # Organization settings prevent opening PRs automatically, so point a + # maintainer at the compare page via the job summary and a tracking issue. + # This also runs when the branch already exists so a re-dispatch after a + # partial failure (branch pushed but the notification never landed, or the + # issue was closed) re-ensures the maintainer notification instead of + # silently skipping it — the step is idempotent (it reuses any open issue). + - name: Open or update the tracking issue + if: steps.guard.outputs.exists == 'true' || steps.detect.outputs.changed == 'true' + run: | + set -euo pipefail + COMPARE_URL="https://github.com/${REPO}/compare/main...${BRANCH}?expand=1" + TITLE="[Shared Package] Open PR to update submodule to v${VERSION}" + BODY_FILE="$(mktemp)" + { + echo "### Shared package submodule update ready for review" + echo '' + echo "Branch \`${BRANCH}\` has been pushed, moving \`${SUBMODULE_PATH}\` to ${RELEASE_TAG}." + echo '' + echo '> [!IMPORTANT]' + echo '> This branch only bumps the submodule pointer and refreshes the lockfile; it has not been built or tested by this workflow. Open the pull request and let the normal PR checks (build, lint, tests) validate it before merging.' + echo '' + echo 'Organization settings prevent this workflow from opening pull requests automatically. Please open it manually:' + echo '' + echo "[Open the pull request](${COMPARE_URL})" + echo '' + echo "Source release: ${RELEASE_URL:-n/a}" + } | tee -a "$GITHUB_STEP_SUMMARY" >"$BODY_FILE" + + # Quote the title in the search so the bracketed "[Shared Package]" + # prefix is matched as a phrase rather than tokenized, and do not + # swallow gh errors: a failed lookup must surface (set -e) rather than + # silently fall through and create a duplicate tracking issue. + EXISTING="$(gh issue list --repo "$REPO" --state open \ + --search "\"${TITLE}\" in:title" --json number,title \ + --jq "map(select(.title == \"${TITLE}\")) | .[0].number // empty")" + if [ -n "${EXISTING}" ]; then + echo "Reusing existing tracking issue #${EXISTING}." + gh issue comment "${EXISTING}" --repo "$REPO" --body-file "$BODY_FILE" + else + gh issue create --repo "$REPO" --title "${TITLE}" --body-file "$BODY_FILE" + fi diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..befe424 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "external/vscode-common-python-lsp"] + path = external/vscode-common-python-lsp + url = https://github.com/microsoft/vscode-common-python-lsp.git diff --git a/.vscodeignore b/.vscodeignore index 63458a2..87b1f39 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -2,6 +2,7 @@ .vscode-test/** out/** node_modules/** +external/** src/** .gitignore .yarnrc diff --git a/README.md b/README.md index e79644c..836d4a0 100644 --- a/README.md +++ b/README.md @@ -118,3 +118,25 @@ In this section, you will find some common issues you might encounter and how to - Set the `flake8.importStrategy` setting to `useBundled` and the `flake8.path` setting to point to the custom binary of Flake8 you want to use; or - Install Flake8 in the selected environment. + +## Development + +This extension bundles the shared [`vscode-common-python-lsp`](https://github.com/microsoft/vscode-common-python-lsp) library as a git submodule at `external/vscode-common-python-lsp`. The submodule must be initialized before installing dependencies, because `npm install` builds the shared library from it. + +When cloning the repository, pull the submodule at the same time: + +```bash +git clone --recurse-submodules https://github.com/microsoft/vscode-flake8.git +``` + +If you already cloned without `--recurse-submodules`, initialize (or update) the submodule from the repository root: + +```bash +git submodule update --init --recursive +``` + +Then install dependencies: + +```bash +npm install +``` diff --git a/build/azure-devdiv-pipeline.pre-release.yml b/build/azure-devdiv-pipeline.pre-release.yml index 7692b9e..b925761 100644 --- a/build/azure-devdiv-pipeline.pre-release.yml +++ b/build/azure-devdiv-pipeline.pre-release.yml @@ -38,6 +38,8 @@ parameters: - name: buildSteps type: stepList default: + - script: git submodule update --init --recursive + displayName: Checkout submodules - script: npm ci displayName: Install NPM dependencies diff --git a/build/azure-devdiv-pipeline.stable.yml b/build/azure-devdiv-pipeline.stable.yml index 7659795..90599c8 100644 --- a/build/azure-devdiv-pipeline.stable.yml +++ b/build/azure-devdiv-pipeline.stable.yml @@ -33,6 +33,8 @@ parameters: - name: buildSteps type: stepList default: + - script: git submodule update --init --recursive + displayName: Checkout submodules - script: npm ci displayName: Install NPM dependencies diff --git a/build/azure-pipeline.pre-release.yml b/build/azure-pipeline.pre-release.yml index 45d2cee..16244bd 100644 --- a/build/azure-pipeline.pre-release.yml +++ b/build/azure-pipeline.pre-release.yml @@ -48,6 +48,8 @@ extends: architecture: 'x64' displayName: Select Python version + - script: git submodule update --init --recursive + displayName: Checkout submodules - script: npm ci displayName: Install NPM dependencies diff --git a/build/azure-pipeline.stable.yml b/build/azure-pipeline.stable.yml index 9c938b5..c22eb7e 100644 --- a/build/azure-pipeline.stable.yml +++ b/build/azure-pipeline.stable.yml @@ -44,6 +44,8 @@ extends: architecture: 'x64' displayName: Select Python version + - script: git submodule update --init --recursive + displayName: Checkout submodules - script: npm ci displayName: Install NPM dependencies diff --git a/build/azure-pipeline.validation.yml b/build/azure-pipeline.validation.yml index 2b545bd..cf4b638 100644 --- a/build/azure-pipeline.validation.yml +++ b/build/azure-pipeline.validation.yml @@ -32,6 +32,8 @@ parameters: - name: buildSteps type: stepList default: + - script: git submodule update --init --recursive + displayName: Checkout submodules - script: npm ci displayName: Install NPM dependencies diff --git a/build/postinstall.js b/build/postinstall.js new file mode 100644 index 0000000..b7fec74 --- /dev/null +++ b/build/postinstall.js @@ -0,0 +1,46 @@ +// Builds the shared package that lives in the git submodule after install. +// +// Note: a clone made without `--recurse-submodules` actually fails earlier, +// when npm resolves the `file:` dependency during the install phase, before +// this postinstall script runs. The guard below only adds a friendlier message +// on the rarer paths that still reach postinstall (e.g. the submodule was +// removed after a prior install); it is not a substitute for initializing the +// submodule. +const { existsSync } = require("fs"); +const { execSync } = require("child_process"); + +const pkgDir = "external/vscode-common-python-lsp/typescript"; + +if (!existsSync(`${pkgDir}/package.json`)) { + console.warn( + `[postinstall] Shared package submodule not found at "${pkgDir}". ` + + "Run `git submodule update --init --recursive` and reinstall to build it.", + ); + process.exit(0); +} + +// Already built (e.g. by a prior install or the packaging pipeline); nothing to do. +if (existsSync(`${pkgDir}/dist/index.js`)) { + process.exit(0); +} + +// The build runs the submodule's `tsc`, which is a devDependency of the shared +// package. A dev-pruned install (`npm ci --omit=dev`, `NODE_ENV=production`, or +// a VSIX packager that prunes) may not have it available. Skip with guidance +// rather than hard-failing the whole install; build/packaging jobs run a full +// install and produce `dist/` there. +if ( + !existsSync(`${pkgDir}/node_modules/.bin/tsc`) && + !existsSync(`${pkgDir}/node_modules/.bin/tsc.cmd`) && + !existsSync(`${pkgDir}/node_modules/typescript`) +) { + console.warn( + `[postinstall] TypeScript toolchain not installed in "${pkgDir}"; ` + + "skipping the shared package build. Run " + + `\`npm --prefix ${pkgDir} install && npm --prefix ${pkgDir} run build\` ` + + "if you need dist/ locally.", + ); + process.exit(0); +} + +execSync(`npm --prefix ${pkgDir} run build`, { stdio: "inherit" }); diff --git a/external/vscode-common-python-lsp b/external/vscode-common-python-lsp new file mode 160000 index 0000000..d1b98bc --- /dev/null +++ b/external/vscode-common-python-lsp @@ -0,0 +1 @@ +Subproject commit d1b98bc26a30507ea407285dd4804ad598787b92 diff --git a/noxfile.py b/noxfile.py index 6ef3f9b..d1ec777 100644 --- a/noxfile.py +++ b/noxfile.py @@ -110,6 +110,22 @@ def install_bundled_libs(session): """Installs the libraries that will be bundled with the extension.""" session.install("wheel") _install_bundle(session) + # Source the shared Python library from the git submodule instead of the + # published package so the bundled copy matches the pinned submodule commit. + shared_python_lib = pathlib.Path("external/vscode-common-python-lsp/python") + if not shared_python_lib.exists(): + session.error( + f"Shared package submodule missing at {shared_python_lib}. " + "Run 'git submodule update --init --recursive' before building." + ) + session.install( + "-t", + "./bundled/libs", + "--no-cache-dir", + "--no-deps", + "--upgrade", + "./external/vscode-common-python-lsp/python", + ) @nox.session(python="3.10") diff --git a/package-lock.json b/package-lock.json index 602721a..bf20734 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,9 +7,10 @@ "": { "name": "flake8", "version": "2026.7.0-dev", + "hasInstallScript": true, "license": "MIT", "dependencies": { - "@vscode/common-python-lsp": "^0.6.0", + "@vscode/common-python-lsp": "file:external/vscode-common-python-lsp/typescript", "@vscode/python-extension": "^1.0.6", "vscode-languageclient": "^8.1.0" }, @@ -41,6 +42,81 @@ "vscode": "^1.74.0" } }, + "external/vscode-common-python-lsp/typescript": { + "name": "@vscode/common-python-lsp", + "version": "0.8.1", + "license": "MIT", + "dependencies": { + "@vscode/python-environments": "https://pkgs.dev.azure.com/azure-public/vside/_packaging/msft_consumption/npm/registry/@vscode/python-environments/-/python-environments-1.0.0.tgz", + "@vscode/python-extension": "^1.0.6", + "dotenv": "^17.4.1", + "fs-extra": "^11.3.4", + "semver": "^7.7.4", + "vscode-languageclient": "^8.1.0" + }, + "devDependencies": { + "@types/chai": "^5.2.3", + "@types/fs-extra": "^11.0.4", + "@types/mocha": "^10.0.10", + "@types/node": "22.x", + "@types/semver": "^7.7.1", + "@types/sinon": "^21.0.0", + "@types/vscode": "^1.74.0", + "@typescript-eslint/eslint-plugin": "^7.18.0", + "@typescript-eslint/parser": "^7.18.0", + "chai": "^6.2.2", + "eslint": "^8.57.1", + "mocha": "^11.7.5", + "prettier": "^3.8.1", + "sinon": "^22.0.0", + "typescript": "^6.0.3" + }, + "engines": { + "node": ">=18.0.0", + "vscode": "^1.74.0" + } + }, + "external/vscode-common-python-lsp/typescript/node_modules/diff": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", + "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "external/vscode-common-python-lsp/typescript/node_modules/sinon": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-22.0.0.tgz", + "integrity": "sha512-sq/6DpdXOrLyfbKlXLg/Usc7xu8YXPeLkOFZRvA3bNUSA2lhbrZ06yuXbH1fkzBPCbz9O10+7hznzUsjaYNm0Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^15.4.0", + "@sinonjs/samsam": "^10.0.2", + "diff": "^9.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "external/vscode-common-python-lsp/typescript/node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/@azu/format-text": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@azu/format-text/-/format-text-1.0.2.tgz", @@ -742,9 +818,9 @@ } }, "node_modules/@sinonjs/fake-timers": { - "version": "15.3.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.3.2.tgz", - "integrity": "sha512-mrn35Jl2pCpns+mE3HaZa1yPN5EYCRgiMI+135COjr2hr8Cls9DXqIZ57vZe2cz7y2XVSq92tcs6kGQcT1J8Rw==", + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz", + "integrity": "sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -973,6 +1049,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/sinon": { "version": "21.0.1", "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-21.0.1.tgz", @@ -1214,22 +1297,8 @@ "license": "ISC" }, "node_modules/@vscode/common-python-lsp": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@vscode/common-python-lsp/-/common-python-lsp-0.6.0.tgz", - "integrity": "sha512-wabQS/YK4qlZtU2/e2JrdvuuwyjVfvoPayCcpv3n+3Ii7+4yumrP6vZo/4zsnTUlapBzckOiSyGuwA5oODJGdg==", - "license": "MIT", - "dependencies": { - "@vscode/python-environments": "https://pkgs.dev.azure.com/azure-public/vside/_packaging/msft_consumption/npm/registry/@vscode/python-environments/-/python-environments-1.0.0.tgz", - "@vscode/python-extension": "^1.0.6", - "dotenv": "^17.4.1", - "fs-extra": "^11.3.4", - "semver": "^7.7.4", - "vscode-languageclient": "^8.1.0" - }, - "engines": { - "node": ">=18.0.0", - "vscode": "^1.74.0" - } + "resolved": "external/vscode-common-python-lsp/typescript", + "link": true }, "node_modules/@vscode/python-environments": { "version": "1.0.0", diff --git a/package.json b/package.json index ad94b42..9b40ad1 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "main": "./dist/extension.js", "l10n": "./l10n", "scripts": { + "postinstall": "node ./build/postinstall.js", "vscode:prepublish": "npm run package", "compile": "webpack", "watch": "webpack --watch", @@ -226,7 +227,7 @@ ] }, "dependencies": { - "@vscode/common-python-lsp": "^0.6.0", + "@vscode/common-python-lsp": "file:external/vscode-common-python-lsp/typescript", "@vscode/python-extension": "^1.0.6", "vscode-languageclient": "^8.1.0" }, diff --git a/requirements.in b/requirements.in index 29d6621..ba24765 100644 --- a/requirements.in +++ b/requirements.in @@ -5,7 +5,10 @@ # Run following command: # uv pip compile --generate-hashes --upgrade ./requirements.in > .\requirements.txt +# NOTE: pygls and packaging are also runtime dependencies of the bundled shared +# package (external/vscode-common-python-lsp), which is installed with --no-deps. +# Keep them pinned here so the submodule's Python lib stays installable. +# lsprotocol is pulled in transitively via pygls. pygls packaging flake8 -vscode-common-python-lsp==0.6.0 diff --git a/requirements.txt b/requirements.txt index 837b4ef..9d9fc25 100644 --- a/requirements.txt +++ b/requirements.txt @@ -26,7 +26,6 @@ lsprotocol==2025.0.0 \ --hash=sha256:f9d78f25221f2a60eaa4a96d3b4ffae011b107537facee61d3da3313880995c7 # via # pygls - # vscode-common-python-lsp mccabe==0.7.0 \ --hash=sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325 \ --hash=sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e @@ -36,7 +35,6 @@ packaging==26.0 \ --hash=sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529 # via # -r ./requirements.in - # vscode-common-python-lsp pycodestyle==2.14.0 \ --hash=sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783 \ --hash=sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d @@ -50,14 +48,9 @@ pygls==2.0.1 \ --hash=sha256:d29748042cea5bedc98285eb3e2c0c60bf3fc73786319519001bf72bbe8f36cc # via # -r ./requirements.in - # vscode-common-python-lsp typing-extensions==4.15.0 \ --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \ --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 # via # cattrs # exceptiongroup -vscode-common-python-lsp==0.6.0 \ - --hash=sha256:499bf066372ed86a62f140a668a4b0f72328cdf8d08057df45892fc1c5b627f6 \ - --hash=sha256:6892a401311217f501d9b2f86a82a9629c89d565082330adb77bf8cddf634a72 - # via -r ./requirements.in diff --git a/tsconfig.json b/tsconfig.json index 20572e5..ac0c046 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,5 +14,7 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "noUnusedParameters": true - } + }, + "include": ["src"], + "exclude": ["node_modules", "external", "out", "dist", ".vscode-test"] }