Skip to content

Commit

Permalink
chore(ci): add release pipeline and documentation (#508)
Browse files Browse the repository at this point in the history
Co-authored-by: Radu-Cristian Popa <radu.popa@breakpointit.eu>
  • Loading branch information
sidvishnoi and raducristianpopa authored Aug 21, 2024
1 parent 4319d13 commit eda0d64
Show file tree
Hide file tree
Showing 8 changed files with 397 additions and 0 deletions.
50 changes: 50 additions & 0 deletions .github/actions/bump-manifest-version.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// @ts-check
/* eslint-disable @typescript-eslint/no-var-requires, no-console */
const fs = require('node:fs/promises')

/** @param {import('github-script').AsyncFunctionArguments} AsyncFunctionArguments */
module.exports = async ({ core }) => {
const manifestPath = './src/manifest.json'
const manifestFile = await fs.readFile(manifestPath, 'utf8')
const manifest = JSON.parse(manifestFile)
/**@type {string} */
const existingVersion = manifest.version

const bumpType = /** @type {BumpType} */ (process.env.INPUT_VERSION)
if (!bumpType) {
throw new Error('Missing bump type')
}

const version = bumpVersion(existingVersion, bumpType).join('.')

console.log({ existingVersion, bumpType, version })

manifest.version = version
await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2))
core.setOutput('version', version)
}

/**
* @typedef {'build' | 'patch' | 'minor'} BumpType
* @param {string} existingVersion
* @param {BumpType} type
* @return {[major: number, minor: number, patch: number, build: number]}
*/
function bumpVersion(existingVersion, type) {
const parts = existingVersion.split('.').map(Number)
if (parts.length !== 4 || parts.some((e) => !Number.isSafeInteger(e))) {
throw new Error('Existing version does not have right format')
}
const [major, minor, patch, build] = parts

switch (type) {
case 'build':
return [major, minor, patch, build + 1]
case 'patch':
return [major, minor, patch + 1, 0]
case 'minor':
return [major, minor + 1, 0, 0]
default:
throw new Error('Unknown bump type: ' + type)
}
}
40 changes: 40 additions & 0 deletions .github/actions/validate-stable-release.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// @ts-check

/**
* 1. Validate input version.
* 2. Check if given tag/release is already promoted to stable. If so, crash.
* @param {import('github-script').AsyncFunctionArguments} AsyncFunctionArguments
*/
module.exports = async ({ github, context }) => {
if (context.ref !== 'refs/heads/main') {
throw new Error('This action only works on main branch')
}

const { owner, repo } = context.repo
const previewVersionTag = process.env.INPUT_VERSION
if (!previewVersionTag) {
throw new Error('Missing env.INPUT_VERSION')
}
if (!previewVersionTag.match(/^v[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+-preview$/)) {
throw new Error('Input "version" must match vX.X.X.X-preview')
}

const versionTag = previewVersionTag.replace('-preview', '')
try {
await github.rest.repos.getReleaseByTag({
owner,
repo,
tag: versionTag
})
throw new Error('Release already promoted to stable')
} catch (error) {
if (!error.status) {
throw error
}
if (error.status === 404) {
// do nothing
} else {
throw new Error(`Failed to check: HTTP ${error.status}`, { cause: error })
}
}
}
51 changes: 51 additions & 0 deletions .github/workflows/bump-manifest-version.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Bump Manifest Version

on:
workflow_dispatch:
inputs:
version:
description: 'Version to bump to'
required: true
default: 'build'
type: choice
options:
- build
- patch
- minor

permissions:
contents: write
pull-requests: write

jobs:
bump-version:
name: Bump version
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with: { node-version-file: '.nvmrc' }

- name: Bump version
id: bump
uses: actions/github-script@v7
env:
INPUT_VERSION: ${{ github.event.inputs.version }}
with:
script: |
const script = require('./.github/actions/bump-manifest-version.cjs')
await script({ github, context, core })
- name: Create pull request
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: 'chore: release version ${{ steps.bump.outputs.version }}'
title: 'chore: release version ${{ steps.bump.outputs.version }}'
body: |
Bump extension version to ${{ steps.bump.outputs.version }}
branch: preview/${{ steps.bump.outputs.version }}
labels: preview
63 changes: 63 additions & 0 deletions .github/workflows/release-preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Release for Preview Channel

on:
push:
branches:
- main
- 'v[0-9]+.x'
paths:
- 'src/manifest.json'

defaults:
run:
shell: bash

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: write

jobs:
release-preview:
if: "${{ startsWith(github.event.head_commit.message, 'chore: release version') }}"
runs-on: ubuntu-latest
# environment: production
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Environment setup
uses: ./.github/actions/setup

- name: Build
run: pnpm build --channel=preview

- name: Get built version
uses: actions/github-script@v7
id: version
with:
script: |
const script = require('./.github/actions/get-built-version.cjs')
await script({ github, context, core })
- name: Delete existing release # To keep the workflow idempotent.
run: gh release delete v${{ steps.version.outputs.version }}-preview --cleanup-tag --yes
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}

- name: Release
uses: softprops/action-gh-release@v2
with:
files: |
dist/*.zip
tag_name: v${{ steps.version.outputs.version }}-preview
name: Preview v${{ steps.version.outputs.version }}
prerelease: true

# - name: Upload to stores
# if: github.ref_name == 'main'
# run: |
# echo 'TODO'
79 changes: 79 additions & 0 deletions .github/workflows/release-stable.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: Release Stable
run-name: Release Stable ${{ github.event.inputs.version }}

on:
workflow_dispatch:
inputs:
version:
description: 'Tag (vX.X.X.X-preview)'
required: true
type: string

defaults:
run:
shell: bash

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: write

jobs:
release-stable:
runs-on: ubuntu-latest
# environment: production
name: Release
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
# We want to build extension using the code and dependencies as of
# this tag. The scripts, workflows are back-ported, so they can be
# used consistently.
ref: ${{ github.event.inputs.version }}

- name: Environment setup
uses: ./.github/actions/setup

- name: Validate stable release
uses: actions/github-script@v7
env:
INPUT_VERSION: ${{ github.event.inputs.version }}
with:
script: |
const script = require('./.github/actions/validate-stable-release.cjs')
await script({ github, context, core })
- name: Build
run: pnpm build --channel=release

- name: Get built version
uses: actions/github-script@v7
id: version
with:
script: |
const script = require('./.github/actions/get-built-version.cjs')
await script({ github, context, core })
- name: Delete existing release # To keep the workflow idempotent.
run: gh release delete v${{ steps.version.outputs.version }} --cleanup-tag --yes
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}

- name: Release
uses: softprops/action-gh-release@v2
with:
files: |
dist/*.zip
tag_name: v${{ steps.version.outputs.version }}
name: v${{ steps.version.outputs.version }}
prerelease: false
fail_on_unmatched_files: true

# - name: Upload to stores
# run: |
# echo 'TODO'
1 change: 1 addition & 0 deletions cspell-dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ linkcode
endregion
metafile
iife
backported

# packages and 3rd party tools/libraries
awilix
Expand Down
Loading

0 comments on commit eda0d64

Please sign in to comment.