mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2025-12-24 02:39:20 -05:00
- Removed fork-specific .gitattributes - Restored upstream .github/workflows/ - Restored upstream cookbook/version_info.py
236 lines
10 KiB
YAML
236 lines
10 KiB
YAML
name: Create Upstream PR
|
||
|
||
on:
|
||
workflow_run:
|
||
workflows: ["Push Workflow"]
|
||
types:
|
||
- completed
|
||
branches: [working]
|
||
workflow_dispatch:
|
||
|
||
permissions:
|
||
contents: write
|
||
pull-requests: write
|
||
|
||
jobs:
|
||
create-upstream-pr:
|
||
runs-on: ubuntu-latest
|
||
concurrency:
|
||
group: upstream-pr
|
||
cancel-in-progress: true
|
||
if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success'
|
||
steps:
|
||
- name: Generate GitHub App token (for branch push)
|
||
id: generate_token_push
|
||
uses: actions/create-github-app-token@v2
|
||
with:
|
||
app-id: ${{ secrets.BOT_APP_ID }}
|
||
private-key: ${{ secrets.BOT_PRIVATE_KEY }}
|
||
|
||
- name: Checkout fork
|
||
uses: actions/checkout@v4
|
||
with:
|
||
fetch-depth: 0
|
||
token: ${{ steps.generate_token_push.outputs.token }}
|
||
|
||
- name: Setup git user
|
||
run: |
|
||
git config user.name "GitHub Action"
|
||
git config user.email "action@github.com"
|
||
|
||
- name: Add upstream remote
|
||
run: |
|
||
git remote get-url upstream || git remote add upstream https://github.com/TandoorRecipes/recipes.git
|
||
git fetch upstream
|
||
|
||
- name: Ensure jq is available
|
||
run: |
|
||
if ! command -v jq &> /dev/null; then
|
||
sudo apt-get update && sudo apt-get install -y jq
|
||
fi
|
||
|
||
- name: Create upstream PR branch
|
||
id: create_branch
|
||
run: |
|
||
BRANCH_NAME="upstream-pr-$(date +%Y%m%d-%H%M%S)"
|
||
git checkout -b "$BRANCH_NAME" || { echo "❌ Failed to create branch $BRANCH_NAME"; exit 1; }
|
||
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
||
echo "✅ Created branch: $BRANCH_NAME"
|
||
|
||
- name: Restore upstream infrastructure files
|
||
id: restore_infra
|
||
run: |
|
||
BRANCH_NAME="${{ steps.create_branch.outputs.branch_name }}"
|
||
git checkout "$BRANCH_NAME"
|
||
git rm .gitattributes || echo "ℹ️ .gitattributes not present, skipping removal."
|
||
git checkout upstream/tandoor-1 -- .github/workflows/ || echo "ℹ️ No workflows to restore."
|
||
git checkout upstream/tandoor-1 -- cookbook/version_info.py || echo "ℹ️ No version_info.py to restore."
|
||
git add .
|
||
if ! git diff --cached --quiet; then
|
||
git commit -m $'Restore upstream infrastructure files for PR\n\n- Removed fork-specific .gitattributes\n- Restored upstream .github/workflows/\n- Restored upstream cookbook/version_info.py'
|
||
echo "✅ Infrastructure files restored and committed."
|
||
else
|
||
echo "ℹ️ No infrastructure changes to commit."
|
||
fi
|
||
|
||
- name: Push branch to fork (after infra commit)
|
||
env:
|
||
GITHUB_TOKEN: ${{ steps.generate_token_push.outputs.token }}
|
||
run: |
|
||
BRANCH_NAME="${{ steps.create_branch.outputs.branch_name }}"
|
||
echo "Pushing branch $BRANCH_NAME after infra file restore."
|
||
git push --set-upstream https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git "$BRANCH_NAME"
|
||
echo "✅ Branch pushed: $BRANCH_NAME (infra files)"
|
||
|
||
|
||
- name: Merge upstream branch
|
||
id: merge_upstream
|
||
run: |
|
||
BRANCH_NAME="${{ steps.create_branch.outputs.branch_name }}"
|
||
git checkout "$BRANCH_NAME"
|
||
if git merge --no-edit upstream/tandoor-1; then
|
||
echo "✅ Merged upstream/tandoor-1 into $BRANCH_NAME"
|
||
if ! git diff --cached --quiet || [ -n "$(git log origin/$BRANCH_NAME..$BRANCH_NAME --oneline)" ]; then
|
||
echo "merge_commit=true" >> $GITHUB_OUTPUT
|
||
else
|
||
echo "merge_commit=false" >> $GITHUB_OUTPUT
|
||
fi
|
||
else
|
||
echo "❌ Merge conflict detected during merge with upstream/tandoor-1. Please resolve conflicts manually." >&2
|
||
exit 1
|
||
fi
|
||
|
||
- name: Push branch to fork (after merge)
|
||
if: steps.merge_upstream.outputs.merge_commit == 'true'
|
||
env:
|
||
GITHUB_TOKEN: ${{ steps.generate_token_push.outputs.token }}
|
||
run: |
|
||
BRANCH_NAME="${{ steps.create_branch.outputs.branch_name }}"
|
||
echo "Pushing branch $BRANCH_NAME after merge."
|
||
git push https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git "$BRANCH_NAME"
|
||
echo "✅ Branch pushed: $BRANCH_NAME (after merge)"
|
||
|
||
|
||
- name: Get commit list
|
||
id: get_commits
|
||
run: |
|
||
BRANCH_NAME="${{ steps.create_branch.outputs.branch_name }}"
|
||
COMMITS_RAW=$(git log upstream/tandoor-1..$BRANCH_NAME --oneline)
|
||
if [ -z "$COMMITS_RAW" ]; then
|
||
echo "has_changes=false" >> $GITHUB_OUTPUT
|
||
echo "✅ No commits to contribute - exiting gracefully"
|
||
exit 0
|
||
fi
|
||
echo "commits_raw<<EOF" >> $GITHUB_OUTPUT
|
||
printf "%s\n" "$COMMITS_RAW" >> $GITHUB_OUTPUT
|
||
echo "EOF" >> $GITHUB_OUTPUT
|
||
echo "has_changes=true" >> $GITHUB_OUTPUT
|
||
|
||
|
||
- name: Get changed files
|
||
id: get_files
|
||
run: |
|
||
BRANCH_NAME="${{ steps.create_branch.outputs.branch_name }}"
|
||
CHANGED_FILES=$(git diff upstream/tandoor-1..$BRANCH_NAME --name-only)
|
||
echo "changed_files<<EOF" >> $GITHUB_OUTPUT
|
||
printf "%s\n" "$CHANGED_FILES" >> $GITHUB_OUTPUT
|
||
echo "EOF" >> $GITHUB_OUTPUT
|
||
CODE_COUNT=$(echo "$CHANGED_FILES" | grep -c '^' || true)
|
||
echo "code_count=$CODE_COUNT" >> $GITHUB_OUTPUT
|
||
|
||
- name: Summarize changes
|
||
id: summarize_changes
|
||
run: |
|
||
CODE_COUNT=${{ steps.get_files.outputs.code_count }}
|
||
CHANGES_SUMMARY="Modified $CODE_COUNT code files"
|
||
echo "changes_summary=$CHANGES_SUMMARY" >> $GITHUB_OUTPUT
|
||
|
||
- name: Prepare commit subjects and JSON
|
||
id: prepare_commits
|
||
run: |
|
||
COMMITS_RAW="${{ steps.get_commits.outputs.commits_raw }}"
|
||
CODE_FILES=( $(echo "${{ steps.get_files.outputs.changed_files }}") )
|
||
FILTERED_COMMITS_JSON="[]"
|
||
COMMIT_SUBJECTS_ARRAY=()
|
||
INFRA_PATTERNS='^\.github/|^cookbook/version_info\.py$|^\.gitattributes$'
|
||
while IFS= read -r commit_line; do
|
||
if [ -z "$commit_line" ]; then continue; fi
|
||
COMMIT_SHA=$(echo "$commit_line" | cut -d' ' -f1)
|
||
COMMIT_SUBJECT=$(echo "$commit_line" | cut -d' ' -f2-)
|
||
mapfile -t COMMIT_FILES < <(git diff-tree --no-commit-id --name-only -r "$COMMIT_SHA" | grep -Ev "$INFRA_PATTERNS")
|
||
# Only include commit if it touches at least one non-infra file that is still different
|
||
INCLUDE_COMMIT=false
|
||
for file in "${COMMIT_FILES[@]}"; do
|
||
for code_file in "${CODE_FILES[@]}"; do
|
||
if [ "$file" = "$code_file" ]; then
|
||
INCLUDE_COMMIT=true
|
||
break 2
|
||
fi
|
||
done
|
||
done
|
||
if [ "$INCLUDE_COMMIT" = true ]; then
|
||
COMMIT_SUBJECTS_ARRAY+=("- $COMMIT_SUBJECT")
|
||
FILES_JSON=$(printf '%s\n' "${COMMIT_FILES[@]}" | jq -R . | jq -s .)
|
||
COMMIT_JSON=$(jq -n --arg sha "$COMMIT_SHA" --arg subject "$COMMIT_SUBJECT" --argjson files "$FILES_JSON" '{sha: $sha, subject: $subject, files: $files}')
|
||
FILTERED_COMMITS_JSON=$(echo "$FILTERED_COMMITS_JSON" | jq --argjson item "$COMMIT_JSON" '. + [$item]')
|
||
fi
|
||
done <<< "$COMMITS_RAW"
|
||
echo 'commits_json<<EOF' >> $GITHUB_OUTPUT
|
||
printf "%s\n" "$FILTERED_COMMITS_JSON" >> $GITHUB_OUTPUT
|
||
echo 'EOF' >> $GITHUB_OUTPUT
|
||
echo "commit_subjects<<EOF" >> $GITHUB_OUTPUT
|
||
printf '%s\n' "${COMMIT_SUBJECTS_ARRAY[@]}" >> $GITHUB_OUTPUT
|
||
echo 'EOF' >> $GITHUB_OUTPUT
|
||
|
||
|
||
- name: Build PR content
|
||
if: steps.get_commits.outputs.has_changes == 'true'
|
||
id: build_pr_content
|
||
uses: actions/github-script@v7
|
||
env:
|
||
COMMITS_JSON: ${{ steps.prepare_commits.outputs.commits_json }}
|
||
CHANGES_SUMMARY: ${{ steps.summarize_changes.outputs.changes_summary }}
|
||
BRANCH_NAME: ${{ steps.create_branch.outputs.branch_name }}
|
||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||
with:
|
||
github-token: ${{ steps.generate_token_push.outputs.token }}
|
||
script: |
|
||
const commits = JSON.parse(process.env.COMMITS_JSON || '[]');
|
||
const changesSummary = process.env.CHANGES_SUMMARY || 'Changes from fork';
|
||
const branchName = process.env.BRANCH_NAME || '';
|
||
const repo = process.env.GITHUB_REPOSITORY || '';
|
||
const [owner, reponame] = repo.split('/');
|
||
const nCommits = commits.length;
|
||
let prTitle = 'Sync ' + nCommits + ' commit' + (nCommits !== 1 ? 's' : '') + ' from fork:';
|
||
if (nCommits > 0) {
|
||
prTitle += ' ' + commits[0].subject;
|
||
}
|
||
let prBody = `This PR syncs ${nCommits} commit${nCommits !== 1 ? 's' : ''} from branch ${branchName}.\n\n`;
|
||
prBody += `**Changes Summary:**\n${changesSummary}\n\n`;
|
||
prBody += `Commits included:\n`;
|
||
for (const c of commits) {
|
||
prBody += `- ${c.subject} ([${c.sha}](https://github.com/${owner}/${reponame}/commit/${c.sha}))\n`;
|
||
}
|
||
prBody += `\n---\n`;
|
||
core.setOutput('prTitle', prTitle);
|
||
core.setOutput('prBody', prBody);
|
||
|
||
|
||
- name: Print PR creation instructions
|
||
if: steps.get_commits.outputs.has_changes == 'true'
|
||
env:
|
||
BRANCH_NAME: ${{ steps.create_branch.outputs.branch_name }}
|
||
PR_TITLE: ${{ steps.build_pr_content.outputs.prTitle }}
|
||
PR_BODY: ${{ steps.build_pr_content.outputs.prBody }}
|
||
run: |
|
||
echo "✅ Branch pushed: $BRANCH_NAME"
|
||
echo
|
||
echo "To create a pull request, open:"
|
||
echo "https://github.com/TandoorRecipes/recipes/compare/tandoor-1...${{ github.repository_owner }}:$BRANCH_NAME?expand=1"
|
||
echo
|
||
echo "Suggested PR title:"
|
||
echo "$PR_TITLE"
|
||
echo
|
||
echo "Suggested PR body:"
|
||
echo "$PR_BODY"
|