Add extra ENV vars

This commit is contained in:
Reinier Criel 2025-11-11 10:37:39 -08:00
parent 76acf43128
commit 9b102412af
2 changed files with 48 additions and 3 deletions

View file

@ -2,6 +2,10 @@ import { ui } from "../../environment/userInteraction.js";
import { safeSpawn } from "../../utils/safeSpawn.js"; import { safeSpawn } from "../../utils/safeSpawn.js";
import { mergeSafeChainProxyEnvironmentVariables } from "../../registryProxy/registryProxy.js"; import { mergeSafeChainProxyEnvironmentVariables } from "../../registryProxy/registryProxy.js";
import { getCombinedCaBundlePath } from "../../registryProxy/certBundle.js"; import { getCombinedCaBundlePath } from "../../registryProxy/certBundle.js";
import { knownPipRegistries } from "../../registryProxy/parsePackageFromUrl.js";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
/** /**
* @param {string} command * @param {string} command
@ -20,6 +24,33 @@ export async function runPip(command, args) {
env.REQUESTS_CA_BUNDLE = combinedCaPath; env.REQUESTS_CA_BUNDLE = combinedCaPath;
env.SSL_CERT_FILE = combinedCaPath; env.SSL_CERT_FILE = combinedCaPath;
// To counter behavior that is sometimes seen where pip ignores REQUESTS_CA_BUNDLE/SSL_CERT_FILE,
// 1. Set additional env vars for pip
// 2. Create a pip config file that specifies the cert and trusted hosts
env.PIP_CERT = combinedCaPath;
// Create a temporary pip config file
const tmpDir = os.tmpdir();
const pipConfigPath = path.join(tmpDir, `safe-chain-pip-${Date.now()}.ini`);
// Trusted hosts: use knownPipRegistries from parsePackageFromUrl
const trustedHosts = Array.from(new Set(knownPipRegistries));
// Proxy settings
const httpProxy = env.HTTP_PROXY || '';
const httpsProxy = env.HTTPS_PROXY || '';
// Build pip config INI
let pipConfig = '[global]\n';
pipConfig += `cert = ${combinedCaPath}\n`;
if (httpProxy) pipConfig += `proxy = ${httpProxy}\n`;
if (httpsProxy) pipConfig += `proxy = ${httpsProxy}\n`;
if (trustedHosts.length) pipConfig += `trusted-host = ${trustedHosts.join(' ')}\n`;
await fs.writeFile(pipConfigPath, pipConfig);
env.PIP_CONFIG_FILE = pipConfigPath;
const result = await safeSpawn(command, args, { const result = await safeSpawn(command, args, {
stdio: "inherit", stdio: "inherit",
env, env,

View file

@ -43,6 +43,23 @@ describe("runPipCommand environment variable handling", () => {
mock.reset(); mock.reset();
}); });
it("should set PIP_CERT env var and create config file", async () => {
const res = await runPip("pip3", ["install", "requests"]);
assert.strictEqual(res.status, 0);
assert.ok(capturedArgs, "safeSpawn should have been called");
// Check PIP_CERT env var
assert.strictEqual(
capturedArgs.options.env.PIP_CERT,
"/tmp/test-combined-ca.pem",
"PIP_CERT should be set to combined bundle path"
);
// Check PIP_CONFIG_FILE env var exists and is a non-empty string
const configPath = capturedArgs.options.env.PIP_CONFIG_FILE;
assert.ok(configPath, "PIP_CONFIG_FILE should be set");
assert.strictEqual(typeof configPath, "string", "PIP_CONFIG_FILE should be a string");
assert.ok(configPath.length > 0, "PIP_CONFIG_FILE should be a non-empty path");
});
it("should set REQUESTS_CA_BUNDLE and SSL_CERT_FILE for default PyPI (no explicit index)", async () => { it("should set REQUESTS_CA_BUNDLE and SSL_CERT_FILE for default PyPI (no explicit index)", async () => {
const res = await runPip("pip3", ["install", "requests"]); const res = await runPip("pip3", ["install", "requests"]);
assert.strictEqual(res.status, 0); assert.strictEqual(res.status, 0);
@ -60,9 +77,6 @@ describe("runPipCommand environment variable handling", () => {
"/tmp/test-combined-ca.pem", "/tmp/test-combined-ca.pem",
"SSL_CERT_FILE should be set to combined bundle path" "SSL_CERT_FILE should be set to combined bundle path"
); );
// Args should be unchanged (no arg injection)
assert.deepStrictEqual(capturedArgs.args, ["install", "requests"]);
}); });
it("should set CA environment variables even for external/test PyPI mirror (covers non-CLI traffic)", async () => { it("should set CA environment variables even for external/test PyPI mirror (covers non-CLI traffic)", async () => {