Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fix issues with https-agent-proxy for cli and openapi-core packages #1461

Merged
merged 4 commits into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions .changeset/wild-pets-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@redocly/openapi-core": patch
"@redocly/cli": patch
---

Users can run the CLI tool behind a proxy by using `HTTP_PROXY` or `HTTPS_PROXY` environment variables to configure the proxy settings.
24 changes: 24 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,30 @@ To give a Docker container access to your OpenAPI description files, you need to
docker run --rm -v $PWD:/spec redocly/cli lint openapi.yaml
```

## Run CLI behind a proxy

If you need to run the CLI tool behind a proxy, you can use the `HTTP_PROXY` and `HTTPS_PROXY` environment variables to configure the proxy settings. These environment variables are commonly used to specify the proxy server for HTTP and HTTPS traffic, respectively.

### Set up Proxy Environment Variables

Before running the CLI behind a proxy, make sure to set the appropriate proxy environment variables. Open a terminal and use the following commands:

```bash
# For HTTP proxy
export HTTP_PROXY=http://your-http-proxy-server:port

# For HTTPS proxy
export HTTPS_PROXY=http://your-https-proxy-server:port
```

### Use Environment Variables with CLI Commands

You can also directly include the proxy environment variables in the command itself. For example:

```bash
HTTPS_PROXY=https://your-https-proxy-server:port redocly lint --extends minimal openapi.yaml
```

## Next steps

- Set up [autocomplete for Redocly CLI](./guides/autocomplete.md).
Expand Down
44 changes: 44 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/cli/src/__mocks__/@redocly/openapi-core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const __redoclyClient = {
export const RedoclyClient = jest.fn(() => __redoclyClient);
export const loadConfig = jest.fn(() => ConfigFixture);
export const getMergedConfig = jest.fn();
export const getProxyAgent = jest.fn();
export const lint = jest.fn();
export const bundle = jest.fn(() => ({ bundle: { parsed: null }, problems: null }));
export const getTotals = jest.fn(() => ({ errors: 0 }));
Expand Down
3 changes: 3 additions & 0 deletions packages/cli/src/cms/api/__tests__/api.client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ describe('ApiClient', () => {
'Content-Type': 'application/json',
Authorization: `Bearer ${testToken}`,
},
signal: expect.any(Object),
}
);

Expand Down Expand Up @@ -122,6 +123,8 @@ describe('ApiClient', () => {
type: 'CICD',
autoMerge: true,
}),
signal: expect.any(Object),
agent: undefined,
}
);

Expand Down
23 changes: 18 additions & 5 deletions packages/cli/src/cms/api/api-client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fetchWithTimeout from '../../utils/fetch-with-timeout';
import fetch from 'node-fetch';
import * as FormData from 'form-data';

import { getProxyAgent } from '@redocly/openapi-core';
import type { Response } from 'node-fetch';
import type { ReadStream } from 'fs';
import type {
Expand All @@ -25,7 +25,7 @@ class RemotesApiClient {
}

async getDefaultBranch(organizationId: string, projectId: string) {
const response = await fetch(
const response = await fetchWithTimeout(
`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/source`,
{
method: 'GET',
Expand All @@ -36,6 +36,10 @@ class RemotesApiClient {
}
);

if (!response) {
throw new Error(`Failed to get default branch.`);
}

try {
const source = await this.getParsedResponse<ProjectSourceResponse>(response);

Expand All @@ -53,7 +57,7 @@ class RemotesApiClient {
mountBranchName: string;
}
): Promise<UpsertRemoteResponse> {
const response = await fetch(
const response = await fetchWithTimeout(
`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/remotes`,
{
method: 'POST',
Expand All @@ -70,6 +74,10 @@ class RemotesApiClient {
}
);

if (!response) {
throw new Error(`Failed to upsert.`);
}

try {
return await this.getParsedResponse<UpsertRemoteResponse>(response);
} catch (err) {
Expand Down Expand Up @@ -110,6 +118,7 @@ class RemotesApiClient {
Authorization: `Bearer ${this.apiKey}`,
},
body: formData,
agent: getProxyAgent(),
}
);

Expand All @@ -121,7 +130,7 @@ class RemotesApiClient {
}

async getRemotesList(organizationId: string, projectId: string, mountPath: string) {
const response = await fetch(
const response = await fetchWithTimeout(
`${this.domain}/api/orgs/${organizationId}/projects/${projectId}/remotes?filter=mountPath:/${mountPath}/`,
{
method: 'GET',
Expand All @@ -132,6 +141,10 @@ class RemotesApiClient {
}
);

if (!response) {
throw new Error(`Failed to get remotes list.`);
}

try {
return await this.getParsedResponse<ListRemotesResponse>(response);
} catch (err) {
Expand Down Expand Up @@ -160,7 +173,7 @@ class RemotesApiClient {
);

if (!response) {
throw new Error(`Failed to get push status: Time is up`);
throw new Error(`Failed to get push status.`);
}

try {
Expand Down
11 changes: 9 additions & 2 deletions packages/cli/src/commands/push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
slash,
Region,
getMergedConfig,
getProxyAgent,
} from '@redocly/openapi-core';
import {
exitWithError,
Expand Down Expand Up @@ -62,8 +63,12 @@ export async function handlePush(argv: PushOptions, config: Config): Promise<voi
const client = new RedoclyClient(config.region);
const isAuthorized = await client.isAuthorizedWithRedoclyByRegion();
if (!isAuthorized) {
const clientToken = await promptClientToken(client.domain);
await client.login(clientToken);
try {
const clientToken = await promptClientToken(client.domain);
await client.login(clientToken);
} catch (e) {
exitWithError(e);
}
}

const startedAt = performance.now();
Expand Down Expand Up @@ -439,6 +444,7 @@ function uploadFileToS3(url: string, filePathOrBuffer: string | Buffer) {
typeof filePathOrBuffer === 'string'
? fs.statSync(filePathOrBuffer).size
: filePathOrBuffer.byteLength;

const readStream =
typeof filePathOrBuffer === 'string' ? fs.createReadStream(filePathOrBuffer) : filePathOrBuffer;

Expand All @@ -448,5 +454,6 @@ function uploadFileToS3(url: string, filePathOrBuffer: string | Buffer) {
'Content-Length': fileSizeInBytes.toString(),
},
body: readStream,
agent: getProxyAgent(),
});
}
7 changes: 6 additions & 1 deletion packages/cli/src/utils/fetch-with-timeout.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import nodeFetch from 'node-fetch';
import AbortController from 'abort-controller';
import { getProxyAgent } from '@redocly/openapi-core';

const TIMEOUT = 3000;

Expand All @@ -10,7 +11,11 @@ export default async (url: string, options = {}) => {
controller.abort();
}, TIMEOUT);

const res = await nodeFetch(url, { signal: controller.signal, ...options });
const res = await nodeFetch(url, {
signal: controller.signal,
...options,
agent: getProxyAgent(),
});
clearTimeout(timeout);
return res;
} catch (e) {
Expand Down
4 changes: 3 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"path": "path-browserify",
"os": false,
"node-fetch": false,
"colorette": false
"colorette": false,
"https-proxy-agent": false
},
"homepage": "https://github.com/Redocly/redocly-cli",
"keywords": [
Expand All @@ -36,6 +37,7 @@
"dependencies": {
"@redocly/ajv": "^8.11.0",
"colorette": "^1.2.0",
"https-proxy-agent": "^7.0.4",
"js-levenshtein": "^1.1.6",
"js-yaml": "^4.1.0",
"lodash.isequal": "^4.5.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { isAbsoluteUrl, isRef, Location, refBaseName } from './ref-utils';
import { initRules } from './config/rules';
import { reportUnresolvedRef } from './rules/no-unresolved-refs';
import { isPlainObject, isTruthy } from './utils';
import { isRedoclyRegistryURL } from './redocly';
import { isRedoclyRegistryURL } from './redocly/domains';
import { RemoveUnusedComponents as RemoveUnusedComponentsOas2 } from './decorators/oas2/remove-unused-components';
import { RemoveUnusedComponents as RemoveUnusedComponentsOas3 } from './decorators/oas3/remove-unused-components';
import { ConfigTypes } from './types/redocly-yaml';
Expand Down
24 changes: 1 addition & 23 deletions packages/core/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
Oas3RuleSet,
Async2RuleSet,
} from '../oas-types';
import { isBrowser, env } from '../env';
import { isBrowser } from '../env';

import type { NodeType } from '../types';
import type {
Expand All @@ -35,25 +35,6 @@ const IGNORE_BANNER =
`# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.\n` +
`# See https://redoc.ly/docs/cli/ for more information.\n`;

export const DEFAULT_REGION = 'us';

function getDomains() {
const domains: { [region in Region]: string } = {
us: 'redocly.com',
eu: 'eu.redocly.com',
};

// FIXME: temporary fix for our lab environments
const domain = env.REDOCLY_DOMAIN;
if (domain?.endsWith('.redocly.host')) {
domains[domain.split('.')[0] as Region] = domain;
}
if (domain === 'redoc.online') {
domains[domain as Region] = domain;
}
return domains;
}

function getIgnoreFilePath(configFile?: string): string | undefined {
if (configFile) {
return doesYamlFileExist(configFile)
Expand All @@ -64,9 +45,6 @@ function getIgnoreFilePath(configFile?: string): string | undefined {
}
}

export const DOMAINS = getDomains();
export const AVAILABLE_REGIONS = Object.keys(DOMAINS) as Region[];

export class StyleguideConfig {
plugins: Plugin[];
ignore: Record<string, Record<string, Set<string>>> = {};
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/config/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as path from 'path';
import { RedoclyClient } from '../redocly';
import { isEmptyObject, doesYamlFileExist } from '../utils';
import { parseYaml } from '../js-yaml';
import { Config, DOMAINS } from './config';
import { Config } from './config';
import { ConfigValidationError, transformConfig } from './utils';
import { resolveConfig, resolveConfigFileAndRefs } from './config-resolvers';
import { bundleConfig } from '../bundle';
Expand All @@ -12,6 +12,7 @@ import type { Document } from '../resolve';
import type { RegionalTokenWithValidity } from '../redocly/redocly-client-types';
import type { RawConfig, RawUniversalConfig, Region } from './types';
import type { BaseResolver, ResolvedRefMap } from '../resolve';
import { DOMAINS } from '../redocly/domains';

async function addConfigMetadata({
rawConfig,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { UserContext } from '../../walk';
import { isRedoclyRegistryURL } from '../../redocly';
import { isRedoclyRegistryURL } from '../../redocly/domains';

import { Oas3Decorator, Oas2Decorator } from '../../visitors';

Expand Down
13 changes: 11 additions & 2 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
export { BundleOutputFormat, readFileFromUrl, slash, doesYamlFileExist, isTruthy } from './utils';
export {
BundleOutputFormat,
readFileFromUrl,
slash,
doesYamlFileExist,
isTruthy,
getProxyAgent,
} from './utils';
export { Oas3_1Types } from './types/oas3_1';
export { Oas3Types } from './types/oas3';
export { Oas2Types } from './types/oas2';
Expand Down Expand Up @@ -41,7 +48,9 @@ export {
ResolvedApi,
} from './config';

export { RedoclyClient, isRedoclyRegistryURL } from './redocly';
export { RedoclyClient } from './redocly';

export * from './redocly/domains';

export {
Source,
Expand Down
Loading
Loading