Skip to content

refactor(env-vars): use envalid in user-mgnt #354

refactor(env-vars): use envalid in user-mgnt

refactor(env-vars): use envalid in user-mgnt #354

# 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 "github-actions@github.com"
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"