Skip to content
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
1 change: 1 addition & 0 deletions ng-dev/release/publish/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ ts_library(
visibility = ["//ng-dev:__subpackages__"],
deps = [
"//ng-dev/commit-message",
"//ng-dev/pr/common/labels",
"//ng-dev/pr/merge",
"//ng-dev/release/build",
"//ng-dev/release/config",
Expand Down
18 changes: 17 additions & 1 deletion ng-dev/release/publish/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ import {Prompt} from '../../utils/prompt.js';
import {glob} from 'fast-glob';
import {PnpmVersioning} from './pnpm-versioning.js';
import {Commit} from '../../utils/git/octokit-types.js';
import {updateRenovateConfigTargetLabels} from './actions/renovate-config-updates.js';
import {targetLabels} from '../../pr/common/labels/target.js';

/** Interface describing a Github repository. */
export interface GithubRepo {
Expand Down Expand Up @@ -217,13 +219,27 @@ export abstract class ReleaseAction {
}

// Commit message for the release point.
const commitMessage = getCommitMessageForRelease(newVersion);
const filesToCommit = [
workspaceRelativePackageJsonPath,
workspaceRelativeChangelogPath,
...this.getAspectLockFiles(),
];

if (newVersion.patch === 0 && !newVersion.prerelease) {
// Switch the renovate labels for `target: rc` to `target: patch`
const renovateConfigPath = await updateRenovateConfigTargetLabels(
this.projectDir,
targetLabels['TARGET_RC'].name,
targetLabels['TARGET_PATCH'].name,
);

if (renovateConfigPath) {
filesToCommit.push(renovateConfigPath);
}
}

const commitMessage = getCommitMessageForRelease(newVersion);

// Create a release staging commit including changelog and version bump.
await this.createCommit(commitMessage, filesToCommit);

Expand Down
97 changes: 92 additions & 5 deletions ng-dev/release/publish/actions/renovate-config-updates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ import {existsSync} from 'node:fs';
import {green, Log} from '../../../utils/logging.js';
import {join} from 'node:path';
import {writeFile, readFile} from 'node:fs/promises';
import {targetLabels} from '../../../pr/common/labels/target.js';

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

return null;
}

const config = await readFile(renovateConfigPath, 'utf-8');
const configJson = JSON.parse(config) as Record<string, unknown>;
const baseBranches = configJson.baseBranches;

if (!Array.isArray(baseBranches) || baseBranches.length !== 2) {
Log.warn(
` ✘ Skipped updating Renovate config: "baseBranches" must contain exactly 2 branches.`,
Expand All @@ -34,8 +36,93 @@ export async function updateRenovateConfig(
}

configJson.baseBranches = ['main', newBranchName];

updateRenovateTargetLabel(
configJson,
targetLabels['TARGET_PATCH'].name,
targetLabels['TARGET_RC'].name,
);
await writeFile(renovateConfigPath, JSON.stringify(configJson, undefined, 2));
Log.info(green(` ✓ Updated Renovate config.`));

Log.info(green(` ✓ Updated Renovate config.`));
return renovateConfigPath;
}

/**
* Updates a specific target label in the `renovate.json` configuration file.
* This function specifically targets and replaces one label with another within the `packageRules`.
*
* @param projectDir - The path to the project directory.
* @param fromLabel - The label name to be replaced.
* @param toLabel - The new label name to replace `fromLabel` with.
* @returns A promise that resolves to the path of the modified `renovate.json` file if updated,
* or `null` if the file was not found or the `baseBranches` array has an unexpected format.
*/
export async function updateRenovateConfigTargetLabels(
projectDir: string,
fromLabel: string,
toLabel: string,
): Promise<string | null> {
const renovateConfigPath = join(projectDir, 'renovate.json');
if (!existsSync(renovateConfigPath)) {
Log.warn(` ✘ Skipped updating Renovate config as it was not found.`);

return null;
}

const config = await readFile(renovateConfigPath, 'utf-8');
const configJson = JSON.parse(config) as Record<string, unknown>;

// Check baseBranches just in case, though this function's primary focus is labels
const baseBranches = configJson.baseBranches;
if (!Array.isArray(baseBranches) || baseBranches.length !== 2) {
Log.warn(
` ✘ Skipped updating Renovate config: "baseBranches" must contain exactly 2 branches.`,
);

return null;
}

if (updateRenovateTargetLabel(configJson, fromLabel, toLabel)) {
await writeFile(renovateConfigPath, JSON.stringify(configJson, undefined, 2));
Log.info(green(` ✓ Updated target label in Renovate config.`));

return renovateConfigPath;
} else {
Log.info(green(` ✓ No changes to target labels in Renovate config.`));
return null;
}
}

/**
* Updates a specific target label within the `packageRules` of a Renovate configuration.
*
* @param configJson - The parsed JSON object of the Renovate configuration.
* @param fromLabel - The label name to be replaced.
* @param toLabel - The new label name to replace `fromLabel` with.
* @returns `true` is the label has been updated, otherwise `false`.
*/
function updateRenovateTargetLabel(
configJson: Record<string, unknown>,
fromLabel: string,
toLabel: string,
): boolean {
if (!Array.isArray(configJson.packageRules)) {
return false;
}

let updated = false;
for (const rule of configJson.packageRules) {
if (!Array.isArray(rule.addLabels)) {
continue;
}

const idx = (rule.addLabels as string[]).findIndex((x) => x === fromLabel);
if (idx >= 0) {
rule.addLabels[idx] = toLabel;
updated = true;
}
}

return updated;
}