Skip to content

Commit 4246b9a

Browse files
committed
feat: add changelog and gh release support
1 parent c3cb943 commit 4246b9a

File tree

8 files changed

+321
-30
lines changed

8 files changed

+321
-30
lines changed

action.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ inputs:
55
github-token:
66
required: true
77
description: GitHub personal access token
8+
outputs:
9+
next-version:
10+
description: Next version to be released
11+
release-type:
12+
description: Type of release
813
runs:
914
using: 'node16'
1015
main: 'dist/index.js'

dist/index.js

Lines changed: 157 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,25 @@
11
{
2-
"name": "typescript-action",
3-
"version": "0.0.0",
2+
"name": "publish-semver-release-action",
3+
"version": "0.3.0",
44
"private": true,
5-
"description": "TypeScript template action",
5+
"description": "🚀 A GitHub action to publish a new release of the repository",
66
"main": "lib/main.js",
77
"scripts": {
88
"build": "tsc",
99
"format": "prettier --write '**/*.ts'",
1010
"format-check": "prettier --check '**/*.ts'",
1111
"lint": "eslint src/**/*.ts",
1212
"package": "ncc build --source-map --license licenses.txt",
13+
"publish": "npm run build && npm run package",
1314
"test": "vitest",
1415
"all": "npm run build && npm run format && npm run lint && npm run package && npm test"
1516
},
1617
"repository": {
1718
"type": "git",
18-
"url": "git+https://github.com/actions/typescript-action.git"
19+
"url": "git+https://github.com/clicampo/action-publish-semver-release.git"
1920
},
2021
"keywords": [
21-
"actions",
22-
"node",
23-
"setup"
22+
"actions"
2423
],
2524
"author": "",
2625
"license": "MIT",

src/changelog.ts

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,19 @@ import { getInput } from '@actions/core'
22
import { getExecOutput } from '@actions/exec'
33
import { getOctokit } from '@actions/github'
44
import type { Context } from '@actions/github/lib/context'
5+
import type { ReleaseType } from './version'
6+
import { getReleaseTypeFromCommitMessage } from './version'
7+
8+
type CommitsByReleaseType = Record<ReleaseType, { message: string; url: string; author: string }[]>
9+
type CommitList = Awaited<
10+
ReturnType<
11+
ReturnType<typeof getOctokit>['rest']['repos']['listCommits']
12+
>
13+
>['data']
514

615
const run = async(command: string) => (await getExecOutput(command)).stdout
716

8-
export const generateChangelog = async(context: Context) => {
17+
const getLastCommits = async(context: Context) => {
918
const githubToken = getInput('github-token') || process.env.GH_TOKEN
1019
if (githubToken === '' || githubToken === undefined)
1120
throw new Error('GitHub token is required')
@@ -21,13 +30,73 @@ export const generateChangelog = async(context: Context) => {
2130
repo: context.repo.repo,
2231
})
2332

24-
// Get all commits since last tag
2533
const lastCommits = []
2634
for (const commit of commits) {
2735
if (commit.sha === lastTaggedCommitSha)
2836
break
2937
lastCommits.push(commit)
3038
}
3139

32-
return lastCommits.map(commit => `- ${commit.commit.message}`).join('\n')
40+
return lastCommits
41+
}
42+
43+
const groupCommitsByReleaseType = (commits: CommitList) => {
44+
return commits
45+
.map((commit) => {
46+
const { message, url, author } = commit.commit
47+
const type = getReleaseTypeFromCommitMessage(message)
48+
return { message, type, url, author: String(author?.name) }
49+
})
50+
.reduce((commitsByType, commit) => {
51+
if (commit.type === null)
52+
return commitsByType
53+
if (!(commit.type in commitsByType))
54+
commitsByType[commit.type] = []
55+
commitsByType[commit.type].push(commit)
56+
return commitsByType
57+
}, {} as (CommitsByReleaseType))
58+
}
59+
60+
const formatCommitsByType = (commitsByType: CommitsByReleaseType) => {
61+
let changelog = ''
62+
const getCommitInfo = (commit: { message: string; url: string; author: string }) => {
63+
const message = commit.message.split(':')[1].trim()
64+
const scope = commit.message.match(/^(.*?): /)?.[1] ?? ''
65+
const commitSha = commit.url.split('/').pop()?.slice(0, 8)
66+
return { message, scope, commitSha }
67+
}
68+
if (commitsByType.major) {
69+
changelog += `${[
70+
'## ⚠️ This release introduces breaking changes',
71+
'### Features',
72+
].join('\n')}\n`
73+
}
74+
if (commitsByType.minor) {
75+
if (!commitsByType.major)
76+
changelog += '### Features\n'
77+
78+
const featureCommits = [
79+
...(commitsByType.major || []),
80+
...(commitsByType.minor || []),
81+
]
82+
for (const commit of featureCommits) {
83+
const { message, scope, commitSha } = getCommitInfo(commit)
84+
changelog += `- **(${scope})** ${message} ([${commitSha}](${commit.url}))\n`
85+
}
86+
}
87+
if (commitsByType.patch) {
88+
changelog += '### Bug Fixes\n'
89+
for (const commit of commitsByType.patch) {
90+
const { message, scope, commitSha } = getCommitInfo(commit)
91+
changelog += `- **(${scope})** ${message} ([${commitSha}](${commit.url}))\n`
92+
}
93+
}
94+
95+
return changelog
96+
}
97+
98+
export const generateChangelog = async(context: Context) => {
99+
const lastCommits = await getLastCommits(context)
100+
const commitsByType = groupCommitsByReleaseType(lastCommits)
101+
return formatCommitsByType(commitsByType)
33102
}

0 commit comments

Comments
 (0)