From dd1243d67165887a4434cfd4f734a5cf71f423e6 Mon Sep 17 00:00:00 2001 From: Bill Schnurr Date: Thu, 12 Nov 2026 09:46:08 -0700 Subject: [PATCH 1/6] Use template-authenticated internal feeds for release builds Match flake8/environments: pass Pylance_PublicPackages as the custom npm registry, use npm ci instead of the proxy-forcing ci:msft helper, and let PipAuthenticate supply Python feed credentials without per-step proxy overrides. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- build/azure-devdiv-pipeline.pre-release.yml | 18 ++++-------------- build/azure-devdiv-pipeline.stable.yml | 18 ++++-------------- 2 files changed, 8 insertions(+), 28 deletions(-) diff --git a/build/azure-devdiv-pipeline.pre-release.yml b/build/azure-devdiv-pipeline.pre-release.yml index 8ad15e48..7162ce31 100644 --- a/build/azure-devdiv-pipeline.pre-release.yml +++ b/build/azure-devdiv-pipeline.pre-release.yml @@ -33,10 +33,8 @@ variables: value: VSCode-python-debugger - name: VsixName value: python-debugger.vsix - - name: NPM_CONFIG_REGISTRY - value: 'https://packagefeedproxy.microsoft.io/npm/' - - name: PIP_PROXY_INDEX_URL - value: 'https://packagefeedproxy.microsoft.io/pypi/simple/' + - name: AZURE_ARTIFACTS_FEED + value: 'https://devdiv.pkgs.visualstudio.com/DevDiv/_packaging/Pylance_PublicPackages/npm/registry/' parameters: - name: publishExtension @@ -74,28 +72,20 @@ parameters: - name: buildSteps type: stepList default: - - script: npm run ci:msft + - script: npm ci displayName: Install NPM dependencies - script: python -m pip install -U pip displayName: Upgrade pip - env: - PIP_INDEX_URL: $(PIP_PROXY_INDEX_URL) - script: python -m pip install wheel displayName: Install wheel - env: - PIP_INDEX_URL: $(PIP_PROXY_INDEX_URL) - script: python -m pip install nox displayName: Install nox - env: - PIP_INDEX_URL: $(PIP_PROXY_INDEX_URL) - script: python -m nox --session install_bundled_libs displayName: Install Python dependencies - env: - PIP_INDEX_URL: $(PIP_PROXY_INDEX_URL) - script: python ./build/update_ext_version.py --for-publishing displayName: Update build number @@ -112,7 +102,7 @@ extends: buildPlatforms: ${{ parameters.buildPlatforms }} buildSteps: ${{ parameters.buildSteps }} standardizedVersioning: true - customNPMRegistry: $(NPM_CONFIG_REGISTRY) + customNPMRegistry: $(AZURE_ARTIFACTS_FEED) publishExtension: ${{ parameters.publishExtension }} teamName: $(TeamName) ghCreateTag: true diff --git a/build/azure-devdiv-pipeline.stable.yml b/build/azure-devdiv-pipeline.stable.yml index feaccba7..1aabf87d 100644 --- a/build/azure-devdiv-pipeline.stable.yml +++ b/build/azure-devdiv-pipeline.stable.yml @@ -28,10 +28,8 @@ variables: value: VSCode-python-debugger - name: VsixName value: python-debugger.vsix - - name: NPM_CONFIG_REGISTRY - value: 'https://packagefeedproxy.microsoft.io/npm/' - - name: PIP_PROXY_INDEX_URL - value: 'https://packagefeedproxy.microsoft.io/pypi/simple/' + - name: AZURE_ARTIFACTS_FEED + value: 'https://devdiv.pkgs.visualstudio.com/DevDiv/_packaging/Pylance_PublicPackages/npm/registry/' parameters: - name: publishExtension @@ -69,28 +67,20 @@ parameters: - name: buildSteps type: stepList default: - - script: npm run ci:msft + - script: npm ci displayName: Install NPM dependencies - script: python -m pip install -U pip displayName: Upgrade pip - env: - PIP_INDEX_URL: $(PIP_PROXY_INDEX_URL) - script: python -m pip install wheel displayName: Install wheel - env: - PIP_INDEX_URL: $(PIP_PROXY_INDEX_URL) - script: python -m pip install nox displayName: Install nox - env: - PIP_INDEX_URL: $(PIP_PROXY_INDEX_URL) - script: python -m nox --session install_bundled_libs displayName: Install Python dependencies - env: - PIP_INDEX_URL: $(PIP_PROXY_INDEX_URL) - script: python ./build/update_ext_version.py --release --for-publishing displayName: Update build number @@ -107,7 +97,7 @@ extends: buildPlatforms: ${{ parameters.buildPlatforms }} buildSteps: ${{ parameters.buildSteps }} standardizedVersioning: true - customNPMRegistry: $(NPM_CONFIG_REGISTRY) + customNPMRegistry: $(AZURE_ARTIFACTS_FEED) publishExtension: ${{ parameters.publishExtension }} teamName: $(TeamName) ghCreateTag: true From 7306e634442f3dae94bfcd9a3d79b5f297ffbf9b Mon Sep 17 00:00:00 2001 From: Bill Schnurr Date: Thu, 12 Nov 2026 10:33:34 -0700 Subject: [PATCH 2/6] Honor authenticated release registries in ci:msft Preserve the release variable names and ci:msft command. Honor the effective npm registry for lockfile rewriting and installation while inheriting the template-authenticated npmrc. Resolve PIP_PROXY_INDEX_URL from PipAuthenticate output so per-step pip settings retain feed credentials. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- build/azure-devdiv-pipeline.pre-release.yml | 17 +++++++++-- build/azure-devdiv-pipeline.stable.yml | 17 +++++++++-- build/setup-npm-registry.js | 20 ++++++------- build/setup-npm-registry.test.js | 31 +++++++++++++++++++-- 4 files changed, 66 insertions(+), 19 deletions(-) diff --git a/build/azure-devdiv-pipeline.pre-release.yml b/build/azure-devdiv-pipeline.pre-release.yml index 7162ce31..df51f4af 100644 --- a/build/azure-devdiv-pipeline.pre-release.yml +++ b/build/azure-devdiv-pipeline.pre-release.yml @@ -33,8 +33,11 @@ variables: value: VSCode-python-debugger - name: VsixName value: python-debugger.vsix - - name: AZURE_ARTIFACTS_FEED + - name: NPM_CONFIG_REGISTRY value: 'https://devdiv.pkgs.visualstudio.com/DevDiv/_packaging/Pylance_PublicPackages/npm/registry/' + - name: PIP_PROXY_INDEX_URL + # PipAuthenticate supplies the internal index URL with credentials before buildSteps. + value: '$(PIP_INDEX_URL)' parameters: - name: publishExtension @@ -72,20 +75,28 @@ parameters: - name: buildSteps type: stepList default: - - script: npm ci + - script: npm run ci:msft displayName: Install NPM dependencies - script: python -m pip install -U pip displayName: Upgrade pip + env: + PIP_INDEX_URL: $(PIP_PROXY_INDEX_URL) - script: python -m pip install wheel displayName: Install wheel + env: + PIP_INDEX_URL: $(PIP_PROXY_INDEX_URL) - script: python -m pip install nox displayName: Install nox + env: + PIP_INDEX_URL: $(PIP_PROXY_INDEX_URL) - script: python -m nox --session install_bundled_libs displayName: Install Python dependencies + env: + PIP_INDEX_URL: $(PIP_PROXY_INDEX_URL) - script: python ./build/update_ext_version.py --for-publishing displayName: Update build number @@ -102,7 +113,7 @@ extends: buildPlatforms: ${{ parameters.buildPlatforms }} buildSteps: ${{ parameters.buildSteps }} standardizedVersioning: true - customNPMRegistry: $(AZURE_ARTIFACTS_FEED) + customNPMRegistry: $(NPM_CONFIG_REGISTRY) publishExtension: ${{ parameters.publishExtension }} teamName: $(TeamName) ghCreateTag: true diff --git a/build/azure-devdiv-pipeline.stable.yml b/build/azure-devdiv-pipeline.stable.yml index 1aabf87d..c13fdfa9 100644 --- a/build/azure-devdiv-pipeline.stable.yml +++ b/build/azure-devdiv-pipeline.stable.yml @@ -28,8 +28,11 @@ variables: value: VSCode-python-debugger - name: VsixName value: python-debugger.vsix - - name: AZURE_ARTIFACTS_FEED + - name: NPM_CONFIG_REGISTRY value: 'https://devdiv.pkgs.visualstudio.com/DevDiv/_packaging/Pylance_PublicPackages/npm/registry/' + - name: PIP_PROXY_INDEX_URL + # PipAuthenticate supplies the internal index URL with credentials before buildSteps. + value: '$(PIP_INDEX_URL)' parameters: - name: publishExtension @@ -67,20 +70,28 @@ parameters: - name: buildSteps type: stepList default: - - script: npm ci + - script: npm run ci:msft displayName: Install NPM dependencies - script: python -m pip install -U pip displayName: Upgrade pip + env: + PIP_INDEX_URL: $(PIP_PROXY_INDEX_URL) - script: python -m pip install wheel displayName: Install wheel + env: + PIP_INDEX_URL: $(PIP_PROXY_INDEX_URL) - script: python -m pip install nox displayName: Install nox + env: + PIP_INDEX_URL: $(PIP_PROXY_INDEX_URL) - script: python -m nox --session install_bundled_libs displayName: Install Python dependencies + env: + PIP_INDEX_URL: $(PIP_PROXY_INDEX_URL) - script: python ./build/update_ext_version.py --release --for-publishing displayName: Update build number @@ -97,7 +108,7 @@ extends: buildPlatforms: ${{ parameters.buildPlatforms }} buildSteps: ${{ parameters.buildSteps }} standardizedVersioning: true - customNPMRegistry: $(AZURE_ARTIFACTS_FEED) + customNPMRegistry: $(NPM_CONFIG_REGISTRY) publishExtension: ${{ parameters.publishExtension }} teamName: $(TeamName) ghCreateTag: true diff --git a/build/setup-npm-registry.js b/build/setup-npm-registry.js index 8f084651..162b242c 100644 --- a/build/setup-npm-registry.js +++ b/build/setup-npm-registry.js @@ -6,7 +6,7 @@ const { readFileSync, writeFileSync } = require('node:fs'); const path = require('node:path'); const { microsoft } = require('./package-feeds.json'); -function rewritePackageLock(source) { +function rewritePackageLock(source, registry = microsoft.npm) { const lockfile = JSON.parse(source); if ( ![2, 3].includes(lockfile.lockfileVersion) || @@ -19,16 +19,16 @@ function rewritePackageLock(source) { return source.replace(/("resolved"\s*:\s*)("(?:\\.|[^"\\])*")/g, (match, prefix, encodedUrl) => { const url = JSON.parse(encodedUrl); - const rewritten = url.replace(/^https:\/\/registry\.(?:npmjs\.org|yarnpkg\.com)\//, microsoft.npm); + const rewritten = url.replace(/^https:\/\/registry\.(?:npmjs\.org|yarnpkg\.com)\//, registry); return rewritten === url ? match : prefix + JSON.stringify(rewritten); }); } -function rewritePackageLocks(root = path.resolve(__dirname, '..')) { +function rewritePackageLocks(root = path.resolve(__dirname, '..'), registry = microsoft.npm) { const files = [path.join(root, 'package-lock.json')]; const updates = files.map((file) => { const source = readFileSync(file, 'utf8'); - return { file, source, rewritten: rewritePackageLock(source) }; + return { file, source, rewritten: rewritePackageLock(source, registry) }; }); for (const { file, source, rewritten } of updates) { if (rewritten !== source) { @@ -48,13 +48,13 @@ if (require.main === module) { if (install && !process.env.npm_execpath) { throw new Error('Run npm install using npm run ci:msft.'); } - rewritePackageLocks(); + const registry = process.env.npm_config_registry || process.env.NPM_CONFIG_REGISTRY || microsoft.npm; + rewritePackageLocks(undefined, registry); if (install) { - const result = spawnSync( - process.execPath, - [process.env.npm_execpath, 'ci', `--registry=${microsoft.npm}`], - { cwd: path.resolve(__dirname, '..'), stdio: 'inherit' }, - ); + const result = spawnSync(process.execPath, [process.env.npm_execpath, 'ci', `--registry=${registry}`], { + cwd: path.resolve(__dirname, '..'), + stdio: 'inherit', + }); if (result.error || result.signal || result.status !== 0) { const reason = result.error?.message || result.signal || `exit code ${result.status}`; const error = new Error(`npm ci failed: ${reason}`); diff --git a/build/setup-npm-registry.test.js b/build/setup-npm-registry.test.js index 3d7eae5c..71125a62 100644 --- a/build/setup-npm-registry.test.js +++ b/build/setup-npm-registry.test.js @@ -100,12 +100,12 @@ test('missing or invalid root lockfile fails explicitly', (t) => { assert.throws(() => rewritePackageLocks(root), /Expected an npm lockfile/); }); -function runCli({ args = ['--ci'], npmCli = 'npm-cli.js', result = { status: 0 } } = {}) { +function runCli({ args = ['--ci'], npmCli = 'npm-cli.js', result = { status: 0 }, env = {} } = {}) { const calls = []; const errors = []; const process = { argv: ['node', 'setup-npm-registry.js', ...args], - env: { npm_execpath: npmCli }, + env: { npm_execpath: npmCli, ...env }, execPath: 'node', }; const module = { exports: {} }; @@ -113,7 +113,7 @@ function runCli({ args = ['--ci'], npmCli = 'npm-cli.js', result = { status: 0 } if (name === 'node:fs') { return { readFileSync: () => source, - writeFileSync: (file) => calls.push({ file }), + writeFileSync: (file, source) => calls.push({ file, source }), }; } if (name === 'node:child_process') { @@ -150,6 +150,31 @@ test('ci:msft rewrites the root lockfile before installing with the shared proxy assert.equal(scripts['ci:msft'], 'node ./build/setup-npm-registry.js --ci'); }); +test('ci:msft preserves template authentication for the configured internal registry', () => { + const registry = 'https://devdiv.pkgs.visualstudio.com/DevDiv/_packaging/Pylance_PublicPackages/npm/registry/'; + for (const settings of [ + { NPM_CONFIG_REGISTRY: registry }, + { npm_config_registry: registry }, + { NPM_CONFIG_REGISTRY: proxyRegistry, npm_config_registry: registry }, + ]) { + const env = { + ...settings, + NPM_CONFIG_USERCONFIG: path.join(tmpdir(), 'authenticated.npmrc'), + NODE_AUTH_TOKEN: 'test-token', + }; + const context = runCli({ env }); + const install = context.calls.at(-1); + assert.deepEqual(install.args, ['npm-cli.js', 'ci', `--registry=${registry}`]); + assert.equal(install.env, undefined); + assert.equal(context.process.env.NPM_CONFIG_USERCONFIG, env.NPM_CONFIG_USERCONFIG); + assert.equal(context.process.env.NODE_AUTH_TOKEN, env.NODE_AUTH_TOKEN); + for (const write of context.calls.slice(0, -1)) { + assert.equal(write.source, rewritePackageLock(source, registry)); + } + assert.equal(context.errors.length, 0); + } +}); + for (const result of [{ status: 7 }, { status: null, signal: 'SIGTERM' }, { error: new Error('not found') }]) { test(`ci:msft reports npm failure: ${result.signal || result.error?.message || result.status}`, () => { const context = runCli({ result }); From 9ad2e04ba4c368126f99e207ecf0b692998f8f68 Mon Sep 17 00:00:00 2001 From: Bill Schnurr Date: Thu, 12 Nov 2026 11:09:08 -0700 Subject: [PATCH 3/6] Use internal Microsoft package feed defaults Replace packagefeedproxy defaults with Pylance_PublicPackages for npm and DevDiv/debugpy for pip. Preserve public defaults and update existing setup/registry tests. No workflow or credential changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- build/package-feeds.json | 4 ++-- build/setup-npm-registry.test.js | 14 +++++++------- build/setup.test.js | 8 ++++++-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/build/package-feeds.json b/build/package-feeds.json index c32a71a0..75592b5e 100644 --- a/build/package-feeds.json +++ b/build/package-feeds.json @@ -4,7 +4,7 @@ "pip": "https://pypi.org/simple/" }, "microsoft": { - "npm": "https://packagefeedproxy.microsoft.io/npm/", - "pip": "https://packagefeedproxy.microsoft.io/pypi/simple/" + "npm": "https://devdiv.pkgs.visualstudio.com/DevDiv/_packaging/Pylance_PublicPackages/npm/registry/", + "pip": "https://pkgs.dev.azure.com/devdiv/DevDiv/_packaging/debugpy/pypi/simple/" } } diff --git a/build/setup-npm-registry.test.js b/build/setup-npm-registry.test.js index 71125a62..8d86e03c 100644 --- a/build/setup-npm-registry.test.js +++ b/build/setup-npm-registry.test.js @@ -11,7 +11,7 @@ const { rewritePackageLock, rewritePackageLocks } = require('./setup-npm-registr const { normalizePackageLock } = require('./normalize-package-lock'); const publicRegistry = 'https://registry.npmjs.org/'; -const proxyRegistry = 'https://packagefeedproxy.microsoft.io/npm/'; +const microsoftRegistry = 'https://devdiv.pkgs.visualstudio.com/DevDiv/_packaging/Pylance_PublicPackages/npm/registry/'; const source = `${JSON.stringify( { lockfileVersion: 3, @@ -34,8 +34,8 @@ const source = `${JSON.stringify( test('rewrites public resolved URLs without changing versions, integrity or formatting', () => { const expected = source - .replaceAll(publicRegistry, proxyRegistry) - .replace('https://registry.yarnpkg.com/', proxyRegistry); + .replaceAll(publicRegistry, microsoftRegistry) + .replace('https://registry.yarnpkg.com/', microsoftRegistry); assert.equal(rewritePackageLock(source), expected); assert.equal(rewritePackageLock(expected), expected); assert.equal(normalizePackageLock(expected), source.replace('https://registry.yarnpkg.com/', publicRegistry)); @@ -47,7 +47,7 @@ test('supports version 2 legacy dependencies as well as packages', () => { packages: {}, dependencies: { example: { resolved: `${publicRegistry}example/-/example.tgz` } }, }); - assert.equal(rewritePackageLock(legacy), legacy.replace(publicRegistry, proxyRegistry)); + assert.equal(rewritePackageLock(legacy), legacy.replace(publicRegistry, microsoftRegistry)); }); test('preserves other fields, private feeds, Git dependencies and lookalike hosts', () => { @@ -137,12 +137,12 @@ function runCli({ args = ['--ci'], npmCli = 'npm-cli.js', result = { status: 0 } return { calls, errors, process }; } -test('ci:msft rewrites the root lockfile before installing with the shared proxy registry', () => { +test('ci:msft rewrites the root lockfile before installing with the Microsoft registry', () => { const context = runCli(); assert.equal(context.calls.length, 2); assert.ok(context.calls[0].file.endsWith('package-lock.json')); assert.equal(context.calls[1].command, 'node'); - assert.deepEqual(context.calls[1].args, ['npm-cli.js', 'ci', `--registry=${proxyRegistry}`]); + assert.deepEqual(context.calls[1].args, ['npm-cli.js', 'ci', `--registry=${microsoftRegistry}`]); assert.equal(context.calls[1].cwd, path.resolve(__dirname, '..')); assert.equal(context.calls[1].stdio, 'inherit'); assert.equal(context.errors.length, 0); @@ -155,7 +155,7 @@ test('ci:msft preserves template authentication for the configured internal regi for (const settings of [ { NPM_CONFIG_REGISTRY: registry }, { npm_config_registry: registry }, - { NPM_CONFIG_REGISTRY: proxyRegistry, npm_config_registry: registry }, + { NPM_CONFIG_REGISTRY: 'https://stale.example/npm/', npm_config_registry: registry }, ]) { const env = { ...settings, diff --git a/build/setup.test.js b/build/setup.test.js index e75ac2f6..6a30fb00 100644 --- a/build/setup.test.js +++ b/build/setup.test.js @@ -90,11 +90,15 @@ for (const platform of ['win32', 'linux']) { for (const call of calls) { assert.equal( call.env.npm_config_registry, - microsoft ? 'https://packagefeedproxy.microsoft.io/npm/' : 'https://registry.npmjs.org/', + microsoft + ? 'https://devdiv.pkgs.visualstudio.com/DevDiv/_packaging/Pylance_PublicPackages/npm/registry/' + : 'https://registry.npmjs.org/', ); assert.equal( call.env.PIP_INDEX_URL, - microsoft ? 'https://packagefeedproxy.microsoft.io/pypi/simple/' : 'https://pypi.org/simple/', + microsoft + ? 'https://pkgs.dev.azure.com/devdiv/DevDiv/_packaging/debugpy/pypi/simple/' + : 'https://pypi.org/simple/', ); assert.equal(call.env.NPM_CONFIG_REGISTRY, undefined); assert.equal(call.env.HTTPS_PROXY, context.originalEnv.HTTPS_PROXY); From 0548be230e9654828dc95bf7701728046acc83c8 Mon Sep 17 00:00:00 2001 From: Bill Schnurr Date: Thu, 12 Nov 2026 11:26:24 -0700 Subject: [PATCH 4/6] Normalize registry overrides before rewriting and installing Ensure slashless npm registry overrides end in a slash before building lockfile download URLs and invoking npm ci. Add regressions for public/internal registry rewrites and both uppercase/lowercase environment overrides while preserving npm authentication. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- build/setup-npm-registry.js | 11 +++++++++-- build/setup-npm-registry.test.js | 9 +++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/build/setup-npm-registry.js b/build/setup-npm-registry.js index 162b242c..f902c595 100644 --- a/build/setup-npm-registry.js +++ b/build/setup-npm-registry.js @@ -6,6 +6,10 @@ const { readFileSync, writeFileSync } = require('node:fs'); const path = require('node:path'); const { microsoft } = require('./package-feeds.json'); +function normalizeRegistry(registry) { + return registry.endsWith('/') ? registry : `${registry}/`; +} + function rewritePackageLock(source, registry = microsoft.npm) { const lockfile = JSON.parse(source); if ( @@ -17,9 +21,10 @@ function rewritePackageLock(source, registry = microsoft.npm) { throw new Error('Expected an npm lockfile with lockfileVersion 2 or 3 and a packages section.'); } + const normalizedRegistry = normalizeRegistry(registry); return source.replace(/("resolved"\s*:\s*)("(?:\\.|[^"\\])*")/g, (match, prefix, encodedUrl) => { const url = JSON.parse(encodedUrl); - const rewritten = url.replace(/^https:\/\/registry\.(?:npmjs\.org|yarnpkg\.com)\//, registry); + const rewritten = url.replace(/^https:\/\/registry\.(?:npmjs\.org|yarnpkg\.com)\//, normalizedRegistry); return rewritten === url ? match : prefix + JSON.stringify(rewritten); }); } @@ -48,7 +53,9 @@ if (require.main === module) { if (install && !process.env.npm_execpath) { throw new Error('Run npm install using npm run ci:msft.'); } - const registry = process.env.npm_config_registry || process.env.NPM_CONFIG_REGISTRY || microsoft.npm; + const registry = normalizeRegistry( + process.env.npm_config_registry || process.env.NPM_CONFIG_REGISTRY || microsoft.npm, + ); rewritePackageLocks(undefined, registry); if (install) { const result = spawnSync(process.execPath, [process.env.npm_execpath, 'ci', `--registry=${registry}`], { diff --git a/build/setup-npm-registry.test.js b/build/setup-npm-registry.test.js index 8d86e03c..485cb169 100644 --- a/build/setup-npm-registry.test.js +++ b/build/setup-npm-registry.test.js @@ -41,6 +41,13 @@ test('rewrites public resolved URLs without changing versions, integrity or form assert.equal(normalizePackageLock(expected), source.replace('https://registry.yarnpkg.com/', publicRegistry)); }); +test('normalizes slashless registry overrides before rewriting resolved URLs', () => { + for (const registry of [publicRegistry, microsoftRegistry]) { + const expected = source.replaceAll(publicRegistry, registry).replace('https://registry.yarnpkg.com/', registry); + assert.equal(rewritePackageLock(source, registry.slice(0, -1)), expected); + } +}); + test('supports version 2 legacy dependencies as well as packages', () => { const legacy = JSON.stringify({ lockfileVersion: 2, @@ -155,6 +162,8 @@ test('ci:msft preserves template authentication for the configured internal regi for (const settings of [ { NPM_CONFIG_REGISTRY: registry }, { npm_config_registry: registry }, + { NPM_CONFIG_REGISTRY: registry.slice(0, -1) }, + { npm_config_registry: registry.slice(0, -1) }, { NPM_CONFIG_REGISTRY: 'https://stale.example/npm/', npm_config_registry: registry }, ]) { const env = { From 8f0a26d38a417a1bf5de388a98b8b74c627174c6 Mon Sep 17 00:00:00 2001 From: Bill Schnurr Date: Thu, 12 Nov 2026 14:27:28 -0700 Subject: [PATCH 5/6] Use internal package feeds in GitHub workflows Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/pr-check.yml | 2 +- .github/workflows/push-check.yml | 2 +- build/setup.test.js | 6 ++++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 0effe3b0..4a9904eb 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -21,8 +21,8 @@ on: - cron: '15 16 * * 2' env: - NPM_CONFIG_REGISTRY: https://packagefeedproxy.microsoft.io/npm/ - PIP_INDEX_URL: https://packagefeedproxy.microsoft.io/pypi/simple/ + NPM_CONFIG_REGISTRY: https://devdiv.pkgs.visualstudio.com/DevDiv/_packaging/Pylance_PublicPackages/npm/registry/ + PIP_INDEX_URL: https://pkgs.dev.azure.com/devdiv/DevDiv/_packaging/debugpy/pypi/simple/ jobs: analyze: diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index 47812b0c..be16b7ed 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -6,7 +6,7 @@ on: permissions: {} env: - PIP_INDEX_URL: https://packagefeedproxy.microsoft.io/pypi/simple/ + PIP_INDEX_URL: https://pkgs.dev.azure.com/devdiv/DevDiv/_packaging/debugpy/pypi/simple/ NODE_VERSION: 22.17.0 TEST_RESULTS_DIRECTORY: . # Force a path with spaces and unicode chars to test extension works in these scenarios diff --git a/.github/workflows/push-check.yml b/.github/workflows/push-check.yml index c958f625..dfa15494 100644 --- a/.github/workflows/push-check.yml +++ b/.github/workflows/push-check.yml @@ -11,7 +11,7 @@ on: permissions: {} env: - PIP_INDEX_URL: https://packagefeedproxy.microsoft.io/pypi/simple/ + PIP_INDEX_URL: https://pkgs.dev.azure.com/devdiv/DevDiv/_packaging/debugpy/pypi/simple/ NODE_VERSION: 22.17.0 TEST_RESULTS_DIRECTORY: . # Force a path with spaces and unicode chars to test extension works in these scenarios diff --git a/build/setup.test.js b/build/setup.test.js index 6a30fb00..664c0e6a 100644 --- a/build/setup.test.js +++ b/build/setup.test.js @@ -184,10 +184,11 @@ test('Microsoft entry point selects the shared Microsoft setup implementation', test('PR and push installs use the npm helper without a redundant registry environment setting', () => { const root = path.join(__dirname, '..'); + const { microsoft } = require('./package-feeds.json'); for (const file of ['pr-check.yml', 'push-check.yml']) { const workflow = readFileSync(path.join(root, '.github', 'workflows', file), 'utf8'); assert.doesNotMatch(workflow, /^\s*NPM_CONFIG_REGISTRY:/m); - assert.match(workflow, /^\s*PIP_INDEX_URL: https:\/\/packagefeedproxy\.microsoft\.io\/pypi\/simple\//m); + assert.ok(workflow.split(/\r?\n/).includes(` PIP_INDEX_URL: ${microsoft.pip}`)); assert.doesNotMatch(workflow, /^\s*run: npm ci\b/m); } for (const action of ['lint', 'build-vsix']) { @@ -195,5 +196,6 @@ test('PR and push installs use the npm helper without a redundant registry envir assert.match(workflow, /^\s*run: npm run ci:msft\s*$/m); } const codeql = readFileSync(path.join(root, '.github', 'workflows', 'codeql-analysis.yml'), 'utf8'); - assert.match(codeql, /^\s*NPM_CONFIG_REGISTRY: https:\/\/packagefeedproxy\.microsoft\.io\/npm\//m); + assert.ok(codeql.split(/\r?\n/).includes(` NPM_CONFIG_REGISTRY: ${microsoft.npm}`)); + assert.ok(codeql.split(/\r?\n/).includes(` PIP_INDEX_URL: ${microsoft.pip}`)); }); From 7387b62919c68ff3a523f8cbd5d7223944e9dbab Mon Sep 17 00:00:00 2001 From: Bill Schnurr Date: Fri, 13 Nov 2026 14:15:17 -0700 Subject: [PATCH 6/6] Authenticate GitHub package feed installs using Azure OIDC Port the isort feed authentication action and npm/pip configuration. Retain per-platform debugger bundles because debugpy wheels are host-specific. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/actions/authenticate-feeds/action.yml | 18 +++++++ .github/actions/build-vsix/action.yml | 3 ++ .github/actions/lint/action.yml | 3 ++ .github/workflows/codeql-analysis.yml | 5 +- .github/workflows/info-needed-closer.yml | 21 ++++++++ .github/workflows/issue-labels.yml | 19 +++++++ .github/workflows/pr-check.yml | 25 ++++++++- .github/workflows/push-check.yml | 14 ++++- README.md | 4 +- build/package-feeds.json | 2 +- build/setup-github-feeds.js | 52 ++++++++++++++++++ build/setup-npm-registry.test.js | 54 +++++++++++++++++++ build/setup.test.js | 16 ++++-- 13 files changed, 225 insertions(+), 11 deletions(-) create mode 100644 .github/actions/authenticate-feeds/action.yml create mode 100644 build/setup-github-feeds.js diff --git a/.github/actions/authenticate-feeds/action.yml b/.github/actions/authenticate-feeds/action.yml new file mode 100644 index 00000000..b3ba3d1d --- /dev/null +++ b/.github/actions/authenticate-feeds/action.yml @@ -0,0 +1,18 @@ +name: 'Authenticate package feeds' +description: 'Authenticate to Pylance_PublicPackages using GitHub OIDC' + +runs: + using: 'composite' + steps: + - name: Azure OIDC login + uses: azure/login@a641126d1b8aa4d1fa005f4f92df94a3a4c4c906 # v3.1.0 + with: + client-id: 92c669e8-02ad-4ce6-ad73-f222fc7177e2 + tenant-id: 72f988bf-86f1-41af-91ab-2d7cd011db47 + allow-no-subscriptions: true + + - name: Configure authenticated npm and pip feeds + shell: bash + run: | + token="$(az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken --output tsv | tr -d '\r')" + NODE_AUTH_TOKEN="$token" node "$GITHUB_ACTION_PATH/../../../build/setup-github-feeds.js" diff --git a/.github/actions/build-vsix/action.yml b/.github/actions/build-vsix/action.yml index 775d04f1..f853f933 100644 --- a/.github/actions/build-vsix/action.yml +++ b/.github/actions/build-vsix/action.yml @@ -9,6 +9,9 @@ inputs: runs: using: 'composite' steps: + - name: Authenticate package feeds + uses: ./.github/actions/authenticate-feeds + - name: Install Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: diff --git a/.github/actions/lint/action.yml b/.github/actions/lint/action.yml index 22a8ee0f..97234331 100644 --- a/.github/actions/lint/action.yml +++ b/.github/actions/lint/action.yml @@ -9,6 +9,9 @@ inputs: runs: using: 'composite' steps: + - name: Authenticate package feeds + uses: ./.github/actions/authenticate-feeds + - name: Install Node uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4a9904eb..3ff88e42 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -22,7 +22,6 @@ on: env: NPM_CONFIG_REGISTRY: https://devdiv.pkgs.visualstudio.com/DevDiv/_packaging/Pylance_PublicPackages/npm/registry/ - PIP_INDEX_URL: https://pkgs.dev.azure.com/devdiv/DevDiv/_packaging/debugpy/pypi/simple/ jobs: analyze: @@ -32,6 +31,7 @@ jobs: actions: read contents: read security-events: write + id-token: write strategy: fail-fast: false @@ -46,6 +46,9 @@ jobs: with: persist-credentials: false + - name: Authenticate package feeds + uses: ./.github/actions/authenticate-feeds + # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1 diff --git a/.github/workflows/info-needed-closer.yml b/.github/workflows/info-needed-closer.yml index f1db26d7..3e919e52 100644 --- a/.github/workflows/info-needed-closer.yml +++ b/.github/workflows/info-needed-closer.yml @@ -9,10 +9,31 @@ on: permissions: issues: write +env: + NPM_CONFIG_REGISTRY: https://devdiv.pkgs.visualstudio.com/DevDiv/_packaging/Pylance_PublicPackages/npm/registry/ + jobs: main: runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + issues: write steps: + - name: Checkout feed authentication action + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: ci-actions + persist-credentials: false + sparse-checkout: | + /.github/actions/authenticate-feeds/ + /build/setup-github-feeds.js + /build/package-feeds.json + sparse-checkout-cone-mode: false + + - name: Authenticate package feeds + uses: ./ci-actions/.github/actions/authenticate-feeds + - name: Checkout Actions uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: diff --git a/.github/workflows/issue-labels.yml b/.github/workflows/issue-labels.yml index bd034bbc..90a9871e 100644 --- a/.github/workflows/issue-labels.yml +++ b/.github/workflows/issue-labels.yml @@ -5,6 +5,7 @@ on: types: [opened, reopened] env: + NPM_CONFIG_REGISTRY: https://devdiv.pkgs.visualstudio.com/DevDiv/_packaging/Pylance_PublicPackages/npm/registry/ TRIAGERS: '["karthiknadig","eleanorjboyd","anthonykim1"]' permissions: @@ -15,7 +16,25 @@ jobs: add-classify-label: name: "Add 'triage-needed' and remove assignees" runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + issues: write steps: + - name: Checkout feed authentication action + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: ci-actions + persist-credentials: false + sparse-checkout: | + /.github/actions/authenticate-feeds/ + /build/setup-github-feeds.js + /build/package-feeds.json + sparse-checkout-cone-mode: false + + - name: Authenticate package feeds + uses: ./ci-actions/.github/actions/authenticate-feeds + - name: Checkout Actions uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml index be16b7ed..f881624e 100644 --- a/.github/workflows/pr-check.yml +++ b/.github/workflows/pr-check.yml @@ -6,7 +6,7 @@ on: permissions: {} env: - PIP_INDEX_URL: https://pkgs.dev.azure.com/devdiv/DevDiv/_packaging/debugpy/pypi/simple/ + NPM_CONFIG_REGISTRY: https://devdiv.pkgs.visualstudio.com/DevDiv/_packaging/Pylance_PublicPackages/npm/registry/ NODE_VERSION: 22.17.0 TEST_RESULTS_DIRECTORY: . # Force a path with spaces and unicode chars to test extension works in these scenarios @@ -17,6 +17,9 @@ jobs: build-vsix: name: Create VSIX runs-on: ubuntu-latest + permissions: + contents: read + id-token: write steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -31,6 +34,9 @@ jobs: lint: name: Lint runs-on: ubuntu-latest + permissions: + contents: read + id-token: write steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -45,6 +51,9 @@ jobs: tests: name: Tests runs-on: ${{ matrix.os }} + permissions: + contents: read + id-token: write defaults: run: working-directory: ${{ env.special-working-directory }} @@ -61,6 +70,20 @@ jobs: path: ${{ env.special-working-directory-relative }} persist-credentials: false + - name: Checkout feed authentication action + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: ci-actions + persist-credentials: false + sparse-checkout: | + /.github/actions/authenticate-feeds/ + /build/setup-github-feeds.js + /build/package-feeds.json + sparse-checkout-cone-mode: false + + - name: Authenticate package feeds + uses: ./ci-actions/.github/actions/authenticate-feeds + # Install bundled libs using 3.10 (minimum supported; debugpy dropped 3.9 support, # and pip 26.1+ requires Python >= 3.10). - name: Use Python 3.10 diff --git a/.github/workflows/push-check.yml b/.github/workflows/push-check.yml index dfa15494..4e14dd30 100644 --- a/.github/workflows/push-check.yml +++ b/.github/workflows/push-check.yml @@ -11,7 +11,7 @@ on: permissions: {} env: - PIP_INDEX_URL: https://pkgs.dev.azure.com/devdiv/DevDiv/_packaging/debugpy/pypi/simple/ + NPM_CONFIG_REGISTRY: https://devdiv.pkgs.visualstudio.com/DevDiv/_packaging/Pylance_PublicPackages/npm/registry/ NODE_VERSION: 22.17.0 TEST_RESULTS_DIRECTORY: . # Force a path with spaces and unicode chars to test extension works in these scenarios @@ -22,6 +22,9 @@ jobs: build-vsix: name: Create VSIX runs-on: ubuntu-latest + permissions: + contents: read + id-token: write steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -36,6 +39,9 @@ jobs: lint: name: Lint runs-on: ubuntu-latest + permissions: + contents: read + id-token: write steps: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -50,6 +56,9 @@ jobs: tests: name: Tests runs-on: ${{ matrix.os }} + permissions: + contents: read + id-token: write defaults: run: working-directory: ${{ env.special-working-directory }} @@ -66,6 +75,9 @@ jobs: path: ${{ env.special-working-directory-relative }} persist-credentials: false + - name: Authenticate package feeds + uses: ./testDir/.github/actions/authenticate-feeds + # Install bundled libs using 3.10 (minimum supported; debugpy dropped 3.9 support, # and pip 26.1+ requires Python >= 3.10). - name: Use Python 3.10 diff --git a/README.md b/README.md index 75731974..4f776ffa 100644 --- a/README.md +++ b/README.md @@ -121,9 +121,9 @@ To normalize manually, run `npm run normalize-lockfile`, review the changes, and ### GitHub Actions package feeds -The PR and push workflows set `PIP_INDEX_URL` to `https://packagefeedproxy.microsoft.io/pypi/simple/` for pip and nox installs. Their Node installation steps select `https://packagefeedproxy.microsoft.io/npm/` explicitly through `npm run ci:msft`, so these workflows do not set `NPM_CONFIG_REGISTRY`. CodeQL retains both environment variables because its autobuild step does not use the install helper. The npm endpoint follows the [TypeScript CI package-feed-proxy job](https://github.com/microsoft/TypeScript/blob/main/.github/workflows/ci.yml). +Package-installing GitHub workflows authenticate to the DevDiv `Pylance_PublicPackages` npm and pip feeds using the shared `.github/actions/authenticate-feeds` action and Azure OIDC. Authenticating jobs require `contents: read` and `id-token: write`. Authentication runs before Python setup or package installation, exporting a masked, authenticated `PIP_INDEX_URL`, `PIP_NO_INPUT=1`, `NODE_AUTH_TOKEN`, and a temporary `NPM_CONFIG_USERCONFIG`. Workflows retain `NPM_CONFIG_REGISTRY` for npm requests; credentials are not written into repository files. -The shared lint and VSIX build actions and applicable PR/push test jobs use `npm run ci:msft`, rewriting working-copy lockfile URLs before installation and explicitly selecting the proxy for npm requests. Workflows do not commit these rewrites or change local registry configuration or Dependabot configuration. Existing dependency caches are retained; the VSIX build also retains its offline-cache preference. CodeQL retains workflow-level feed settings for its autobuild step. +The shared lint and VSIX build actions and applicable PR/push test jobs use `npm run ci:msft`, rewriting working-copy lockfile URLs before installation and explicitly selecting the feed for npm requests. Workflows do not commit these rewrites or change local registry configuration or Dependabot configuration. Existing dependency caches are retained; the VSIX build also retains its offline-cache preference. Test jobs continue bundling separately on each platform: debugpy's default wheel download selects a host-specific wheel, so the Ubuntu VSIX bundle cannot safely be shared with Windows tests. ### Azure release package feeds diff --git a/build/package-feeds.json b/build/package-feeds.json index 75592b5e..e6bde26c 100644 --- a/build/package-feeds.json +++ b/build/package-feeds.json @@ -5,6 +5,6 @@ }, "microsoft": { "npm": "https://devdiv.pkgs.visualstudio.com/DevDiv/_packaging/Pylance_PublicPackages/npm/registry/", - "pip": "https://pkgs.dev.azure.com/devdiv/DevDiv/_packaging/debugpy/pypi/simple/" + "pip": "https://pkgs.dev.azure.com/devdiv/DevDiv/_packaging/Pylance_PublicPackages/pypi/simple/" } } diff --git a/build/setup-github-feeds.js b/build/setup-github-feeds.js new file mode 100644 index 00000000..964da43e --- /dev/null +++ b/build/setup-github-feeds.js @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +const { appendFileSync, writeFileSync } = require('node:fs'); +const path = require('node:path'); +const { URL } = require('node:url'); +const { microsoft } = require('./package-feeds.json'); + +try { + const { NODE_AUTH_TOKEN: token, RUNNER_TEMP: temp, GITHUB_ENV: environmentFile } = process.env; + if (!token || token === 'null' || /[\r\n]/.test(token)) { + throw new Error('A nonempty, single-line Azure DevOps token is required.'); + } + if (!temp || !environmentFile) { + throw new Error('RUNNER_TEMP and GITHUB_ENV are required.'); + } + + console.log(`::add-mask::${token}`); + const pipIndex = new URL(microsoft.pip); + pipIndex.username = 'build'; + pipIndex.password = token; + console.log(`::add-mask::${pipIndex.href}`); + + const registry = new URL(microsoft.npm); + const registryScope = `//${registry.host}${registry.pathname}`; + const feedScope = registryScope.replace(/registry\/$/, ''); + const npmrc = path.join(temp, 'python-debugger-feeds.npmrc'); + writeFileSync( + npmrc, + [ + `registry=${registry.href}`, + `${registryScope}:_authToken=\${NODE_AUTH_TOKEN}`, + `${feedScope}:_authToken=\${NODE_AUTH_TOKEN}`, + '', + ].join('\n'), + 'utf8', + ); + appendFileSync( + environmentFile, + [ + `NODE_AUTH_TOKEN=${token}`, + `NPM_CONFIG_USERCONFIG=${npmrc}`, + `PIP_INDEX_URL=${pipIndex.href}`, + 'PIP_NO_INPUT=1', + '', + ].join('\n'), + 'utf8', + ); +} catch (error) { + console.error(`[setup] GitHub feed authentication failed: ${error.message}`); + process.exitCode = 1; +} diff --git a/build/setup-npm-registry.test.js b/build/setup-npm-registry.test.js index 485cb169..ed928415 100644 --- a/build/setup-npm-registry.test.js +++ b/build/setup-npm-registry.test.js @@ -2,10 +2,12 @@ // Licensed under the MIT License. const assert = require('node:assert/strict'); +const { spawnSync } = require('node:child_process'); const { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } = require('node:fs'); const { tmpdir } = require('node:os'); const path = require('node:path'); const { test } = require('node:test'); +const { URL } = require('node:url'); const vm = require('node:vm'); const { rewritePackageLock, rewritePackageLocks } = require('./setup-npm-registry'); const { normalizePackageLock } = require('./normalize-package-lock'); @@ -207,3 +209,55 @@ test('standalone registry rewrite does not install dependencies or require npm', assert.ok(context.calls.every((call) => call.file)); assert.equal(context.errors.length, 0); }); + +function configureGitHubFeeds(t, token) { + const root = mkdtempSync(path.join(__dirname, '..', '.feed-auth-test-')); + t.after(() => rmSync(root, { recursive: true, force: true })); + const environmentFile = path.join(root, 'github-env'); + writeFileSync(environmentFile, 'EXISTING=value\n'); + const result = spawnSync(process.execPath, [path.join(__dirname, 'setup-github-feeds.js')], { + encoding: 'utf8', + env: { ...process.env, RUNNER_TEMP: root, GITHUB_ENV: environmentFile, NODE_AUTH_TOKEN: token }, + }); + return { root, environmentFile, result }; +} + +test('GitHub feed authentication configures both feeds without storing npm credentials in npmrc', (t) => { + const token = 'test-token.+/='; + const { root, environmentFile, result } = configureGitHubFeeds(t, token); + assert.equal(result.status, 0, result.stderr); + const npmrc = path.join(root, 'python-debugger-feeds.npmrc'); + const config = readFileSync(npmrc, 'utf8'); + assert.ok(config.includes(`registry=${microsoftRegistry}\n`)); + assert.ok(config.includes('/npm/registry/:_authToken=${NODE_AUTH_TOKEN}\n')); + assert.ok(config.includes('/npm/:_authToken=${NODE_AUTH_TOKEN}\n')); + assert.ok(!config.includes(token)); + const environment = readFileSync(environmentFile, 'utf8'); + assert.ok(environment.startsWith('EXISTING=value\n')); + assert.ok(environment.includes(`NPM_CONFIG_USERCONFIG=${npmrc}\n`)); + assert.ok(environment.includes(`NODE_AUTH_TOKEN=${token}\n`)); + assert.ok(environment.includes('PIP_NO_INPUT=1\n')); + const index = new URL( + environment + .split('\n') + .find((line) => line.startsWith('PIP_INDEX_URL=')) + .slice(14), + ); + assert.equal(index.hostname, 'pkgs.dev.azure.com'); + assert.equal(index.pathname, '/devdiv/DevDiv/_packaging/Pylance_PublicPackages/pypi/simple/'); + assert.equal(index.username, 'build'); + assert.equal(decodeURIComponent(index.password), token); + assert.ok(result.stdout.includes(`::add-mask::${token}\n`)); + assert.ok(result.stdout.includes(`::add-mask::${index.href}\n`)); +}); + +test('GitHub feed authentication rejects missing or multiline tokens without modifying configuration', (t) => { + for (const token of ['', 'null', 'token\nINJECTED=value']) { + const { root, environmentFile, result } = configureGitHubFeeds(t, token); + assert.equal(result.status, 1); + assert.match(result.stderr, /single-line Azure DevOps token is required/); + assert.equal(readFileSync(environmentFile, 'utf8'), 'EXISTING=value\n'); + assert.throws(() => readFileSync(path.join(root, 'python-debugger-feeds.npmrc')), /ENOENT/); + assert.ok(!result.stderr.includes('INJECTED')); + } +}); diff --git a/build/setup.test.js b/build/setup.test.js index 664c0e6a..bf003f21 100644 --- a/build/setup.test.js +++ b/build/setup.test.js @@ -97,7 +97,7 @@ for (const platform of ['win32', 'linux']) { assert.equal( call.env.PIP_INDEX_URL, microsoft - ? 'https://pkgs.dev.azure.com/devdiv/DevDiv/_packaging/debugpy/pypi/simple/' + ? 'https://pkgs.dev.azure.com/devdiv/DevDiv/_packaging/Pylance_PublicPackages/pypi/simple/' : 'https://pypi.org/simple/', ); assert.equal(call.env.NPM_CONFIG_REGISTRY, undefined); @@ -182,20 +182,26 @@ test('Microsoft entry point selects the shared Microsoft setup implementation', assert.equal(scripts['setup:msft'], 'node ./build/setup-msft.js'); }); -test('PR and push installs use the npm helper without a redundant registry environment setting', () => { +test('CI installs use authenticated feeds and retain the npm registry for the install helper', () => { const root = path.join(__dirname, '..'); const { microsoft } = require('./package-feeds.json'); for (const file of ['pr-check.yml', 'push-check.yml']) { const workflow = readFileSync(path.join(root, '.github', 'workflows', file), 'utf8'); - assert.doesNotMatch(workflow, /^\s*NPM_CONFIG_REGISTRY:/m); - assert.ok(workflow.split(/\r?\n/).includes(` PIP_INDEX_URL: ${microsoft.pip}`)); + assert.ok(workflow.split(/\r?\n/).includes(` NPM_CONFIG_REGISTRY: ${microsoft.npm}`)); + assert.doesNotMatch(workflow, /^\s*PIP_INDEX_URL:/m); + assert.match(workflow, /uses: \.\/(?:ci-actions|testDir)\/\.github\/actions\/authenticate-feeds/); assert.doesNotMatch(workflow, /^\s*run: npm ci\b/m); } for (const action of ['lint', 'build-vsix']) { const workflow = readFileSync(path.join(root, '.github', 'actions', action, 'action.yml'), 'utf8'); assert.match(workflow, /^\s*run: npm run ci:msft\s*$/m); + assert.match( + workflow, + /steps:\s+- name: Authenticate package feeds\s+uses: \.\/\.github\/actions\/authenticate-feeds/, + ); } const codeql = readFileSync(path.join(root, '.github', 'workflows', 'codeql-analysis.yml'), 'utf8'); assert.ok(codeql.split(/\r?\n/).includes(` NPM_CONFIG_REGISTRY: ${microsoft.npm}`)); - assert.ok(codeql.split(/\r?\n/).includes(` PIP_INDEX_URL: ${microsoft.pip}`)); + assert.doesNotMatch(codeql, /^\s*PIP_INDEX_URL:/m); + assert.match(codeql, /uses: \.\/\.github\/actions\/authenticate-feeds/); });