feat: FormWizard #527
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This Source Code Form is subject to the terms of the Mozilla Public | |
# License, v. 2.0. If a copy of the MPL was not distributed with this | |
# file, You can obtain one at https://mozilla.org/MPL/2.0/. | |
# | |
# OpenCRVS is also distributed under the terms of the Civil Registration | |
# & Healthcare Disclaimer located at http://opencrvs.org/license. | |
# | |
# Copyright (C) The OpenCRVS Authors located at https://github.com/opencrvs/opencrvs-core/blob/master/AUTHORS. | |
name: Auto PR to Release Branch | |
on: | |
pull_request: | |
types: [closed] | |
workflow_dispatch: | |
inputs: | |
pr_number: | |
description: 'PR number to process' | |
required: true | |
default: '' | |
dry_run: | |
description: 'Dry run' | |
required: false | |
default: false | |
type: boolean | |
jobs: | |
resolve-releases: | |
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' }} | |
runs-on: ubuntu-latest | |
outputs: | |
matrix: ${{ steps.resolve-applicable-versions.outputs.matrix }} | |
steps: | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
- name: Get PR details from workflow dispatch | |
if: ${{ github.event_name == 'workflow_dispatch' }} | |
id: get_pr_details_dispatch | |
run: | | |
PR_NUMBER=${{ github.event.inputs.pr_number }} | |
PR_DATA=$(curl -s -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/opencrvs/opencrvs-core/pulls/$PR_NUMBER) | |
echo "MILESTONE=$(printf '%s' $PR_DATA | jq -r '.milestone.title')" >> $GITHUB_ENV | |
env: | |
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} | |
- name: Get PR details from event | |
if: ${{ github.event_name == 'pull_request' }} | |
id: get_pr_details_event | |
run: | | |
echo "MILESTONE=${{ github.event.pull_request.milestone.title }}" >> $GITHUB_ENV | |
env: | |
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} | |
- name: Check for milestone and if release branch exists | |
continue-on-error: true | |
id: resolve-applicable-versions | |
run: | | |
if [ -z "${{ env.MILESTONE }}" ] || [ "${{ env.MILESTONE }}" = "null" ]; then | |
echo "No milestone set. Exiting." | |
exit 1 | |
fi | |
filter_versions() { | |
local input_version=$1 | |
# List remote branches, extract versions, and sort them semantically | |
versions=$(git ls-remote --heads origin 'release-*' | awk -F'release-' '{print $2}' | sort -V) | |
# Filter out versions less than the input version | |
filtered_versions=$(echo "$versions" | awk -v input="$input_version" ' | |
function compare_versions(v1, v2) { | |
split(v1, a, /[.v]/); | |
split(v2, b, /[.v]/); | |
for (i = 2; i <= 4; i++) { | |
if (a[i] < b[i]) return -1; | |
if (a[i] > b[i]) return 1; | |
} | |
return 0; | |
} | |
{ | |
if (compare_versions($0, input) >= 0) { | |
print $0 | |
} | |
}') | |
# Keep only the highest patch version for each minor version | |
echo "$filtered_versions" | awk -F. ' | |
{ | |
minor = $1 "." $2; | |
patches[minor] = $0; | |
} | |
END { | |
for (minor in patches) { | |
print patches[minor]; | |
} | |
}' | sort -V | |
} | |
versions=$(filter_versions "${{ env.MILESTONE }}") | |
json_array=$(echo "$versions" | jq -R -s -c 'split("\n") | map(select(. != ""))') | |
echo "matrix=$json_array" >> $GITHUB_OUTPUT | |
create-pr: | |
needs: resolve-releases | |
runs-on: ubuntu-22.04 | |
if: ${{ always() && needs.resolve-releases.result == 'success' }} | |
strategy: | |
fail-fast: false | |
matrix: | |
version: ${{fromJson(needs.resolve-releases.outputs.matrix)}} | |
steps: | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
- name: Get PR details from workflow dispatch | |
if: ${{ github.event_name == 'workflow_dispatch' }} | |
id: get_pr_details_dispatch | |
run: | | |
PR_NUMBER=${{ github.event.inputs.pr_number }} | |
PR_DATA=$(curl -s -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/opencrvs/opencrvs-core/pulls/$PR_NUMBER) | |
# printf escapes the newlines in the JSON, so we can use jq to parse output such as: | |
# "body": "![image](https://github.com/user-attachments/assets/8eee5bcf-7692-490f-a19f-576623e09961)\r\n", | |
echo "PR_ID=$(printf '%s' $PR_DATA | jq -r '.number')" >> $GITHUB_ENV | |
echo "PR_AUTHOR=$(printf '%s' $PR_DATA | jq -r '.user.login')" >> $GITHUB_ENV | |
echo "PR_MERGER=$(printf '%s' $PR_DATA | jq -r '.merged_by.login')" >> $GITHUB_ENV | |
echo "BASE_BRANCH=$(printf '%s' $PR_DATA | jq -r '.base.ref')" >> $GITHUB_ENV | |
echo "HEAD_BRANCH=$(printf '%s' $PR_DATA | jq -r '.head.ref')" >> $GITHUB_ENV | |
echo "PR_TITLE=$(printf '%s' $PR_DATA | jq -r '.title')" >> $GITHUB_ENV | |
echo "BASE_SHA=$(printf '%s' $PR_DATA | jq -r '.base.sha')" >> $GITHUB_ENV | |
env: | |
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} | |
- name: Get PR details from event | |
if: ${{ github.event_name == 'pull_request' }} | |
id: get_pr_details_event | |
run: | | |
PR_NUMBER=${{ github.event.pull_request.number }} | |
echo "PR_ID=${{ github.event.pull_request.number }}" >> $GITHUB_ENV | |
echo "PR_AUTHOR=${{ github.event.pull_request.user.login }}" >> $GITHUB_ENV | |
echo "BASE_BRANCH=${{ github.event.pull_request.base.ref }}" >> $GITHUB_ENV | |
echo "HEAD_BRANCH=${{ github.event.pull_request.head.ref }}" >> $GITHUB_ENV | |
echo "PR_TITLE=${{ github.event.pull_request.title }}" >> $GITHUB_ENV | |
echo "BASE_SHA=${{ github.event.pull_request.base.sha }}" >> $GITHUB_ENV | |
PR_DETAILS=$(gh pr view $PR_NUMBER --json mergedBy) | |
MERGED_BY_LOGIN=$(echo "$PR_DETAILS" | jq -r '.mergedBy.login') | |
echo "PR_MERGER=$MERGED_BY_LOGIN" >> $GITHUB_ENV | |
env: | |
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} | |
- name: Create and push the new branch for the PR | |
env: | |
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} | |
run: | | |
SEMANTIC_PR_TITLE="${{ env.PR_TITLE }}" | |
RELEASE_BRANCH="release-${{ matrix.version }}" | |
MILESTONE="${{ matrix.version }}" | |
# Check for semantic prefix | |
if [[ $SEMANTIC_PR_TITLE =~ ^(feat|fix|docs|style|refactor|perf|test|chore|build|ci|revert|wip|merge)\: ]]; then | |
SEMANTIC_PR_TITLE="${BASH_REMATCH[1]}(${MILESTONE}): ${SEMANTIC_PR_TITLE#*: }" | |
else | |
SEMANTIC_PR_TITLE="🍒 Merge changes from PR #${{ env.PR_ID }} to $RELEASE_BRANCH" | |
fi | |
PR_BODY="Automated PR to merge changes from develop to $RELEASE_BRANCH" | |
# Configure git | |
git config user.name "github-actions" | |
git config user.email "[email protected]" | |
git config advice.mergeConflict false | |
# Fetch and checkout the release branch | |
git fetch --all --unshallow | |
git checkout $RELEASE_BRANCH | |
# Create a new branch for the PR | |
NEW_BRANCH="auto-pr-$RELEASE_BRANCH-${{ env.PR_ID }}-$RANDOM" | |
git checkout -b $NEW_BRANCH | |
echo "HEAD_BRANCH: ${{ env.HEAD_BRANCH }}" | |
echo "BASE_SHA: ${{ env.BASE_SHA }}" | |
COMMIT_RANGE="${{ env.BASE_SHA }}..origin/${{ env.HEAD_BRANCH }}" | |
echo "Commit range: ${COMMIT_RANGE}" | |
NON_MERGE_COMMITS=$(git log ${COMMIT_RANGE} --reverse --no-merges --pretty=format:"%h" -- | xargs) | |
echo "Ordered non-merge commits: $NON_MERGE_COMMITS" | |
# Attempt to cherry-pick the commits from the original PR | |
CHERRY_PICK_OUTPUT=$(git cherry-pick ${NON_MERGE_COMMITS} 2>&1) || { | |
git cherry-pick --abort || true | |
# If cherry-pick fails, create a placeholder commit | |
echo "Cherry-pick failed. Creating placeholder commit." | |
git reset --hard | |
git commit --allow-empty -m "Placeholder commit for PR #${{ env.PR_ID }}" | |
# Add manual cherry-pick commands to the PR body | |
PR_BODY="${PR_BODY} | |
**I failed to cherry-pick the changes automatically because of the following:** | |
\`\`\` | |
$CHERRY_PICK_OUTPUT | |
\`\`\` | |
**To continue manually you can use these commands:** | |
\`\`\` | |
git fetch origin $NEW_BRANCH:$NEW_BRANCH | |
git fetch origin ${{ env.HEAD_BRANCH }}:${{ env.HEAD_BRANCH }} | |
git checkout $NEW_BRANCH | |
git reset --hard HEAD~1 # Remove placeholder commit | |
git cherry-pick $NON_MERGE_COMMITS | |
\`\`\` | |
" | |
} | |
if [ "${{ github.event.inputs.dry_run }}" == "true" ]; then | |
echo "This is a dry run." | |
echo "Would have pushed the new branch $NEW_BRANCH" | |
echo "PR title: $SEMANTIC_PR_TITLE" | |
echo "PR body:" | |
echo "$PR_BODY" | |
exit 0 | |
fi | |
# Push the new branch | |
git push origin $NEW_BRANCH | |
# Create a pull request and assign the original PR author as the reviewer | |
AUTHOR=${{ env.PR_AUTHOR }} | |
if [[ $AUTHOR == *renovate* ]]; then | |
if [ -z "${{ env.PR_MERGER }}" ]; then | |
AUTHOR="" | |
else | |
AUTHOR=${{ env.PR_MERGER }} | |
fi | |
fi | |
gh pr create --title "$SEMANTIC_PR_TITLE" --body "$PR_BODY" --head "$NEW_BRANCH" --base "$RELEASE_BRANCH" --assignee "$AUTHOR" --reviewer "$AUTHOR" |