Skip to content

Commit 18091f5

Browse files
authored
lock down supported scenarios for gen-changelog (graphql#4605)
`npm run changelog` works in conjunction with `npm run release:prepare`. scenarios: 1. maintainer runs `npm run changelog` prior to running `npm run release:prepare` to check future changelog 2. maintainer runs `npm run changelog` automatically while running `npm run release:prepare` 3. maintainer runs `npm run changelog` immediately after `npm run release:prepare`, perhaps to check or tweak the changelog
1 parent 26f9686 commit 18091f5

File tree

1 file changed

+74
-24
lines changed

1 file changed

+74
-24
lines changed

resources/gen-changelog.ts

Lines changed: 74 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -70,32 +70,82 @@ function parseFromRevArg(rawArgs: ReadonlyArray<string>): string | null {
7070
);
7171
}
7272

73+
function readVersionFromPackageJSONAtRef(ref: string): string {
74+
const packageJSONAtRef = git().catFile('blob', `${ref}:package.json`);
75+
return JSON.parse(packageJSONAtRef).version;
76+
}
77+
78+
function resolveChangelogRangeConfig(
79+
workingTreeVersion: string,
80+
fromRev: string | null,
81+
): {
82+
title: string;
83+
rangeStart: string;
84+
rangeEnd: string;
85+
} {
86+
const workingTreeReleaseTag = `v${workingTreeVersion}`;
87+
88+
// packageJSON in the working tree can differ from HEAD:package.json during
89+
// release:prepare after npm version updates files but before committing.
90+
// Supported scenario 1: release preparation not started
91+
// - working-tree version tag exists
92+
// - HEAD version older than or equal to working-tree version, must also exist
93+
if (git().tagExists(workingTreeReleaseTag)) {
94+
return {
95+
title: 'Unreleased',
96+
rangeStart: fromRev ?? workingTreeReleaseTag,
97+
rangeEnd: 'HEAD',
98+
};
99+
}
100+
101+
const headVersion = readVersionFromPackageJSONAtRef('HEAD');
102+
const headReleaseTag = `v${headVersion}`;
103+
104+
// Supported scenario 2: release preparation started
105+
// - working-tree version tag not yet created
106+
// - HEAD version tag exists
107+
if (git().tagExists(headReleaseTag)) {
108+
return {
109+
title: workingTreeReleaseTag,
110+
rangeStart: fromRev ?? headReleaseTag,
111+
rangeEnd: 'HEAD',
112+
};
113+
}
114+
115+
// Supported scenario 3:
116+
// - release preparation committed
117+
// - working-tree version tag equal to HEAD version tag, both not yet created
118+
// - HEAD~1 version tag exists
119+
const parentVersion = readVersionFromPackageJSONAtRef('HEAD~1');
120+
const parentTag = `v${parentVersion}`;
121+
const parentTagExists = git().tagExists(parentTag);
122+
if (workingTreeReleaseTag === headReleaseTag && parentTagExists) {
123+
console.warn(`Release committed, should already contain this changelog!`);
124+
125+
return {
126+
title: workingTreeReleaseTag,
127+
rangeStart: fromRev ?? parentTag,
128+
rangeEnd: 'HEAD~1',
129+
};
130+
}
131+
132+
throw new Error(
133+
'Unable to determine changelog range. One of the following scenarios must be true:\n' +
134+
`1) HEAD/working-tree release tags exist, i.e. release preparation not started.\n` +
135+
`2) HEAD release tag exists, but working-tree release tag not yet created, i.e. release preparation started, not yet committed.\n` +
136+
`3) HEAD/working-tree release tags not yet created, i.e. release preparation committed, not yet released, no additional commits on branch.`,
137+
);
138+
}
139+
73140
async function genChangeLog(): Promise<string> {
74-
const { version } = packageJSON;
75-
const releaseTag = `v${version}`;
141+
const workingTreeVersion = packageJSON.version;
76142
const fromRev = parseFromRevArg(process.argv.slice(2));
77-
const releaseTagExists = git().tagExists(releaseTag);
78-
79-
let tag: string | null;
80-
let baseRef: string;
81-
let endRef: string;
82-
if (releaseTagExists) {
83-
tag = null;
84-
baseRef = fromRev ?? releaseTag;
85-
endRef = 'HEAD';
86-
} else {
87-
tag = releaseTag;
88-
if (fromRev != null) {
89-
baseRef = fromRev;
90-
} else {
91-
const parentPackageJSON = git().catFile('blob', 'HEAD~1:package.json');
92-
const parentVersion = JSON.parse(parentPackageJSON).version;
93-
baseRef = `v${parentVersion}`;
94-
}
95-
endRef = 'HEAD~1';
96-
}
143+
const { title, rangeStart, rangeEnd } = resolveChangelogRangeConfig(
144+
workingTreeVersion,
145+
fromRev,
146+
);
97147

98-
const commitsRange = `${baseRef}..${endRef}`;
148+
const commitsRange = `${rangeStart}..${rangeEnd}`;
99149
const commitsList = git().revList('--reverse', commitsRange);
100150

101151
const allPRs = await getPRsInfo(commitsList);
@@ -139,7 +189,7 @@ async function genChangeLog(): Promise<string> {
139189
throw new Error(validationIssues.join('\n\n'));
140190
}
141191

142-
let changelog = `## ${tag ?? 'Unreleased'} (${date})\n`;
192+
let changelog = `## ${title} (${date})\n`;
143193
for (const [label, config] of Object.entries(labelsConfig)) {
144194
const prs = byLabel[label];
145195
if (prs != null) {

0 commit comments

Comments
 (0)