-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
build: refactor release-note-generator
- Loading branch information
1 parent
7c229e6
commit 23cf2e9
Showing
7 changed files
with
165 additions
and
91 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
GITHUB_TOKEN= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,3 +12,5 @@ mix-manifest.json | |
vendor | ||
.php_cs.cache | ||
.phpunit.result.cache | ||
|
||
.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import fetch from 'node-fetch' | ||
import semver from 'semver' | ||
|
||
let latestRelease = null; | ||
export async function getLatestRelease() { | ||
if (latestRelease) return latestRelease | ||
const releaseData = await fetch("https://api.github.com/repos/reed-jones/phase/releases/latest") | ||
return latestRelease = await releaseData.json() | ||
} | ||
|
||
let releases = [] | ||
export async function getReleases() { | ||
if (releases.length) return releases | ||
const releasesData = await fetch("https://api.github.com/repos/reed-jones/phase/releases") | ||
return releases = await releasesData.json(); | ||
} | ||
|
||
export async function createRelease(version, body) { | ||
const data = { | ||
tag_name: version, | ||
name: version, | ||
draft: false, | ||
prerelease: false, | ||
body | ||
} | ||
|
||
const response = await fetch('https://api.github.com/repos/reed-jones/phase/releases', { | ||
method: 'post', | ||
body: JSON.stringify(data), | ||
headers: { | ||
'Content-Type': 'application/json', | ||
Accept: 'application/vnd.github.v3+json', | ||
Authorization: `token ${process.env.GITHUB_TOKEN}` | ||
} | ||
}); | ||
return await response.json(); | ||
} | ||
|
||
let tags = [] | ||
export async function getTags() { | ||
if (tags.length) return tags | ||
const tagsData = await fetch("https://api.github.com/repos/reed-jones/phase/tags") | ||
const rawTags = await tagsData.json() | ||
return tags = rawTags.sort((a,b) => semver.compare(b.name, a.name)) | ||
} | ||
|
||
export async function getLatestTag() { | ||
if (tags.length) return tags[0] | ||
await getTags() | ||
return tags[0] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,99 +1,56 @@ | ||
import { join } from "path"; | ||
import { existsSync } from "fs"; | ||
import { execSync } from "child_process"; | ||
import fetch from "node-fetch"; | ||
import semver from "semver"; | ||
|
||
process.chdir(join(__dirname, "..")); | ||
|
||
const parseChangelog = (line) => { | ||
const regex = /^\[(?<hash>.{7,7})\] (?<type>[\w\s\d]*)(\((?<scope>.*)\))?: (?<change>.*) \((?<author>.*)\)$/gm; | ||
let m; | ||
|
||
while ((m = regex.exec(line)) !== null) { | ||
// This is necessary to avoid infinite loops with zero-width matches | ||
if (m.index === regex.lastIndex) { | ||
regex.lastIndex++; | ||
} | ||
return m.groups; | ||
} | ||
}; | ||
import { getLatestRelease, getLatestTag, createRelease } from "./github-api"; | ||
import { | ||
git, | ||
parseChangelogLine, | ||
acceptableLines, | ||
formatLine, | ||
formatDiff, | ||
} from "./utils"; | ||
|
||
async function generateChangelog() { | ||
const sections = []; | ||
|
||
const releaseData = await fetch( | ||
"https://api.github.com/repos/reed-jones/phase/releases/latest" | ||
); | ||
const { tag_name } = await releaseData.json(); | ||
|
||
const tagData = await fetch( | ||
"https://api.github.com/repos/reed-jones/phase/tags" | ||
); | ||
const tags = await tagData.json(); | ||
tags.sort((a, b) => semver.compare(b.name, a.name)); | ||
const { tag_name } = await getLatestRelease(); | ||
const tag = await getLatestTag(); | ||
|
||
const log = | ||
execSync(`git log --pretty=format:"[%h] %s (%an <%ae>)" ${tag_name}...HEAD`) | ||
.toString() | ||
.trim() | ||
.split("\n") | ||
.map(parseChangelog) | ||
.filter( | ||
(line) => | ||
line && | ||
!line.change.startsWith("wip") && | ||
(!line.scope || !line.scope.includes("release")) | ||
) | ||
.sort((a, b) => a.type.localeCompare(b.type)) | ||
.map( | ||
(line) => | ||
`[${line.hash}]: **${line.type}${ | ||
line.scope ? `(**_${line.scope}_**)` : "" | ||
}**: ${line.change} - (${line.author})` | ||
) | ||
git(`log --pretty=format:"[%h] %s (%an <%ae>)" ${tag_name}...HEAD`) | ||
.map(parseChangelogLine) | ||
.filter(acceptableLines) | ||
.map(formatLine) | ||
.join("\n") || "No notable changes tracked"; | ||
|
||
sections.push( | ||
`## Notable changes since the last stable release (${tag_name}):` | ||
); | ||
sections.push(`\n${log}\n`); | ||
|
||
const pkgs = Array.from( | ||
new Set( | ||
execSync( | ||
`git diff --name-only ${tag_name}...HEAD packages ':(exclude)*/package.json' ':(exclude)*/composer.json' ':(exclude)*/__tests__/*'` | ||
const pkgs = [ | ||
...new Set( | ||
git( | ||
[ | ||
`diff --name-only ${tag_name}...HEAD packages`, | ||
`':(exclude)*/package.json'`, | ||
`':(exclude)*/composer.json'`, | ||
`':(exclude)*/__tests__/*'`, | ||
].join(" ") | ||
) | ||
.toString() | ||
.trim() | ||
.split("\n") | ||
.map((line) => { | ||
const [_, _base, _package] = line.split('/') | ||
const file = `./packages/${_base}/${_package}/package.json`; | ||
const isForNpm = existsSync(file); | ||
const version = isForNpm | ||
? require(join("..", file)).version | ||
: tags[0].name.replace(/^v/, ""); | ||
|
||
const pkg = { | ||
base: _base, | ||
package: _package, | ||
version, | ||
manager: isForNpm ? "npm" : "composer", | ||
}; | ||
|
||
return `[${`_${pkg.manager}_`.padEnd(10, " ")}] **${pkg.base}/${ | ||
pkg.package | ||
}**@_${pkg.version}_`.toLowerCase() | ||
}) | ||
.filter((s) => Boolean(s)) | ||
) | ||
); | ||
|
||
sections.push("## Packages updated in this release"); | ||
sections.push(`\n${pkgs.join("\n")}\n`); | ||
|
||
console.log(sections.join("\n")); | ||
.map(formatDiff(tag)) | ||
.filter(Boolean) | ||
), | ||
]; | ||
|
||
const date = new Date | ||
const formattedDate = `${date.getFullYear()}-${`${date.getMonth() + 1}`.padStart(2, '0')}-${`${date.getDate()}`.padStart(2, '0')}`; | ||
|
||
const body = [ | ||
`## [${tag.name}](https://github.com/reed-jones/phase/compare/${tag_name}...${tag.name}) - ${formattedDate}`, | ||
'\n', | ||
`## Notable commits since the last release (${tag_name}...${tag.name}):`, | ||
`\n${log}`, | ||
`\n`, | ||
"## Packages updated in this release", | ||
`\n${pkgs.join("\n")}\n`, | ||
].join("\n"); | ||
|
||
return createRelease(tag.name, body) | ||
// return body | ||
} | ||
|
||
generateChangelog().catch(console.error); | ||
generateChangelog() | ||
.then(console.log) | ||
.catch(console.error); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,65 @@ | ||
import { execSync } from "child_process"; | ||
import { existsSync } from "fs"; | ||
import { join } from 'path' | ||
|
||
export const packages = [ | ||
"packages/@phased/types", | ||
"packages/@phased/state", | ||
"packages/@phased/routing", | ||
"packages/@phased/webpack-plugin", | ||
"packages/@phased/laravel-mix", | ||
"packages/@phased/phase" | ||
"packages/@phased/phase", | ||
]; | ||
|
||
export function git(cmd) { | ||
return execSync(`git ${cmd}`).toString().trim().split("\n"); | ||
} | ||
|
||
// release/changelog parseing | ||
export function parseChangelogLine(line) { | ||
const regex = /^\[(?<hash>.{7,7})\] (?<type>[\w\s\d]*)(\((?<scope>.*)\))?: (?<change>.*) \((?<author>.*)\)$/gm; | ||
let m; | ||
|
||
while ((m = regex.exec(line)) !== null) { | ||
// This is necessary to avoid infinite loops with zero-width matches | ||
if (m.index === regex.lastIndex) { | ||
regex.lastIndex++; | ||
} | ||
return m.groups; | ||
} | ||
} | ||
|
||
export function acceptableLines(line) { | ||
if (!line) return false; | ||
if (line.change.startsWith("wip")) return false; | ||
if (!line.scope || !line.scope.includes("release")) return true; | ||
return false; | ||
} | ||
|
||
export function formatLine(line) { | ||
return `[${line.hash}]: **${line.type}${ | ||
line.scope ? `(**_${line.scope}_**)` : "" | ||
}**: ${line.change} - (${line.author})`; | ||
} | ||
|
||
export function formatDiff(tag) { | ||
return function(line) { | ||
const [_, _base, _package] = line.split("/") | ||
const file = `./packages/${_base}/${_package}/package.json` | ||
const isForNpm = existsSync(file) | ||
const version = isForNpm | ||
? require(join("..", file)).version | ||
: tag.name.replace(/^v/, "") | ||
|
||
const pkg = { | ||
base: _base, | ||
package: _package, | ||
version, | ||
manager: isForNpm ? "npm" : "composer", | ||
} | ||
|
||
return `[${`_${pkg.manager}_`.padEnd(10, " ")}] **${pkg.base}/${ | ||
pkg.package | ||
}**@_${pkg.version}_`.toLowerCase(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4772,6 +4772,11 @@ dotenv@^6.2.0: | |
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064" | ||
integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w== | ||
|
||
dotenv@^8.2.0: | ||
version "8.2.0" | ||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" | ||
integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== | ||
|
||
[email protected], duplexer@^0.1.1: | ||
version "0.1.1" | ||
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" | ||
|