Auto PR (develop -> main) #217
This file contains hidden or 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
| name: Auto PR (develop -> main) | |
| on: | |
| workflow_run: | |
| workflows: ["CI"] | |
| types: [completed] | |
| branches: [develop] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| models: read | |
| jobs: | |
| auto-pr: | |
| name: Create or Update PR | |
| runs-on: ubuntu-latest | |
| if: > | |
| github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.head_branch == 'develop' && | |
| github.event.workflow_run.event == 'push' | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| ref: develop | |
| fetch-depth: 0 | |
| - name: Check for existing PR | |
| id: check-pr | |
| run: | | |
| PR_COUNT=$(gh pr list --base main --head develop --state open --json number --jq 'length') | |
| echo "count=$PR_COUNT" >> $GITHUB_OUTPUT | |
| if [ "$PR_COUNT" -gt 0 ]; then | |
| PR_NUMBER=$(gh pr list --base main --head develop --state open --json number --jq '.[0].number') | |
| echo "number=$PR_NUMBER" >> $GITHUB_OUTPUT | |
| PR_BODY=$(gh pr view "$PR_NUMBER" --json body --jq '.body') | |
| if echo "$PR_BODY" | grep -q "Auto-generated by GitHub Actions"; then | |
| echo "is_auto=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "is_auto=false" >> $GITHUB_OUTPUT | |
| fi | |
| else | |
| echo "is_auto=false" >> $GITHUB_OUTPUT | |
| fi | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| - name: Predict semantic-release version | |
| if: steps.check-pr.outputs.count == '0' || steps.check-pr.outputs.is_auto == 'true' | |
| id: version | |
| run: | | |
| git fetch origin main:main 2>/dev/null || true | |
| COMMITS=$(git log main..HEAD --pretty=format:"%s" --no-merges) | |
| BUMP="none" | |
| FEAT_COUNT=0 | |
| FIX_COUNT=0 | |
| BREAKING=false | |
| while IFS= read -r MSG; do | |
| if echo "$MSG" | grep -qiE 'BREAKING[ -]CHANGE|^[a-z]+(\(.+\))?!:'; then | |
| BREAKING=true | |
| fi | |
| if echo "$MSG" | grep -qE '^feat(\(.+\))?:'; then | |
| FEAT_COUNT=$((FEAT_COUNT + 1)) | |
| fi | |
| if echo "$MSG" | grep -qE '^fix(\(.+\))?:'; then | |
| FIX_COUNT=$((FIX_COUNT + 1)) | |
| fi | |
| done <<< "$COMMITS" | |
| if [ "$BREAKING" = true ]; then | |
| BUMP="major" | |
| elif [ "$FEAT_COUNT" -gt 0 ]; then | |
| BUMP="minor" | |
| elif [ "$FIX_COUNT" -gt 0 ]; then | |
| BUMP="patch" | |
| fi | |
| # Get current latest tag | |
| CURRENT=$(git describe --tags --abbrev=0 main 2>/dev/null || echo "v0.0.0") | |
| CURRENT="${CURRENT#v}" | |
| IFS='.' read -r CUR_MAJOR CUR_MINOR CUR_PATCH <<< "$CURRENT" | |
| case "$BUMP" in | |
| major) NEXT="$((CUR_MAJOR + 1)).0.0" ;; | |
| minor) NEXT="${CUR_MAJOR}.$((CUR_MINOR + 1)).0" ;; | |
| patch) NEXT="${CUR_MAJOR}.${CUR_MINOR}.$((CUR_PATCH + 1))" ;; | |
| none) NEXT="" ;; | |
| esac | |
| echo "bump=$BUMP" >> $GITHUB_OUTPUT | |
| echo "current=v${CURRENT}" >> $GITHUB_OUTPUT | |
| echo "next=${NEXT:+v$NEXT}" >> $GITHUB_OUTPUT | |
| echo "feat_count=$FEAT_COUNT" >> $GITHUB_OUTPUT | |
| echo "fix_count=$FIX_COUNT" >> $GITHUB_OUTPUT | |
| echo "breaking=$BREAKING" >> $GITHUB_OUTPUT | |
| - name: Analyze changes with AI | |
| if: steps.check-pr.outputs.count == '0' || steps.check-pr.outputs.is_auto == 'true' | |
| id: analyze | |
| run: | | |
| COMMITS=$(git log main..HEAD --pretty=format:"- %s (%h)" --no-merges) | |
| FILES_CHANGED=$(git diff main..HEAD --stat) | |
| BUMP="${{ steps.version.outputs.bump }}" | |
| CURRENT="${{ steps.version.outputs.current }}" | |
| NEXT="${{ steps.version.outputs.next }}" | |
| cat > /tmp/prompt.txt <<PROMPT | |
| Create a pull request title and description for merging develop into main. | |
| Project: OpenVox Operator - a Kubernetes Operator for deploying and managing OpenVox (Puppet) environments on Kubernetes/OpenShift using rootless containers. | |
| Predicted release: ${CURRENT} -> ${NEXT} (${BUMP} bump) | |
| ALL commits since last release (complete list, nothing omitted): | |
| $COMMITS | |
| Files changed (summary): | |
| $FILES_CHANGED | |
| IMPORTANT: The FIRST line of your response must be a concise PR title (max 60 chars, no markdown, no prefix like "Title:"). It should summarize the most important changes. Examples: "Add Database CRD and update CI workflows", "Fix auth handling and bump dependencies". | |
| Then leave one blank line and write the description. You MUST cover ALL commits listed above - do not skip any. | |
| ## Summary | |
| - 5-8 bullet points explaining WHAT changed and WHY | |
| ## Changes | |
| - Categorized list covering EVERY commit (features, fixes, refactor, chore, docs, tests, ci) | |
| ## Testing | |
| - How to verify these changes | |
| PROMPT | |
| REQUEST_JSON=$(jq -n \ | |
| --arg system "You are a technical writer creating concise PR descriptions for a Kubernetes Operator project written in Go with Helm charts. Focus on WHY changes were made, not just WHAT changed. Be professional and concise. The very first line of your response MUST be a short PR title without any markdown formatting." \ | |
| --arg user "$(cat /tmp/prompt.txt)" \ | |
| '{ | |
| "messages": [ | |
| {"role": "system", "content": $system}, | |
| {"role": "user", "content": $user} | |
| ], | |
| "model": "gpt-4.1", | |
| "temperature": 0.7, | |
| "max_tokens": 4000 | |
| }') | |
| API_RESPONSE=$(echo "$REQUEST_JSON" | curl -s -X POST \ | |
| "https://models.inference.ai.azure.com/chat/completions" \ | |
| -H "Content-Type: application/json" \ | |
| -H "Authorization: Bearer ${{ github.token }}" \ | |
| -d @-) | |
| ANALYSIS=$(echo "$API_RESPONSE" | jq -r '.choices[0].message.content // empty') | |
| if [ -z "$ANALYSIS" ]; then | |
| echo "::warning::AI analysis failed, using fallback description" | |
| ANALYSIS="Release ${NEXT:-develop -> main} | |
| ## Changes | |
| $COMMITS | |
| ## Files changed | |
| \`\`\` | |
| $FILES_CHANGED | |
| \`\`\`" | |
| fi | |
| echo "$ANALYSIS" > /tmp/pr_description.txt | |
| echo "description_file=/tmp/pr_description.txt" >> $GITHUB_OUTPUT | |
| - name: Build PR title and body | |
| if: steps.check-pr.outputs.count == '0' || steps.check-pr.outputs.is_auto == 'true' | |
| id: pr-content | |
| run: | | |
| BUMP="${{ steps.version.outputs.bump }}" | |
| CURRENT="${{ steps.version.outputs.current }}" | |
| NEXT="${{ steps.version.outputs.next }}" | |
| FEAT_COUNT="${{ steps.version.outputs.feat_count }}" | |
| FIX_COUNT="${{ steps.version.outputs.fix_count }}" | |
| BREAKING="${{ steps.version.outputs.breaking }}" | |
| # Build title from release prediction | |
| if [ "$BUMP" = "none" ]; then | |
| TITLE="Release develop -> main (no release)" | |
| else | |
| TITLE="Release ${NEXT} (${BUMP})" | |
| fi | |
| echo "title=$TITLE" >> $GITHUB_OUTPUT | |
| # Build version badge | |
| if [ "$BUMP" = "none" ]; then | |
| VERSION_INFO="No release-triggering commits detected (chore/ci/docs only)." | |
| else | |
| VERSION_INFO="**Predicted release:** \`${CURRENT}\` -> \`${NEXT}\` (**${BUMP}**)" | |
| DETAILS="" | |
| if [ "$BREAKING" = "true" ]; then | |
| DETAILS="${DETAILS} BREAKING CHANGE detected" | |
| fi | |
| if [ "$FEAT_COUNT" -gt 0 ]; then | |
| DETAILS="${DETAILS} ${FEAT_COUNT} feature(s)" | |
| fi | |
| if [ "$FIX_COUNT" -gt 0 ]; then | |
| DETAILS="${DETAILS} ${FIX_COUNT} fix(es)" | |
| fi | |
| if [ -n "$DETAILS" ]; then | |
| VERSION_INFO="${VERSION_INFO} | |
| ${DETAILS}" | |
| fi | |
| fi | |
| # Build body: skip first line (title) from AI output | |
| AI_DESCRIPTION=$(tail -n +2 /tmp/pr_description.txt) | |
| cat > /tmp/pr_body.txt <<EOF | |
| $AI_DESCRIPTION | |
| --- | |
| ### Release prediction | |
| $VERSION_INFO | |
| --- | |
| **Source:** \`develop\` | **Target:** \`main\` | **Trigger:** Successful CI on develop | |
| > Auto-generated by GitHub Actions & AI - updated automatically on new commits. | |
| EOF | |
| - name: Update existing PR | |
| if: steps.check-pr.outputs.count != '0' && steps.check-pr.outputs.is_auto == 'true' | |
| run: | | |
| gh pr edit ${{ steps.check-pr.outputs.number }} \ | |
| --title "${{ steps.pr-content.outputs.title }}" \ | |
| --body-file /tmp/pr_body.txt | |
| echo "::notice::Updated PR #${{ steps.check-pr.outputs.number }}" | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| - name: Create PR | |
| if: steps.check-pr.outputs.count == '0' | |
| run: | | |
| gh pr create \ | |
| --title "${{ steps.pr-content.outputs.title }}" \ | |
| --body-file /tmp/pr_body.txt \ | |
| --base main \ | |
| --head develop | |
| env: | |
| GH_TOKEN: ${{ github.token }} |