mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-11 22:46:27 +00:00
feat(release-notes)!: support configurable fetching stage (#22781)
Changes fetchReleaseNotes from boolean to enum, with values off, branch, pr. Closes #20476 BREAKING CHANGE: Release notes won't be fetched early for commitBody insertion unless explicitly configured with fetchReleaseNotes=branch
This commit is contained in:
parent
aa14b777c0
commit
c2d3ca856f
14 changed files with 66 additions and 114 deletions
|
@ -931,7 +931,14 @@ A similar one could strip leading `v` prefixes:
|
|||
|
||||
## fetchReleaseNotes
|
||||
|
||||
Set this to `false` if you want to disable release notes fetching.
|
||||
Use this config option to configure release notes fetching.
|
||||
The available options are:
|
||||
|
||||
- `off` - disable release notes fetching
|
||||
- `branch` - fetch release notes while creating/updating branch
|
||||
- `pr`(default) - fetches release notes while creating/updating pull-request
|
||||
|
||||
It is not recommended to set fetchReleaseNotes=branch unless you are embedding release notes in commit information, because it results in a performance decrease.
|
||||
|
||||
Renovate can fetch release notes when they are hosted on one of these platforms:
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import { FetchReleaseNotesMigration } from './fetch-release-notes-migration';
|
||||
|
||||
describe('config/migrations/custom/fetch-release-notes-migration', () => {
|
||||
it('migrates', () => {
|
||||
expect(FetchReleaseNotesMigration).toMigrate(
|
||||
{
|
||||
fetchReleaseNotes: false as never,
|
||||
},
|
||||
{
|
||||
fetchReleaseNotes: 'off',
|
||||
}
|
||||
);
|
||||
expect(FetchReleaseNotesMigration).toMigrate(
|
||||
{
|
||||
fetchReleaseNotes: true as never,
|
||||
},
|
||||
{
|
||||
fetchReleaseNotes: 'pr',
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
import is from '@sindresorhus/is';
|
||||
import { AbstractMigration } from '../base/abstract-migration';
|
||||
|
||||
export class FetchReleaseNotesMigration extends AbstractMigration {
|
||||
override readonly propertyName = 'fetchReleaseNotes';
|
||||
|
||||
override run(value: unknown): void {
|
||||
if (is.boolean(value)) {
|
||||
this.rewrite(value ? 'pr' : 'off');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import { DepTypesMigration } from './custom/dep-types-migration';
|
|||
import { DryRunMigration } from './custom/dry-run-migration';
|
||||
import { EnabledManagersMigration } from './custom/enabled-managers-migration';
|
||||
import { ExtendsMigration } from './custom/extends-migration';
|
||||
import { FetchReleaseNotesMigration } from './custom/fetch-release-notes-migration';
|
||||
import { GoModTidyMigration } from './custom/go-mod-tidy-migration';
|
||||
import { HostRulesMigration } from './custom/host-rules-migration';
|
||||
import { IgnoreNodeModulesMigration } from './custom/ignore-node-modules-migration';
|
||||
|
@ -148,6 +149,7 @@ export class MigrationsService {
|
|||
DatasourceMigration,
|
||||
RecreateClosedMigration,
|
||||
StabilityDaysMigration,
|
||||
FetchReleaseNotesMigration,
|
||||
];
|
||||
|
||||
static run(originalConfig: RenovateConfig): RenovateConfig {
|
||||
|
|
|
@ -2567,9 +2567,10 @@ const options: RenovateOptions[] = [
|
|||
},
|
||||
{
|
||||
name: 'fetchReleaseNotes',
|
||||
description: 'Controls if release notes are fetched.',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Controls if and when release notes are fetched.',
|
||||
type: 'string',
|
||||
allowedValues: ['off', 'branch', 'pr'],
|
||||
default: 'pr',
|
||||
cli: false,
|
||||
env: false,
|
||||
},
|
||||
|
|
|
@ -261,7 +261,7 @@ export interface RenovateConfig
|
|||
vulnerabilitySeverity?: string;
|
||||
regexManagers?: RegExManager[];
|
||||
|
||||
fetchReleaseNotes?: boolean;
|
||||
fetchReleaseNotes?: FetchReleaseNotesOptions;
|
||||
secrets?: Record<string, string>;
|
||||
|
||||
constraints?: Record<string, string>;
|
||||
|
@ -302,6 +302,8 @@ export type UpdateType =
|
|||
| 'bump'
|
||||
| 'replacement';
|
||||
|
||||
export type FetchReleaseNotesOptions = 'off' | 'branch' | 'pr';
|
||||
|
||||
export type MatchStringsStrategy = 'any' | 'recursive' | 'combination';
|
||||
|
||||
export type MergeStrategy =
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { mockedFunction, partial } from '../../../../test/util';
|
||||
import type { BranchUpgradeConfig } from '../../types';
|
||||
import { getChangeLogJSON } from '../update/pr/changelog';
|
||||
import { embedChangelogs, needsChangelogs } from '.';
|
||||
import { embedChangelogs } from '.';
|
||||
|
||||
jest.mock('../update/pr/changelog');
|
||||
|
||||
|
@ -27,23 +27,4 @@ describe('workers/repository/changelog/index', () => {
|
|||
{ logJSON: null },
|
||||
]);
|
||||
});
|
||||
|
||||
it('needsChangelogs', () => {
|
||||
expect(needsChangelogs(partial<BranchUpgradeConfig>())).toBeFalse();
|
||||
expect(
|
||||
needsChangelogs(
|
||||
partial<BranchUpgradeConfig>({
|
||||
commitBody: '{{#if logJSON.hasReleaseNotes}}has changelog{{/if}}',
|
||||
})
|
||||
)
|
||||
).toBeFalse();
|
||||
expect(
|
||||
needsChangelogs(
|
||||
partial<BranchUpgradeConfig>({
|
||||
commitBody: '{{#if logJSON.hasReleaseNotes}}has changelog{{/if}}',
|
||||
}),
|
||||
['commitBody']
|
||||
)
|
||||
).toBeTrue();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
import * as p from '../../../util/promises';
|
||||
import {
|
||||
containsTemplates,
|
||||
exposedConfigOptions,
|
||||
} from '../../../util/template';
|
||||
import type { BranchUpgradeConfig } from '../../types';
|
||||
import { getChangeLogJSON } from '../update/pr/changelog';
|
||||
|
||||
|
@ -21,17 +17,3 @@ export async function embedChangelogs(
|
|||
): Promise<void> {
|
||||
await p.map(branches, embedChangelog, { concurrency: 10 });
|
||||
}
|
||||
|
||||
export function needsChangelogs(
|
||||
upgrade: BranchUpgradeConfig,
|
||||
fields = exposedConfigOptions.filter((o) => o !== 'commitBody')
|
||||
): boolean {
|
||||
// commitBody is now compiled when commit is done
|
||||
for (const field of fields) {
|
||||
// fields set by `getChangeLogJSON`
|
||||
if (containsTemplates(upgrade[field], ['logJSON', 'releases'])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ import {
|
|||
fs,
|
||||
git,
|
||||
mocked,
|
||||
mockedFunction,
|
||||
partial,
|
||||
platform,
|
||||
scm,
|
||||
|
@ -34,7 +33,6 @@ import * as _mergeConfidence from '../../../../util/merge-confidence';
|
|||
import * as _sanitize from '../../../../util/sanitize';
|
||||
import * as _limits from '../../../global/limits';
|
||||
import type { BranchConfig, BranchUpgradeConfig } from '../../../types';
|
||||
import { needsChangelogs } from '../../changelog';
|
||||
import type { ResultWithPr } from '../pr';
|
||||
import * as _prWorker from '../pr';
|
||||
import * as _prAutomerge from '../pr/automerge';
|
||||
|
@ -816,9 +814,8 @@ describe('workers/repository/update/branch/index', () => {
|
|||
ignoreTests: true,
|
||||
prCreation: 'not-pending',
|
||||
commitBody: '[skip-ci]',
|
||||
fetchReleaseNotes: true,
|
||||
fetchReleaseNotes: 'branch',
|
||||
} satisfies BranchConfig;
|
||||
mockedFunction(needsChangelogs).mockReturnValueOnce(true);
|
||||
scm.getBranchCommit.mockResolvedValue('123test'); //TODO:not needed?
|
||||
expect(await branchWorker.processBranch(inconfig)).toEqual({
|
||||
branchExists: true,
|
||||
|
|
|
@ -34,7 +34,7 @@ import { toMs } from '../../../../util/pretty-time';
|
|||
import * as template from '../../../../util/template';
|
||||
import { isLimitReached } from '../../../global/limits';
|
||||
import type { BranchConfig, BranchResult, PrBlockedBy } from '../../../types';
|
||||
import { embedChangelog, needsChangelogs } from '../../changelog';
|
||||
import { embedChangelogs } from '../../changelog';
|
||||
import { ensurePr } from '../pr';
|
||||
import { checkAutoMerge } from '../pr/automerge';
|
||||
import { setArtifactErrorStatus } from './artifacts';
|
||||
|
@ -482,6 +482,10 @@ export async function processBranch(
|
|||
} else {
|
||||
logger.debug('No updated lock files in branch');
|
||||
}
|
||||
if (config.fetchReleaseNotes === 'branch') {
|
||||
await embedChangelogs(config.upgrades);
|
||||
}
|
||||
|
||||
const postUpgradeCommandResults = await executePostUpgradeCommands(
|
||||
config
|
||||
);
|
||||
|
@ -540,14 +544,6 @@ export async function processBranch(
|
|||
|
||||
// compile commit message with body, which maybe needs changelogs
|
||||
if (config.commitBody) {
|
||||
if (
|
||||
config.fetchReleaseNotes &&
|
||||
needsChangelogs(config, ['commitBody'])
|
||||
) {
|
||||
// we only need first upgrade, the others are only needed on PR update
|
||||
// we add it to first, so PR fetch can skip fetching for that update
|
||||
await embedChangelog(config.upgrades[0]);
|
||||
}
|
||||
// changelog is on first upgrade
|
||||
config.commitMessage = `${config.commitMessage!}\n\n${template.compile(
|
||||
config.commitBody,
|
||||
|
|
|
@ -102,7 +102,7 @@ describe('workers/repository/update/pr/index', () => {
|
|||
platform.createPr.mockResolvedValueOnce(pr);
|
||||
limits.isLimitReached.mockReturnValueOnce(true);
|
||||
|
||||
config.fetchReleaseNotes = true;
|
||||
config.fetchReleaseNotes = 'pr';
|
||||
|
||||
const res = await ensurePr(config);
|
||||
|
||||
|
@ -871,13 +871,13 @@ describe('workers/repository/update/pr/index', () => {
|
|||
bodyFingerprint: fingerprint(
|
||||
generatePrBodyFingerprintConfig({
|
||||
...config,
|
||||
fetchReleaseNotes: true,
|
||||
fetchReleaseNotes: 'pr',
|
||||
})
|
||||
),
|
||||
lastEdited: new Date('2020-01-20T00:00:00Z').toISOString(),
|
||||
};
|
||||
prCache.getPrCache.mockReturnValueOnce(cachedPr);
|
||||
const res = await ensurePr({ ...config, fetchReleaseNotes: true });
|
||||
const res = await ensurePr({ ...config, fetchReleaseNotes: 'pr' });
|
||||
expect(res).toEqual({
|
||||
type: 'with-pr',
|
||||
pr: existingPr,
|
||||
|
@ -904,13 +904,13 @@ describe('workers/repository/update/pr/index', () => {
|
|||
bodyFingerprint: fingerprint(
|
||||
generatePrBodyFingerprintConfig({
|
||||
...config,
|
||||
fetchReleaseNotes: true,
|
||||
fetchReleaseNotes: 'pr',
|
||||
})
|
||||
),
|
||||
lastEdited: new Date('2020-01-20T00:00:00Z').toISOString(),
|
||||
};
|
||||
prCache.getPrCache.mockReturnValueOnce(cachedPr);
|
||||
const res = await ensurePr({ ...config, fetchReleaseNotes: true });
|
||||
const res = await ensurePr({ ...config, fetchReleaseNotes: 'pr' });
|
||||
expect(res).toEqual({
|
||||
type: 'with-pr',
|
||||
pr: {
|
||||
|
|
|
@ -234,7 +234,7 @@ export async function ensurePr(
|
|||
}`;
|
||||
}
|
||||
|
||||
if (config.fetchReleaseNotes) {
|
||||
if (config.fetchReleaseNotes === 'pr') {
|
||||
// fetch changelogs when not already done;
|
||||
await embedChangelogs(upgrades);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { RenovateConfig, mocked, mockedFunction } from '../../../../test/util';
|
||||
import { RenovateConfig, mocked } from '../../../../test/util';
|
||||
import { getConfig } from '../../../config/defaults';
|
||||
import * as _changelog from '../changelog';
|
||||
import { branchifyUpgrades } from './branchify';
|
||||
|
@ -124,7 +124,7 @@ describe('workers/repository/updates/branchify', () => {
|
|||
});
|
||||
|
||||
it('no fetch changelogs', async () => {
|
||||
config.fetchReleaseNotes = false;
|
||||
config.fetchReleaseNotes = 'off';
|
||||
flattenUpdates.mockResolvedValueOnce([
|
||||
{
|
||||
depName: 'foo',
|
||||
|
@ -153,38 +153,5 @@ describe('workers/repository/updates/branchify', () => {
|
|||
expect(embedChangelogs).not.toHaveBeenCalled();
|
||||
expect(Object.keys(res.branches)).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('fetch changelogs if required', async () => {
|
||||
config.fetchReleaseNotes = true;
|
||||
config.repoIsOnboarded = true;
|
||||
mockedFunction(_changelog.needsChangelogs).mockReturnValueOnce(true);
|
||||
flattenUpdates.mockResolvedValueOnce([
|
||||
{
|
||||
depName: 'foo',
|
||||
branchName: 'foo',
|
||||
prTitle: 'some-title',
|
||||
version: '1.1.0',
|
||||
groupName: 'My Group',
|
||||
group: { branchName: 'renovate/{{groupSlug}}' },
|
||||
},
|
||||
{
|
||||
depName: 'foo',
|
||||
branchName: 'foo',
|
||||
prTitle: 'some-title',
|
||||
version: '2.0.0',
|
||||
},
|
||||
{
|
||||
depName: 'bar',
|
||||
branchName: 'bar-{{version}}',
|
||||
prTitle: 'some-title',
|
||||
version: '1.1.0',
|
||||
groupName: 'My Group',
|
||||
group: { branchName: 'renovate/my-group' },
|
||||
},
|
||||
]);
|
||||
const res = await branchifyUpgrades(config, {});
|
||||
expect(embedChangelogs).toHaveBeenCalledOnce();
|
||||
expect(Object.keys(res.branches)).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -3,7 +3,6 @@ import type { Merge } from 'type-fest';
|
|||
import type { RenovateConfig, ValidationMessage } from '../../../config/types';
|
||||
import { addMeta, logger, removeMeta } from '../../../logger';
|
||||
import type { BranchConfig, BranchUpgradeConfig } from '../../types';
|
||||
import { embedChangelogs, needsChangelogs } from '../changelog';
|
||||
import { flattenUpdates } from './flatten';
|
||||
import { generateBranchConfig } from './generate';
|
||||
|
||||
|
@ -72,22 +71,6 @@ export async function branchifyUpgrades(
|
|||
})
|
||||
.reverse();
|
||||
|
||||
if (config.fetchReleaseNotes && config.repoIsOnboarded) {
|
||||
const branches = branchUpgrades[branchName].filter((upg) =>
|
||||
needsChangelogs(upg)
|
||||
);
|
||||
if (branches.length) {
|
||||
logger.warn(
|
||||
{
|
||||
branches: branches.map((b) => b.branchName),
|
||||
docs: 'https://docs.renovatebot.com/templates/',
|
||||
},
|
||||
'Fetching changelogs early is deprecated. Remove `logJSON` and `releases` from config templates. They are only allowed in `commitBody` template. See template docs for allowed templates'
|
||||
);
|
||||
await embedChangelogs(branches);
|
||||
}
|
||||
}
|
||||
|
||||
const branch = generateBranchConfig(branchUpgrades[branchName]);
|
||||
branch.branchName = branchName;
|
||||
branch.packageFiles = packageFiles;
|
||||
|
|
Loading…
Reference in a new issue