Skip to content

Commit

Permalink
feat: added support for release automation (#849)
Browse files Browse the repository at this point in the history
* feat: added auto release note generator yml file

Signed-off-by: Logan Nguyen <[email protected]>

* feat: added .bump-version.js script

Signed-off-by: Logan Nguyen <[email protected]>

* feat: added release-automation.yml

Signed-off-by: Logan Nguyen <[email protected]>

* feat: added check-pr-label-milestone workflow

Signed-off-by: Logan Nguyen <[email protected]>

* fix: updated pr-label-milestone-check.yml

Signed-off-by: Logan Nguyen <[email protected]>

* fix: removed .bump-version.js script

This reverts commit f18a358.

Signed-off-by: Logan Nguyen <[email protected]>

---------

Signed-off-by: Logan Nguyen <[email protected]>
  • Loading branch information
quiet-node authored Jul 15, 2024
1 parent 96138c2 commit 80c2027
Show file tree
Hide file tree
Showing 4 changed files with 300 additions and 0 deletions.
12 changes: 12 additions & 0 deletions .github/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
changelog:
exclude:
labels: ['wontfix', 'question', 'duplicate', 'invalid']
categories:
- title: 'Enhancements'
labels: ['enhancement']
- title: 'Bug Fixes'
labels: ['bug']
- title: 'Documentation'
labels: ['documentation']
- title: 'Dependency Upgrades'
labels: ['dependencies']
79 changes: 79 additions & 0 deletions .github/scripts/check-pr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
const axios = require('axios');

const githubToken = process.env.GITHUB_TOKEN;
const { GITHUB_REPOSITORY, GITHUB_PR_NUMBER } = process.env;

const [owner, repo] = GITHUB_REPOSITORY.split('/');

async function getPRDetails() {
const url = `https://api.github.com/repos/${owner}/${repo}/pulls/${GITHUB_PR_NUMBER}`;
const response = await axios.get(url, {
headers: {
Authorization: `token ${githubToken}`,
},
});
return response.data;
}

async function getIssueDetails(issueNumber) {
try {
const url = `https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}`;
const response = await axios.get(url, {
headers: {
Authorization: `token ${githubToken}`,
},
});
return response.data;
} catch (error) {
if (error.response && error.response.status === 404) {
console.log(`Issue #${issueNumber} not found, skipping...`);
return null;
} else {
throw error;
}
}
}

async function run() {
try {
const pr = await getPRDetails();
const { labels: prLabels, milestone: prMilestone, body: prBody } = pr;

if (prLabels.length === 0) {
throw new Error('The PR has no labels.');
}
if (!prMilestone) {
throw new Error('The PR has no milestone.');
}

const issueNumberMatches = prBody.match(/#(\d+)/g);

if (!issueNumberMatches) {
console.log('No associated issues found in PR description.');
} else {
for (const match of issueNumberMatches) {
const issueNumber = match.replace('#', '');
const issue = await getIssueDetails(issueNumber);
if (issue) {
const { labels: issueLabels, milestone: issueMilestone } = issue;

if (issueLabels.length === 0) {
throw new Error(`Associated issue #${issueNumber} has no labels.`);
}
if (!issueMilestone) {
throw new Error(
`Associated issue #${issueNumber} has no milestone.`
);
}
}
}
}

console.log('PR and all associated issues have labels and milestones.');
} catch (error) {
console.error(error.message);
process.exit(1);
}
}

run();
27 changes: 27 additions & 0 deletions .github/workflows/pr-label-milestone-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: PR Label and Milestone Check

on:
pull_request:
types: [opened, edited, labeled, unlabeled, synchronize]

jobs:
check_pr:
runs-on: [self-hosted, Linux, medium, ephemeral]

steps:
- name: Checkout code
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

- name: Set up Node.js
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
with:
node-version: '20'

- name: Install dependencies
run: npm install axios

- name: Check PR labels and milestones
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_PR_NUMBER: ${{ github.event.number }}
run: node .github/scripts/check-pr.js
182 changes: 182 additions & 0 deletions .github/workflows/release-automation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
name: Release Branch Automation

on:
workflow_dispatch:
inputs:
version:
description: 'Release Version (semver ie. 0.9.0):'
type: string
required: true

jobs:
branch_bump_tag:
runs-on: [self-hosted, Linux, medium, ephemeral]
env:
RELEASE_NOTES_FILENAME: release_notes
outputs:
create_pr: ${{ env.CREATE_PR }}
next_version_snapshot: ${{ env.NEXT_VERSION_SNAPSHOT }}
pr_title: ${{ env.PR_TITLE }}
release_branch: ${{ env.RELEASE_BRANCH }}

steps:
- name: Harden Runner
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
with:
egress-policy: audit

- name: Parse Version
id: version_parser
uses: step-security/semver-utils@f437161847710e2a9e7b99f75442cbad9aa9ec14 # v1.0.0
with:
lenient: false
version: ${{ github.event.inputs.version }}

- name: Set Release Environment Variables
run: |
PREMINOR_VERSION=${{ steps.version_parser.outputs.inc-preminor }}
NEXT_VERSION_SNAPSHOT=${PREMINOR_VERSION//-0/-SNAPSHOT}
RELEASE_BRANCH="release/${{ steps.version_parser.outputs.major }}.${{ steps.version_parser.outputs.minor }}"
[[ -z "${{ steps.version_parser.outputs.prerelease }}" ]] && \
VERSION=${{ steps.version_parser.outputs.release }} || \
VERSION="${{ steps.version_parser.outputs.release }}-${{ steps.version_parser.outputs.prerelease }}"
RELEASE_TAG="v${VERSION}"
cat >> $GITHUB_ENV <<EOF
NEXT_VERSION_SNAPSHOT=$NEXT_VERSION_SNAPSHOT
RELEASE_BRANCH=$RELEASE_BRANCH
RELEASE_TAG=$RELEASE_TAG
VERSION=$VERSION
EOF
- name: Checkout repository
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
with:
fetch-depth: 0
ref: main
token: ${{ secrets.GH_ACCESS_TOKEN }}

- name: Import GPG Key
id: gpg_importer
uses: step-security/ghaction-import-gpg@a7c87df2279f2bf2e69ba8289dfbf35fe05a4e08 # v1.0.0
with:
git_commit_gpgsign: true
git_tag_gpgsign: true
git_user_signingkey: true
gpg_private_key: ${{ secrets.GPG_KEY_CONTENTS }}
passphrase: ${{ secrets.GPG_KEY_PASSPHRASE }}

- name: Create and Switch to Release Branch
run: |
if ! git ls-remote --exit-code --heads --quiet origin refs/heads/${RELEASE_BRANCH}; then
git checkout -b ${RELEASE_BRANCH}
git push -u origin ${RELEASE_BRANCH}
# create a PR to bump main branch to the next snapshot version
echo "CREATE_PR=true" >> $GITHUB_ENV
echo "PR_TITLE=Bump versions for v$NEXT_VERSION_SNAPSHOT" >> $GITHUB_ENV
else
git checkout ${RELEASE_BRANCH}
fi
- name: Set up Node.js
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: '20'

- name: Install make
run: sudo apt-get update; sudo apt-get install build-essential -y

- name: Install dependencies
run: npm ci

- name: Bump Versions
run: npm version ${{ env.VERSION }}

- name: Create Release Notes
if: ${{ steps.milestone.outputs.milestone_id != '' }}
uses: Decathlon/release-notes-generator-action@98423db7024696a339f3988ac8a2b051c5860741 # v3.1.6
env:
FILENAME: ${{ env.RELEASE_NOTES_FILENAME }}
GITHUB_TOKEN: ${{ secrets.GH_ACCESS_TOKEN }}
MILESTONE_NUMBER: ${{ steps.milestone.outputs.milestone_id }}

- name: Commit and Tag
uses: stefanzweifel/git-auto-commit-action@8621497c8c39c72f3e2a999a26b4ca1b5058a842 # v5.0.1
with:
commit_author: ${{ steps.gpg_importer.outputs.name }} <${{ steps.gpg_importer.outputs.email }}>
commit_message: Bump versions for ${{ env.RELEASE_TAG }}
commit_options: '--no-verify --signoff'
commit_user_name: ${{ steps.gpg_importer.outputs.name }}
commit_user_email: ${{ steps.gpg_importer.outputs.email }}
tagging_message: ${{ env.RELEASE_TAG }}

- name: Create Github Release
uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0
with:
bodyFile: ${{ env.RELEASE_NOTES_FILENAME }}.md
commit: ${{ env.RELEASE_BRANCH }}
draft: true
name: ${{ env.RELEASE_TAG }}
omitBody: ${{ steps.milestone.outputs.milestone_id == '' }}
prerelease: ${{ steps.version_parser.outputs.prerelease != '' }}
tag: ${{ env.RELEASE_TAG }}
token: ${{ secrets.GH_ACCESS_TOKEN }}

create_snapshot_pr:
name: Create snapshot PR
runs-on: [self-hosted, Linux, medium, ephemeral]
needs: branch_bump_tag
if: ${{ needs.branch_bump_tag.outputs.create_pr == 'true' }}
env:
NEXT_VERSION_SNAPSHOT: ${{ needs.branch_bump_tag.outputs.next_version_snapshot }}
RELEASE_BRANCH: ${{ needs.branch_bump_tag.outputs.release_branch }}

steps:
- name: Harden Runner
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
with:
egress-policy: audit

- name: Checkout Repository
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
with:
fetch-depth: 0
ref: main
token: ${{ secrets.GH_ACCESS_TOKEN }}

- name: Import GPG Key
id: gpg_importer
uses: step-security/ghaction-import-gpg@a7c87df2279f2bf2e69ba8289dfbf35fe05a4e08 # v1.0.0
with:
git_commit_gpgsign: true
git_tag_gpgsign: true
git_user_signingkey: true
gpg_private_key: ${{ secrets.GPG_KEY_CONTENTS }}
passphrase: ${{ secrets.GPG_KEY_PASSPHRASE }}

- name: Set up Node.js
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: '20'

- name: Install make
run: sudo apt-get update; sudo apt-get install build-essential -y

- name: Install dependencies
run: npm ci

- name: Bump Versions
run: npm version ${{ env.NEXT_VERSION_SNAPSHOT }}

- name: Create Pull Request
uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e # v6.0.5
with:
body: ''
branch: create-pull-request/${{ env.NEXT_VERSION_SNAPSHOT }}
commit-message: Bump versions for v${{ env.NEXT_VERSION_SNAPSHOT }}
committer: ${{ steps.gpg_importer.outputs.name }} <${{ steps.gpg_importer.outputs.email }}>
author: ${{ steps.gpg_importer.outputs.name }} <${{ steps.gpg_importer.outputs.email }}>
delete-branch: true
signoff: true
title: ${{ needs.branch_bump_tag.outputs.pr_title }}
token: ${{ secrets.GH_ACCESS_TOKEN }}

0 comments on commit 80c2027

Please sign in to comment.