Skip to content

Commit

Permalink
Adds scripts and configurations to support hotfix release workflows (#…
Browse files Browse the repository at this point in the history
…2545)

* Add scripts and configs to support hotfix release

* Uncomment publish

* fixes

* fixes

* fixes

* Use git diff tree instead of show
  • Loading branch information
varadarajan-tw authored Nov 15, 2024
1 parent 83d22bf commit 410efab
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 32 deletions.
25 changes: 22 additions & 3 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
push:
branches:
- main
- release
- hotfix/**

jobs:
build-and-publish:
Expand Down Expand Up @@ -50,7 +50,7 @@ jobs:
- name: Publish
run: |
yarn lerna publish from-package --yes --allowBranch=main --loglevel=verbose --dist-tag latest
yarn lerna publish from-package --yes --loglevel=verbose --dist-tag latest
release:
needs: build-and-publish # comment when testing locally with https://github.com/nektos/act
Expand All @@ -68,7 +68,7 @@ jobs:
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Generate Release Tag
- name: Generate Tags
id: get-release-tag
run: ./scripts/generate-release-tags.sh

Expand All @@ -81,3 +81,22 @@ jobs:
script: |
const script = require('./scripts/github-action/create-github-release.js')
await script({github, context, core, exec})
# When hotfix is merged back to main, we tag all the packages that were changed in the hotfix commit.
tag-hotfix:
if: startsWith(github.event.head_commit.message, 'Hotfix:') == true && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Generate Package Specific Tags
id: get-release-tag
run: ./scripts/generate-package-tags.sh
12 changes: 7 additions & 5 deletions .github/workflows/version-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ on:
description: 'Base branch to create PR to'
required: true
default: 'main'
type: choice
options:
- main
- release
run_id:
description: 'Unique identifier for the run'
required: false
Expand Down Expand Up @@ -67,7 +63,13 @@ jobs:
run: NODE_ENV=production yarn build

- name: Version Packages
run: yarn lerna version minor --yes --allow-branch ${{ github.event.inputs.branch }} --no-git-tag-version --no-commit-hooks --no-private
run: |
# minor version bump for main branch and patch for hotfixes
if [ "${{ github.event.inputs.base_branch }}" == "main" ]; then
yarn lerna version minor --yes --allow-branch ${{ github.event.inputs.branch }} --no-git-tag-version --no-commit-hooks --no-private
else
yarn lerna version patch --yes --allow-branch ${{ github.event.inputs.branch }} --no-git-tag-version --no-commit-hooks --no-private
fi
- name: Commit and push
id: commit_and_push
Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"npmClientArgs": ["--ignore-engines", "--ignore-optional"]
},
"version": {
"allowBranch": ["main", "release"]
"allowBranch": ["main", "hotfix/*"]
}
}
}
14 changes: 14 additions & 0 deletions scripts/generate-package-tags.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

#!/bin/bash

set -e # exit on error

# this script assumes the last commit was publish commit and gets all package.json files changed in the last commit
# and generates tags for each package.json file.
files=$(git show --pretty="" --name-only HEAD | grep -Ei '^packages/.*package\.json$')
for file in $files; do
tag=$(cat $file | jq -r '.name + "@" + .version')
echo "Tagging $sha with $tag"
git tag -a $tag -m "Release $tag" --force
git push origin $tag
done
36 changes: 24 additions & 12 deletions scripts/generate-release-tags.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ sha=$(git rev-parse HEAD);
branch=$(git rev-parse --symbolic-full-name --abbrev-ref HEAD);
message=$(git log -1 --pretty=%B);

if [[ $branch != "main" && $branch != "release" ]];
# Only generate release tags for main branch and hotfix branches.
if [[ $branch != "main" && ! $branch == hotfix/* ]];
then
echo "Skipping release tag generation as branch is not main or release"
echo "Skipping release tag generation as branch is not main or hotfix"
exit 0
fi;

Expand All @@ -26,6 +27,14 @@ then
exit 1
fi

# If branch is hotfix, prefix should be hotfix. If not, it should be release.
if [[ $branch == hotfix/* ]];
then
prefix="hotfix"
else
prefix="release"
fi

# Generate and push release tag. Release tag format: release-YYYY-MM-DD[.N] e.g. release-2024-01-01
if ! n=$(git rev-list --count $sha~ --grep "Publish" --since="00:00"); then
echo 'Failed to compute release tag. Exiting.'
Expand All @@ -36,18 +45,21 @@ else
*) suffix=".$n" ;; # subsequent commits get a suffix, starting with .1
esac

tag=$(printf release-$(date '+%Y-%m-%d%%s') $suffix)
tag=$(printf $prefix-$(date '+%Y-%m-%d%%s') $suffix)
echo "Tagging $sha with $tag"
git tag -a $tag -m "Release $tag" --force
git push origin $tag
fi

# this script assumes the last commit was publish commit and gets all package.json files changed in the last commit
# and generates tags for each package.json file.
files=$(git show --pretty="" --name-only HEAD | grep -Ei '^packages/.*package\.json$')
for file in $files; do
tag=$(cat $file | jq -r '.name + "@" + .version')
echo "Tagging $sha with $tag"
git tag -a $tag -m "Release $tag" --force
git push origin $tag
done

# For hotfix, we don't tag each package version and we do it when hotfix changes are merged to main branch.
if [[ $branch == hotfix/* ]];
then
echo "Skipping package tag generation for hotfix branch"
exit 0
fi;

# Generate and push package tags.
curr_path=$(pwd)
dir_name=$(dirname $0)
./$dir_name/generate-package-tags.sh
38 changes: 27 additions & 11 deletions scripts/github-action/create-github-release.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ module.exports = async ({ github, context, core, exec }) => {
const latestReleaseTag = latestRelease ? latestRelease.data.tag_name : null

// Extract package tags that are published in the current release by lerna version
const packageTags = await extractPackageTags(GITHUB_SHA, exec, core)
const packageTags = await extractPackageNames(GITHUB_SHA, exec, core)
const tagsContext = { currentRelease: newReleaseTag, prevRelease: latestReleaseTag, packageTags }
const changeLog = formatChangeLog(prs, tagsContext, context)

Expand Down Expand Up @@ -81,7 +81,8 @@ async function getReleaseTag(core, exec) {
'describe',
'--abbrev=0',
'--tags',
'--match=release-*'
'--match=release-*',
'--match=hotfix-*'
])
if (exitCode !== 0) {
// if the release tag is not found, then we cannot proceed further
Expand All @@ -90,18 +91,33 @@ async function getReleaseTag(core, exec) {
return stdout.trim()
}

// Extract package tags that are published in the current release by lerna version
async function extractPackageTags(sha, exec, core) {
const { stdout, stderr, exitCode } = await exec.getExecOutput('git', ['tag', '--points-at', sha])
// Extract packages published in the current release
async function extractPackageNames(sha, exec, core) {
const { stdout, stderr, exitCode } = await exec.getExecOutput('git', [
'diff-tree',
'--no-commit-id',
'--name-only',
sha,
'-r'
])
if (exitCode !== 0) {
// if the package tags are not found, then we cannot proceed further
core.error(`Failed to extract package tags: ${stderr}`)
}
// filter out only the tags that are related to segment packages
return stdout
.split('\n')
.filter(Boolean)
.filter((tag) => tag.includes('@segment/') && !tag.includes('staging'))
// filter out files that are not package.json
const files = stdout.split('\n').filter((file) => file.startsWith('packages/') && file.endsWith('package.json'))
// get the package versions and names from package.json files
const packageTags = await Promise.all(
files.map(async (file) => {
const { stdout, stderr, exitCode } = await exec.getExecOutput('cat', [file])
if (exitCode !== 0) {
core.error(`Failed to extract package tags: ${stderr}`)
}
const pkg = JSON.parse(stdout)
return `${pkg.name}@${pkg.version}`
})
)
return packageTags
}

// Get PRs between two commits
Expand Down Expand Up @@ -227,7 +243,7 @@ function formatTable(prs, tableConfig, title = '') {
`
}
/*
* Map PR with affected destinations
* Map PR with affected destinations
*/
function mapPRWithAffectedDestinations(pr) {
let affectedDestinations = []
Expand Down

0 comments on commit 410efab

Please sign in to comment.