Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions .github/workflows/backport-hotfix.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Opens a backport PR to `dev` whenever a hotfix lands on `main`.
# A hotfix is identified by the `[hotfix]` marker in the head commit message
# (same convention used by the CD workflow to force-publish all packages).
# The hotfix commit is cherry-picked onto a branch from `dev`; if it conflicts,
# the job fails so the backport can be done manually.
name: Backport hotfix to dev

on:
push:
branches:
- main

permissions:
contents: write
pull-requests: write

jobs:
backport:
name: Open backport PR to dev
if: contains(github.event.head_commit.message, '[hotfix]')
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v6
with:
token: ${{ secrets.VTEX_GITHUB_BOT_TOKEN }}
fetch-depth: 0

- name: Configure CI Git User
run: |
git config user.name vtexgithubbot
git config user.email vtexgithubbot@github.com

- name: Create backport branch and cherry-pick hotfix
id: backport
run: |
SHORT_SHA="${GITHUB_SHA::8}"
BRANCH="backport/hotfix-${SHORT_SHA}"

git fetch origin dev
git checkout -b "$BRANCH" origin/dev

if git cherry-pick "$GITHUB_SHA"; then
echo "branch=$BRANCH" >> "$GITHUB_OUTPUT"
echo "conflict=false" >> "$GITHUB_OUTPUT"
else
git cherry-pick --abort || true
echo "conflict=true" >> "$GITHUB_OUTPUT"
fi

- name: Push backport branch
if: steps.backport.outputs.conflict == 'false'
run: git push origin "${{ steps.backport.outputs.branch }}"

- name: Open backport PR
if: steps.backport.outputs.conflict == 'false'
env:
GH_TOKEN: ${{ secrets.VTEX_GITHUB_BOT_TOKEN }}
BRANCH: ${{ steps.backport.outputs.branch }}
# Passed via env (not inlined into the script) to avoid shell injection
# from arbitrary commit message content.
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
run: |
gh pr create \
--base dev \
--head "$BRANCH" \
--title "chore: backport hotfix ${GITHUB_SHA::8} to dev" \
--body "$(cat <<EOF
Automated backport of hotfix [\`${GITHUB_SHA::8}\`](https://github.com/${GITHUB_REPOSITORY}/commit/${GITHUB_SHA}) merged into \`main\`.

Keeps \`dev\` aligned with the fix so it is not lost on the next \`dev\` → \`main\` release.

Original commit message:

\`\`\`
${COMMIT_MESSAGE}
\`\`\`
EOF
)"

- name: Fail when cherry-pick conflicts
if: steps.backport.outputs.conflict == 'true'
run: |
echo "::error::Cherry-pick of ${GITHUB_SHA} onto dev had conflicts. Please backport this hotfix to dev manually."
exit 1
9 changes: 8 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,15 @@ jobs:
- name: Verify publish manifests
run: pnpm release:verify

# Hotfixes pushed straight to main carry a [hotfix] marker in the head
# commit message so all packages are versioned together (force-publish),
# instead of only the changed ones.
- name: Publish (main - hotfix)
if: github.ref_name == 'main' && contains(github.event.head_commit.message, '[hotfix]')
run: pnpm release:hotfix

- name: Publish (main)
if: github.ref_name == 'main'
if: github.ref_name == 'main' && !contains(github.event.head_commit.message, '[hotfix]')
run: pnpm release

- name: Publish (dev)
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"ci:dkcicd": "pnpm build && pnpm lint && pnpm size && pnpm test:coverage && mkdir -p coverage && pnpm dlx lcov-result-merger 'packages/*/coverage/lcov.info' coverage/lcov.info --prepend-source-files",
"size": "turbo run size",
"release": "lerna version --conventional-commits --conventional-graduate --yes && pnpm -r publish --access public --no-git-checks",
"release:hotfix": "lerna version --conventional-commits --force-publish --yes && pnpm -r publish --access public --no-git-checks",
"release:dev": "lerna version --conventional-commits --conventional-prerelease --preid=dev --force-publish --yes && pnpm -r publish --access public --tag dev --no-git-checks",
"release:v3": "lerna version --conventional-commits --yes && pnpm -r publish --access public --tag v3-latest --no-git-checks",
"release:verify": "node ./scripts/verify-publish-manifests.mjs",
Expand Down
Loading