Skip to content

Commit

Permalink
Merge pull request #36 from matrix-org/t3chguy/preview
Browse files Browse the repository at this point in the history
  • Loading branch information
t3chguy committed Sep 26, 2022
2 parents 04acfbb + f7ea01c commit aadaf39
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 54 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,15 @@
"js-yaml": "^4.1.0",
"loglevel": "^1.7.1",
"semver": "^7.3.5",
"yargs": "^17.0.1"
"yargs": "^17.5.1"
},
"devDependencies": {
"@octokit/types": "^6.18.1",
"@types/cli-color": "^2.0.2",
"@types/jest": "^26.0.24",
"@types/node": "^16.3.0",
"@types/semver": "^7.3.8",
"@types/yargs": "^17.0.13",
"@typescript-eslint/eslint-plugin": "^4.28.5",
"@typescript-eslint/parser": "^4.28.5",
"eslint": "^7.31.0",
Expand Down
51 changes: 29 additions & 22 deletions src/changelog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ async function* readChangelog(project: Project): AsyncGenerator<IChangelogEntry>
const fp = fs.createReadStream(path.join(project.dir, 'CHANGELOG.md'));
const rl = readline.createInterface(fp);

let version;
let version: string;
let fullText = '';
for await (const line of rl) {
const matches = /^Changes in \[([\d\w.-]+)\]/.exec(line);
const matches = /^Changes in \[([\w.-]+)]/.exec(line);
if (matches) {
if (version) {
yield {
Expand Down Expand Up @@ -107,7 +107,7 @@ function sanitiseMarkdown(text: string): string {
return text;
}

function engJoin(things): string {
function engJoin(things: string[]): string {
if (things.length === 1) return things[0];

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

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

const lines = [];
const lines: string[] = [];

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

const shouldInclude = changes.filter(c => c.shouldInclude);
const breaking = shouldInclude.filter(c => c.breaking);
Expand Down Expand Up @@ -218,6 +220,10 @@ function isPrereleaseFor(version: SemVer, forVersion: SemVer): boolean {
);
}

export async function previewChangelog(project: Project, changes: IChange[]) {
console.log(makeChangelogEntry(changes, null, project));
}

export async function updateChangelog(project: Project, changes: IChange[], forVersion: string) {
const forReleaseSemVer = semver.parse(forVersion);

Expand All @@ -233,6 +239,15 @@ export async function updateChangelog(project: Project, changes: IChange[], forV
// This is the exact version we should be updating: replace it
await outHandle.write(makeChangelogEntry(changes, forVersion, project));
changeWritten = true;
} else if (isPrereleaseFor(semver.parse(entry.version), forReleaseSemVer)) {
log.debug(`Found ${entry.version} which is a prerelease of the version we should be updating`);
// This is a prerelease of the version we're trying to write, so remove the
// prerelease entry from the changelog and replace it with the entry we're
// writing, if we haven't already written it
if (!changeWritten) {
await outHandle.write(makeChangelogEntry(changes, forVersion, project));
changeWritten = true;
}
} else if (forReleaseSemVer.compare(entry.version) === 1) {
// This one comes before the one we're updating, so if we haven't yet written
// our changeset, we need to do it now.
Expand All @@ -243,14 +258,6 @@ export async function updateChangelog(project: Project, changes: IChange[], forV
}
// and then write the one we found too
await outHandle.write(entry.text);
} else if (isPrereleaseFor(semver.parse(entry.version), forReleaseSemVer)) {
log.debug(`Found ${entry.version} which is a prerelease of the version we should be updating`);
// This is a prerelease of the version we're trying to write, so remove the
// prerelease entry from the changelog and replace it with the entry we're
// writing, if we haven't already written it
if (!changeWritten) {
await outHandle.write(makeChangelogEntry(changes, forVersion, project));
}
} else {
log.debug(`Found ${entry.version} which is newer than the version we should be updating`);
await outHandle.write(entry.text);
Expand Down
76 changes: 51 additions & 25 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ limitations under the License.
*/

import log from 'loglevel';
import yargs from 'yargs/yargs';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import clc from 'cli-color';
import semver from 'semver';
Expand All @@ -31,7 +31,7 @@ import {
import { getLatestRelease, getReleaseBefore, getReleases, releasesContains } from "./releases";
import { ChangesByProject, getPackageJsonAtVersion, Project, branchExists, BranchMode } from './projects';
import { formatIssue } from './issue';
import { updateChangelog } from './changelog';
import { previewChangelog, updateChangelog } from './changelog';
import { Octokit } from '@octokit/rest';

function formatChangeType(changeType: ChangeType) {
Expand Down Expand Up @@ -74,21 +74,35 @@ function printChangeStatus(change: IChange, projectName: string, owner: string,
}

async function main() {
const args = yargs(hideBin(process.argv)).option('debug', {
alias: 'd',
type: 'boolean',
description: "Enable debug mode",
}).option('check', {
type: 'boolean',
description: "Don't update changelog, just output information on what changes would be included",
}).help().usage("Usage: $0 [-d] [--check] <version>").argv;

if (args._.length !== 1 && !args.check) {
const args = yargs(hideBin(process.argv)).version(false).options({
"debug": {
alias: 'd',
type: 'boolean',
description: "Enable debug mode",
},
"check": {
type: 'boolean',
description: "Don't update changelog, just output information on what changes would be included",
conflicts: ["preview"],
},
"preview": {
type: "boolean",
description: "Generate changelog as normal, but without version header and output to STDOUT.",
conflicts: ["check"],
},
}).command("* [version]", "Generate changelog for the given version", yargs => (
yargs.positional("version", {
description: "The version to generate the changelog for, " +
"required if --check and/or --preview are not specified.",
type: "string",
})
)).help().parseSync();

if (!args.version && !args.check && !args.preview) {
// Surely yargs should be able to do this? It seems incredibly confusing and I already regret using it
console.log("No version specified");
return;
}
const targetRelease = args._[0] as string;

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

if (targetRelease) {
const targetReleaseSemVer = semver.parse(targetRelease);
if (args.version) {
const targetReleaseSemVer = semver.parse(args.version);
const targetIsPrerelease = targetReleaseSemVer.prerelease.length > 0;
const toVerReleaseBranch =
`release-v${targetReleaseSemVer.major}.${targetReleaseSemVer.minor}.${targetReleaseSemVer.patch}`;
if (releasesContains(rels, targetRelease)) {
log.debug("Found existing release for " + targetRelease);
if (releasesContains(rels, args.version)) {
log.debug("Found existing release for " + args.version);
// nb. getReleases only gets the most recent 100 so this won't work
// for older releases
fromVer = getReleaseBefore(rels, targetRelease, targetIsPrerelease).name;
toVer = targetRelease;
} else if (targetRelease !== 'develop' && await branchExists(dir, toVerReleaseBranch)) {
log.debug("Found release branch for " + targetRelease);
fromVer = getReleaseBefore(rels, args.version, targetIsPrerelease).name;
toVer = args.version;
} else if (args.version !== 'develop' && await branchExists(dir, toVerReleaseBranch)) {
log.debug("Found release branch for " + args.version);
// 'to' release has had a release branch cut but not yet a full release
// compare to the tip of the release branch
fromVer = getLatestRelease(rels, targetIsPrerelease).name;
toVer = toVerReleaseBranch;
branchMode = BranchMode.Release;
} else if (args.version !== 'develop' && await branchExists(dir, "staging")) {
log.debug("Found release branch for " + args.version);
// 'to' release has had a release branch cut but not yet a full release
// compare to the tip of the release branch
fromVer = getLatestRelease(rels, targetIsPrerelease).name;
toVer = "staging";
branchMode = BranchMode.Release;
} else {
log.debug("Found neither release nor branch for " + targetRelease);
log.debug("Found neither release nor branch for " + args.version);
// the 'to' release is an doesn't-yet-exist future release -
// compare to the tip of develop (a better piece of software
// might make this configurable...)
Expand Down Expand Up @@ -177,7 +198,7 @@ async function main() {

const numBreaking = allChanges.filter(c => c.breaking).length;
const numFeatures = allChanges.filter(c => c.changeType == ChangeType.FEATURE).length;
let suggestedBumpType;
let suggestedBumpType: "major" | "minor" | "patch";
if (numBreaking) {
suggestedBumpType = 'major';
} else if (numFeatures) {
Expand All @@ -193,8 +214,13 @@ async function main() {
return;
}

log.debug("Updating changelog entry for " + targetRelease);
await updateChangelog(project, allChanges, targetRelease);
if (args.preview) {
await previewChangelog(project, allChanges);
return;
}

log.debug("Updating changelog entry for " + args.version);
await updateChangelog(project, allChanges, args.version);
}

main();
50 changes: 44 additions & 6 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,11 @@
dependencies:
"@babel/types" "^7.3.0"

"@types/cli-color@^2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@types/cli-color/-/cli-color-2.0.2.tgz#01bd593722a12c26ec84c170ab251fe2d35856c5"
integrity sha512-1ErQIcmNHtNViGKTtB/TIKqMkC2RkKI2nBneCr9hSCPo9H05g9VzjlaXPW3H0vaI8zFGjJZvSav+VKDKCtKgKA==

"@types/graceful-fs@^4.1.2":
version "4.1.5"
resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15"
Expand Down Expand Up @@ -855,6 +860,13 @@
dependencies:
"@types/yargs-parser" "*"

"@types/yargs@^17.0.13":
version "17.0.13"
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.13.tgz#34cced675ca1b1d51fcf4d34c3c6f0fa142a5c76"
integrity sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==
dependencies:
"@types/yargs-parser" "*"

"@typescript-eslint/eslint-plugin@^4.28.5":
version "4.28.5"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.5.tgz#8197f1473e7da8218c6a37ff308d695707835684"
Expand Down Expand Up @@ -1006,6 +1018,11 @@ ansi-regex@^5.0.0:
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==

ansi-regex@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==

ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
Expand Down Expand Up @@ -3171,13 +3188,29 @@ string-width@^4.1.0, string-width@^4.2.0:
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"

string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"

strip-ansi@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532"
integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==
dependencies:
ansi-regex "^5.0.0"

strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"

strip-bom@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878"
Expand Down Expand Up @@ -3556,6 +3589,11 @@ [email protected], yargs-parser@^20.2.2:
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==

yargs-parser@^21.0.0:
version "21.1.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==

yargs@^16.0.3:
version "16.2.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
Expand All @@ -3569,18 +3607,18 @@ yargs@^16.0.3:
y18n "^5.0.5"
yargs-parser "^20.2.2"

yargs@^17.0.1:
version "17.0.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.0.1.tgz#6a1ced4ed5ee0b388010ba9fd67af83b9362e0bb"
integrity sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ==
yargs@^17.5.1:
version "17.5.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.5.1.tgz#e109900cab6fcb7fd44b1d8249166feb0b36e58e"
integrity sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==
dependencies:
cliui "^7.0.2"
escalade "^3.1.1"
get-caller-file "^2.0.5"
require-directory "^2.1.1"
string-width "^4.2.0"
string-width "^4.2.3"
y18n "^5.0.5"
yargs-parser "^20.2.2"
yargs-parser "^21.0.0"

[email protected]:
version "3.1.1"
Expand Down

0 comments on commit aadaf39

Please sign in to comment.