Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f763ea4
test: enforce headless package conformance
edvilme Aug 17, 2026
cb05041
Potential fix for pull request finding
edvilme Aug 18, 2026
e1f3481
Merge branch 'main' into package-manager-integration-coverage-feedback
edvilme Aug 18, 2026
df6d1a2
test: disable unstable package network CI (#1717)
edvilme Aug 18, 2026
efe2f9c
Enforce headless package manager behavior
edvilme Aug 19, 2026
cd739a5
Run package network tests in integration matrix
edvilme Aug 19, 2026
13cd44d
Stabilize package network integration setup
edvilme Aug 19, 2026
6f93cf0
Use existing Conda environment in network tests
edvilme Aug 19, 2026
a435996
Wait for package network results
edvilme Aug 19, 2026
a9de0e7
Skip ambiguous package version results
edvilme Aug 19, 2026
8fe8255
Avoid blocking Conda prerequisite lookup
edvilme Aug 19, 2026
0462436
test: address package manager review feedback
edvilme Aug 19, 2026
148891f
test: add ephemeral environment fixtures
edvilme Aug 19, 2026
6764006
test: allow unavailable pip versions
edvilme Aug 19, 2026
ebb222d
test: harden fixture cleanup
edvilme Aug 19, 2026
0601d34
test: preserve package refresh failures
edvilme Aug 19, 2026
26250c5
fix: distinguish unsupported version lookup
edvilme Aug 19, 2026
76c45af
test: harden package integration retries
edvilme Aug 19, 2026
a0fe872
test: assert version lookup capability
edvilme Aug 19, 2026
afac6d2
fix: keep version lookup error internal
edvilme Aug 19, 2026
1917253
Merge main into package manager integration coverage
edvilme Aug 20, 2026
eb998c5
fix: stabilize pip package integration tests
edvilme Aug 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/instructions/testing-workflow.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ This guide covers the full testing lifecycle:
4. **🛠️ Fixing Problems** - Resolve compilation and runtime issues
5. **✅ Validation** - Ensure coverage and resilience

## Learnings

- Pip commands that return JSON must pass `--disable-pip-version-check`; the process helper combines stderr with stdout, so update notices can otherwise make valid JSON unparseable (1).

### When to Use This Guide

**User Requests Testing:**
Expand Down
17 changes: 9 additions & 8 deletions .github/workflows/pr-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,9 @@ jobs:
name: Integration Tests
runs-on: ${{ matrix.os }}
needs: [smoke-tests]
env:
CONDA_PLUGINS_AUTO_ACCEPT_TOS: 'true'
VSC_PYTHON_PACKAGE_NETWORK_TEST: '1'
Comment thread
edvilme marked this conversation as resolved.
Comment thread
edvilme marked this conversation as resolved.
Comment thread
edvilme marked this conversation as resolved.
Comment thread
edvilme marked this conversation as resolved.
Comment thread
edvilme marked this conversation as resolved.
strategy:
Comment thread
edvilme marked this conversation as resolved.
fail-fast: false
matrix:
Expand Down Expand Up @@ -325,6 +328,12 @@ jobs:
- name: Compile Tests
run: npm run compile-tests

- name: Set up Conda
uses: conda-incubator/setup-miniconda@fc2d68f6413eb2d87b895e92f8584b5b94a10167 # v3
with:
activate-environment: ''
auto-activate: false

- name: Run Integration Tests (Linux)
if: runner.os == 'Linux'
uses: GabrielBB/xvfb-action@86d97bde4a65fe9b290c0b3fb92c2c4ed0e5302d # v1.6
Expand All @@ -335,14 +344,6 @@ jobs:
if: runner.os != 'Linux'
Comment thread
edvilme marked this conversation as resolved.
run: npm run integration-test

- name: Run Package Manager Network Integration Tests
if: runner.os == 'Linux' && matrix.python-version == '3.12'
uses: GabrielBB/xvfb-action@86d97bde4a65fe9b290c0b3fb92c2c4ed0e5302d # v1.6
env:
VSC_PYTHON_PACKAGE_NETWORK_TEST: '1'
with:
run: npm run integration-test -- --grep "Package Manager"

integration-tests-multiroot:
name: Integration Tests (Multi-Root)
runs-on: ${{ matrix.os }}
Expand Down
17 changes: 9 additions & 8 deletions .github/workflows/push-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ jobs:
name: Integration Tests
runs-on: ${{ matrix.os }}
needs: [smoke-tests]
env:
CONDA_PLUGINS_AUTO_ACCEPT_TOS: 'true'
VSC_PYTHON_PACKAGE_NETWORK_TEST: '1'
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -326,6 +329,12 @@ jobs:
- name: Compile Tests
run: npm run compile-tests

- name: Set up Conda
uses: conda-incubator/setup-miniconda@fc2d68f6413eb2d87b895e92f8584b5b94a10167 # v3
with:
activate-environment: ''
auto-activate: false

- name: Run Integration Tests (Linux)
if: runner.os == 'Linux'
uses: GabrielBB/xvfb-action@86d97bde4a65fe9b290c0b3fb92c2c4ed0e5302d # v1.6
Expand All @@ -335,11 +344,3 @@ jobs:
- name: Run Integration Tests (non-Linux)
if: runner.os != 'Linux'
run: npm run integration-test

- name: Run Package Manager Network Integration Tests
if: runner.os == 'Linux' && matrix.python-version == '3.12'
uses: GabrielBB/xvfb-action@86d97bde4a65fe9b290c0b3fb92c2c4ed0e5302d # v1.6
env:
VSC_PYTHON_PACKAGE_NETWORK_TEST: '1'
with:
run: npm run integration-test -- --grep "Package Manager"
10 changes: 10 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,16 @@ export async function activate(context: ExtensionContext): Promise<PythonEnviron
commands.registerCommand('python-envs.test.getPackageManagerIds', () =>
envManagers.packageManagers.map((manager) => manager.id),
),
commands.registerCommand(
'python-envs.test.resolveEnvironmentWithManager',
async (managerId: string, environmentUri: Uri) => {
const manager = envManagers.getEnvironmentManager(managerId);
if (!manager) {
throw new Error(`Environment manager not found: ${managerId}`);
}
return manager.resolve(environmentUri);
},
),
commands.registerCommand(
'python-envs.test.getDirectPackageNames',
async (environment: PythonEnvironment) => {
Expand Down
70 changes: 39 additions & 31 deletions src/managers/builtin/pipPackageManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import type { Pep440Version } from '@renovatebot/pep440';
import { compare, explain as parse, rcompare } from '@renovatebot/pep440';
import {
CancellationError,
CancellationToken,
Disposable,
Event,
EventEmitter,
LogOutputChannel,
MarkdownString,
ProgressLocation,
ThemeIcon,
window,
} from 'vscode';
import {
DidChangePackagesEventArgs,
Expand All @@ -22,6 +22,7 @@ import {
PythonEnvironment,
PythonEnvironmentApi,
} from '../../api';
import { showErrorMessage, withProgress } from '../../common/window.apis';
import { updatePackagesAndNotify } from '../common/packageChanges';
import { runPython, runUV, shouldUseUv } from './helpers';
import { getWorkspacePackagesToInstall } from './pipUtils';
Expand Down Expand Up @@ -75,45 +76,52 @@ export class PipPackageManager implements PackageManager, Disposable {
install: toInstall,
uninstall: toUninstall,
};
await window.withProgress(
const execute = async (token?: CancellationToken): Promise<void> => {
try {
await managePackages(environment, manageOptions, this, token);
await updatePackagesAndNotify(
this,
environment,
this.packages.get(environment.envId.id),
(changes) => {
this._onDidChangePackages.fire({ environment, manager: this, changes });
},
() => this.fetchPackages(environment, !manageOptions.runHeadless),
);
} catch (e) {
if (e instanceof CancellationError) {
throw e;
}
this.log.error('Error managing packages', e);
if (!manageOptions.runHeadless) {
setImmediate(async () => {
const result = await showErrorMessage('Error managing packages', 'View Output');
if (result === 'View Output') {
this.log.show();
}
});
}
throw e;
}
};

if (manageOptions.runHeadless) {
await execute();
return;
}

await withProgress(
{
location: ProgressLocation.Notification,
title: 'Installing packages',
cancellable: true,
},
async (_progress, token) => {
try {
await managePackages(environment, manageOptions, this, token);
await updatePackagesAndNotify(
this,
environment,
this.packages.get(environment.envId.id),
(changes) => {
this._onDidChangePackages.fire({ environment, manager: this, changes });
},
() => this.fetchPackages(environment, !manageOptions.runHeadless),
);
} catch (e) {
if (e instanceof CancellationError) {
throw e;
}
this.log.error('Error managing packages', e);
if (!manageOptions.runHeadless) {
setImmediate(async () => {
const result = await window.showErrorMessage('Error managing packages', 'View Output');
if (result === 'View Output') {
this.log.show();
}
});
}
throw e;
}
},
async (_progress, token) => execute(token),
);
}

async refresh(environment: PythonEnvironment): Promise<void> {
await window.withProgress(
await withProgress(
{
location: ProgressLocation.Window,
title: 'Refreshing packages',
Expand Down
2 changes: 1 addition & 1 deletion src/managers/builtin/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ async function execPipList(environment: PythonEnvironment, log?: LogOutputChanne
try {
return await runPython(
environment.execInfo.run.executable,
['-m', 'pip', 'list', '--format=json', ...(args ?? [])],
['-m', 'pip', 'list', '--format=json', '--disable-pip-version-check', ...(args ?? [])],
undefined,
log,
undefined,
Expand Down
69 changes: 33 additions & 36 deletions src/managers/conda/condaPackageManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { explain as parse, rcompare } from '@renovatebot/pep440';
import * as path from 'path';
import {
CancellationError,
CancellationToken,
Disposable,
Event,
EventEmitter,
Expand Down Expand Up @@ -72,37 +73,44 @@ export class CondaPackageManager implements PackageManager, Disposable {
install: toInstall,
uninstall: toUninstall,
};
const execute = async (token?: CancellationToken): Promise<void> => {
try {
await managePackages(environment, manageOptions, token, this.log);
await updatePackagesAndNotify(
this,
environment,
this.packages.get(environment.envId.id),
(changes) => {
this._onDidChangePackages.fire({ environment, manager: this, changes });
},
);
} catch (e) {
if (e instanceof CancellationError) {
throw e;
}

this.log.error('Error installing packages', e);
if (!manageOptions.runHeadless) {
setImmediate(async () => {
await showErrorMessageWithLogs(CondaStrings.condaInstallError, this.log);
});
}
throw e;
}
};

if (manageOptions.runHeadless) {
await execute();
return;
}

await withProgress(
{
location: ProgressLocation.Notification,
title: CondaStrings.condaInstallingPackages,
cancellable: true,
},
async (_progress, token) => {
try {
await managePackages(environment, manageOptions, token, this.log);
await updatePackagesAndNotify(
this,
environment,
this.packages.get(environment.envId.id),
(changes) => {
this._onDidChangePackages.fire({ environment, manager: this, changes });
},
);
} catch (e) {
if (e instanceof CancellationError) {
throw e;
}

this.log.error('Error installing packages', e);
if (!manageOptions.runHeadless) {
setImmediate(async () => {
await showErrorMessageWithLogs(CondaStrings.condaInstallError, this.log);
});
}
throw e;
}
},
async (_progress, token) => execute(token),
);
}

Expand Down Expand Up @@ -178,16 +186,6 @@ export class CondaPackageManager implements PackageManager, Disposable {
}
}

/**
* Lists available versions for a package via `conda search --json`, newest first.
*
* Conda always supports version lookup, so operational failures (command, network, or
* malformed/unparseable output) propagate instead of being swallowed into `undefined`.
*
* @param _environment - Unused; conda resolves versions from its configured channels.
* @param packageName - The package whose versions should be listed.
* @returns A promise that resolves to an array of {@link Pep440Version} objects (newest first).
*/
async getPackageAvailableVersions(
_environment: PythonEnvironment,
packageName: string,
Expand All @@ -197,7 +195,6 @@ export class CondaPackageManager implements PackageManager, Disposable {
if (!parsed || typeof parsed !== 'object' || !Array.isArray(parsed[packageName])) {
throw new Error(`Conda returned unexpected package version data for: ${packageName}`);
}

const uniqueVersions = new Map<string, Pep440Version>();
parsed[packageName]
.filter((entry: { version?: string }) => !!entry.version?.trim())
Expand Down
2 changes: 1 addition & 1 deletion src/managers/conda/condaUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1253,7 +1253,7 @@ export async function deleteCondaEnvironment(environment: PythonEnvironment, log
export async function managePackages(
environment: PythonEnvironment,
options: PackageManagementOptions,
token: CancellationToken,
token: CancellationToken | undefined,
log: LogOutputChannel,
): Promise<void> {
if (options.uninstall && options.uninstall.length > 0) {
Expand Down
Loading
Loading