Skip to content

Commit

Permalink
fix: fix issues with https-agent-proxy for cli and openapi-core packa…
Browse files Browse the repository at this point in the history
…ges (#1461)

* Reapply "fix: add https-proxy-agent to fetch requests (#1433)" (#1458)
* fix: add https-proxy-agent package to browser excluded packages
  • Loading branch information
malis42 committed Mar 4, 2024
1 parent d373e89 commit 7ec5cfc
Show file tree
Hide file tree
Showing 20 changed files with 281 additions and 96 deletions.
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

1 comment on commit 7ec5cfc

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage report

St.
Category Percentage Covered / Total
🟡 Statements 76.95% 4468/5806
🟡 Branches 67.05% 2362/3523
🟡 Functions 70.41% 728/1034
🟡 Lines 77.17% 4204/5448

Test suite run success

722 tests passing in 102 suites.

Report generated by 🧪jest coverage report action from 7ec5cfc

Please sign in to comment.