Skip to content

Commit

Permalink
feat(config/migration): migrate config with a PR (#15122)
Browse files Browse the repository at this point in the history
Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com>
Co-authored-by: Michael Kriese <michael.kriese@visualon.de>
Co-authored-by: Rhys Arkins <rhys@arkins.net>
  • Loading branch information
4 people committed Jun 18, 2022
1 parent 7f51e58 commit dd3598c
Show file tree
Hide file tree
Showing 27 changed files with 1,435 additions and 0 deletions.
25 changes: 25 additions & 0 deletions docs/usage/configuration-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,31 @@ If enabled, all issues created by Renovate are set as confidential, even in a pu
!!! note
This option is applicable to GitLab only.

## configMigration

If enabled, Renovate will raise a pull request if config file migration is needed.

We're adding new features to Renovate bot often.
Most times you can keep using your Renovate config and benefit from the new features right away.
But sometimes you need to change your Renovate configuration.
To help you with this, Renovate will create config migration pull requests.

Example:

After we changed the [`baseBranches`](https://docs.renovatebot.com/configuration-options/#basebranches) feature, the Renovate configuration migration pull request would make this change:

```diff
{
- "baseBranch": "main"
+ "baseBranches": ["main"]
}
```

<!-- prettier-ignore -->
!!! info
This feature writes plain JSON for `.json` files, and JSON5 for `.json5` files.
JSON5 content can potentially be down leveled (`.json` files) and all comments will be removed.

## configWarningReuseIssue

Renovate's default behavior is to reuse/reopen a single Config Warning issue in each repository so as to keep the "noise" down.
Expand Down
7 changes: 7 additions & 0 deletions lib/config/options/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,13 @@ const options: RenovateOptions[] = [
globalOnly: true,
cli: false,
},
{
name: 'configMigration',
description: 'Enable this to get config migration PRs when needed.',
stage: 'repository',
type: 'boolean',
default: false,
},
{
name: 'productLinks',
description: 'Links which are used in PRs, issues and comments.',
Expand Down
5 changes: 5 additions & 0 deletions lib/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ export interface RenovateConfig
RenovateSharedConfig,
UpdateConfig<PackageRule>,
AssigneesAndReviewersConfig,
ConfigMigration,
Record<string, unknown> {
depName?: string;
baseBranches?: string[];
Expand Down Expand Up @@ -431,6 +432,10 @@ export interface PackageRuleInputConfig extends Record<string, unknown> {
packageRules?: (PackageRule & PackageRuleInputConfig)[];
}

export interface ConfigMigration {
configMigration?: boolean;
}

export interface MigratedConfig {
isMigrated: boolean;
migratedConfig: RenovateConfig;
Expand Down
3 changes: 3 additions & 0 deletions lib/util/template/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ const prBodyFields = [
'table',
'notes',
'changelogs',
'hasWarningsErrors',
'errors',
'warnings',
'configDescription',
'controls',
'footer',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"filename": "renovate.json",
"content": "{\n \"extends\": [\n \":separateMajorReleases\",\n \":prImmediately\",\n \":renovatePrefix\",\n \":semanticPrefixFixDepsChoreOthers\",\n \":updateNotScheduled\",\n \":automergeDisabled\",\n \":maintainLockFilesDisabled\",\n \":autodetectPinVersions\",\n \"group:monorepos\"\n ],\n \"onboarding\": false,\n \"rangeStrategy\": \"replace\",\n \"semanticCommits\": \"enabled\",\n \"timezone\": \"US/Central\",\n \"baseBranches\": [\n \"main\"\n ]\n}\n"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"filename": "renovate.json5",
"content": "{\n extends: [\n ':separateMajorReleases',\n ':prImmediately',\n ':renovatePrefix',\n ':semanticPrefixFixDepsChoreOthers',\n ':updateNotScheduled',\n ':automergeDisabled',\n ':maintainLockFilesDisabled',\n ':autodetectPinVersions',\n 'group:monorepos',\n ],\n onboarding: false,\n rangeStrategy: 'replace',\n semanticCommits: 'enabled',\n timezone: 'US/Central',\n baseBranches: [\n 'main',\n ],\n}\n"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"extends": [
":separateMajorReleases",
":prImmediately",
":renovatePrefix",
":semanticPrefixFixDepsChoreOthers",
":updateNotScheduled",
":automergeDisabled",
":maintainLockFilesDisabled",
":autodetectPinVersions",
"group:monorepos"
],
"onboarding": false,
"rangeStrategy": "replace",
"semanticCommits": "enabled",
"timezone": "US/Central",
"baseBranches": [
"main"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"extends": [
":separateMajorReleases",
":prImmediately",
":renovatePrefix",
":semanticPrefixFixDepsChoreOthers",
":updateNotScheduled",
":automergeDisabled",
":maintainLockFilesDisabled",
":autodetectPinVersions",
"group:monorepos",
"helpers:oddIsUnstablePackages"
],
"onboarding": false,
"pinVersions": false,
"semanticCommits": true,
"timezone": "US/Central",
"baseBranch": "main"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
extends: [
':separateMajorReleases',
':prImmediately',
':renovatePrefix',
':semanticPrefixFixDepsChoreOthers',
':updateNotScheduled',
':automergeDisabled',
':maintainLockFilesDisabled',
':autodetectPinVersions',
'group:monorepos',
'helpers:oddIsUnstablePackages'
],
onboarding: false,
pinVersions: false,
semanticCommits: true,
timezone: 'US/Central',
baseBranch: 'main' // thats a comment
}
46 changes: 46 additions & 0 deletions lib/workers/repository/config-migration/branch/commit-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type { RenovateConfig } from '../../../../config/types';
import * as template from '../../../../util/template';
import type { CommitMessage } from '../../model/commit-message';
import { CommitMessageFactory } from '../../model/commit-message-factory';

export class ConfigMigrationCommitMessageFactory {
private readonly config: RenovateConfig;

private readonly configFile: string;

constructor(config: RenovateConfig, configFile: string) {
this.config = config;
this.configFile = configFile;
}

create(): CommitMessage {
const { commitMessage } = this.config;

this.config.commitMessageAction =
this.config.commitMessageAction === 'Update'
? ''
: this.config.commitMessageAction;

this.config.commitMessageTopic =
this.config.commitMessageTopic === 'dependency {{depName}}'
? `Migrate config ${this.configFile}`
: this.config.commitMessageTopic;

this.config.commitMessageExtra = '';
this.config.semanticCommitScope = 'config';

const commitMessageFactory = new CommitMessageFactory(this.config);
const commit = commitMessageFactory.create();

if (commitMessage) {
commit.subject = template.compile(commitMessage, {
...this.config,
commitMessagePrefix: '',
});
} else {
commit.subject = `Migrate config ${this.configFile}`;
}

return commit;
}
}
179 changes: 179 additions & 0 deletions lib/workers/repository/config-migration/branch/create.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import { Fixtures } from '../../../../../test/fixtures';
import { RenovateConfig, getConfig, platform } from '../../../../../test/util';
import { commitFiles } from '../../../../util/git';
import { createConfigMigrationBranch } from './create';
import type { MigratedData } from './migrated-data';

jest.mock('../../../../util/git');

describe('workers/repository/config-migration/branch/create', () => {
const raw = Fixtures.getJson('./renovate.json');
const indent = ' ';
const renovateConfig = JSON.stringify(raw, undefined, indent) + '\n';
const filename = 'renovate.json';

let config: RenovateConfig;
let migratedConfigData: MigratedData;

beforeEach(() => {
jest.clearAllMocks();
config = getConfig();
migratedConfigData = { content: renovateConfig, filename };
});

describe('createConfigMigrationBranch', () => {
it('applies the default commit message', async () => {
await createConfigMigrationBranch(config, migratedConfigData);
expect(commitFiles).toHaveBeenCalledWith({
branchName: 'renovate/migrate-config',
files: [
{
type: 'addition',
path: 'renovate.json',
contents: renovateConfig,
},
],
message: 'Migrate config renovate.json',
platformCommit: false,
});
});

it('commits via platform', async () => {
config.platformCommit = true;

await createConfigMigrationBranch(config, migratedConfigData);

expect(platform.commitFiles).toHaveBeenCalledWith({
branchName: 'renovate/migrate-config',
files: [
{
type: 'addition',
path: 'renovate.json',
contents: renovateConfig,
},
],
message: 'Migrate config renovate.json',
platformCommit: true,
});
});

it('applies supplied commit message', async () => {
const message = 'We can migrate config if we want to, or we can not';

config.commitMessage = message;

await createConfigMigrationBranch(config, migratedConfigData);

expect(commitFiles).toHaveBeenCalledWith({
branchName: 'renovate/migrate-config',
files: [
{
type: 'addition',
path: 'renovate.json',
contents: renovateConfig,
},
],
message: message,
platformCommit: false,
});
});

describe('applies the commitMessagePrefix value', () => {
it('to the default commit message', async () => {
config.commitMessagePrefix = 'PREFIX:';
config.commitMessage = '';

const message = `PREFIX: migrate config renovate.json`;
await createConfigMigrationBranch(config, migratedConfigData);

expect(commitFiles).toHaveBeenCalledWith({
branchName: 'renovate/migrate-config',
files: [
{
type: 'addition',
path: 'renovate.json',
contents: renovateConfig,
},
],
message: message,
platformCommit: false,
});
});

it('to the supplied commit message prefix, topic & action', async () => {
const prefix = 'PREFIX:';
const topic = 'thats a topic';
const action = 'action';

const message = `${prefix} ${action} ${topic}`;

config.commitMessagePrefix = prefix;
config.commitMessageTopic = topic;
config.commitMessageAction = action;

await createConfigMigrationBranch(config, migratedConfigData);

expect(commitFiles).toHaveBeenCalledWith({
branchName: 'renovate/migrate-config',
files: [
{
type: 'addition',
path: 'renovate.json',
contents: renovateConfig,
},
],
message: message,
platformCommit: false,
});
});
});

describe('applies semanticCommit prefix', () => {
it('to the default commit message', async () => {
const prefix = 'chore(config)';
const message = `${prefix}: migrate config renovate.json`;

config.semanticCommits = 'enabled';

await createConfigMigrationBranch(config, migratedConfigData);

expect(commitFiles).toHaveBeenCalledWith({
branchName: 'renovate/migrate-config',
files: [
{
type: 'addition',
path: 'renovate.json',
contents: renovateConfig,
},
],
message: message,
platformCommit: false,
});
});

it('to the supplied commit message topic', async () => {
const prefix = 'chore(config)';
const topic = 'supplied topic';
const message = `${prefix}: ${topic}`;

config.semanticCommits = 'enabled';
config.commitMessageTopic = topic;

await createConfigMigrationBranch(config, migratedConfigData);

expect(commitFiles).toHaveBeenCalledWith({
branchName: 'renovate/migrate-config',
files: [
{
type: 'addition',
path: 'renovate.json',
contents: renovateConfig,
},
],
message: message,
platformCommit: false,
});
});
});
});
});

0 comments on commit dd3598c

Please sign in to comment.