Skip to content

Merge pull request #992 from Max-Health-Inc/develop #3449

Merge pull request #992 from Max-Health-Inc/develop

Merge pull request #992 from Max-Health-Inc/develop #3449

Workflow file for this run

name: Auto PR Creation
on:
push:
branches:
- "dev/*"
- "develop"
- "test"
- "ai/dev/*"
- "ai/test/*"
- "ai/checklist/*"
# Manual re-trigger. Needed because a push that happens while Actions is stopped
# (quota, outage) is never replayed, leaving the branch promoted but PR-less with
# no way to recover short of an empty commit. Run it from the branch to promote.
workflow_dispatch:
permissions:
contents: write
pull-requests: write
jobs:
pr-to-develop:
name: Create/Update PR from dev/* → develop
if: startsWith(github.ref, 'refs/heads/dev/') && !contains(github.event.head_commit.message, '🔄 Update version') && !contains(github.event.head_commit.message, '🤖 Update client APIs') && !contains(github.event.head_commit.message, 'proxy-smart-releaser')
runs-on: ubuntu-latest
steps:
- name: Generate token
id: generate_token
uses: actions/create-github-app-token@v3
with:
client-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Check if PR already exists
id: check-pr
run: |
PR_EXISTS=$(gh pr list --base develop --head ${{ github.ref_name }} --state open --json number --jq length)
echo "pr_exists=$PR_EXISTS" >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Check if develop branch exists
id: check-branch
run: |
git fetch origin --prune
if git ls-remote --heads origin develop | grep -q "refs/heads/develop"; then
echo "Develop branch exists on remote"
echo "needs_branch_creation=false" >> $GITHUB_OUTPUT
else
echo "Develop branch doesn't exist → needs creation"
echo "needs_branch_creation=true" >> $GITHUB_OUTPUT
fi
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Create develop branch if it doesn't exist
if: steps.check-branch.outputs.needs_branch_creation == 'true' && steps.check-pr.outputs.pr_exists == '0'
run: |
# Try to create develop branch from main, fallback to current branch if main doesn't exist
if git ls-remote --heads origin main | grep -q "refs/heads/main"; then
git fetch origin main:main
git checkout -b develop main
echo "Created develop branch from main"
else
echo "Main branch doesn't exist, creating develop from current branch"
git checkout -b develop
fi
git push origin develop
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Check if branches differ
id: check-diff
run: |
# Now check for differences between current dev branch and develop (develop branch should exist now)
# Switch back to the original dev branch first
git checkout ${{ github.ref_name }}
# Fetch develop branch without checking it out
git fetch origin develop
COMMITS_AHEAD=$(git rev-list --count origin/develop..HEAD)
COMMITS_BEHIND=$(git rev-list --count HEAD..origin/develop)
echo "commits_ahead=$COMMITS_AHEAD" >> $GITHUB_OUTPUT
echo "commits_behind=$COMMITS_BEHIND" >> $GITHUB_OUTPUT
echo "${{ github.ref_name }} is $COMMITS_AHEAD commits ahead and $COMMITS_BEHIND commits behind develop"
if [ "$COMMITS_AHEAD" -gt 0 ]; then
echo "has_changes=true" >> $GITHUB_OUTPUT
else
echo "has_changes=false" >> $GITHUB_OUTPUT
fi
# Count TODOs in the codebase
TODO_COUNT=$(grep -r " TODO:" \
--include="*.ts" --include="*.js" --include="*.tsx" --include="*.jsx" \
--include="*.vue" --include="*.py" --include="*.java" --include="*.cs" \
--include="*.go" --include="*.rs" --include="*.cpp" --include="*.c" \
--include="*.h" . | wc -l || echo "0")
echo "todo_count=$TODO_COUNT" >> $GITHUB_OUTPUT
echo "Found $TODO_COUNT TODOs in the codebase"
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Create Pull Request
if: steps.check-pr.outputs.pr_exists == '0' && steps.check-diff.outputs.has_changes == 'true'
run: |
gh pr create \
--base develop \
--head ${{ github.ref_name }} \
--title "🔄 Auto-PR: Merge \`${{ github.ref_name }}\` → \`develop\`" \
--body "**Automated Pull Request** 🤖
This PR was automatically created to merge changes from \`${{ github.ref_name }}\` into \`develop\`.
**Changes:**
- Commits ahead of develop: ${{ steps.check-diff.outputs.commits_ahead }}
- Commits behind develop: ${{ steps.check-diff.outputs.commits_behind }}
- TODOs remaining in codebase: ${{ steps.check-diff.outputs.todo_count }}
**Review:** Please review the changes before merging."
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Skip PR creation - no changes
if: steps.check-pr.outputs.pr_exists == '0' && steps.check-diff.outputs.has_changes == 'false'
run: |
echo "⏭️ Skipping PR creation - no new commits in ${{ github.ref_name }} compared to develop"
- name: Update existing PR
if: steps.check-pr.outputs.pr_exists != '0'
run: |
echo "PR from ${{ github.ref_name }} to develop already exists. Updating with latest info..."
# Get the PR number
PR_NUMBER=$(gh pr list --base develop --head ${{ github.ref_name }} --state open --json number --jq '.[0].number')
# Update PR body with current stats
gh pr edit $PR_NUMBER --body "**Automated Pull Request** 🤖
This PR was automatically created to merge changes from \`${{ github.ref_name }}\` into \`develop\`.
**Changes:**
- Commits ahead of develop: ${{ steps.check-diff.outputs.commits_ahead }}
- Commits behind develop: ${{ steps.check-diff.outputs.commits_behind }}
- TODOs remaining in codebase: ${{ steps.check-diff.outputs.todo_count }}
**Review:** Please review the changes before merging.
_Last updated: $(date -u '+%Y-%m-%d %H:%M:%S UTC')_"
echo "Updated PR #$PR_NUMBER with latest commit and TODO counts"
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
pr-to-test:
name: Create/Update PR from develop → test
# Unconditional: the PR must exist for whatever is on develop, whoever pushed
# it. Previously this job skipped on automation commits, so a real change
# followed by a version-sync left no PR at all. The loop guard now sits on the
# only step that pushes (Resolve version conflicts), which is where it belongs.
if: github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest
steps:
- name: Generate token
id: generate_token
uses: actions/create-github-app-token@v3
with:
client-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- uses: actions/checkout@v6
with:
fetch-depth: 0
token: ${{ steps.generate_token.outputs.token }}
- name: Check if PR already exists
id: check-pr
run: |
PR_EXISTS=$(gh pr list --base test --head develop --state open --json number --jq length)
echo "pr_exists=$PR_EXISTS" >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Check if test branch exists
id: check-branch
run: |
git fetch origin --prune
if git ls-remote --heads origin test | grep -q "refs/heads/test"; then
echo "Test branch exists on remote"
echo "needs_branch_creation=false" >> $GITHUB_OUTPUT
else
echo "Test branch doesn't exist → needs creation"
echo "needs_branch_creation=true" >> $GITHUB_OUTPUT
fi
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Create test branch if it doesn't exist
if: steps.check-branch.outputs.needs_branch_creation == 'true' && steps.check-pr.outputs.pr_exists == '0'
run: |
# Try to create test branch from main, fallback to develop if main doesn't exist
if git ls-remote --heads origin main | grep -q "refs/heads/main"; then
git fetch origin main:main
git checkout -b test main
echo "Created test branch from main"
else
echo "Main branch doesn't exist, creating test from develop"
git checkout -b test develop
fi
git push origin test
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Check if branches differ
id: check-diff
run: |
# Now check for differences between develop and test (test branch should exist now)
# Switch back to develop branch first
git checkout develop
# Fetch test branch without checking it out
git fetch origin test
COMMITS_AHEAD=$(git rev-list --count origin/test..HEAD)
COMMITS_BEHIND=$(git rev-list --count HEAD..origin/test)
echo "commits_ahead=$COMMITS_AHEAD" >> $GITHUB_OUTPUT
echo "commits_behind=$COMMITS_BEHIND" >> $GITHUB_OUTPUT
echo "Develop is $COMMITS_AHEAD commits ahead and $COMMITS_BEHIND commits behind test"
if [ "$COMMITS_AHEAD" -gt 0 ]; then
echo "has_changes=true" >> $GITHUB_OUTPUT
else
echo "has_changes=false" >> $GITHUB_OUTPUT
fi
# Count TODOs in the codebase
TODO_COUNT=$(grep -r " TODO:" --include="*.ts" --include="*.js" --include="*.tsx" --include="*.jsx" --include="*.vue" --include="*.py" --include="*.java" --include="*.cs" --include="*.go" --include="*.rs" --include="*.cpp" --include="*.c" --include="*.h" . | wc -l || echo "0")
echo "todo_count=$TODO_COUNT" >> $GITHUB_OUTPUT
echo "Found $TODO_COUNT TODOs in the codebase"
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
# When develop is behind test (e.g. version bump commits on test),
# the PR will have merge conflicts on package.json version strings.
# Resolve by merging test into develop, keeping develop's versions.
# version-operations will re-stamp the correct beta version after merge anyway.
# THE LOOP GUARD. This step pushes to develop, which re-triggers this
# workflow. Skipping on our own absorb commit is what stops it recursing —
# it used to sit on the job, which also suppressed PR creation entirely.
- name: Resolve version conflicts with test
if: steps.check-diff.outputs.has_changes == 'true' && steps.check-diff.outputs.commits_behind != '0' && !contains(github.event.head_commit.message, 'proxy-smart-releaser')
run: |
echo "::group::Resolve version conflicts"
echo "Develop is ${{ steps.check-diff.outputs.commits_behind }} commits behind test — absorbing..."
git config user.name "proxy-smart-releaser[bot]"
git config user.email "proxy-smart-releaser[bot]@users.noreply.github.com"
# Merge test into develop, keeping develop's side on any conflicts
if git merge origin/test -X ours --no-edit \
-m "chore: absorb test branch (resolve version conflicts) [proxy-smart-releaser]"; then
echo "Merge completed cleanly"
else
echo "⚠️ Conflicts remain after -X ours — force-resolving all as ours"
CONFLICTED=$(git diff --name-only --diff-filter=U)
if [ -n "$CONFLICTED" ]; then
echo "$CONFLICTED" | xargs git checkout --ours
git add -A
fi
git commit --no-edit \
-m "chore: absorb test branch (resolve version conflicts) [proxy-smart-releaser]" || true
fi
# Push only if HEAD actually moved
if [ "$(git rev-parse HEAD)" != "$(git rev-parse origin/develop)" ]; then
# Fetch + merge (not rebase!) to handle concurrent pushes from release-alpha.
# Rebase fails silently on merge commits; merge -X ours is safe.
git fetch origin develop
git merge origin/develop -X ours --no-edit \
-m "chore: merge latest develop [proxy-smart-releaser]" || {
CONFLICTED=$(git diff --name-only --diff-filter=U)
if [ -n "$CONFLICTED" ]; then
echo "$CONFLICTED" | xargs git checkout --ours
git add -A
fi
git commit --no-edit \
-m "chore: merge latest develop [proxy-smart-releaser]" || true
}
git push origin develop || {
echo "⚠️ Push failed — will absorb on next cycle."
exit 0
}
echo "✅ Pushed merge commit — PR should now be conflict-free"
else
echo "✅ Already up-to-date with test"
fi
echo "::endgroup::"
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Create Pull Request
if: steps.check-pr.outputs.pr_exists == '0' && steps.check-diff.outputs.has_changes == 'true'
run: |
gh pr create \
--base test \
--head develop \
--title "🧪 Auto-PR: Merge \`develop\` → \`test\`" \
--body "**Automated Pull Request** 🤖
Commits ahead of test: ${{ steps.check-diff.outputs.commits_ahead }}
Commits behind test: ${{ steps.check-diff.outputs.commits_behind }}
TODOs remaining in codebase: ${{ steps.check-diff.outputs.todo_count }}"
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Skip PR creation – no changes
if: steps.check-pr.outputs.pr_exists == '0' && steps.check-diff.outputs.has_changes == 'false'
run: echo "⏭️ No new commits in develop compared to test"
- name: Update existing PR
if: steps.check-pr.outputs.pr_exists != '0'
run: |
echo "PR from develop to test already exists. Updating with latest info..."
# Get the PR number
PR_NUMBER=$(gh pr list --base test --head develop --state open --json number --jq '.[0].number')
# Update PR body with current stats
gh pr edit $PR_NUMBER --body "**Automated Pull Request** 🤖
Commits ahead of test: ${{ steps.check-diff.outputs.commits_ahead }}
Commits behind test: ${{ steps.check-diff.outputs.commits_behind }}
TODOs remaining in codebase: ${{ steps.check-diff.outputs.todo_count }}
_Last updated: $(date -u '+%Y-%m-%d %H:%M:%S UTC')_"
echo "Updated PR #$PR_NUMBER with latest commit and TODO counts"
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
# Gate for AUTO-MERGE only, deliberately after the PR exists. It used to run
# first, so a 10-minute wait delayed the PR itself and an absorb commit (which
# never has an Alpha Release) burned the whole timeout. Creating a PR is always
# safe; only merging it needs a green build.
- name: Wait for Alpha Release check
# Nothing to gate when there is no diff, and an absorb commit never gets an
# Alpha Release — waiting on one would just burn the full timeout.
if: steps.check-diff.outputs.has_changes == 'true' && !contains(github.event.head_commit.message, 'proxy-smart-releaser')
run: |
SHA="${{ github.sha }}"
CHECK_PREFIX="Alpha Release"
MAX_WAIT=600 # 10 min — Alpha Release typically finishes in ~3 min
POLL_INTERVAL=15
ELAPSED=0
echo "Waiting for '$CHECK_PREFIX' on $SHA..."
while [ "$ELAPSED" -lt "$MAX_WAIT" ]; do
# Match any check run whose name starts with "Alpha Release"
# GitHub nests job names as "Workflow / Job / Nested Job"
CHECKS=$(gh api "repos/${{ github.repository }}/commits/$SHA/check-runs" \
--jq "[.check_runs[] | select(.name | startswith(\"$CHECK_PREFIX\"))]" \
2>/dev/null || echo "[]")
COUNT=$(echo "$CHECKS" | jq 'length')
COMPLETED=$(echo "$CHECKS" | jq '[.[] | select(.status == "completed")] | length')
FAILED=$(echo "$CHECKS" | jq '[.[] | select(.conclusion == "failure" or .conclusion == "cancelled" or .conclusion == "timed_out")] | length')
echo " [$CHECK_PREFIX] found=$COUNT completed=$COMPLETED failed=$FAILED (${ELAPSED}s)"
if [ "$COUNT" -gt 0 ] && [ "$COUNT" -eq "$COMPLETED" ]; then
if [ "$FAILED" -gt 0 ]; then
echo "❌ Alpha Release failed ($FAILED check(s) failed) — aborting."
echo " A failing build must not be promoted to test."
exit 1
fi
# success or skipped — both are fine
echo "✅ Alpha Release completed ($COMPLETED checks passed) — proceeding."
break
fi
# If check doesn't exist yet (not_found), keep waiting — it may not have registered yet
sleep "$POLL_INTERVAL"
ELAPSED=$((ELAPSED + POLL_INTERVAL))
done
if [ "$ELAPSED" -ge "$MAX_WAIT" ]; then
echo "⚠️ Timed out after ${MAX_WAIT}s — Alpha Release still not completed."
echo " Proceeding with PR creation (auto-merge requires branch protection checks anyway)."
fi
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Enable auto-merge
if: steps.check-diff.outputs.has_changes == 'true'
run: |
PR_NUMBER=$(gh pr list --base test --head develop --state open --json number --jq '.[0].number')
if [ -z "$PR_NUMBER" ]; then echo "No open PR — nothing to auto-merge"; exit 0; fi
gh pr merge $PR_NUMBER --auto --merge || echo "⚠️ Auto-merge could not be enabled (may need repo setting)"
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
pr-to-main:
name: Create/Update PR from test → main
# Unconditional, same reasoning as pr-to-test: the last push to test is often
# `chore: absorb main branch ... [proxy-smart-releaser]`, and skipping on it
# meant no test → main PR was ever opened for the work underneath.
if: github.ref == 'refs/heads/test'
runs-on: ubuntu-latest
steps:
- name: Generate token
id: generate_token
uses: actions/create-github-app-token@v3
with:
client-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- uses: actions/checkout@v6
with:
fetch-depth: 0
token: ${{ steps.generate_token.outputs.token }}
- name: Check if PR already exists
id: check-pr
run: |
PR_EXISTS=$(gh pr list --base main --head test --state open --json number --jq length)
echo "pr_exists=$PR_EXISTS" >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Check if branches differ
id: check-diff
run: |
# Fetch all branches to ensure we have the latest refs
git fetch origin --prune
# Check if main branch exists on remote
if git ls-remote --heads origin main | grep -q "refs/heads/main"; then
echo "Main branch exists on remote"
git fetch origin main:main
# Check if there are any commits between main and test
COMMITS_AHEAD=$(git rev-list --count main..test)
COMMITS_BEHIND=$(git rev-list --count test..main)
echo "commits_ahead=$COMMITS_AHEAD" >> $GITHUB_OUTPUT
echo "commits_behind=$COMMITS_BEHIND" >> $GITHUB_OUTPUT
echo "Branch test is $COMMITS_AHEAD commits ahead and $COMMITS_BEHIND commits behind main"
# Only create PR if we have commits to contribute
if [ "$COMMITS_AHEAD" -gt 0 ]; then
echo "has_changes=true" >> $GITHUB_OUTPUT
else
echo "has_changes=false" >> $GITHUB_OUTPUT
fi
else
echo "Main branch doesn't exist on remote, this will be the first PR to create main"
echo "commits_ahead=1" >> $GITHUB_OUTPUT
echo "commits_behind=0" >> $GITHUB_OUTPUT
echo "has_changes=true" >> $GITHUB_OUTPUT
fi
# Count TODOs in the codebase
TODO_COUNT=$(grep -r " TODO:" --include="*.ts" --include="*.js" --include="*.tsx" --include="*.jsx" --include="*.vue" --include="*.py" --include="*.java" --include="*.cs" --include="*.go" --include="*.rs" --include="*.cpp" --include="*.c" --include="*.h" . | wc -l || echo "0")
echo "todo_count=$TODO_COUNT" >> $GITHUB_OUTPUT
echo "Found $TODO_COUNT TODOs in the codebase"
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
# Same as pr-to-test: resolve version conflicts by merging main into test
# Loop guard — this step pushes to test. See pr-to-test.
- name: Resolve version conflicts with main
if: steps.check-diff.outputs.has_changes == 'true' && steps.check-diff.outputs.commits_behind != '0' && !contains(github.event.head_commit.message, 'proxy-smart-releaser')
run: |
echo "::group::Resolve version conflicts"
echo "Test is ${{ steps.check-diff.outputs.commits_behind }} commits behind main — absorbing..."
git config user.name "proxy-smart-releaser[bot]"
git config user.email "proxy-smart-releaser[bot]@users.noreply.github.com"
git checkout test
if git merge origin/main -X ours --no-edit \
-m "chore: absorb main branch (resolve version conflicts) [proxy-smart-releaser]"; then
echo "Merge completed cleanly"
else
echo "⚠️ Conflicts remain after -X ours — force-resolving all as ours"
CONFLICTED=$(git diff --name-only --diff-filter=U)
if [ -n "$CONFLICTED" ]; then
echo "$CONFLICTED" | xargs git checkout --ours
git add -A
fi
git commit --no-edit \
-m "chore: absorb main branch (resolve version conflicts) [proxy-smart-releaser]" || true
fi
if [ "$(git rev-parse HEAD)" != "$(git rev-parse origin/test)" ]; then
# Fetch + merge (not rebase!) to handle concurrent pushes from release-beta.
# Rebase fails silently on merge commits; merge -X ours is safe.
git fetch origin test
git merge origin/test -X ours --no-edit \
-m "chore: merge latest test [proxy-smart-releaser]" || {
CONFLICTED=$(git diff --name-only --diff-filter=U)
if [ -n "$CONFLICTED" ]; then
echo "$CONFLICTED" | xargs git checkout --ours
git add -A
fi
git commit --no-edit \
-m "chore: merge latest test [proxy-smart-releaser]" || true
}
git push origin test || {
echo "⚠️ Push failed — will absorb on next cycle."
exit 0
}
echo "✅ Pushed merge commit — PR should now be conflict-free"
else
echo "✅ Already up-to-date with main"
fi
echo "::endgroup::"
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Create Pull Request
if: steps.check-pr.outputs.pr_exists == '0' && steps.check-diff.outputs.has_changes == 'true'
run: |
gh pr create \
--base main \
--head test \
--title "🚀 Auto-PR: Merge \`test\` → \`main\`" \
--body "**Automated Pull Request** 🤖
This PR was automatically created to merge changes from \`test\` into \`main\`.
**Changes:**
- Commits ahead of main: ${{ steps.check-diff.outputs.commits_ahead }}
- Commits behind main: ${{ steps.check-diff.outputs.commits_behind }}
- TODOs remaining in codebase: ${{ steps.check-diff.outputs.todo_count }}
**Review:** Please review the changes before merging."
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Skip PR creation - no changes
if: steps.check-pr.outputs.pr_exists == '0' && steps.check-diff.outputs.has_changes == 'false'
run: |
echo "⏭️ Skipping PR creation - no new commits in test compared to main"
- name: Update existing PR
if: steps.check-pr.outputs.pr_exists != '0'
run: |
echo "PR from test to main already exists. Updating with latest info..."
# Get the PR number
PR_NUMBER=$(gh pr list --base main --head test --state open --json number --jq '.[0].number')
# Update PR body with current stats
gh pr edit $PR_NUMBER --body "**Automated Pull Request** 🤖
This PR was automatically created to merge changes from \`test\` into \`main\`.
**Changes:**
- Commits ahead of main: ${{ steps.check-diff.outputs.commits_ahead }}
- Commits behind main: ${{ steps.check-diff.outputs.commits_behind }}
- TODOs remaining in codebase: ${{ steps.check-diff.outputs.todo_count }}
**Review:** Please review the changes before merging.
_Last updated: $(date -u '+%Y-%m-%d %H:%M:%S UTC')_"
echo "Updated PR #$PR_NUMBER with latest commit and TODO counts"
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
ai-dev-to-ai-test:
name: Create/Update PR from ai/dev/* → ai/test/*
if: startsWith(github.ref, 'refs/heads/ai/dev/') && !contains(github.event.head_commit.message, '🔄 Update version') && !contains(github.event.head_commit.message, '🤖 Update client APIs') && !contains(github.event.head_commit.message, 'proxy-smart-releaser')
runs-on: ubuntu-latest
steps:
- name: Generate token
id: generate_token
uses: actions/create-github-app-token@v3
with:
client-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Extract branch name
id: extract-branch
run: |
# Extract the branch name after ai/dev/
BRANCH_NAME=${{ github.ref_name }}
TARGET_BRANCH="ai/test/${BRANCH_NAME#ai/dev/}"
echo "target_branch=$TARGET_BRANCH" >> $GITHUB_OUTPUT
echo "Target branch: $TARGET_BRANCH"
- name: Check if PR already exists
id: check-pr
run: |
PR_EXISTS=$(gh pr list --base ${{ steps.extract-branch.outputs.target_branch }} --head ${{ github.ref_name }} --state open --json number --jq length)
echo "pr_exists=$PR_EXISTS" >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Check if target branch exists
id: check-branch
run: |
git fetch origin --prune
if git ls-remote --heads origin ${{ steps.extract-branch.outputs.target_branch }} | grep -q "refs/heads/${{ steps.extract-branch.outputs.target_branch }}"; then
echo "Target branch exists on remote"
echo "needs_branch_creation=false" >> $GITHUB_OUTPUT
else
echo "Target branch doesn't exist → needs creation"
echo "needs_branch_creation=true" >> $GITHUB_OUTPUT
fi
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Create target branch if it doesn't exist
if: steps.check-branch.outputs.needs_branch_creation == 'true' && steps.check-pr.outputs.pr_exists == '0'
run: |
# Create target ai/test branch from current ai/dev branch
git checkout -b ${{ steps.extract-branch.outputs.target_branch }}
git push origin ${{ steps.extract-branch.outputs.target_branch }}
echo "Created ${{ steps.extract-branch.outputs.target_branch }} branch from current ai/dev branch"
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Check if branches differ
id: check-diff
run: |
git checkout ${{ github.ref_name }}
git fetch origin ${{ steps.extract-branch.outputs.target_branch }}
COMMITS_AHEAD=$(git rev-list --count origin/${{ steps.extract-branch.outputs.target_branch }}..HEAD)
COMMITS_BEHIND=$(git rev-list --count HEAD..origin/${{ steps.extract-branch.outputs.target_branch }})
echo "commits_ahead=$COMMITS_AHEAD" >> $GITHUB_OUTPUT
echo "commits_behind=$COMMITS_BEHIND" >> $GITHUB_OUTPUT
echo "${{ github.ref_name }} is $COMMITS_AHEAD commits ahead and $COMMITS_BEHIND commits behind ${{ steps.extract-branch.outputs.target_branch }}"
if [ "$COMMITS_AHEAD" -gt 0 ]; then
echo "has_changes=true" >> $GITHUB_OUTPUT
else
echo "has_changes=false" >> $GITHUB_OUTPUT
fi
- name: Create Pull Request
if: steps.check-pr.outputs.pr_exists == '0' && steps.check-diff.outputs.has_changes == 'true'
run: |
gh pr create \
--base ${{ steps.extract-branch.outputs.target_branch }} \
--head ${{ github.ref_name }} \
--title "🤖 AI-PR: Merge \`${{ github.ref_name }}\` → \`${{ steps.extract-branch.outputs.target_branch }}\`" \
--body "**AI-Generated Pull Request** 🤖
This PR was automatically created to merge AI-enhanced changes from \`${{ github.ref_name }}\` into \`${{ steps.extract-branch.outputs.target_branch }}\`.
**Changes:**
- Commits ahead of target: ${{ steps.check-diff.outputs.commits_ahead }}
- Commits behind target: ${{ steps.check-diff.outputs.commits_behind }}
**Review:** AI-generated code - please review carefully before merging."
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Update existing PR
if: steps.check-pr.outputs.pr_exists != '0'
run: |
PR_NUMBER=$(gh pr list --base ${{ steps.extract-branch.outputs.target_branch }} --head ${{ github.ref_name }} --state open --json number --jq '.[0].number')
gh pr edit $PR_NUMBER --body "**AI-Generated Pull Request** 🤖
This PR was automatically created to merge AI-enhanced changes from \`${{ github.ref_name }}\` into \`${{ steps.extract-branch.outputs.target_branch }}\`.
**Changes:**
- Commits ahead of target: ${{ steps.check-diff.outputs.commits_ahead }}
- Commits behind target: ${{ steps.check-diff.outputs.commits_behind }}
**Review:** AI-generated code - please review carefully before merging.
_Last updated: $(date -u '+%Y-%m-%d %H:%M:%S UTC')_"
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
ai-test-to-dev:
name: Create/Update PR from ai/test/* → dev/*
if: startsWith(github.ref, 'refs/heads/ai/test/') && !contains(github.event.head_commit.message, '🔄 Update version') && !contains(github.event.head_commit.message, '🤖 Update client APIs') && !contains(github.event.head_commit.message, 'proxy-smart-releaser')
runs-on: ubuntu-latest
steps:
- name: Generate token
id: generate_token
uses: actions/create-github-app-token@v3
with:
client-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Extract branch name
id: extract-branch
run: |
# Extract the branch name after ai/test/
BRANCH_NAME=${{ github.ref_name }}
TARGET_BRANCH="dev/${BRANCH_NAME#ai/test/}"
echo "target_branch=$TARGET_BRANCH" >> $GITHUB_OUTPUT
echo "Target branch: $TARGET_BRANCH"
- name: Check if PR already exists
id: check-pr
run: |
PR_EXISTS=$(gh pr list --base ${{ steps.extract-branch.outputs.target_branch }} --head ${{ github.ref_name }} --state open --json number --jq length)
echo "pr_exists=$PR_EXISTS" >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Check if target branch exists
id: check-branch
run: |
git fetch origin --prune
if git ls-remote --heads origin ${{ steps.extract-branch.outputs.target_branch }} | grep -q "refs/heads/${{ steps.extract-branch.outputs.target_branch }}"; then
echo "Target branch exists on remote"
echo "needs_branch_creation=false" >> $GITHUB_OUTPUT
else
echo "Target branch doesn't exist → needs creation"
echo "needs_branch_creation=true" >> $GITHUB_OUTPUT
fi
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Create target branch if it doesn't exist
if: steps.check-branch.outputs.needs_branch_creation == 'true' && steps.check-pr.outputs.pr_exists == '0'
run: |
# Create target dev branch from develop if it exists, otherwise from main
if git ls-remote --heads origin develop | grep -q "refs/heads/develop"; then
git fetch origin develop:develop
git checkout -b ${{ steps.extract-branch.outputs.target_branch }} develop
echo "Created ${{ steps.extract-branch.outputs.target_branch }} branch from develop"
elif git ls-remote --heads origin main | grep -q "refs/heads/main"; then
git fetch origin main:main
git checkout -b ${{ steps.extract-branch.outputs.target_branch }} main
echo "Created ${{ steps.extract-branch.outputs.target_branch }} branch from main"
else
echo "Neither develop nor main exists, creating from current branch"
git checkout -b ${{ steps.extract-branch.outputs.target_branch }}
fi
git push origin ${{ steps.extract-branch.outputs.target_branch }}
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Check if branches differ
id: check-diff
run: |
git checkout ${{ github.ref_name }}
git fetch origin ${{ steps.extract-branch.outputs.target_branch }}
COMMITS_AHEAD=$(git rev-list --count origin/${{ steps.extract-branch.outputs.target_branch }}..HEAD)
COMMITS_BEHIND=$(git rev-list --count HEAD..origin/${{ steps.extract-branch.outputs.target_branch }})
echo "commits_ahead=$COMMITS_AHEAD" >> $GITHUB_OUTPUT
echo "commits_behind=$COMMITS_BEHIND" >> $GITHUB_OUTPUT
echo "${{ github.ref_name }} is $COMMITS_AHEAD commits ahead and $COMMITS_BEHIND commits behind ${{ steps.extract-branch.outputs.target_branch }}"
if [ "$COMMITS_AHEAD" -gt 0 ]; then
echo "has_changes=true" >> $GITHUB_OUTPUT
else
echo "has_changes=false" >> $GITHUB_OUTPUT
fi
- name: Create Pull Request
if: steps.check-pr.outputs.pr_exists == '0' && steps.check-diff.outputs.has_changes == 'true'
run: |
gh pr create \
--base ${{ steps.extract-branch.outputs.target_branch }} \
--head ${{ github.ref_name }} \
--title "🧪 AI-Test-PR: Merge \`${{ github.ref_name }}\` → \`${{ steps.extract-branch.outputs.target_branch }}\`" \
--body "**AI Test → Dev Pull Request** 🧪
This PR was automatically created to merge AI-generated test changes from \`${{ github.ref_name }}\` into \`${{ steps.extract-branch.outputs.target_branch }}\`.
**Changes:**
- Commits ahead of target: ${{ steps.check-diff.outputs.commits_ahead }}
- Commits behind target: ${{ steps.check-diff.outputs.commits_behind }}
**Review:** AI-generated tests and code - please review test coverage and implementation."
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Update existing PR
if: steps.check-pr.outputs.pr_exists != '0'
run: |
PR_NUMBER=$(gh pr list --base ${{ steps.extract-branch.outputs.target_branch }} --head ${{ github.ref_name }} --state open --json number --jq '.[0].number')
gh pr edit $PR_NUMBER --body "**AI Test → Dev Pull Request** 🧪
This PR was automatically created to merge AI-generated test changes from \`${{ github.ref_name }}\` into \`${{ steps.extract-branch.outputs.target_branch }}\`.
**Changes:**
- Commits ahead of target: ${{ steps.check-diff.outputs.commits_ahead }}
- Commits behind target: ${{ steps.check-diff.outputs.commits_behind }}
**Review:** AI-generated tests and code - please review test coverage and implementation.
_Last updated: $(date -u '+%Y-%m-%d %H:%M:%S UTC')_"
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
ai-checklist-to-ai-dev:
name: Create/Update PR from ai/checklist/* → ai/dev/*
if: startsWith(github.ref, 'refs/heads/ai/checklist/') && !contains(github.event.head_commit.message, '🔄 Update version') && !contains(github.event.head_commit.message, '🤖 Update client APIs') && !contains(github.event.head_commit.message, 'proxy-smart-releaser')
runs-on: ubuntu-latest
steps:
- name: Generate token
id: generate_token
uses: actions/create-github-app-token@v3
with:
client-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Extract branch name
id: extract-branch
run: |
# Extract the branch name after ai/checklist/
BRANCH_NAME=${{ github.ref_name }}
TARGET_BRANCH="ai/dev/${BRANCH_NAME#ai/checklist/}"
echo "target_branch=$TARGET_BRANCH" >> $GITHUB_OUTPUT
echo "Target branch: $TARGET_BRANCH"
- name: Check if PR already exists
id: check-pr
run: |
PR_EXISTS=$(gh pr list --base ${{ steps.extract-branch.outputs.target_branch }} --head ${{ github.ref_name }} --state open --json number --jq length)
echo "pr_exists=$PR_EXISTS" >> $GITHUB_OUTPUT
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Check if target branch exists
id: check-branch
run: |
git fetch origin --prune
if git ls-remote --heads origin ${{ steps.extract-branch.outputs.target_branch }} | grep -q "refs/heads/${{ steps.extract-branch.outputs.target_branch }}"; then
echo "Target branch exists on remote"
echo "needs_branch_creation=false" >> $GITHUB_OUTPUT
else
echo "Target branch doesn't exist → needs creation"
echo "needs_branch_creation=true" >> $GITHUB_OUTPUT
fi
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Create target branch if it doesn't exist
if: steps.check-branch.outputs.needs_branch_creation == 'true' && steps.check-pr.outputs.pr_exists == '0'
run: |
# Create target ai/dev branch from current ai/checklist branch
git checkout -b ${{ steps.extract-branch.outputs.target_branch }}
git push origin ${{ steps.extract-branch.outputs.target_branch }}
echo "Created ${{ steps.extract-branch.outputs.target_branch }} branch from ${{ github.ref_name }}"
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Check if branches differ
id: check-diff
run: |
git checkout ${{ github.ref_name }}
git fetch origin ${{ steps.extract-branch.outputs.target_branch }}
COMMITS_AHEAD=$(git rev-list --count origin/${{ steps.extract-branch.outputs.target_branch }}..HEAD)
COMMITS_BEHIND=$(git rev-list --count HEAD..origin/${{ steps.extract-branch.outputs.target_branch }})
echo "commits_ahead=$COMMITS_AHEAD" >> $GITHUB_OUTPUT
echo "commits_behind=$COMMITS_BEHIND" >> $GITHUB_OUTPUT
echo "${{ github.ref_name }} is $COMMITS_AHEAD commits ahead and $COMMITS_BEHIND commits behind ${{ steps.extract-branch.outputs.target_branch }}"
if [ "$COMMITS_AHEAD" -gt 0 ]; then
echo "has_changes=true" >> $GITHUB_OUTPUT
else
echo "has_changes=false" >> $GITHUB_OUTPUT
fi
- name: Create Pull Request
if: steps.check-pr.outputs.pr_exists == '0' && steps.check-diff.outputs.has_changes == 'true'
run: |
gh pr create \
--base ${{ steps.extract-branch.outputs.target_branch }} \
--head ${{ github.ref_name }} \
--title "✅ AI-Checklist-PR: Merge \`${{ github.ref_name }}\` → \`${{ steps.extract-branch.outputs.target_branch }}\`" \
--body "**AI Checklist → Test Pull Request** ✅
This PR was automatically created to merge AI-implemented checklist items from \`${{ github.ref_name }}\` into \`${{ steps.extract-branch.outputs.target_branch }}\`.
**Changes:**
- Commits ahead of target: ${{ steps.check-diff.outputs.commits_ahead }}
- Commits behind target: ${{ steps.check-diff.outputs.commits_behind }}
**Review:** AI-implemented checklist features - please review implementation and prepare for testing."
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
- name: Update existing PR
if: steps.check-pr.outputs.pr_exists != '0'
run: |
PR_NUMBER=$(gh pr list --base ${{ steps.extract-branch.outputs.target_branch }} --head ${{ github.ref_name }} --state open --json number --jq '.[0].number')
gh pr edit $PR_NUMBER --body "**AI Checklist → Test Pull Request** ✅
This PR was automatically created to merge AI-implemented checklist items from \`${{ github.ref_name }}\` into \`${{ steps.extract-branch.outputs.target_branch }}\`.
**Changes:**
- Commits ahead of target: ${{ steps.check-diff.outputs.commits_ahead }}
- Commits behind target: ${{ steps.check-diff.outputs.commits_behind }}
**Review:** AI-implemented checklist features - please review implementation and prepare for testing.
_Last updated: $(date -u '+%Y-%m-%d %H:%M:%S UTC')_"
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}