Skip to content

Commit 48b9981

Browse files
committed
feat(ng-dev): update target labels in renovate config
This commit enhances the release tool to dynamically adjust Renovate labels based on the current version. Example: angular/angular#61894
1 parent d8615e0 commit 48b9981

File tree

2 files changed

+113
-6
lines changed

2 files changed

+113
-6
lines changed

ng-dev/release/publish/actions.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ import {Prompt} from '../../utils/prompt.js';
4444
import {glob} from 'fast-glob';
4545
import {PnpmVersioning} from './pnpm-versioning.js';
4646
import {Commit} from '../../utils/git/octokit-types.js';
47+
import {updateRenovateConfigTargetLabels} from './actions/renovate-config-updates.js';
48+
import {targetLabels} from '../../pr/common/labels/target.js';
4749

4850
/** Interface describing a Github repository. */
4951
export interface GithubRepo {
@@ -217,13 +219,27 @@ export abstract class ReleaseAction {
217219
}
218220

219221
// Commit message for the release point.
220-
const commitMessage = getCommitMessageForRelease(newVersion);
221222
const filesToCommit = [
222223
workspaceRelativePackageJsonPath,
223224
workspaceRelativeChangelogPath,
224225
...this.getAspectLockFiles(),
225226
];
226227

228+
if (newVersion.patch === 0 && !newVersion.prerelease) {
229+
// Switch the renovate labels for `target: rc` to `target: patch`
230+
const renovateConfigPath = await updateRenovateConfigTargetLabels(
231+
this.projectDir,
232+
targetLabels['TARGET_RC'].name,
233+
targetLabels['TARGET_PATCH'].name,
234+
);
235+
236+
if (renovateConfigPath) {
237+
filesToCommit.push(renovateConfigPath);
238+
}
239+
}
240+
241+
const commitMessage = getCommitMessageForRelease(newVersion);
242+
227243
// Create a release staging commit including changelog and version bump.
228244
await this.createCommit(commitMessage, filesToCommit);
229245

ng-dev/release/publish/actions/renovate-config-updates.ts

Lines changed: 96 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@ import {existsSync} from 'node:fs';
22
import {green, Log} from '../../../utils/logging.js';
33
import {join} from 'node:path';
44
import {writeFile, readFile} from 'node:fs/promises';
5+
import {targetLabels} from '../../../pr/common/labels/target.js';
56

67
/**
78
* Updates the `renovate.json` configuration file to include a new base branch.
9+
* It also updates specific target labels within the package rules.
810
*
9-
* @param projectDir - The project directory path.
11+
* @param projectDir - The path to the project directory.
1012
* @param newBranchName - The name of the new branch to add to the base branches list.
11-
* @returns A promise that resolves to an string containing the path to the modified `renovate.json` file,
12-
* or null if config updating is disabled.
13+
* @returns A promise that resolves to the path of the modified `renovate.json` file if updated,
14+
* or `null` if the file was not found or the `baseBranches` array has an unexpected format.
1315
*/
1416
export async function updateRenovateConfig(
1517
projectDir: string,
@@ -18,13 +20,13 @@ export async function updateRenovateConfig(
1820
const renovateConfigPath = join(projectDir, 'renovate.json');
1921
if (!existsSync(renovateConfigPath)) {
2022
Log.warn(` ✘ Skipped updating Renovate config as it was not found.`);
21-
2223
return null;
2324
}
2425

2526
const config = await readFile(renovateConfigPath, 'utf-8');
2627
const configJson = JSON.parse(config) as Record<string, unknown>;
2728
const baseBranches = configJson.baseBranches;
29+
2830
if (!Array.isArray(baseBranches) || baseBranches.length !== 2) {
2931
Log.warn(
3032
` ✘ Skipped updating Renovate config: "baseBranches" must contain exactly 2 branches.`,
@@ -34,8 +36,97 @@ export async function updateRenovateConfig(
3436
}
3537

3638
configJson.baseBranches = ['main', newBranchName];
39+
40+
updateRenovateTargetLabel(
41+
configJson,
42+
targetLabels['TARGET_PATCH'].name,
43+
targetLabels['TARGET_RC'].name,
44+
);
45+
3746
await writeFile(renovateConfigPath, JSON.stringify(configJson, undefined, 2));
38-
Log.info(green(` ✓ Updated Renovate config.`));
3947

48+
Log.info(green(` ✓ Updated Renovate config.`));
4049
return renovateConfigPath;
4150
}
51+
52+
/**
53+
* Updates a specific target label in the `renovate.json` configuration file.
54+
* This function specifically targets and replaces one label with another within the `packageRules`.
55+
*
56+
* @param projectDir - The path to the project directory.
57+
* @param fromLabel - The label name to be replaced.
58+
* @param toLabel - The new label name to replace `fromLabel` with.
59+
* @returns A promise that resolves to `true` if a label was updated and the file was written `false` otherwise.
60+
*/
61+
export async function updateRenovateConfigTargetLabels(
62+
projectDir: string,
63+
fromLabel: string,
64+
toLabel: string,
65+
): Promise<string | null> {
66+
const renovateConfigPath = join(projectDir, 'renovate.json');
67+
if (!existsSync(renovateConfigPath)) {
68+
Log.warn(` ✘ Skipped updating Renovate config as it was not found.`);
69+
70+
return null;
71+
}
72+
73+
const config = await readFile(renovateConfigPath, 'utf-8');
74+
const configJson = JSON.parse(config) as Record<string, unknown>;
75+
76+
// Check baseBranches just in case, though this function's primary focus is labels
77+
const baseBranches = configJson.baseBranches;
78+
if (!Array.isArray(baseBranches) || baseBranches.length !== 2) {
79+
Log.warn(
80+
` ✘ Skipped updating Renovate config: "baseBranches" must contain exactly 2 branches.`,
81+
);
82+
83+
return null;
84+
}
85+
86+
if (updateRenovateTargetLabel(configJson, fromLabel, toLabel)) {
87+
await writeFile(renovateConfigPath, JSON.stringify(configJson, undefined, 2));
88+
Log.info(green(` ✓ Updated target label in Renovate config.`));
89+
90+
return renovateConfigPath;
91+
} else {
92+
Log.info(green(` ✓ No changes to target labels in Renovate config.`));
93+
return null;
94+
}
95+
}
96+
97+
/**
98+
* Updates a specific target label within the `packageRules` of a Renovate configuration.
99+
* This is a private helper function.
100+
*
101+
* @param configJson - The parsed JSON object of the Renovate configuration.
102+
* @param fromLabel - The label name to be replaced.
103+
* @param toLabel - The new label name to replace `fromLabel` with.
104+
* @returns `true` is the label has been updated, otherwise `false`.
105+
*/
106+
function updateRenovateTargetLabel(
107+
configJson: Record<string, unknown>,
108+
fromLabel: string,
109+
toLabel: string,
110+
): boolean {
111+
if (!Array.isArray(configJson.packageRules)) {
112+
return false;
113+
}
114+
115+
let updated = false;
116+
for (const rule of configJson.packageRules) {
117+
if (!Array.isArray(rule.addLabels)) {
118+
continue;
119+
}
120+
121+
for (let index = 0; index < rule.addLabels.length; index++) {
122+
if (fromLabel !== rule.addLabels[index]) {
123+
continue;
124+
}
125+
126+
rule.addLabels[index] = toLabel;
127+
updated = true;
128+
}
129+
}
130+
131+
return updated;
132+
}

0 commit comments

Comments
 (0)