Skip to content

Commit

Permalink
Merge pull request #803 from bemusic/cd-release
Browse files Browse the repository at this point in the history
Set up deployment pipeline for the new release workflow
  • Loading branch information
dtinth authored Dec 30, 2022
2 parents 8aaca49 + 2e417df commit 7336738
Show file tree
Hide file tree
Showing 9 changed files with 246 additions and 90 deletions.
74 changes: 52 additions & 22 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,38 +30,33 @@ jobs:
node build-scripts build
env:
SCOREBOARD_SERVER: ${{ secrets.SCOREBOARD_SERVER }}
- name: Compress build output
if: always()
run: tar -cvzf dist.tar.gz dist
- name: Checks
run: node build-scripts pre-deploy
- name: Deploy
if: false
- name: Release
if: github.event_name == 'push'
id: release
run: |
if git log --format=%B -n 1 | egrep '^:bookmark: v[0-9]+\.[0-9]+\.[0-9]+'
then
if [ -n "$GH_APP_CREDENTIALS_TOKEN" ]
then
GH_TOKEN="$(npx obtain-github-app-installation-access-token ci $GH_APP_CREDENTIALS_TOKEN)"
echo "::add-mask::$GH_TOKEN"
git remote add www https://akibot:[email protected]/bemusic/bemusic.github.io.git
git config --global user.email "[email protected]"
git config --global user.name "Aki running on GitHub Actions"
node build-scripts deploy
else
echo "GH_APP_CREDENTIALS_TOKEN is not set, skipped"
fi
else
echo 'Not a release commit, skipped!'
fi
node build-scripts release --confirm
env:
GH_APP_CREDENTIALS_TOKEN: ${{ secrets.GH_APP_CREDENTIALS_TOKEN }}
- name: Compress build output
if: always()
run: tar -cvzf dist.tar.gz dist
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload artifact
if: always()
uses: actions/upload-artifact@v3
with:
name: bemuse-build-${{ github.sha }}
path: dist.tar.gz
outputs:
released-tag: ${{ steps.release.outputs.tag }}
deploy:
uses: ./.github/workflows/deploy-production.yml
needs: build
secrets: inherit
if: needs.build.outputs.released-tag
with:
tag: ${{ needs.build.outputs.released-tag }}
e2e:
runs-on: ubuntu-latest
needs: build
Expand Down Expand Up @@ -157,3 +152,38 @@ jobs:
- uses: codecov/codecov-action@v2
with:
token: ${{ secrets.CODECOV_TOKEN }}
changelog:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
env:
AUTHOR: ${{ github.event.pull_request.user.login }}
PR: ${{ github.event.pull_request.number }}
PR_NEW_FILE_URL:
https://github.com/${{ github.event.pull_request.head.repo.full_name
}}/new/${{ github.event.pull_request.head.ref }}
steps:
- name: Generate changelog file
uses: actions/github-script@v6
with:
script: |
const path = `changelog/pr-${process.env.PR}.md`
const contents = [
'---',
'author: ' + process.env.AUTHOR,
'category: feature/internals/bugfix/improvement',
'type: major/minor/patch',
'pr: ' + process.env.PR,
'---',
'',
'(Write your changelog entry here)',
].join('\n')
const url = `${process.env.PR_NEW_FILE_URL}?filename=${encodeURIComponent(path)}&value=${encodeURIComponent(contents)}`
const summary = `[Click here to create a changelog entry.](${url})`
require('fs').appendFileSync(process.env.GITHUB_STEP_SUMMARY, summary)
console.log('To create a changelog entry, create a file at', path)
console.log('')
console.log('Example contents:')
console.log('')
console.log(contents)
console.log('')
console.log(url)
58 changes: 58 additions & 0 deletions .github/workflows/deploy-production.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Deploy to production

on:
workflow_dispatch:
inputs:
tag:
description: 'Git tag to deploy'
required: true
workflow_call:
inputs:
tag:
description: 'Git tag to deploy'
required: true
type: 'string'
secrets:
GH_APP_CREDENTIALS_TOKEN:
description: 'GitHub App credentials token'
required: true

env:
NODE_FLAGS: --max_old_space_size=4096
TAG: ${{ inputs.tag }}

jobs:
deploy:
environment:
name: Production
url: https://bemuse.ninja
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v3
- name: Set up project
uses: ./.github/actions/setup-project
- name: Download release
run: |
gh release download $TAG --pattern dist.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Extract build
run: tar -xvzf dist.tar.gz
- name: Checks
run: node build-scripts pre-deploy
- name: Deploy
run: |
if [ -n "$GH_APP_CREDENTIALS_TOKEN" ]
then
GH_TOKEN="$(npx obtain-github-app-installation-access-token ci $GH_APP_CREDENTIALS_TOKEN)"
echo "::add-mask::$GH_TOKEN"
git remote add www https://akibot:[email protected]/bemusic/bemusic.github.io.git
git config --global user.email "[email protected]"
git config --global user.name "Aki running on GitHub Actions"
node build-scripts deploy
else
echo "GH_APP_CREDENTIALS_TOKEN is not set, skipped"
fi
env:
GH_APP_CREDENTIALS_TOKEN: ${{ secrets.GH_APP_CREDENTIALS_TOKEN }}
3 changes: 2 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ pnpm-lock.yaml
yarn.lock
package-lock.json
shrinkwrap.json
**/*.md
**/*.md
changelog/*.md
132 changes: 71 additions & 61 deletions build-scripts/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ const glob = require('glob')
const matter = require('gray-matter')
const semverInc = require('semver/functions/inc')
const semverGt = require('semver/functions/gt')
const { updateChangelog } = require('./lib/changelog')
const tempWrite = require('temp-write')
const { updateChangelog, getReleaseChangelog } = require('./lib/changelog')

yargs
.demandCommand()
Expand Down Expand Up @@ -92,9 +93,7 @@ yargs
},
},
async (argv) => {
const currentVersion = JSON.parse(
fs.readFileSync('bemuse/package.json', 'utf8')
).version
const currentVersion = getCurrentVersion()
console.log(`Current version: ${currentVersion}`)
let targetVersion = semverInc(currentVersion, 'patch')

Expand Down Expand Up @@ -166,67 +165,57 @@ yargs
fs.unlinkSync(file)
}
}

const writeOutput = (key, value) => {
console.log(`Output: ${key}=${value}`)
if (process.env.GITHUB_OUTPUT) {
fs.appendFileSync(process.env.GITHUB_OUTPUT, `${key}=${value}\n`)
}
}
writeOutput('version', targetVersion)
}
)
.command('release', 'Release a new version of Bemuse', {}, async () => {
await run('git fetch')
await run('git checkout origin/master')
await run('git branch -d master || true')
await run('git checkout -b master')
await run('node build-scripts release:changelog')
const version = (
await exec('node build-scripts release:get-next-version')
).trim()
await exec(
`node build-scripts release:write-version --newVersion '${version}'`
)
await run(`git commit -a -m ':bookmark: v${version}' || true`)
await run(`git tag "v${version}"`)
await run(`git push --follow-tags --set-upstream origin master`)
})
.command(
'release:changelog',
'Remove prerelease version suffixes from CHANGELOG.md',
{},
async () => {
const data = fs.readFileSync('CHANGELOG.md', 'utf8')
const date = new Date().toJSON().split('T')[0]
fs.writeFileSync(
'CHANGELOG.md',
data.replace(/(## v[\d.]+?)(?:\.0)?(?:\.0)?-pre\.\d+/g, `$1 (${date})`)
)
}
)
.command(
'release:get-next-version',
'Prints the version of the upcoming release',
{},
async () => {
const { version } = JSON.parse(
fs.readFileSync('bemuse/package.json', 'utf8')
)
console.log(version.replace(/-.*/, ''))
}
)
.command(
'release:write-version',
'Sets the version of Bemuse project',
{ newVersion: { type: 'string', demand: true } },
async (args) => {
const contents = fs.readFileSync('bemuse/package.json', 'utf8')
const newContents = contents.replace(
/"version":\s*"([^"]+)"/,
`"version": "${args.newVersion}"`
)
fs.writeFileSync('bemuse/package.json', newContents, 'utf8')
'release',
'Create GitHub Release',
{
confirm: {
alias: 'f',
type: 'boolean',
default: false,
description: 'Creates the release',
},
},
async (argv) => {
const sha = await exec('git rev-parse HEAD')
const currentVersion = getCurrentVersion()
console.log(`Current version: ${currentVersion}`)

const gitTag = `v${currentVersion}`
const exitCode = await check(`gh release view ${gitTag}`)
if (exitCode === 0) {
console.log('Release already exists.')
return
}

const changelog = fs.readFileSync('CHANGELOG.md', 'utf8')
const releaseNotes =
getReleaseChangelog(changelog, currentVersion) ||
'(Release notes not found)'
const isPreRelease = currentVersion.includes('-')
const date = new Date().toISOString().split('T')[0]
const releaseName = `Bemuse v${currentVersion} (${date})`
const notesFile = tempWrite.sync(releaseNotes)
const releaseCommand = `gh release create ${gitTag} --title "${releaseName}" --notes-file ${notesFile} ${
isPreRelease ? '--prerelease' : ''
} --target "${sha}"`
if (argv.confirm) {
await run(releaseCommand)
} else {
console.log('Dry-run: %s', releaseCommand)
}

const uploadCommand = `gh release upload ${gitTag} dist.tar.gz`
if (argv.confirm) {
await run(uploadCommand)
} else {
console.log('Dry-run: %s', uploadCommand)
}

writeOutput('tag', gitTag)
}
)
.command(
Expand Down Expand Up @@ -255,6 +244,10 @@ yargs
)
.parse()

function getCurrentVersion() {
return JSON.parse(fs.readFileSync('bemuse/package.json', 'utf8')).version
}

async function run(shellCommand) {
console.error(`Running: "${shellCommand}"`)
await execa(shellCommand, {
Expand All @@ -263,10 +256,27 @@ async function run(shellCommand) {
})
}

async function check(shellCommand) {
console.error(`Running: "${shellCommand}"`)
const result = await execa(shellCommand, {
shell: true,
stdio: 'inherit',
reject: false,
})
return result.exitCode
}

async function exec(shellCommand) {
console.error(`Running: "${shellCommand}"`)
const result = await execa(shellCommand, {
shell: true,
})
return result.stdout
}

function writeOutput(key, value) {
console.log(`Output: ${key}=${value}`)
if (process.env.GITHUB_OUTPUT) {
fs.appendFileSync(process.env.GITHUB_OUTPUT, `${key}=${value}\n`)
}
}
24 changes: 23 additions & 1 deletion build-scripts/lib/changelog.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,26 @@ function updateChangelog(existingChangelog, entries, version = 'UNRELEASED') {
const prettierConfig = JSON.parse(require('fs').readFileSync('.prettierrc', 'utf8'))
return prettier.format(markdown, { ...prettierConfig, parser: 'markdown' })
}
exports.updateChangelog = updateChangelog
exports.updateChangelog = updateChangelog

/**
* Find the changelog for a specific release.
* @param {string} changelog
* @param {string} version
*/
function getReleaseChangelog(changelog, version) {
const lines = changelog.split('\n')
/** @type {string[] | undefined} */
let output
for (const line of lines) {
if (line.trim().startsWith('## ' + version)) {
output = []
} else if (output && line.trim().startsWith('## ')) {
break
} else if (output) {
output.push(line)
}
}
return output && output.join('\n')
}
exports.getReleaseChangelog = getReleaseChangelog
1 change: 1 addition & 0 deletions build-scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"merge-stream": "^2.0.0",
"prettier": "^2.7.1",
"semver": "^7.3.8",
"temp-write": "^4.0.0",
"vinyl-fs": "^3.0.3",
"yargs": "^15.3.1",
"zod": "^3.20.2"
Expand Down
8 changes: 8 additions & 0 deletions changelog/pr-803.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
author: dtinth
category: internals
type: minor
pr: 803
---

Revamped the deployment pipeline to streamline [the release process](https://bemuse.ninja/project/docs/workflows/).
Loading

0 comments on commit 7336738

Please sign in to comment.