6767# - Conflict label "autorestack-needs-conflict-resolution" exists on PR3
6868# - feature3 branch was NOT updated (still at pre-conflict SHA)
6969#
70- # Manual Conflict Resolution (Steps 12-13 ):
70+ # Manual Conflict Resolution (Steps 12-14 ):
7171# - Test simulates user resolving the conflict manually
7272# - Merge main into feature3, resolve conflict (keep feature3's changes)
7373# - Push the resolved branch
74- # - Verify the resolved state is correct
74+ # - The push triggers the 'synchronize' event on PR3
75+ # - The action detects the conflict label and removes it
76+ # - Verify the label is removed and resolution comment is posted
7577#
7678# =============================================================================
7779set -e # Exit immediately if a command exits with a non-zero status.
@@ -163,6 +165,98 @@ merge_pr_with_retry() {
163165 return 1
164166}
165167
168+ wait_for_synchronize_workflow () {
169+ local pr_number=$1 # PR number that was updated
170+ local branch_name=$2 # The branch name that was pushed
171+ local expected_conclusion=${3:- success} # Expected conclusion (success, failure, etc.)
172+ local max_attempts=20 # ~7 mins max wait
173+ local attempt=0
174+ local target_run_id=" "
175+ local start_time=$( date +%s)
176+
177+ echo >&2 " Waiting for workflow '$WORKFLOW_FILE ' triggered by synchronize event on PR #$pr_number (branch $branch_name )..."
178+
179+ while [[ $attempt -lt $max_attempts ]]; do
180+ sleep_time=$(( (attempt + 1 ) * 2 ))
181+ echo >&2 " Attempt $(( attempt + 1 )) /$max_attempts : Checking for workflow run..."
182+
183+ if [[ -z " $target_run_id " ]]; then
184+ echo >&2 " Searching for the specific workflow run..."
185+ # List recent runs for the workflow triggered by pull_request event
186+ candidate_run_ids=$( log_cmd gh run list \
187+ --repo " $REPO_FULL_NAME " \
188+ --workflow " $WORKFLOW_FILE " \
189+ --event pull_request \
190+ --limit 15 \
191+ --json databaseId,createdAt --jq ' .[] | select(.createdAt >= "' $( date -d " @$start_time " -Iseconds 2> /dev/null || date -r $start_time +%Y-%m-%dT%H:%M:%S) ' ") | .databaseId' || echo " " )
192+
193+ if [[ -z " $candidate_run_ids " ]]; then
194+ echo >&2 " No recent '$WORKFLOW_FILE ' runs found since start. Sleeping $sleep_time seconds."
195+ sleep $sleep_time
196+ attempt=$(( attempt + 1 ))
197+ continue
198+ fi
199+
200+ echo >&2 " Found candidate run IDs: $candidate_run_ids . Checking runs..."
201+ for run_id in $candidate_run_ids ; do
202+ echo >&2 " Checking candidate run ID: $run_id "
203+ run_info=$( log_cmd gh run view " $run_id " --repo " $REPO_FULL_NAME " --json headBranch,jobs || echo " {}" )
204+
205+ run_head_branch=$( echo " $run_info " | jq -r ' .headBranch // ""' )
206+ # Check if this run has the continue-after-conflict-resolution job
207+ has_continue_job=$( echo " $run_info " | jq -r ' .jobs[] | select(.name == "continue-after-conflict-resolution") | .name' || echo " " )
208+
209+ echo >&2 " Run head branch: $run_head_branch , has continue job: $has_continue_job "
210+
211+ if [[ " $run_head_branch " == " $branch_name " && -n " $has_continue_job " ]]; then
212+ echo >&2 " Found matching workflow run ID: $run_id (synchronize with continue job)"
213+ target_run_id=" $run_id "
214+ break
215+ fi
216+ done
217+ fi
218+
219+ if [[ -z " $target_run_id " ]]; then
220+ echo >&2 " Target workflow run not found among recent runs. Sleeping $sleep_time seconds."
221+ sleep $sleep_time
222+ attempt=$(( attempt + 1 ))
223+ continue
224+ fi
225+
226+ # Monitor the identified target run
227+ echo >&2 " Monitoring workflow run ID: $target_run_id "
228+ run_info=$( log_cmd gh run view " $target_run_id " --repo " $REPO_FULL_NAME " --json status,conclusion)
229+ run_status=$( echo " $run_info " | jq -r ' .status' )
230+ run_conclusion=$( echo " $run_info " | jq -r ' .conclusion' )
231+
232+ echo >&2 " Workflow run $target_run_id status: $run_status , conclusion: $run_conclusion "
233+
234+ if [[ " $run_status " == " completed" ]]; then
235+ if [[ " $run_conclusion " == " $expected_conclusion " ]]; then
236+ echo >&2 " Workflow $target_run_id completed with expected conclusion: $run_conclusion ."
237+ return 0
238+ else
239+ echo >&2 " Workflow $target_run_id completed with unexpected conclusion: $run_conclusion (expected: $expected_conclusion )"
240+ log_cmd gh run view " $target_run_id " --repo " $REPO_FULL_NAME " --log || echo >&2 " Could not fetch logs for run $target_run_id "
241+ return 1
242+ fi
243+ elif [[ " $run_status " == " queued" || " $run_status " == " in_progress" || " $run_status " == " waiting" ]]; then
244+ echo >&2 " Workflow $target_run_id is $run_status . Sleeping $sleep_time seconds."
245+ else
246+ echo >&2 " Workflow $target_run_id has unexpected status: $run_status . Conclusion: $run_conclusion "
247+ log_cmd gh run view " $target_run_id " --repo " $REPO_FULL_NAME " --log || echo >&2 " Could not fetch logs for run $target_run_id "
248+ return 1
249+ fi
250+
251+ sleep $sleep_time
252+ attempt=$(( attempt + 1 ))
253+ done
254+
255+ echo >&2 " Timeout waiting for synchronize workflow run to complete."
256+ gh run list --repo " $REPO_FULL_NAME " --workflow " $WORKFLOW_FILE " --limit 10 || echo >&2 " Could not list recent runs."
257+ return 1
258+ }
259+
166260wait_for_workflow () {
167261 local pr_number=$1 # PR number that was merged
168262 local merged_branch_name=$2 # The head branch name of the merged PR (unused now, but kept for context)
@@ -303,14 +397,15 @@ cat > .github/workflows/"$WORKFLOW_FILE" <<EOF
303397name: Update Stacked PRs on Squash Merge (E2E Test)
304398on:
305399 pull_request:
306- types: [closed]
400+ types: [closed, synchronize ]
307401permissions:
308402 contents: write
309403 pull-requests: write
310404jobs:
311405 update-pr-stack:
312406 # Only run on actual squash merges initiated by the test script
313407 if: |
408+ github.event.action == 'closed' &&
314409 github.event.pull_request.merged == true &&
315410 github.event.pull_request.merge_commit_sha != ''
316411 runs-on: ubuntu-latest
@@ -327,6 +422,24 @@ jobs:
327422 uses: ./
328423 with:
329424 github-token: \$ {{ secrets.GITHUB_TOKEN }}
425+ continue-after-conflict-resolution:
426+ # Run when a PR with the conflict label is updated (user pushed conflict resolution)
427+ if: |
428+ github.event.action == 'synchronize' &&
429+ contains(github.event.pull_request.labels.*.name, 'autorestack-needs-conflict-resolution')
430+ runs-on: ubuntu-latest
431+ steps:
432+ - name: Checkout repository
433+ uses: actions/checkout@v4
434+ with:
435+ fetch-depth: 0
436+ token: \$ {{ secrets.GITHUB_TOKEN }}
437+ - name: Continue PR stack update after conflict resolution
438+ uses: ./
439+ with:
440+ github-token: \$ {{ secrets.GITHUB_TOKEN }}
441+ mode: conflict-resolved
442+ pr-branch: \$ {{ github.event.pull_request.head.ref }}
330443EOF
331444
332445log_cmd git add action.yml update-pr-stack.sh command_utils.sh .github/workflows/" $WORKFLOW_FILE "
610723log_cmd git push origin feature3
611724echo >&2 " Pushed resolved feature3."
612725
613- # 13. Verify conflict resolution
614- echo >&2 " 13. Verifying conflict resolution..."
726+ # 13. Wait for continuation workflow triggered by push
727+ echo >&2 " 13. Waiting for continuation workflow after conflict resolution push..."
728+ if ! wait_for_synchronize_workflow " $PR3_NUM " " feature3" " success" ; then
729+ echo >&2 " Continuation workflow for feature3 conflict resolution did not complete successfully."
730+ exit 1
731+ fi
732+
733+ # 14. Verify continuation workflow effects
734+ echo >&2 " 14. Verifying continuation workflow effects..."
735+
736+ # Verify conflict label was removed from PR3
737+ echo >&2 " Checking that conflict label was removed from PR #$PR3_NUM ..."
738+ sleep 5 # Give GitHub time to process
739+ CONFLICT_LABEL_AFTER=$( log_cmd gh pr view " $PR3_URL " --repo " $REPO_FULL_NAME " --json labels --jq ' .labels[] | select(.name == "autorestack-needs-conflict-resolution") | .name' )
740+ if [[ -z " $CONFLICT_LABEL_AFTER " ]]; then
741+ echo >&2 " ✅ Verification Passed: Conflict label was removed from PR #$PR3_NUM ."
742+ else
743+ echo >&2 " ❌ Verification Failed: Conflict label still exists on PR #$PR3_NUM ."
744+ exit 1
745+ fi
746+
747+ # Verify resolution acknowledgement comment exists on PR3
748+ echo >&2 " Checking for resolution acknowledgement comment on PR #$PR3_NUM ..."
749+ RESOLUTION_COMMENT=$( log_cmd gh pr view " $PR3_URL " --repo " $REPO_FULL_NAME " --json comments --jq ' .comments[] | select(.body | contains("Conflict resolved")) | .body' )
750+ if [[ -n " $RESOLUTION_COMMENT " ]]; then
751+ echo >&2 " ✅ Verification Passed: Resolution acknowledgement comment found on PR #$PR3_NUM ."
752+ else
753+ echo >&2 " ❌ Verification Failed: Resolution acknowledgement comment not found on PR #$PR3_NUM ."
754+ echo >&2 " --- Comments on PR #$PR3_NUM ---"
755+ gh pr view " $PR3_URL " --repo " $REPO_FULL_NAME " --json comments --jq ' .comments[].body' || echo " Failed to get comments"
756+ echo >&2 " -----------------------------"
757+ exit 1
758+ fi
759+
760+ echo >&2 " --- Continuation Workflow Test Completed Successfully ---"
761+
762+ # 15. Verify conflict resolution (content checks)
763+ echo >&2 " 15. Verifying conflict resolution content..."
615764# Fetch the latest state again
616765log_cmd git fetch origin
617766log_cmd git checkout feature3
653802 exit 1
654803fi
655804
656- # Optional: Verify the conflict label could be removed (manual step usually)
657- # We won't automate label removal check as the action doesn't do it.
658-
659805echo >&2 " --- Conflict Scenario Test Completed Successfully ---"
660806
661807
0 commit comments