Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 1 addition & 25 deletions .github/workflows/update-pr-stack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,11 @@ on:
permissions:
contents: write
pull-requests: write
repository-projects: read # See https://github.com/cli/cli/discussions/5307

jobs:
update-pr-stack:
if: github.event.action == 'closed' && github.event.pull_request.merged == true && github.event.pull_request.merge_commit_sha != ''
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Update PR stack
uses: ./
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

continue-after-conflict-resolution:
if: github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'autorestack-needs-conflict-resolution')
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Continue PR stack update after conflict resolution
uses: ./
- uses: Phlogistique/autorestack-action@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
mode: conflict-resolved
pr-branch: ${{ github.event.pull_request.head.ref }}
42 changes: 31 additions & 11 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,52 @@ inputs:
description: 'GitHub token for API access'
required: true
default: ${{ github.token }}
mode:
description: 'Action mode: squash-merge (after PR merge) or conflict-resolved (after manual resolution)'
required: false
default: 'squash-merge'
pr-branch:
description: 'The PR branch that was pushed (required for conflict-resolved mode)'
required: false
default: ''

runs:
using: 'composite'
steps:
- name: Check if action should run
id: check
shell: bash
env:
EVENT_ACTION: ${{ github.event.action }}
PR_LABELS: ${{ join(github.event.pull_request.labels.*.name, ',') }}
PR_MERGED: ${{ github.event.pull_request.merged }}
MERGE_COMMIT_SHA: ${{ github.event.pull_request.merge_commit_sha }}
run: |
if [[ "$EVENT_ACTION" == "closed" && "$PR_MERGED" == "true" && -n "$MERGE_COMMIT_SHA" ]]; then
echo "mode=squash-merge" >> $GITHUB_OUTPUT
elif [[ "$EVENT_ACTION" == "synchronize" && "$PR_LABELS" == *autorestack-needs-conflict-resolution* ]]; then
echo "mode=conflict-resolved" >> $GITHUB_OUTPUT
else
echo "Event does not match any action trigger (action=$EVENT_ACTION, merged=$PR_MERGED, labels=$PR_LABELS)"
echo "mode=skip" >> $GITHUB_OUTPUT
fi

- name: Checkout repository
if: steps.check.outputs.mode != 'skip'
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ inputs.github-token }}

- name: Update PR stack
if: steps.check.outputs.mode != 'skip'
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.github-token }}
GIT_AUTHOR_NAME: github-actions
GIT_AUTHOR_EMAIL: github-actions@github.com
GIT_COMMITTER_NAME: github-actions
GIT_COMMITTER_EMAIL: github-actions@github.com
ACTION_MODE: ${{ inputs.mode }}
ACTION_MODE: ${{ steps.check.outputs.mode }}
SQUASH_COMMIT: ${{ github.event.pull_request.merge_commit_sha }}
MERGED_BRANCH: ${{ github.event.pull_request.head.ref }}
TARGET_BRANCH: ${{ github.event.pull_request.base.ref }}
PR_BRANCH: ${{ inputs.pr-branch }}
run: ${{ github.action_path }}/update-pr-stack.sh
PR_BRANCH: ${{ github.event.pull_request.head.ref }}
run: |
echo "Running in $ACTION_MODE mode"
${{ github.action_path }}/update-pr-stack.sh

branding:
icon: 'git-pull-request'
Expand Down
106 changes: 42 additions & 64 deletions tests/test_e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -210,16 +210,14 @@ wait_for_synchronize_workflow() {
echo >&2 "Found candidate run IDs: $candidate_run_ids. Checking runs..."
for run_id in $candidate_run_ids; do
echo >&2 "Checking candidate run ID: $run_id"
run_info=$(log_cmd gh run view "$run_id" --repo "$REPO_FULL_NAME" --json headBranch,jobs || echo "{}")
run_info=$(log_cmd gh run view "$run_id" --repo "$REPO_FULL_NAME" --json headBranch || echo "{}")

run_head_branch=$(echo "$run_info" | jq -r '.headBranch // ""')
# Check if this run has the continue-after-conflict-resolution job
has_continue_job=$(echo "$run_info" | jq -r '.jobs[] | select(.name == "continue-after-conflict-resolution") | .name' || echo "")

echo >&2 " Run head branch: $run_head_branch, has continue job: $has_continue_job"
echo >&2 " Run head branch: $run_head_branch"

if [[ "$run_head_branch" == "$branch_name" && -n "$has_continue_job" ]]; then
echo >&2 "Found matching workflow run ID: $run_id (synchronize with continue job)"
if [[ "$run_head_branch" == "$branch_name" ]]; then
echo >&2 "Found matching workflow run ID: $run_id (branch matches)"
target_run_id="$run_id"
break
fi
Expand Down Expand Up @@ -372,6 +370,34 @@ wait_for_workflow() {
# --- Test Execution ---
echo >&2 "--- Starting E2E Test ---"

# 0. Sanity checks - ensure we're testing committed code
echo >&2 "0. Running sanity checks..."

# Check that the working directory is clean
if ! git -C "$PROJECT_ROOT" diff --quiet HEAD 2>/dev/null; then
echo >&2 "ERROR: Repository has uncommitted changes."
echo >&2 "Please commit your changes before running e2e tests."
echo >&2 "This ensures we test exactly what will be deployed."
git -C "$PROJECT_ROOT" status --short >&2
exit 1
fi

# Get the current commit SHA from the action repo
ACTION_REPO_COMMIT=$(git -C "$PROJECT_ROOT" rev-parse HEAD)
echo >&2 "Testing commit: $ACTION_REPO_COMMIT"

# Check that the current commit exists on origin
if ! git -C "$PROJECT_ROOT" fetch origin --quiet 2>/dev/null; then
echo >&2 "WARNING: Could not fetch from origin, skipping remote check"
elif ! git -C "$PROJECT_ROOT" branch -r --contains "$ACTION_REPO_COMMIT" 2>/dev/null | grep -q .; then
echo >&2 "ERROR: Current commit $ACTION_REPO_COMMIT does not exist on origin."
echo >&2 "Please push your changes before running e2e tests."
echo >&2 "This ensures the workflow can reference the action at this commit."
exit 1
fi

echo >&2 "✅ Sanity checks passed"

# 1. Setup local repository
echo >&2 "1. Setting up local test repository..."
TEST_DIR=$(mktemp -d)
Expand All @@ -394,65 +420,17 @@ log_cmd git add file.txt
log_cmd git commit -m "Initial commit"
INITIAL_COMMIT_SHA=$(git rev-parse HEAD)

# Copy action files
echo >&2 "Copying action files..."
cp "$PROJECT_ROOT/action.yml" .
cp "$PROJECT_ROOT/update-pr-stack.sh" .
cp "$PROJECT_ROOT/command_utils.sh" .

# Create workflow file pointing to the local action
echo >&2 "Creating workflow file..."
# Copy workflow file from the repo and modify it to use the current commit SHA
# This tests the actual deployed action, not a local copy
echo >&2 "Copying workflow file from repo..."
mkdir -p .github/workflows
cat > .github/workflows/"$WORKFLOW_FILE" <<EOF
name: Update Stacked PRs on Squash Merge (E2E Test)
on:
pull_request:
types: [closed, synchronize]
permissions:
contents: write
pull-requests: write
jobs:
update-pr-stack:
# Only run on actual squash merges initiated by the test script
if: |
github.event.action == 'closed' &&
github.event.pull_request.merged == true &&
github.event.pull_request.merge_commit_sha != ''
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
# Fetch all history for all branches and tags
fetch-depth: 0
# Use a PAT token for checkout to allow pushing updates
token: \${{ secrets.GITHUB_TOKEN }}
- name: Update PR stack
# Use the action from the current repository checkout
uses: ./
with:
github-token: \${{ secrets.GITHUB_TOKEN }}
continue-after-conflict-resolution:
# Run when a PR with the conflict label is updated (user pushed conflict resolution)
if: |
github.event.action == 'synchronize' &&
contains(github.event.pull_request.labels.*.name, 'autorestack-needs-conflict-resolution')
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: \${{ secrets.GITHUB_TOKEN }}
- name: Continue PR stack update after conflict resolution
uses: ./
with:
github-token: \${{ secrets.GITHUB_TOKEN }}
mode: conflict-resolved
pr-branch: \${{ github.event.pull_request.head.ref }}
EOF

log_cmd git add action.yml update-pr-stack.sh command_utils.sh .github/workflows/"$WORKFLOW_FILE"
cp "$PROJECT_ROOT/.github/workflows/$WORKFLOW_FILE" .github/workflows/

# Replace @main with the current commit SHA to test exactly what we pushed
sed -i "s|uses: Phlogistique/autorestack-action@main|uses: Phlogistique/autorestack-action@$ACTION_REPO_COMMIT|g" .github/workflows/"$WORKFLOW_FILE"
echo >&2 "Modified workflow to use action at commit $ACTION_REPO_COMMIT"

log_cmd git add .github/workflows/"$WORKFLOW_FILE"
log_cmd git commit -m "Add action and workflow files"
ACTION_COMMIT_SHA=$(git rev-parse HEAD)

Expand Down
Loading