Generate and Publish Release Notes JSON #167
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
name: Generate and Publish Release Notes JSON | |
on: | |
workflow_dispatch: | |
release: | |
types: | |
- released # A release was published, or a pre-release was changed to a release. | |
jobs: | |
generate: | |
permissions: | |
contents: read # to fetch code (actions/checkout) and to read releases (actions/github-script) | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
ref: dev | |
- name: Setup pnpm | |
uses: pnpm/action-setup@v3 | |
- uses: actions/setup-node@v4 | |
with: | |
node-version: 20 | |
cache: 'pnpm' | |
- name: Install dependencies | |
run: pnpm i | |
- name: Generate Release Notes | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const { mkdir, writeFile } = require('node:fs/promises'); | |
const { join } = require('node:path'); | |
const { default: markdown } = await import('${{ github.workspace }}/docs/build/release-notes/md.mjs'); | |
/** | |
* `/[\w/\-@]+/` is the name of the package. (e.g. @quasar/app-vite) | |
* `v` is the literal letter v between the package name and the version. | |
* `/[\d.\-\w]+/` is the version of the package after `v`. (e.g. 1.0.0, 1.0.0-beta.1) | |
*/ | |
const versionPattern = /([\w/\-@]+)[- ]v([\d.\-\w]+)/; | |
/** | |
* GitHub issue reference (e.g. #123) | |
*/ | |
const gitHubReferencePattern = /#(\d+)/g; | |
const gitHubReferenceToMarkdownLink = (content) => | |
content.replace( | |
gitHubReferencePattern, | |
`[$1](https://github.com/${context.repo.owner}/${context.repo.repo}/issues/$1)` | |
); | |
const groups = { | |
v2: { | |
versionPatterns: { | |
quasar: /^2./, | |
'@quasar/app-webpack': /^(3|4)./, | |
'@quasar/app-vite': /^(1|2)./, | |
}, | |
packages: { | |
quasar: [], | |
'@quasar/app-vite': [], | |
'@quasar/app-webpack': [], | |
'@quasar/cli': [], | |
'@quasar/extras': [], | |
'@quasar/icongenie': [], | |
'@quasar/vite-plugin': [], | |
}, | |
}, | |
}; | |
const allPackageNames = new Set( | |
Object.values(groups).flatMap(({ packages }) => Object.keys(packages)) | |
); | |
const releases = await github.paginate( | |
github.rest.repos.listReleases, | |
{ | |
owner: context.repo.owner, | |
repo: context.repo.repo, | |
per_page: 100, | |
}, | |
({ data }) => | |
data | |
.map((release) => { | |
// Split by space and use the first part for cases like this: `@quasar/app-v1.0.4 - Security update` | |
const matchesList = release.name.split(' ')[0].match(versionPattern); | |
if (!matchesList || matchesList.length < 2) { | |
return; | |
} | |
let [, packageName, version] = matchesList; | |
if (!version) { | |
return; | |
} | |
if (packageName === '@quasar/app') { | |
packageName = '@quasar/app-webpack'; | |
} | |
if (!allPackageNames.has(packageName)) { | |
return; | |
} | |
return { | |
packageName, | |
version, | |
date: release.created_at, | |
body: release.body, | |
}; | |
}) | |
.filter((release) => release !== undefined) | |
); | |
for (const [group, { versionPatterns, packages }] of Object.entries(groups)) { | |
console.log('Started processing group', group); | |
const packageNameList = Object.keys(packages); | |
for (const { packageName, version, date, body } of releases) { | |
if (!packageNameList.includes(packageName)) { | |
continue; | |
} | |
if ( | |
versionPatterns[packageName] && | |
versionPatterns[packageName].test(version) === false | |
) { | |
continue; | |
} | |
packages[packageName].push({ | |
version, | |
date, | |
body: markdown.render(gitHubReferenceToMarkdownLink(body)), | |
}); | |
} | |
console.log('Completed processing group', group); | |
} | |
// The data goes above GitHub Actions 1 MB output limit, so we write the results to files and use actions/upload-artifact and actions/download-artifact to transfer it | |
const directory = './docs/dist/release-notes'; | |
await mkdir(directory, { recursive: true }); | |
const writeJsonFile = (name, data) => | |
writeFile( | |
join(directory, `${name}.json`), | |
JSON.stringify(data), | |
'utf8' | |
); | |
await Promise.all( | |
Object.entries(groups).map(([groupName, { packages }]) => writeJsonFile(groupName, packages)) | |
); | |
- name: Upload release notes JSON files | |
uses: actions/upload-artifact@v4 | |
with: | |
name: release-notes | |
path: docs/dist/release-notes | |
# As we commit them in the `publish` job, we don't really need to keep them for long. | |
# We can't use 0, so we use the minimum possible, 1: https://github.com/actions/upload-artifact/issues/290 | |
retention-days: 1 | |
publish: | |
permissions: {} # No permissions needed for the active repo | |
runs-on: ubuntu-latest | |
needs: generate | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
repository: quasarframework/cdn | |
token: ${{ secrets.CDN_REPO_PAT }} | |
- name: Download generated release notes JSON files | |
uses: actions/download-artifact@v4 | |
with: | |
name: release-notes | |
path: release-notes | |
- uses: stefanzweifel/git-auto-commit-action@v5 | |
with: | |
commit_message: | | |
Generated release notes for Quasar packages | |
Triggered by: ${{ github.event.release.name || 'manual execution' }} | |
${{ github.event.release.html_url }} |