Add proxy to create-app tasks

Signed-off-by: enyineer <nico.enking@gmail.com>
This commit is contained in:
enyineer
2025-10-10 10:09:47 +02:00
parent 944d3fa82e
commit cdbec2e0d5
8 changed files with 171 additions and 42 deletions
+1
View File
@@ -23,3 +23,4 @@
export { findPaths, BACKSTAGE_JSON } from './paths';
export { isChildPath } from './isChildPath';
export type { Paths, ResolveFunc } from './paths';
export { maybeBootstrapProxy } from './proxyBootstrap';
@@ -0,0 +1,108 @@
/*
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { maybeBootstrapProxy } from './proxyBootstrap';
jest.mock('global-agent', () => ({
bootstrap: jest.fn(),
}));
jest.mock('undici', () => ({
setGlobalDispatcher: jest.fn(),
EnvHttpProxyAgent: jest.fn(),
}));
describe('maybeBootstrapProxy', () => {
const originalEnv = process.env;
beforeEach(() => {
jest.resetModules();
process.env = { ...originalEnv };
});
afterEach(() => {
process.env = originalEnv;
jest.clearAllMocks();
});
it('should bootstrap global-agent if GLOBAL_AGENT_HTTP_PROXY is set', () => {
process.env.GLOBAL_AGENT_HTTP_PROXY = 'http://proxy.example.com';
const { bootstrap } =
require('global-agent') as typeof import('global-agent');
maybeBootstrapProxy();
expect(bootstrap).toHaveBeenCalledTimes(1);
});
it('should bootstrap global-agent if GLOBAL_AGENT_HTTPS_PROXY is set', () => {
process.env.GLOBAL_AGENT_HTTPS_PROXY = 'https://proxy.example.com';
const { bootstrap } =
require('global-agent') as typeof import('global-agent');
maybeBootstrapProxy();
expect(bootstrap).toHaveBeenCalledTimes(1);
});
it('should use undici EnvHttpProxyAgent if HTTP_PROXY is set', () => {
process.env.HTTP_PROXY = 'http://proxy.example.com';
const { setGlobalDispatcher, EnvHttpProxyAgent } =
require('undici') as typeof import('undici');
maybeBootstrapProxy();
expect(EnvHttpProxyAgent).toHaveBeenCalledTimes(1);
expect(setGlobalDispatcher).toHaveBeenCalledWith(
expect.any(EnvHttpProxyAgent),
);
});
it('should use undici EnvHttpProxyAgent if HTTPS_PROXY is set', () => {
process.env.HTTPS_PROXY = 'https://proxy.example.com';
const { setGlobalDispatcher, EnvHttpProxyAgent } =
require('undici') as typeof import('undici');
maybeBootstrapProxy();
expect(EnvHttpProxyAgent).toHaveBeenCalledTimes(1);
expect(setGlobalDispatcher).toHaveBeenCalledWith(
expect.any(EnvHttpProxyAgent),
);
});
it('should not bootstrap global-agent or set undici dispatcher if no proxy is set', () => {
const { bootstrap } =
require('global-agent') as typeof import('global-agent');
const { setGlobalDispatcher } =
require('undici') as typeof import('undici');
maybeBootstrapProxy();
expect(bootstrap).not.toHaveBeenCalled();
expect(setGlobalDispatcher).not.toHaveBeenCalled();
});
it('should respect GLOBAL_AGENT_ENVIRONMENT_VARIABLE_NAMESPACE', () => {
process.env.GLOBAL_AGENT_ENVIRONMENT_VARIABLE_NAMESPACE = 'CUSTOM_AGENT_';
process.env.CUSTOM_AGENT_HTTP_PROXY = 'http://proxy.example.com';
const { bootstrap } =
require('global-agent') as typeof import('global-agent');
maybeBootstrapProxy();
expect(bootstrap).toHaveBeenCalledTimes(1);
});
});
+35
View File
@@ -0,0 +1,35 @@
/*
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export function maybeBootstrapProxy() {
// see https://www.npmjs.com/package/global-agent
const globalAgentNamespace =
process.env.GLOBAL_AGENT_ENVIRONMENT_VARIABLE_NAMESPACE ?? 'GLOBAL_AGENT_';
if (
process.env[`${globalAgentNamespace}HTTP_PROXY`] ||
process.env[`${globalAgentNamespace}HTTPS_PROXY`]
) {
const globalAgent =
require('global-agent') as typeof import('global-agent');
globalAgent.bootstrap();
}
if (process.env.HTTP_PROXY || process.env.HTTPS_PROXY) {
const { setGlobalDispatcher, EnvHttpProxyAgent } =
require('undici') as typeof import('undici');
setGlobalDispatcher(new EnvHttpProxyAgent());
}
}
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BACKSTAGE_JSON, maybeBootstrapProxy } from '@backstage/cli-common';
maybeBootstrapProxy();
@@ -31,7 +32,6 @@ import {
Lockfile,
YarnInfoInspectData,
} from '../../../../lib/versioning';
import { BACKSTAGE_JSON } from '@backstage/cli-common';
import { runParallelWorkers } from '../../../../lib/parallel';
import {
getManifestByReleaseLine,
@@ -42,26 +42,6 @@ import { migrateMovedPackages } from './migrate';
import { runYarnInstall } from '../../lib/utils';
import { run } from '../../../../lib/run';
function maybeBootstrapProxy() {
// see https://www.npmjs.com/package/global-agent
const globalAgentNamespace =
process.env.GLOBAL_AGENT_ENVIRONMENT_VARIABLE_NAMESPACE ?? 'GLOBAL_AGENT_';
if (
process.env[`${globalAgentNamespace}HTTP_PROXY`] ||
process.env[`${globalAgentNamespace}HTTPS_PROXY`]
) {
const globalAgent =
require('global-agent') as typeof import('global-agent');
globalAgent.bootstrap();
}
if (process.env.HTTP_PROXY || process.env.HTTPS_PROXY) {
const { setGlobalDispatcher, EnvHttpProxyAgent } =
require('undici') as typeof import('undici');
setGlobalDispatcher(new EnvHttpProxyAgent());
}
}
const DEP_TYPES = [
'dependencies',
'devDependencies',
+4 -1
View File
@@ -47,16 +47,19 @@
"chalk": "^4.0.0",
"commander": "^12.0.0",
"fs-extra": "^11.2.0",
"global-agent": "^3.0.0",
"handlebars": "^4.7.3",
"inquirer": "^8.2.0",
"ora": "^5.3.0",
"recursive-readdir": "^2.2.2"
"recursive-readdir": "^2.2.2",
"undici": "^7.2.3"
},
"devDependencies": {
"@backstage/backend-test-utils": "workspace:^",
"@backstage/cli": "workspace:^",
"@types/command-exists": "^1.2.0",
"@types/fs-extra": "^11.0.0",
"@types/global-agent": "^3",
"@types/inquirer": "^8.1.3",
"@types/node": "^20.16.0",
"@types/recursive-readdir": "^2.2.0",
@@ -36,6 +36,15 @@ import {
import { http, HttpResponse, delay } from 'msw';
import { setupServer } from 'msw/node';
// Avoid mutating the global agents used in other tests
jest.mock('global-agent', () => ({
bootstrap: jest.fn(),
}));
jest.mock('undici', () => ({
setGlobalDispatcher: jest.fn(),
EnvHttpProxyAgent: class {},
}));
jest.spyOn(Task, 'log').mockReturnValue(undefined);
jest.spyOn(Task, 'error').mockReturnValue(undefined);
jest.spyOn(Task, 'section').mockReturnValue(undefined);
+1 -20
View File
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { maybeBootstrapProxy } from '@backstage/cli-common';
maybeBootstrapProxy();
@@ -32,26 +33,6 @@ import { packageVersions } from './versions';
import { promisify } from 'util';
import os from 'os';
function maybeBootstrapProxy() {
// see https://www.npmjs.com/package/global-agent
const globalAgentNamespace =
process.env.GLOBAL_AGENT_ENVIRONMENT_VARIABLE_NAMESPACE ?? 'GLOBAL_AGENT_';
if (
process.env[`${globalAgentNamespace}HTTP_PROXY`] ||
process.env[`${globalAgentNamespace}HTTPS_PROXY`]
) {
const globalAgent =
require('global-agent') as typeof import('global-agent');
globalAgent.bootstrap();
}
if (process.env.HTTP_PROXY || process.env.HTTPS_PROXY) {
const { setGlobalDispatcher, EnvHttpProxyAgent } =
require('undici') as typeof import('undici');
setGlobalDispatcher(new EnvHttpProxyAgent());
}
}
const TASK_NAME_MAX_LENGTH = 14;
const TEN_MINUTES_MS = 1000 * 60 * 10;
const exec = promisify(execCb);
+12
View File
@@ -2802,6 +2802,8 @@ __metadata:
dependencies:
"@backstage/cli": "workspace:^"
"@types/node": "npm:^20.16.0"
global-agent: "npm:^3.0.0"
undici: "npm:^7.2.3"
languageName: unknown
linkType: soft
@@ -3259,18 +3261,21 @@ __metadata:
"@backstage/cli-common": "workspace:^"
"@types/command-exists": "npm:^1.2.0"
"@types/fs-extra": "npm:^11.0.0"
"@types/global-agent": "npm:^3"
"@types/inquirer": "npm:^8.1.3"
"@types/node": "npm:^20.16.0"
"@types/recursive-readdir": "npm:^2.2.0"
chalk: "npm:^4.0.0"
commander: "npm:^12.0.0"
fs-extra: "npm:^11.2.0"
global-agent: "npm:^3.0.0"
handlebars: "npm:^4.7.3"
inquirer: "npm:^8.2.0"
msw: "npm:^2.0.0"
nodemon: "npm:^3.0.1"
ora: "npm:^5.3.0"
recursive-readdir: "npm:^2.2.2"
undici: "npm:^7.2.3"
bin:
backstage-create-app: bin/backstage-create-app
languageName: unknown
@@ -19972,6 +19977,13 @@ __metadata:
languageName: node
linkType: hard
"@types/global-agent@npm:^3":
version: 3.0.0
resolution: "@types/global-agent@npm:3.0.0"
checksum: 10/f961aaf493176e651dcb1bdb8f31b3f933f7ba86a74901c40399622e7f09621d84e0c12cca62ddee12bd8d783656359d25ec88aa57bba3fbcbd58f60f8cbdb18
languageName: node
linkType: hard
"@types/google-protobuf@npm:^3.7.2":
version: 3.15.12
resolution: "@types/google-protobuf@npm:3.15.12"