refactor(cli-common): deprecate bootstrapEnvProxyAgents() in favor of Node.js built-in proxy support
Node.js 22.21.0+ and 24.5.0+ support proxy configuration natively via NODE_USE_ENV_PROXY=1 and --use-env-proxy, making the legacy global-agent and undici proxy workarounds unnecessary. Rather than removing the function immediately, deprecate it with context-aware runtime warnings that guide users based on their current configuration: - Users with GLOBAL_AGENT_* vars are told to switch to standard HTTP_PROXY/HTTPS_PROXY and set NODE_USE_ENV_PROXY=1. - Users with HTTP_PROXY/HTTPS_PROXY but no NODE_USE_ENV_PROXY are told to set it. - Users who have already opted in to Node.js built-in proxy see no warning, and the legacy bootstrap is skipped entirely. See #33444 Signed-off-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli-common': patch
|
||||
---
|
||||
|
||||
Deprecated `bootstrapEnvProxyAgents()` in favor of Node.js built-in proxy support. Set `NODE_USE_ENV_PROXY=1` alongside your `HTTP_PROXY`/`HTTPS_PROXY` environment variables instead. See the [corporate proxy guide](https://backstage.io/docs/tutorials/corporate-proxy/) for details. This function will be removed in a future release.
|
||||
@@ -10,7 +10,7 @@ import { SpawnOptions } from 'node:child_process';
|
||||
// @public
|
||||
export const BACKSTAGE_JSON = 'backstage.json';
|
||||
|
||||
// @public
|
||||
// @public @deprecated
|
||||
export function bootstrapEnvProxyAgents(): void;
|
||||
|
||||
// @public
|
||||
|
||||
@@ -26,14 +26,17 @@ jest.mock('undici', () => ({
|
||||
|
||||
describe('bootstrapEnvProxyAgents', () => {
|
||||
const originalEnv = process.env;
|
||||
const originalExecArgv = process.execArgv;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
process.env = { ...originalEnv };
|
||||
process.execArgv = [...originalExecArgv];
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env = originalEnv;
|
||||
process.execArgv = originalExecArgv;
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
@@ -101,8 +104,153 @@ describe('bootstrapEnvProxyAgents', () => {
|
||||
|
||||
const { bootstrap } =
|
||||
require('global-agent') as typeof import('global-agent');
|
||||
const spy = jest.spyOn(process, 'emitWarning');
|
||||
bootstrapEnvProxyAgents();
|
||||
|
||||
expect(bootstrap).toHaveBeenCalledTimes(1);
|
||||
expect(spy).toHaveBeenCalledWith(
|
||||
expect.stringContaining('CUSTOM_AGENT_*'),
|
||||
expect.objectContaining({
|
||||
type: 'DeprecationWarning',
|
||||
code: 'BACKSTAGE_CLI_GLOBAL_AGENT_PROXY',
|
||||
}),
|
||||
);
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it('should skip undici dispatcher but still bootstrap global-agent when NODE_USE_ENV_PROXY is set', () => {
|
||||
process.env.GLOBAL_AGENT_HTTP_PROXY = 'http://proxy.example.com';
|
||||
process.env.HTTP_PROXY = 'http://proxy.example.com';
|
||||
process.env.NODE_USE_ENV_PROXY = '1';
|
||||
|
||||
const { bootstrap } =
|
||||
require('global-agent') as typeof import('global-agent');
|
||||
const { setGlobalDispatcher } =
|
||||
require('undici') as typeof import('undici');
|
||||
bootstrapEnvProxyAgents();
|
||||
|
||||
expect(bootstrap).toHaveBeenCalledTimes(1);
|
||||
expect(setGlobalDispatcher).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should skip undici dispatcher when --use-env-proxy is in execArgv', () => {
|
||||
process.env.HTTP_PROXY = 'http://proxy.example.com';
|
||||
process.execArgv = ['--use-env-proxy'];
|
||||
|
||||
const { setGlobalDispatcher } =
|
||||
require('undici') as typeof import('undici');
|
||||
bootstrapEnvProxyAgents();
|
||||
|
||||
expect(setGlobalDispatcher).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should skip undici dispatcher when --use-env-proxy is in NODE_OPTIONS', () => {
|
||||
process.env.HTTP_PROXY = 'http://proxy.example.com';
|
||||
process.env.NODE_OPTIONS = '--use-env-proxy';
|
||||
|
||||
const { setGlobalDispatcher } =
|
||||
require('undici') as typeof import('undici');
|
||||
bootstrapEnvProxyAgents();
|
||||
|
||||
expect(setGlobalDispatcher).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should emit a deprecation warning when standard proxy vars are set without NODE_USE_ENV_PROXY', () => {
|
||||
process.env.HTTP_PROXY = 'http://proxy.example.com';
|
||||
const spy = jest.spyOn(process, 'emitWarning');
|
||||
|
||||
bootstrapEnvProxyAgents();
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(
|
||||
expect.stringContaining('NODE_USE_ENV_PROXY=1'),
|
||||
expect.objectContaining({
|
||||
type: 'DeprecationWarning',
|
||||
code: 'BACKSTAGE_CLI_PROXY_BOOTSTRAP',
|
||||
}),
|
||||
);
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it('should not emit a deprecation warning when standard proxy vars are set with NODE_USE_ENV_PROXY', () => {
|
||||
process.env.HTTP_PROXY = 'http://proxy.example.com';
|
||||
process.env.NODE_USE_ENV_PROXY = '1';
|
||||
const spy = jest.spyOn(process, 'emitWarning');
|
||||
|
||||
bootstrapEnvProxyAgents();
|
||||
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it('should not emit a deprecation warning when --use-env-proxy is in execArgv', () => {
|
||||
process.env.HTTP_PROXY = 'http://proxy.example.com';
|
||||
process.execArgv = ['--use-env-proxy'];
|
||||
const spy = jest.spyOn(process, 'emitWarning');
|
||||
|
||||
bootstrapEnvProxyAgents();
|
||||
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it('should not emit a deprecation warning when --use-env-proxy is in NODE_OPTIONS', () => {
|
||||
process.env.HTTP_PROXY = 'http://proxy.example.com';
|
||||
process.env.NODE_OPTIONS = '--use-env-proxy';
|
||||
const spy = jest.spyOn(process, 'emitWarning');
|
||||
|
||||
bootstrapEnvProxyAgents();
|
||||
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it('should emit a deprecation warning when GLOBAL_AGENT proxy vars are set', () => {
|
||||
process.env.GLOBAL_AGENT_HTTP_PROXY = 'http://proxy.example.com';
|
||||
const spy = jest.spyOn(process, 'emitWarning');
|
||||
|
||||
bootstrapEnvProxyAgents();
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(
|
||||
expect.stringContaining('GLOBAL_AGENT_*'),
|
||||
expect.objectContaining({
|
||||
type: 'DeprecationWarning',
|
||||
code: 'BACKSTAGE_CLI_GLOBAL_AGENT_PROXY',
|
||||
}),
|
||||
);
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it('should emit a deprecation warning for GLOBAL_AGENT proxy vars even with NODE_USE_ENV_PROXY', () => {
|
||||
process.env.GLOBAL_AGENT_HTTP_PROXY = 'http://proxy.example.com';
|
||||
process.env.NODE_USE_ENV_PROXY = '1';
|
||||
const spy = jest.spyOn(process, 'emitWarning');
|
||||
|
||||
bootstrapEnvProxyAgents();
|
||||
|
||||
expect(spy).toHaveBeenCalledWith(
|
||||
expect.stringContaining('GLOBAL_AGENT_*'),
|
||||
expect.objectContaining({
|
||||
type: 'DeprecationWarning',
|
||||
code: 'BACKSTAGE_CLI_GLOBAL_AGENT_PROXY',
|
||||
}),
|
||||
);
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it('should not emit a deprecation warning when no proxy vars are set', () => {
|
||||
const spy = jest.spyOn(process, 'emitWarning');
|
||||
|
||||
bootstrapEnvProxyAgents();
|
||||
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,21 +27,49 @@
|
||||
* Make sure to call this function before any other imports.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Use Node.js built-in proxy support by setting `NODE_USE_ENV_PROXY=1`
|
||||
* alongside your `HTTP_PROXY` / `HTTPS_PROXY` environment variables instead.
|
||||
* This function will be removed in a future release.
|
||||
* See {@link https://backstage.io/docs/tutorials/corporate-proxy/ | the corporate proxy guide} for details.
|
||||
*/
|
||||
export function bootstrapEnvProxyAgents() {
|
||||
// see https://www.npmjs.com/package/global-agent
|
||||
const globalAgentNamespace =
|
||||
process.env.GLOBAL_AGENT_ENVIRONMENT_VARIABLE_NAMESPACE ?? 'GLOBAL_AGENT_';
|
||||
if (
|
||||
|
||||
const hasGlobalAgentProxy =
|
||||
process.env[`${globalAgentNamespace}HTTP_PROXY`] ||
|
||||
process.env[`${globalAgentNamespace}HTTPS_PROXY`]
|
||||
) {
|
||||
process.env[`${globalAgentNamespace}HTTPS_PROXY`];
|
||||
const hasStandardProxy = process.env.HTTP_PROXY || process.env.HTTPS_PROXY;
|
||||
|
||||
// Mimics the internal getOptionValue('--use-env-proxy') check in Node.js, which
|
||||
// normalizes the --use-env-proxy CLI flag and NODE_USE_ENV_PROXY=1 env var.
|
||||
// https://github.com/nodejs/node/blob/v22.x/src/node_options.cc
|
||||
const hasNodeEnvProxy =
|
||||
process.env.NODE_USE_ENV_PROXY === '1' ||
|
||||
process.execArgv.includes('--use-env-proxy') ||
|
||||
(process.env.NODE_OPTIONS?.split(/\s+/) ?? []).includes('--use-env-proxy');
|
||||
|
||||
// Node.js never reads GLOBAL_AGENT_* vars, so global-agent must always
|
||||
// handle those to preserve behavior during the deprecation period.
|
||||
if (hasGlobalAgentProxy) {
|
||||
process.emitWarning(
|
||||
`Configuration of proxy agents through ${globalAgentNamespace}* environment variables is deprecated and will be removed in a future release. Switch to the standard HTTP_PROXY/HTTPS_PROXY environment variables and set NODE_USE_ENV_PROXY=1 instead. See https://backstage.io/docs/tutorials/corporate-proxy/ for details.`,
|
||||
{ type: 'DeprecationWarning', code: 'BACKSTAGE_CLI_GLOBAL_AGENT_PROXY' },
|
||||
);
|
||||
|
||||
const globalAgent =
|
||||
require('global-agent') as typeof import('global-agent');
|
||||
globalAgent.bootstrap();
|
||||
}
|
||||
|
||||
if (process.env.HTTP_PROXY || process.env.HTTPS_PROXY) {
|
||||
// Skip undici dispatcher setup when Node.js built-in proxy support is active,
|
||||
// as it already configures the global dispatcher during startup.
|
||||
if (hasStandardProxy && !hasNodeEnvProxy) {
|
||||
process.emitWarning(
|
||||
'bootstrapEnvProxyAgents() is deprecated and will be removed in a future release. Set NODE_USE_ENV_PROXY=1 to use Node.js built-in proxy support instead. See https://backstage.io/docs/tutorials/corporate-proxy/ for details.',
|
||||
{ type: 'DeprecationWarning', code: 'BACKSTAGE_CLI_PROXY_BOOTSTRAP' },
|
||||
);
|
||||
|
||||
const { setGlobalDispatcher, EnvHttpProxyAgent } =
|
||||
require('undici') as typeof import('undici');
|
||||
setGlobalDispatcher(new EnvHttpProxyAgent());
|
||||
|
||||
Reference in New Issue
Block a user