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

feat(self-hosted): convert experimental env vars to config options #29154

Merged
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
247eae6
RENOVATE_X_DELETE_CONFIG_FILE
RahulGautamSingh May 19, 2024
67a5c03
RENOVATE_X_EAGER_GLOBAL_EXTENDS
RahulGautamSingh May 19, 2024
d8e5296
RENOVATE_X_IGNORE_NODE_WARN
RahulGautamSingh May 19, 2024
f481e59
RENOVATE_X_S3_ENDPOINT & RENOVATE_X_S3_PATH_STYLE
RahulGautamSingh May 19, 2024
5aefc25
fix typo & tests
RahulGautamSingh May 19, 2024
65844ed
Merge branch 'main' into feat-27879/convert-exp-env-vars-more
RahulGautamSingh May 19, 2024
8e4e384
fix typo
RahulGautamSingh May 19, 2024
54c1ad9
Merge branch 'main' into feat-27879/convert-exp-env-vars-more
RahulGautamSingh May 30, 2024
5f70763
Apply suggestions from code review
RahulGautamSingh May 30, 2024
8fa4ba7
apply suggestions
RahulGautamSingh May 30, 2024
10e260f
apply suggestions
RahulGautamSingh May 30, 2024
b776734
fix test
RahulGautamSingh May 30, 2024
3fb4e03
fallback to s3 values from global config
RahulGautamSingh May 31, 2024
61e024c
Merge branch 'main' into feat-27879/convert-exp-env-vars-more
RahulGautamSingh May 31, 2024
bdfe43e
fix lint issue
RahulGautamSingh Jun 3, 2024
e459454
rename to globalExtendsEager
RahulGautamSingh Jun 17, 2024
63cf370
remove RENOVATE_X_EAGER_GLOBAL_EXTENDS conversion
RahulGautamSingh Jun 25, 2024
f4b89a3
remove ignoreNOdeWarn conversion
RahulGautamSingh Jun 25, 2024
3b913d1
Merge branch 'main' into feat-27879/convert-exp-env-vars-more
RahulGautamSingh Jul 17, 2024
1f8ec86
Merge branch 'main' into feat-27879/convert-exp-env-vars-more
RahulGautamSingh Jul 18, 2024
99d9e10
Merge branch 'main' into feat-27879/convert-exp-env-vars-more
RahulGautamSingh Jul 19, 2024
74f14c6
refactor
RahulGautamSingh Jul 19, 2024
59fff72
Apply suggestions from code review
RahulGautamSingh Jul 22, 2024
8141886
update docs
RahulGautamSingh Jul 25, 2024
4bc1005
update docs
RahulGautamSingh Jul 25, 2024
cc10cfb
Merge branch 'main' into feat-27879/convert-exp-env-vars-more
RahulGautamSingh Jul 25, 2024
6f5de65
apply suggestions
RahulGautamSingh Jul 30, 2024
3d4f480
move types
RahulGautamSingh Jul 30, 2024
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
24 changes: 23 additions & 1 deletion docs/usage/self-hosted-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,16 @@ The above configuration approach will mean the values are redacted in logs like
"customEnvVariables": {"SECRET_TOKEN": "{{ secrets.SECRET_TOKEN }}"},
```

## deleteConfigFile

If set to `true` Renovate tries to delete the self-hosted config file after reading it.

The process that runs Renovate must have the correct permissions to delete the config file.

<!-- prettier-ignore -->
!!! tip
You can tell Renovate where to find your config file with the `RENOVATE_CONFIG_FILE` environment variable.

## detectGlobalManagerConfig

The purpose of this config option is to allow you (as a bot admin) to configure manager-specific files such as a global `.npmrc` file, instead of configuring it in Renovate config.
Expand Down Expand Up @@ -1082,7 +1092,7 @@ Defines how the report is exposed:
- `<unset>` If unset, no report will be provided, though the debug logs will still have partial information of the report
- `logging` The report will be printed as part of the log messages on `INFO` level
- `file` The report will be written to a path provided by [`reportPath`](#reportpath)
- `s3` The report is pushed to an S3 bucket defined by [`reportPath`](#reportpath). This option reuses [`RENOVATE_X_S3_ENDPOINT`](./self-hosted-experimental.md#renovate_x_s3_endpoint) and [`RENOVATE_X_S3_PATH_STYLE`](./self-hosted-experimental.md#renovate_x_s3_path_style)
- `s3` The report is pushed to an S3 bucket defined by [`reportPath`](#reportpath). This option reuses [`s3Endpoint`](./self-hosted-configuration.md#s3endpoint) and [`s3PathStyle`](./self-hosted-configuration.md#s3PathStyle)

## repositories

Expand Down Expand Up @@ -1140,6 +1150,18 @@ The combinations of `requireConfig` and `onboarding` are:
| `requireConfig=optional` | An onboarding PR will be created if no config file exists. If the onboarding PR is closed and there's no config file, the repository will be processed. | Repository is processed regardless of config file presence. |
| `requireConfig=ignored` | No onboarding PR will be created and repo will be processed while ignoring any config file present. | Repository is processed, any config file is ignored. |

## s3Endpoint

If set, Renovate will use this string as the `endpoint` when creating the AWS S3 client instance.

## s3PathStyle

If set, Renovate will enable `forcePathStyle` when creating the AWS S3 client instance.

Quote from [AWS S3 documentation - Interface BucketEndpointInputConfig](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/interfaces/bucketendpointinputconfig.html):

> Whether to force path-style URLs for S3 objects (e.g., `https://s3.amazonaws.com//` instead of `https://.s3.amazonaws.com/`)
RahulGautamSingh marked this conversation as resolved.
Show resolved Hide resolved
RahulGautamSingh marked this conversation as resolved.
Show resolved Hide resolved

## secrets

Secrets may be configured by a bot admin in `config.js`, which will then make them available for templating within repository configs.
Expand Down
19 changes: 0 additions & 19 deletions docs/usage/self-hosted-experimental.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,6 @@ Skipping the check will speed things up, but may result in versions being return

If set to any value, Renovate will always paginate requests to GitHub fully, instead of stopping after 10 pages.

## `RENOVATE_X_DELETE_CONFIG_FILE`

If `true` Renovate tries to delete the self-hosted config file after reading it.
You can set the config file Renovate should read with the `RENOVATE_CONFIG_FILE` environment variable.

The process that runs Renovate must have the correct permissions to delete the config file.

## `RENOVATE_X_DOCKER_HUB_DISABLE_LABEL_LOOKUP`

If set to any value, Renovate will skip attempting to get release labels (e.g. gitRef, sourceUrl) from manifest annotations for `https://index.docker.io`.
RahulGautamSingh marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -121,18 +114,6 @@ If set, Renovate will rewrite GitHub Enterprise Server's pagination responses to

If set, Renovate will persist repository cache locally after uploading to S3.

## `RENOVATE_X_S3_ENDPOINT`

If set, Renovate will use this string as the `endpoint` when instantiating the AWS S3 client.

## `RENOVATE_X_S3_PATH_STYLE`

If set, Renovate will enable `forcePathStyle` when instantiating the AWS S3 client.

> Whether to force path style URLs for S3 objects (e.g., `https://s3.amazonaws.com//` instead of `https://.s3.amazonaws.com/`)

Source: [AWS S3 documentation - Interface BucketEndpointInputConfig](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/interfaces/bucketendpointinputconfig.html)

## `RENOVATE_X_SQLITE_PACKAGE_CACHE`

If set, Renovate will use SQLite as the backend for the package cache.
Expand Down
3 changes: 3 additions & 0 deletions lib/config/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ export class GlobalConfig {
'autodiscoverRepoSort',
'autodiscoverRepoOrder',
'userAgent',
'deleteConfigFile',
's3Endpoint',
's3PathStyle',
'cachePrivatePackages',
];

Expand Down
23 changes: 23 additions & 0 deletions lib/config/options/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3118,6 +3118,29 @@ const options: RenovateOptions[] = [
default: 90,
globalOnly: true,
},
{
name: 'deleteConfigFile',
description:
'If set to `true`, Renovate tries to delete the self-hosted config file after reading it.',
type: 'boolean',
default: false,
globalOnly: true,
},
{
name: 's3Endpoint',
description:
'If set, Renovate will use this string as the `endpoint` when creating the AWS S3 client instance.',
type: 'string',
globalOnly: true,
},
{
name: 's3PathStyle',
description:
'If set, Renovate will enable `forcePathStyle` when creating the AWS S3 client instance.',
type: 'boolean',
default: false,
globalOnly: true,
},
{
name: 'cachePrivatePackages',
description:
Expand Down
5 changes: 5 additions & 0 deletions lib/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ export interface RepoGlobalConfig {
autodiscoverRepoSort?: RepoSortMethod;
autodiscoverRepoOrder?: SortMethod;
userAgent?: string;
deleteConfigFile?: boolean;
s3Endpoint?: string;
s3PathStyle?: boolean;
RahulGautamSingh marked this conversation as resolved.
Show resolved Hide resolved
cachePrivatePackages?: boolean;
}

Expand Down Expand Up @@ -225,6 +228,8 @@ export interface RenovateConfig
AssigneesAndReviewersConfig,
ConfigMigration,
Record<string, unknown> {
s3Endpoint?: string;
s3PathStyle?: boolean;
reportPath?: string;
reportType?: 'logging' | 'file' | 's3' | null;
depName?: string;
Expand Down
2 changes: 1 addition & 1 deletion lib/instrumentation/reporting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export async function exportStats(config: RenovateConfig): Promise<void> {
ContentType: 'application/json',
};

const client = getS3Client();
const client = getS3Client(config.s3Endpoint, config.s3PathStyle);
RahulGautamSingh marked this conversation as resolved.
Show resolved Hide resolved
const command = new PutObjectCommand(s3Params);
await client.send(command);
}
Expand Down
26 changes: 21 additions & 5 deletions lib/util/s3.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { getS3Client, parseS3Url } from './s3';

describe('util/s3', () => {
afterEach(() => {
delete process.env.RENOVATE_X_S3_ENDPOINT;
delete process.env.RENOVATE_X_S3_PATH_STYLE;
jest.resetModules();
});

Expand All @@ -28,10 +26,13 @@ describe('util/s3', () => {
expect(client1).toBe(client2);
});

it('is uses experimental env', async () => {
process.env.RENOVATE_X_S3_ENDPOINT = 'https://minio.domain.test';
process.env.RENOVATE_X_S3_PATH_STYLE = 'true';
it('uses user-configured s3 values', async () => {
const s3 = await import('./s3');
const globalConfig = await import('../config/global');
globalConfig.GlobalConfig.set({
s3Endpoint: 'https://minio.domain.test',
s3PathStyle: true,
});
const client1 = s3.getS3Client();
const client2 = getS3Client();
expect(client1).not.toBe(client2);
Expand All @@ -44,4 +45,19 @@ describe('util/s3', () => {
});
expect(client1.config.forcePathStyle).toBeTrue();
});

it('uses s3 values from globalConfig instead of GlobalConfig class', async () => {
const s3 = await import('./s3');
const client1 = s3.getS3Client('https://minio.domain.test', true);
const client2 = getS3Client('https://minio.domain.test', true);
expect(client1).not.toBe(client2);
expect(await client1.config.endpoint?.()).toStrictEqual({
hostname: 'minio.domain.test',
path: '/',
port: undefined,
protocol: 'https:',
query: undefined,
});
expect(client1.config.forcePathStyle).toBeTrue();
});
});
14 changes: 11 additions & 3 deletions lib/util/s3.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
// Singleton S3 instance initialized on-demand.
import { S3Client } from '@aws-sdk/client-s3';
import is from '@sindresorhus/is';
import { GlobalConfig } from '../config/global';
import { parseUrl } from './url';

let s3Instance: S3Client | undefined;
export function getS3Client(): S3Client {
export function getS3Client(
// Only needed if GlobalConfig is not initialized due to some error
s3Endpoint?: string,
s3PathStyle?: boolean,
): S3Client {
if (!s3Instance) {
const endpoint = process.env.RENOVATE_X_S3_ENDPOINT;
const forcePathStyle = process.env.RENOVATE_X_S3_PATH_STYLE;
const endpoint = GlobalConfig.get('s3Endpoint') ?? s3Endpoint;
const forcePathStyle = is.undefined(GlobalConfig.get('s3PathStyle'))
? s3PathStyle
: !!GlobalConfig.get('s3PathStyle');
RahulGautamSingh marked this conversation as resolved.
Show resolved Hide resolved
s3Instance = new S3Client({
...(endpoint && { endpoint }),
...(forcePathStyle && { forcePathStyle: true }),
Expand Down
7 changes: 7 additions & 0 deletions lib/workers/global/config/parse/env.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,13 +274,20 @@ describe('workers/global/config/parse/env', () => {
RENOVATE_X_AUTODISCOVER_REPO_SORT: 'alpha',
RENOVATE_X_DOCKER_MAX_PAGES: '10',
RENOVATE_AUTODISCOVER_REPO_ORDER: 'desc',
RENOVATE_X_DELETE_CONFIG_FILE: 'true',
RENOVATE_X_S3_ENDPOINT: 'endpoint',
RENOVATE_X_S3_PATH_STYLE: 'true',
};
const config = await env.getConfig(envParam);
expect(config.dockerMaxPages).toBeUndefined();
expect(config).toMatchObject({
mergeConfidenceEndpoint: 'some-url',
mergeConfidenceDatasources: ['docker'],
autodiscoverRepoSort: 'alpha',
autodiscoverRepoOrder: 'desc',
deleteConfigFile: true,
s3Endpoint: 'endpoint',
s3PathStyle: true,
});
});

Expand Down
3 changes: 3 additions & 0 deletions lib/workers/global/config/parse/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ function massageEnvKeyValues(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
const convertedExperimentalEnvVars = [
'RENOVATE_X_AUTODISCOVER_REPO_SORT',
'RENOVATE_X_AUTODISCOVER_REPO_ORDER',
'RENOVATE_X_DELETE_CONFIG_FILE',
'RENOVATE_X_S3_ENDPOINT',
'RENOVATE_X_S3_PATH_STYLE',
'RENOVATE_X_MERGE_CONFIDENCE_API_BASE_URL',
'RENOVATE_X_MERGE_CONFIDENCE_SUPPORTED_DATASOURCES',
];
Expand Down
68 changes: 29 additions & 39 deletions lib/workers/global/config/parse/file.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,34 +142,16 @@ describe('workers/global/config/parse/file', () => {
expect(logger.fatal).toHaveBeenCalledWith('Unsupported file type');
fs.unlinkSync(configFile);
});

it('removes the config file if RENOVATE_CONFIG_FILE & RENOVATE_X_DELETE_CONFIG_FILE are set', async () => {
fsRemoveSpy.mockImplementationOnce(() => {
// no-op
});
fsPathExistsSpy
.mockResolvedValueOnce(true as never)
.mockResolvedValueOnce(true as never);
const configFile = upath.resolve(tmp.path, './config.json');
fs.writeFileSync(configFile, `{"token": "abc"}`, { encoding: 'utf8' });

await file.getConfig({
RENOVATE_CONFIG_FILE: configFile,
RENOVATE_X_DELETE_CONFIG_FILE: 'true',
});

expect(processExitSpy).not.toHaveBeenCalled();
expect(fsRemoveSpy).toHaveBeenCalledTimes(1);
expect(fsRemoveSpy).toHaveBeenCalledWith(configFile);
fs.unlinkSync(configFile);
});
});

describe('deleteConfigFile()', () => {
it.each([[undefined], [' ']])(
'skip when RENOVATE_CONFIG_FILE is not set ("%s")',
async (configFile) => {
await file.deleteNonDefaultConfig({ RENOVATE_CONFIG_FILE: configFile });
await file.deleteNonDefaultConfig(
{ RENOVATE_CONFIG_FILE: configFile },
true,
);

expect(fsRemoveSpy).toHaveBeenCalledTimes(0);
},
Expand All @@ -178,23 +160,27 @@ describe('workers/global/config/parse/file', () => {
it('skip when config file does not exist', async () => {
fsPathExistsSpy.mockResolvedValueOnce(false as never);

await file.deleteNonDefaultConfig({
RENOVATE_CONFIG_FILE: 'path',
RENOVATE_X_DELETE_CONFIG_FILE: 'true',
});
await file.deleteNonDefaultConfig(
{
RENOVATE_CONFIG_FILE: 'path',
},
true,
);

expect(fsRemoveSpy).toHaveBeenCalledTimes(0);
});

it.each([['false'], [' ']])(
'skip if RENOVATE_X_DELETE_CONFIG_FILE is not set ("%s")',
'skip if deleteConfigFile is not set ("%s")',
async (deleteConfig) => {
fsPathExistsSpy.mockResolvedValueOnce(true as never);

await file.deleteNonDefaultConfig({
RENOVATE_X_DELETE_CONFIG_FILE: deleteConfig,
RENOVATE_CONFIG_FILE: '/path/to/config.js',
});
await file.deleteNonDefaultConfig(
{
RENOVATE_CONFIG_FILE: '/path/to/config.js',
},
deleteConfig === 'true',
);

expect(fsRemoveSpy).toHaveBeenCalledTimes(0);
},
Expand All @@ -207,10 +193,12 @@ describe('workers/global/config/parse/file', () => {
fsPathExistsSpy.mockResolvedValueOnce(true as never);
const configFile = '/path/to/config.js';

await file.deleteNonDefaultConfig({
RENOVATE_CONFIG_FILE: configFile,
RENOVATE_X_DELETE_CONFIG_FILE: 'true',
});
await file.deleteNonDefaultConfig(
{
RENOVATE_CONFIG_FILE: configFile,
},
true,
);

expect(fsRemoveSpy).toHaveBeenCalledTimes(1);
expect(fsRemoveSpy).toHaveBeenCalledWith(configFile);
Expand All @@ -227,10 +215,12 @@ describe('workers/global/config/parse/file', () => {
fsPathExistsSpy.mockResolvedValueOnce(true as never);
const configFile = '/path/to/config.js';

await file.deleteNonDefaultConfig({
RENOVATE_CONFIG_FILE: configFile,
RENOVATE_X_DELETE_CONFIG_FILE: 'true',
});
await file.deleteNonDefaultConfig(
{
RENOVATE_CONFIG_FILE: configFile,
},
true,
);

expect(fsRemoveSpy).toHaveBeenCalledTimes(1);
expect(fsRemoveSpy).toHaveBeenCalledWith(configFile);
Expand Down
5 changes: 2 additions & 3 deletions lib/workers/global/config/parse/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,21 +79,20 @@ export async function getConfig(env: NodeJS.ProcessEnv): Promise<AllConfig> {
logger.debug('No config file found on disk - skipping');
}

await deleteNonDefaultConfig(env); // Try deletion only if RENOVATE_CONFIG_FILE is specified

return migrateAndValidateConfig(config, configFile);
}

export async function deleteNonDefaultConfig(
env: NodeJS.ProcessEnv,
deleteConfigFile: boolean,
): Promise<void> {
const configFile = env.RENOVATE_CONFIG_FILE;

if (is.undefined(configFile) || is.emptyStringOrWhitespace(configFile)) {
return;
}

if (env.RENOVATE_X_DELETE_CONFIG_FILE !== 'true') {
if (!deleteConfigFile) {
return;
}

Expand Down
3 changes: 3 additions & 0 deletions lib/workers/global/config/parse/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ export async function parseConfigs(
config.forkProcessing = 'enabled';
}

// Only try deletion if RENOVATE_CONFIG_FILE is set
await fileParser.deleteNonDefaultConfig(env, !!config.deleteConfigFile);

// Remove log file entries
delete config.logFile;
delete config.logFileLevel;
Expand Down