Skip to content

Commit aadaf39

Browse files
authored
Merge pull request #36 from matrix-org/t3chguy/preview
2 parents 04acfbb + f7ea01c commit aadaf39

File tree

4 files changed

+127
-54
lines changed

4 files changed

+127
-54
lines changed

package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,15 @@
3636
"js-yaml": "^4.1.0",
3737
"loglevel": "^1.7.1",
3838
"semver": "^7.3.5",
39-
"yargs": "^17.0.1"
39+
"yargs": "^17.5.1"
4040
},
4141
"devDependencies": {
4242
"@octokit/types": "^6.18.1",
43+
"@types/cli-color": "^2.0.2",
4344
"@types/jest": "^26.0.24",
4445
"@types/node": "^16.3.0",
4546
"@types/semver": "^7.3.8",
47+
"@types/yargs": "^17.0.13",
4648
"@typescript-eslint/eslint-plugin": "^4.28.5",
4749
"@typescript-eslint/parser": "^4.28.5",
4850
"eslint": "^7.31.0",

src/changelog.ts

+29-22
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ async function* readChangelog(project: Project): AsyncGenerator<IChangelogEntry>
4040
const fp = fs.createReadStream(path.join(project.dir, 'CHANGELOG.md'));
4141
const rl = readline.createInterface(fp);
4242

43-
let version;
43+
let version: string;
4444
let fullText = '';
4545
for await (const line of rl) {
46-
const matches = /^Changes in \[([\d\w.-]+)\]/.exec(line);
46+
const matches = /^Changes in \[([\w.-]+)]/.exec(line);
4747
if (matches) {
4848
if (version) {
4949
yield {
@@ -107,7 +107,7 @@ function sanitiseMarkdown(text: string): string {
107107
return text;
108108
}
109109

110-
function engJoin(things): string {
110+
function engJoin(things: string[]): string {
111111
if (things.length === 1) return things[0];
112112

113113
const firstLot = things.slice(0, things.length - 2);
@@ -140,20 +140,22 @@ export function makeChangeEntry(change: IChange, forProject: IProject): string {
140140
return line;
141141
}
142142

143-
function makeChangelogEntry(changes: IChange[], version: string, forProject: Project): string {
144-
const formattedVersion = semver.parse(version).format(); // easy way of removing the leading 'v'
143+
function makeChangelogEntry(changes: IChange[], version: string | null, forProject: Project): string {
144+
const formattedVersion = version ? semver.parse(version).format() : null; // easy way of removing the leading 'v'
145145
const now = new Date();
146146

147-
const lines = [];
147+
const lines: string[] = [];
148148

149-
const padTwo = n => String(n).padStart(2, '0');
150-
lines.push(`Changes in ` +
151-
`[${formattedVersion}]` +
152-
`(https://github.com/${forProject.owner}/${forProject.repo}/releases/tag/v${formattedVersion}) ` +
153-
`(${now.getFullYear()}-${padTwo(now.getMonth()+1)}-${padTwo(now.getDate())})`,
154-
);
155-
lines.push('='.repeat(lines[0].length));
156-
lines.push('');
149+
if (version !== null) {
150+
const padTwo = (n: number) => String(n).padStart(2, '0');
151+
lines.push(`Changes in ` +
152+
`[${formattedVersion}]` +
153+
`(https://github.com/${forProject.owner}/${forProject.repo}/releases/tag/v${formattedVersion}) ` +
154+
`(${now.getFullYear()}-${padTwo(now.getMonth()+1)}-${padTwo(now.getDate())})`,
155+
);
156+
lines.push('='.repeat(lines[0].length));
157+
lines.push('');
158+
}
157159

158160
const shouldInclude = changes.filter(c => c.shouldInclude);
159161
const breaking = shouldInclude.filter(c => c.breaking);
@@ -218,6 +220,10 @@ function isPrereleaseFor(version: SemVer, forVersion: SemVer): boolean {
218220
);
219221
}
220222

223+
export async function previewChangelog(project: Project, changes: IChange[]) {
224+
console.log(makeChangelogEntry(changes, null, project));
225+
}
226+
221227
export async function updateChangelog(project: Project, changes: IChange[], forVersion: string) {
222228
const forReleaseSemVer = semver.parse(forVersion);
223229

@@ -233,6 +239,15 @@ export async function updateChangelog(project: Project, changes: IChange[], forV
233239
// This is the exact version we should be updating: replace it
234240
await outHandle.write(makeChangelogEntry(changes, forVersion, project));
235241
changeWritten = true;
242+
} else if (isPrereleaseFor(semver.parse(entry.version), forReleaseSemVer)) {
243+
log.debug(`Found ${entry.version} which is a prerelease of the version we should be updating`);
244+
// This is a prerelease of the version we're trying to write, so remove the
245+
// prerelease entry from the changelog and replace it with the entry we're
246+
// writing, if we haven't already written it
247+
if (!changeWritten) {
248+
await outHandle.write(makeChangelogEntry(changes, forVersion, project));
249+
changeWritten = true;
250+
}
236251
} else if (forReleaseSemVer.compare(entry.version) === 1) {
237252
// This one comes before the one we're updating, so if we haven't yet written
238253
// our changeset, we need to do it now.
@@ -243,14 +258,6 @@ export async function updateChangelog(project: Project, changes: IChange[], forV
243258
}
244259
// and then write the one we found too
245260
await outHandle.write(entry.text);
246-
} else if (isPrereleaseFor(semver.parse(entry.version), forReleaseSemVer)) {
247-
log.debug(`Found ${entry.version} which is a prerelease of the version we should be updating`);
248-
// This is a prerelease of the version we're trying to write, so remove the
249-
// prerelease entry from the changelog and replace it with the entry we're
250-
// writing, if we haven't already written it
251-
if (!changeWritten) {
252-
await outHandle.write(makeChangelogEntry(changes, forVersion, project));
253-
}
254261
} else {
255262
log.debug(`Found ${entry.version} which is newer than the version we should be updating`);
256263
await outHandle.write(entry.text);

src/index.ts

+51-25
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ limitations under the License.
1717
*/
1818

1919
import log from 'loglevel';
20-
import yargs from 'yargs/yargs';
20+
import yargs from 'yargs';
2121
import { hideBin } from 'yargs/helpers';
2222
import clc from 'cli-color';
2323
import semver from 'semver';
@@ -31,7 +31,7 @@ import {
3131
import { getLatestRelease, getReleaseBefore, getReleases, releasesContains } from "./releases";
3232
import { ChangesByProject, getPackageJsonAtVersion, Project, branchExists, BranchMode } from './projects';
3333
import { formatIssue } from './issue';
34-
import { updateChangelog } from './changelog';
34+
import { previewChangelog, updateChangelog } from './changelog';
3535
import { Octokit } from '@octokit/rest';
3636

3737
function formatChangeType(changeType: ChangeType) {
@@ -74,21 +74,35 @@ function printChangeStatus(change: IChange, projectName: string, owner: string,
7474
}
7575

7676
async function main() {
77-
const args = yargs(hideBin(process.argv)).option('debug', {
78-
alias: 'd',
79-
type: 'boolean',
80-
description: "Enable debug mode",
81-
}).option('check', {
82-
type: 'boolean',
83-
description: "Don't update changelog, just output information on what changes would be included",
84-
}).help().usage("Usage: $0 [-d] [--check] <version>").argv;
85-
86-
if (args._.length !== 1 && !args.check) {
77+
const args = yargs(hideBin(process.argv)).version(false).options({
78+
"debug": {
79+
alias: 'd',
80+
type: 'boolean',
81+
description: "Enable debug mode",
82+
},
83+
"check": {
84+
type: 'boolean',
85+
description: "Don't update changelog, just output information on what changes would be included",
86+
conflicts: ["preview"],
87+
},
88+
"preview": {
89+
type: "boolean",
90+
description: "Generate changelog as normal, but without version header and output to STDOUT.",
91+
conflicts: ["check"],
92+
},
93+
}).command("* [version]", "Generate changelog for the given version", yargs => (
94+
yargs.positional("version", {
95+
description: "The version to generate the changelog for, " +
96+
"required if --check and/or --preview are not specified.",
97+
type: "string",
98+
})
99+
)).help().parseSync();
100+
101+
if (!args.version && !args.check && !args.preview) {
87102
// Surely yargs should be able to do this? It seems incredibly confusing and I already regret using it
88103
console.log("No version specified");
89104
return;
90105
}
91-
const targetRelease = args._[0] as string;
92106

93107
if (args.debug) {
94108
log.setLevel(log.levels.DEBUG);
@@ -109,26 +123,33 @@ async function main() {
109123
let fromVer: string;
110124
let toVer: string;
111125

112-
if (targetRelease) {
113-
const targetReleaseSemVer = semver.parse(targetRelease);
126+
if (args.version) {
127+
const targetReleaseSemVer = semver.parse(args.version);
114128
const targetIsPrerelease = targetReleaseSemVer.prerelease.length > 0;
115129
const toVerReleaseBranch =
116130
`release-v${targetReleaseSemVer.major}.${targetReleaseSemVer.minor}.${targetReleaseSemVer.patch}`;
117-
if (releasesContains(rels, targetRelease)) {
118-
log.debug("Found existing release for " + targetRelease);
131+
if (releasesContains(rels, args.version)) {
132+
log.debug("Found existing release for " + args.version);
119133
// nb. getReleases only gets the most recent 100 so this won't work
120134
// for older releases
121-
fromVer = getReleaseBefore(rels, targetRelease, targetIsPrerelease).name;
122-
toVer = targetRelease;
123-
} else if (targetRelease !== 'develop' && await branchExists(dir, toVerReleaseBranch)) {
124-
log.debug("Found release branch for " + targetRelease);
135+
fromVer = getReleaseBefore(rels, args.version, targetIsPrerelease).name;
136+
toVer = args.version;
137+
} else if (args.version !== 'develop' && await branchExists(dir, toVerReleaseBranch)) {
138+
log.debug("Found release branch for " + args.version);
125139
// 'to' release has had a release branch cut but not yet a full release
126140
// compare to the tip of the release branch
127141
fromVer = getLatestRelease(rels, targetIsPrerelease).name;
128142
toVer = toVerReleaseBranch;
129143
branchMode = BranchMode.Release;
144+
} else if (args.version !== 'develop' && await branchExists(dir, "staging")) {
145+
log.debug("Found release branch for " + args.version);
146+
// 'to' release has had a release branch cut but not yet a full release
147+
// compare to the tip of the release branch
148+
fromVer = getLatestRelease(rels, targetIsPrerelease).name;
149+
toVer = "staging";
150+
branchMode = BranchMode.Release;
130151
} else {
131-
log.debug("Found neither release nor branch for " + targetRelease);
152+
log.debug("Found neither release nor branch for " + args.version);
132153
// the 'to' release is an doesn't-yet-exist future release -
133154
// compare to the tip of develop (a better piece of software
134155
// might make this configurable...)
@@ -177,7 +198,7 @@ async function main() {
177198

178199
const numBreaking = allChanges.filter(c => c.breaking).length;
179200
const numFeatures = allChanges.filter(c => c.changeType == ChangeType.FEATURE).length;
180-
let suggestedBumpType;
201+
let suggestedBumpType: "major" | "minor" | "patch";
181202
if (numBreaking) {
182203
suggestedBumpType = 'major';
183204
} else if (numFeatures) {
@@ -193,8 +214,13 @@ async function main() {
193214
return;
194215
}
195216

196-
log.debug("Updating changelog entry for " + targetRelease);
197-
await updateChangelog(project, allChanges, targetRelease);
217+
if (args.preview) {
218+
await previewChangelog(project, allChanges);
219+
return;
220+
}
221+
222+
log.debug("Updating changelog entry for " + args.version);
223+
await updateChangelog(project, allChanges, args.version);
198224
}
199225

200226
main();

yarn.lock

+44-6
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,11 @@
777777
dependencies:
778778
"@babel/types" "^7.3.0"
779779

780+
"@types/cli-color@^2.0.2":
781+
version "2.0.2"
782+
resolved "https://registry.yarnpkg.com/@types/cli-color/-/cli-color-2.0.2.tgz#01bd593722a12c26ec84c170ab251fe2d35856c5"
783+
integrity sha512-1ErQIcmNHtNViGKTtB/TIKqMkC2RkKI2nBneCr9hSCPo9H05g9VzjlaXPW3H0vaI8zFGjJZvSav+VKDKCtKgKA==
784+
780785
"@types/graceful-fs@^4.1.2":
781786
version "4.1.5"
782787
resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15"
@@ -855,6 +860,13 @@
855860
dependencies:
856861
"@types/yargs-parser" "*"
857862

863+
"@types/yargs@^17.0.13":
864+
version "17.0.13"
865+
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.13.tgz#34cced675ca1b1d51fcf4d34c3c6f0fa142a5c76"
866+
integrity sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==
867+
dependencies:
868+
"@types/yargs-parser" "*"
869+
858870
"@typescript-eslint/eslint-plugin@^4.28.5":
859871
version "4.28.5"
860872
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.5.tgz#8197f1473e7da8218c6a37ff308d695707835684"
@@ -1006,6 +1018,11 @@ ansi-regex@^5.0.0:
10061018
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
10071019
integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
10081020

1021+
ansi-regex@^5.0.1:
1022+
version "5.0.1"
1023+
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
1024+
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
1025+
10091026
ansi-styles@^3.2.1:
10101027
version "3.2.1"
10111028
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
@@ -3171,13 +3188,29 @@ string-width@^4.1.0, string-width@^4.2.0:
31713188
is-fullwidth-code-point "^3.0.0"
31723189
strip-ansi "^6.0.0"
31733190

3191+
string-width@^4.2.3:
3192+
version "4.2.3"
3193+
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
3194+
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
3195+
dependencies:
3196+
emoji-regex "^8.0.0"
3197+
is-fullwidth-code-point "^3.0.0"
3198+
strip-ansi "^6.0.1"
3199+
31743200
strip-ansi@^6.0.0:
31753201
version "6.0.0"
31763202
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532"
31773203
integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==
31783204
dependencies:
31793205
ansi-regex "^5.0.0"
31803206

3207+
strip-ansi@^6.0.1:
3208+
version "6.0.1"
3209+
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
3210+
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
3211+
dependencies:
3212+
ansi-regex "^5.0.1"
3213+
31813214
strip-bom@^4.0.0:
31823215
version "4.0.0"
31833216
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878"
@@ -3556,6 +3589,11 @@ [email protected], yargs-parser@^20.2.2:
35563589
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
35573590
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
35583591

3592+
yargs-parser@^21.0.0:
3593+
version "21.1.1"
3594+
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
3595+
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
3596+
35593597
yargs@^16.0.3:
35603598
version "16.2.0"
35613599
resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
@@ -3569,18 +3607,18 @@ yargs@^16.0.3:
35693607
y18n "^5.0.5"
35703608
yargs-parser "^20.2.2"
35713609

3572-
yargs@^17.0.1:
3573-
version "17.0.1"
3574-
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.0.1.tgz#6a1ced4ed5ee0b388010ba9fd67af83b9362e0bb"
3575-
integrity sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==
3610+
yargs@^17.5.1:
3611+
version "17.5.1"
3612+
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.5.1.tgz#e109900cab6fcb7fd44b1d8249166feb0b36e58e"
3613+
integrity sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==
35763614
dependencies:
35773615
cliui "^7.0.2"
35783616
escalade "^3.1.1"
35793617
get-caller-file "^2.0.5"
35803618
require-directory "^2.1.1"
3581-
string-width "^4.2.0"
3619+
string-width "^4.2.3"
35823620
y18n "^5.0.5"
3583-
yargs-parser "^20.2.2"
3621+
yargs-parser "^21.0.0"
35843622

35853623
35863624
version "3.1.1"

0 commit comments

Comments
 (0)