@@ -83,18 +83,31 @@ jobs:
8383 app-id : 1108748
8484 private-key : ${{ secrets.DEVOPS_BOT_PRIVATE_KEY }}
8585
86- # Create PR and auto-merge via devops bot (bypasses branch protection)
87- - name : Create and merge PR for clean transforms
86+ # Create PR. For action=auto_push (no review items) the PR is
87+ # auto-merged via the devops bot (bypasses branch protection). For
88+ # action=push_and_pr the sync script has already written best-effort
89+ # 3-way merged content to disk — this step commits it and opens a PR
90+ # tagged [NEEDS REVIEW]. Auto-merge is DISABLED for needs-review PRs
91+ # so a human reconciles any upstream-wins overrides.
92+ - name : Create PR for docs sync
8893 id : push
8994 if : steps.sync.outputs.action == 'auto_push' || steps.sync.outputs.action == 'push_and_pr'
9095 env :
9196 GH_TOKEN : ${{ steps.bot-token.outputs.token }}
97+ ACTION : ${{ steps.sync.outputs.action }}
98+ REVIEW_ITEMS_FILE : ${{ steps.sync.outputs.review_items_file }}
9299 run : |
93100 git config user.name "copilotkit-devops-bot[bot]"
94101 git config user.email "copilotkit-devops-bot[bot]@users.noreply.github.com"
95102
96103 SHORT_SHA=$(git rev-parse --short origin/main)
97- BRANCH="docs-sync/auto/${SHORT_SHA}-$(date +%s)"
104+ if [ "$ACTION" = "push_and_pr" ]; then
105+ BRANCH="docs-sync/needs-review/${SHORT_SHA}-$(date +%s)"
106+ COMMIT_SUBJECT="chore: docs sync from main — needs review ($(date +%Y-%m-%d))"
107+ else
108+ BRANCH="docs-sync/auto/${SHORT_SHA}-$(date +%s)"
109+ COMMIT_SUBJECT="chore: auto-sync docs from main ($(date +%Y-%m-%d))"
110+ fi
98111
99112 git checkout -b "$BRANCH"
100113 git add showcase/shell/src/content/ showcase/shell/.docs-sync-sha
@@ -109,27 +122,76 @@ jobs:
109122 exit 0
110123 fi
111124
112- git commit --no-verify -m "chore: auto-sync docs from main ($(date +%Y-%m-%d)) "
125+ git commit --no-verify -m "$COMMIT_SUBJECT "
113126
114127 # Override git credential to use bot token (checkout configured GITHUB_TOKEN)
115128 git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git"
116129 git push origin "$BRANCH"
117130
118- PR_URL=$(gh pr create \
119- --title "chore: auto-sync docs from main (${SHORT_SHA})" \
120- --body "Automated docs sync. Clean transforms only." \
121- --base "$SHOWCASE_BRANCH" \
122- --head "$BRANCH")
123-
124- echo "Created PR: $PR_URL"
125- echo "files_changed=${CHANGED}" >> "$GITHUB_OUTPUT"
126- echo "pr_url=${PR_URL}" >> "$GITHUB_OUTPUT"
127- # Only set pr_opened=true after the PR URL is captured. Any earlier
128- # failure (push, gh pr create) will leave this unset so downstream
129- # alerts fall through to failure() instead of a falsely-benign path.
130- echo "pr_opened=true" >> "$GITHUB_OUTPUT"
131-
132- gh pr merge "$PR_URL" --merge
131+ if [ "$ACTION" = "push_and_pr" ]; then
132+ # Build the PR body from review-items.txt plus a clear callout.
133+ REVIEW_ITEMS_CONTENT="(no review-items file produced)"
134+ if [ -n "${REVIEW_ITEMS_FILE:-}" ] && [ -f "$REVIEW_ITEMS_FILE" ]; then
135+ REVIEW_ITEMS_CONTENT=$(cat "$REVIEW_ITEMS_FILE")
136+ fi
137+ PR_BODY_FILE=$(mktemp)
138+ {
139+ echo ":warning: **Docs sync — MANUAL REVIEW REQUIRED**"
140+ echo ""
141+ echo "This PR was auto-opened because the docs-sync script detected"
142+ echo "showcase-local modifications overlapping with upstream changes."
143+ echo ""
144+ echo "The script attempted a best-effort 3-way merge:"
145+ echo ""
146+ echo "- Where \`git merge-file\` produced a clean merge, the merged content was written."
147+ echo "- Where \`git merge-file\` produced conflict markers, **upstream content was written as-is** and showcase-local modifications were overridden. **Manual review required.**"
148+ echo ""
149+ echo "### Review items"
150+ echo ""
151+ echo '```'
152+ echo "$REVIEW_ITEMS_CONTENT"
153+ echo '```'
154+ echo ""
155+ echo "### Source"
156+ echo ""
157+ echo "- Upstream ref: [\`${SHORT_SHA}\`](https://github.com/${{ github.repository }}/commit/${SHORT_SHA})"
158+ echo "- Workflow run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
159+ echo ""
160+ echo "**Review before merging.** Auto-merge is intentionally disabled"
161+ echo "for \`needs-review\` PRs — confirm the upstream-wins sections"
162+ echo "preserve any intentional showcase-local divergence you want to"
163+ echo "keep, then merge manually."
164+ } > "$PR_BODY_FILE"
165+
166+ PR_URL=$(gh pr create \
167+ --title "docs-sync(needs-review): sync from main (${SHORT_SHA}) [NEEDS REVIEW]" \
168+ --body-file "$PR_BODY_FILE" \
169+ --base "$SHOWCASE_BRANCH" \
170+ --head "$BRANCH")
171+
172+ echo "Created NEEDS-REVIEW PR: $PR_URL"
173+ echo "files_changed=${CHANGED}" >> "$GITHUB_OUTPUT"
174+ echo "pr_url=${PR_URL}" >> "$GITHUB_OUTPUT"
175+ # Only set after URL captured — error paths leave this unset so
176+ # downstream alerts fall through to failure().
177+ echo "pr_opened=true" >> "$GITHUB_OUTPUT"
178+ echo "needs_review=true" >> "$GITHUB_OUTPUT"
179+ # Intentionally no `gh pr merge` — human must review & merge.
180+ else
181+ PR_URL=$(gh pr create \
182+ --title "chore: auto-sync docs from main (${SHORT_SHA})" \
183+ --body "Automated docs sync. Clean transforms / clean 3-way merges only." \
184+ --base "$SHOWCASE_BRANCH" \
185+ --head "$BRANCH")
186+
187+ echo "Created PR: $PR_URL"
188+ echo "files_changed=${CHANGED}" >> "$GITHUB_OUTPUT"
189+ echo "pr_url=${PR_URL}" >> "$GITHUB_OUTPUT"
190+ echo "pr_opened=true" >> "$GITHUB_OUTPUT"
191+ echo "needs_review=false" >> "$GITHUB_OUTPUT"
192+
193+ gh pr merge "$PR_URL" --merge
194+ fi
133195
134196 # Build all Slack payloads via jq into tmpfiles. This guarantees any
135197 # review-item filename containing ", \, or control chars is safely
@@ -174,13 +236,16 @@ jobs:
174236 --arg items "$REVIEW_ITEMS" \
175237 --arg pr_url "${PR_URL:-}" \
176238 --arg run_url "$RUN_URL" \
177- '{text: (":warning: *Docs sync*: files needing manual review\n```" + $items + "```\nReview: " + $pr_url + "\n<" + $run_url + "|View run>")}' \
239+ '{text: (":warning: *Docs sync*: auto-opened *NEEDS REVIEW* PR (best-effort 3-way merge; upstream-wins where conflicts) — human must review + merge \n```" + $items + "```\nReview: " + $pr_url + "\n<" + $run_url + "|View run>")}' \
178240 > slack-payloads/review-with-pr.json
179241
242+ # Fallback path: review items flagged but no PR opened (e.g. 3-way
243+ # merge produced bit-for-bit identical content to what's already on
244+ # disk so nothing to commit). Keep an alert so it's visible.
180245 jq -n \
181246 --arg items "$REVIEW_ITEMS" \
182247 --arg run_url "$RUN_URL" \
183- '{text: (":warning: *Docs sync*: files needing manual review (no auto- PR opened)\n```" + $items + "```\n<" + $run_url + "|View run>")}' \
248+ '{text: (":warning: *Docs sync*: review items flagged but produced no diff (no PR opened)\n```" + $items + "```\n<" + $run_url + "|View run>")}' \
184249 > slack-payloads/review-no-pr.json
185250
186251 # failure alert
@@ -200,7 +265,7 @@ jobs:
200265
201266 - name : Notify Slack (auto-sync)
202267 id : notify-auto-sync
203- if : always() && steps.push.outcome == 'success' && steps.push.outputs.files_changed != '0'
268+ if : always() && steps.push.outcome == 'success' && steps.push.outputs.files_changed != '0' && steps.push.outputs.needs_review != 'true'
204269 uses : slackapi/slack-github-action@v2.1.0
205270 with :
206271 webhook : ${{ secrets.SLACK_WEBHOOK_OSS_ALERTS }}
@@ -209,7 +274,7 @@ jobs:
209274
210275 - name : Notify Slack (merge failed)
211276 id : notify-merge-failed
212- if : failure() && steps.push.outputs.pr_opened == 'true'
277+ if : failure() && steps.push.outputs.pr_opened == 'true' && steps.push.outputs.needs_review != 'true'
213278 uses : slackapi/slack-github-action@v2.1.0
214279 with :
215280 webhook : ${{ secrets.SLACK_WEBHOOK_OSS_ALERTS }}
0 commit comments