-
Notifications
You must be signed in to change notification settings - Fork 142
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Generate github release notes from changelog + tags (#553)
* add release script * add test for scripts * make changelogPath work with irregular changelog names * add title * return array of tags * fix comment
- Loading branch information
Showing
14 changed files
with
443 additions
and
21 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
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 |
---|---|---|
|
@@ -13,10 +13,17 @@ jobs: | |
steps: | ||
- name: Checkout Repo | ||
uses: actions/checkout@v3 | ||
- name: Setup Node.js 12.x | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: 12.x | ||
cache: "yarn" | ||
- name: Install Dependencies | ||
run: HUSKY=0 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 yarn install --immutable | ||
- name: Create Github Release From Tags | ||
env: | ||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
run: | | ||
git config --global user.name "Segment Github" | ||
git config --global user.email "[email protected]" | ||
bash scripts/create-release-from-tags.sh | ||
yarn ts-node-script --files scripts/create-release-from-tags/run.ts |
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 was deleted.
Oops, something went wrong.
11 changes: 11 additions & 0 deletions
11
scripts/create-release-from-tags/__tests__/fixtures/first-release-example.md
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,11 @@ | ||
# @segment/analytics-core | ||
|
||
## 1.99.0 | ||
|
||
### Minor Changes | ||
|
||
* [#606](https://github.com/segmentio/analytics-next/pull/606) [\`b9c6356\`](https://github.com/segmentio/analytics-next/commit/b9c6356b7d35ee8acb6ecbd1eebc468d18d63958) Thanks [@silesky] - foo!) | ||
|
||
### Patch Changes | ||
|
||
* [#404](https://github.com/segmentio/analytics-next/pull/404) [\`b9abc6\`](https://github.com/segmentio/analytics-next/commit/b9c6356b7d35ee8acb6ecbd1eebc468d18d63958) Thanks [@silesky] - bar!) |
17 changes: 17 additions & 0 deletions
17
scripts/create-release-from-tags/__tests__/fixtures/reg-example.md
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,17 @@ | ||
# @segment/analytics-core | ||
|
||
## 1.99.0 | ||
|
||
### Minor Changes | ||
|
||
* [#606](https://github.com/segmentio/analytics-next/pull/606) [\`b9c6356\`](https://github.com/segmentio/analytics-next/commit/b9c6356b7d35ee8acb6ecbd1eebc468d18d63958) Thanks [@silesky] - foo!) | ||
|
||
### Patch Changes | ||
|
||
* [#404](https://github.com/segmentio/analytics-next/pull/404) [\`b9abc6\`](https://github.com/segmentio/analytics-next/commit/b9c6356b7d35ee8acb6ecbd1eebc468d18d63958) Thanks [@silesky] - bar!) | ||
|
||
## 1.39.2 | ||
|
||
### Patch Changes | ||
|
||
* [#513](https://github.com/segmentio/analytics-next/pull/513) [\`1d36ca1\`](https://github.com/segmentio/analytics-next/commit/1d36ca1440fc5df9171d16278d8918b3e5a32128) Thanks [@silesky](https://github.com/silesky)! - test |
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,40 @@ | ||
import { parseReleaseNotes } from '..' | ||
import fs from 'fs' | ||
import path from 'path' | ||
|
||
const readFixture = (filename: string) => { | ||
return fs.readFileSync(path.join(__dirname, 'fixtures', filename), { | ||
encoding: 'utf8', | ||
}) | ||
} | ||
|
||
describe('parseReleaseNotes', () => { | ||
test('should work with reg example', () => { | ||
const fixture = readFixture('reg-example.md') | ||
expect(parseReleaseNotes(fixture, '1.99.0')).toMatchInlineSnapshot(` | ||
" | ||
### Minor Changes | ||
* [#606](https://github.com/segmentio/analytics-next/pull/606) [\\\\\`b9c6356\\\\\`](https://github.com/segmentio/analytics-next/commit/b9c6356b7d35ee8acb6ecbd1eebc468d18d63958) Thanks [@silesky] - foo!) | ||
### Patch Changes | ||
* [#404](https://github.com/segmentio/analytics-next/pull/404) [\\\\\`b9abc6\\\\\`](https://github.com/segmentio/analytics-next/commit/b9c6356b7d35ee8acb6ecbd1eebc468d18d63958) Thanks [@silesky] - bar!) | ||
" | ||
`) | ||
}) | ||
|
||
test('should work if first release', () => { | ||
const fixture = readFixture('first-release-example.md') | ||
expect(parseReleaseNotes(fixture, '1.99.0')).toMatchInlineSnapshot(` | ||
" | ||
### Minor Changes | ||
* [#606](https://github.com/segmentio/analytics-next/pull/606) [\\\\\`b9c6356\\\\\`](https://github.com/segmentio/analytics-next/commit/b9c6356b7d35ee8acb6ecbd1eebc468d18d63958) Thanks [@silesky] - foo!) | ||
### Patch Changes | ||
* [#404](https://github.com/segmentio/analytics-next/pull/404) [\\\\\`b9abc6\\\\\`](https://github.com/segmentio/analytics-next/commit/b9c6356b7d35ee8acb6ecbd1eebc468d18d63958) Thanks [@silesky] - bar!)" | ||
`) | ||
}) | ||
}) |
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,192 @@ | ||
import spawn from '@npmcli/promise-spawn' | ||
import getPackages from 'get-monorepo-packages' | ||
import path from 'path' | ||
import fs from 'fs' | ||
import { exists } from '../utils/exists' | ||
|
||
export type Config = { | ||
isDryRun: boolean | ||
tags: Tag[] | ||
} | ||
|
||
export type Tag = { | ||
name: string | ||
versionNumber: string | ||
raw: string | ||
} | ||
|
||
/** | ||
* | ||
* @returns list of tags | ||
* @example ["@segment/[email protected]", "@segment/[email protected]"] | ||
*/ | ||
export const getCurrentGitTags = async (): Promise<Tag[]> => { | ||
const { stdout, stderr, code } = await spawn('git', [ | ||
'tag', | ||
'--points-at', | ||
'HEAD', | ||
'--column', | ||
]) | ||
if (code !== 0) { | ||
throw new Error(stderr.toString()) | ||
} | ||
|
||
return parseRawTags(stdout.toString()) | ||
} | ||
|
||
export const getConfig = async ({ | ||
DRY_RUN, | ||
TAGS, | ||
}: NodeJS.ProcessEnv): Promise<Config> => { | ||
const isDryRun = Boolean(DRY_RUN) | ||
const tags = TAGS ? parseRawTags(TAGS) : await getCurrentGitTags() | ||
|
||
if (!tags.length) { | ||
throw new Error('No git tags found.') | ||
} | ||
return { | ||
isDryRun, | ||
tags, | ||
} | ||
} | ||
|
||
const getChangelogPath = (packageName: string): string | undefined => { | ||
const result = getPackages('.').find((p) => | ||
p.package.name.includes(packageName) | ||
) | ||
if (!result) | ||
throw new Error(`could not find package with name: ${packageName}.`) | ||
|
||
let changelogPath = undefined | ||
for (const fileName of ['CHANGELOG.MD', 'CHANGELOG.md']) { | ||
if (changelogPath) break | ||
const myPath = path.join(result.location, fileName) | ||
const pathExists = fs.existsSync(myPath) | ||
if (pathExists) { | ||
changelogPath = myPath | ||
} | ||
} | ||
|
||
if (changelogPath) { | ||
return changelogPath | ||
} else { | ||
console.log(`could not find changelog path for ${result.location}`) | ||
} | ||
} | ||
|
||
/** | ||
* | ||
* @returns list of tags | ||
* @example ["@segment/[email protected]", "@segment/[email protected]"] | ||
*/ | ||
const createGithubRelease = async ( | ||
tag: string, | ||
releaseNotes?: string | ||
): Promise<void> => { | ||
const { stderr, code } = await spawn('gh', [ | ||
'release', | ||
'create', | ||
tag, | ||
'--title', | ||
tag, | ||
'--notes', | ||
releaseNotes || '', | ||
]) | ||
if (code !== 0) { | ||
throw new Error(stderr.toString()) | ||
} | ||
} | ||
|
||
/** | ||
* | ||
* @param rawTag - ex. "@segment/[email protected]" | ||
*/ | ||
const extractPartsFromTag = (rawTag: string): Tag | undefined => { | ||
const [name, version] = rawTag.split(/@(\d.*)/) | ||
if (!name || !version) return undefined | ||
return { | ||
name, | ||
versionNumber: version?.replace('\n', '') as string, | ||
raw: rawTag, | ||
} | ||
} | ||
|
||
/** | ||
* | ||
* @param rawTags - string delimited list of tags (e.g. `@segment/[email protected] @segment/[email protected]`) | ||
*/ | ||
export const parseRawTags = (rawTags: string): Tag[] => { | ||
return rawTags.trim().split(' ').map(extractPartsFromTag).filter(exists) | ||
} | ||
|
||
/** | ||
* | ||
* @returns the release notes that correspond to a given tag. | ||
*/ | ||
export const parseReleaseNotes = ( | ||
changelogText: string, | ||
versionNumber: string | ||
): string => { | ||
const h2tag = /(##\s.*\d.*)/gi | ||
let begin: number | ||
let end: number | ||
|
||
changelogText.split('\n').forEach((line, idx) => { | ||
if (begin && end) return | ||
if (line.includes(versionNumber)) { | ||
begin = idx + 1 | ||
} else if (begin && h2tag.test(line)) { | ||
end = idx - 1 | ||
} | ||
}) | ||
|
||
const result = changelogText.split('\n').filter((_, idx) => { | ||
return idx >= begin && idx <= (end ?? Infinity) | ||
}) | ||
return result.join('\n') | ||
} | ||
|
||
const getReleaseNotes = (tag: Tag): string | undefined => { | ||
const { name, versionNumber } = tag | ||
const changelogPath = getChangelogPath(name) | ||
if (!changelogPath) { | ||
console.log(`no changelog path for ${name}... skipping.`) | ||
return | ||
} | ||
const changelogText = fs.readFileSync(changelogPath, { encoding: 'utf8' }) | ||
const releaseNotes = parseReleaseNotes(changelogText, versionNumber) | ||
if (!releaseNotes) { | ||
console.log( | ||
`Could not find release notes for tags ${tag.raw} in ${changelogPath}.` | ||
) | ||
} | ||
return releaseNotes | ||
} | ||
|
||
const createGithubReleaseFromTag = async ( | ||
tag: Tag, | ||
{ dryRun = false } = {} | ||
): Promise<void> => { | ||
const notes = getReleaseNotes(tag) | ||
if (notes) { | ||
console.log( | ||
`\n ---> Outputting release titled: ${tag.raw} with notes: \n ${notes}` | ||
) | ||
} | ||
|
||
if (dryRun) { | ||
console.log(`--> Dry run: ${tag.raw} not released.`) | ||
return undefined | ||
} | ||
|
||
await createGithubRelease(tag.raw, notes) | ||
return undefined | ||
} | ||
|
||
export const createReleaseFromTags = async (config: Config) => { | ||
console.log('Processing tags:', config.tags, '\n') | ||
|
||
for (const tag of config.tags) { | ||
await createGithubReleaseFromTag(tag, { dryRun: config.isDryRun }) | ||
} | ||
} |
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,8 @@ | ||
import { createReleaseFromTags, getConfig } from '.' | ||
|
||
async function run() { | ||
const config = await getConfig(process.env) | ||
return createReleaseFromTags(config) | ||
} | ||
|
||
void run() |
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,10 @@ | ||
module.exports = { | ||
preset: 'ts-jest', | ||
testEnvironment: 'node', | ||
testMatch: ["**/?(*.)+(test).[jt]s?(x)"], | ||
globals: { | ||
'ts-jest': { | ||
isolatedModules: true, | ||
}, | ||
}, | ||
} |
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,7 @@ | ||
/** | ||
* This type guard can be passed into a function such as native filter | ||
* in order to remove nullish values from a list in a type-safe way. | ||
*/ | ||
export const exists = <T>(value: T): value is NonNullable<T> => { | ||
return value != null && value !== undefined | ||
} |
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,9 @@ | ||
declare module 'get-monorepo-packages' { | ||
export default function getPackages(pathToRoot: string): { | ||
location: string | ||
package: { | ||
name: string | ||
version: string | ||
} | ||
}[] | ||
} |
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,10 @@ | ||
declare module '@npmcli/promise-spawn' { | ||
import { EventEmitter } from 'events' | ||
import { SpawnOptions } from 'child_process' | ||
|
||
export default function spawn( | ||
cmd: string, | ||
args?: string[], | ||
opts?: SpawnOptions | ||
): Promise<{ stdout: Buffer; code: number; stderr: Buffer }> & EventEmitter | ||
} |
Oops, something went wrong.