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
91 changes: 32 additions & 59 deletions .github/workflows/fix-remote-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -639,65 +639,38 @@ jobs:
echo "Waiting for CI checks to complete before triggering auto-merge..."
echo ""

MAX_WAIT_MINUTES=15
CHECK_INTERVAL=30 # Check every 30 seconds
MAX_ATTEMPTS=$((MAX_WAIT_MINUTES * 60 / CHECK_INTERVAL))
ATTEMPT=1

while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
echo "⏳ Check attempt $ATTEMPT/$MAX_ATTEMPTS (waiting ${CHECK_INTERVAL}s between checks)..."

# Get check status
STATUS=$(gh pr view $PR_NUMBER --repo "$REPO" --json statusCheckRollup --jq '
.statusCheckRollup
| if . == null or length == 0 then
"NO_CHECKS"
else
if any(.conclusion == null or .conclusion == "" or .status == "IN_PROGRESS" or .status == "QUEUED" or .status == "PENDING") then
"RUNNING"
elif any(.conclusion == "FAILURE") then
"FAILED"
else
"COMPLETED"
end
end
')

echo " Status: $STATUS"

if [ "$STATUS" = "COMPLETED" ]; then
echo ""
echo "✅ All checks completed successfully!"
echo "checks_completed=true" >> $GITHUB_OUTPUT
break
elif [ "$STATUS" = "FAILED" ]; then
echo ""
echo "❌ Some checks failed. Will not trigger auto-merge."
echo " The monitor workflow will re-attempt fixes on its next run."
echo "checks_completed=false" >> $GITHUB_OUTPUT
exit 0
elif [ "$STATUS" = "NO_CHECKS" ] && [ $ATTEMPT -gt 2 ]; then
# If no checks after 1 minute, something might be wrong
echo ""
echo "⚠️ No checks found on PR. This might indicate:"
echo " - Repository has no CI configured"
echo " - Checks haven't started yet (will retry)"
echo ""
fi

# If this is the last attempt, give up and let scheduled monitor handle it
if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then
echo ""
echo "⏱️ Timeout: Checks still running after ${MAX_WAIT_MINUTES} minutes"
echo " Will not trigger auto-merge now."
echo " The monitor workflow (runs every 6h) will merge when checks complete."
echo "checks_completed=false" >> $GITHUB_OUTPUT
exit 0
fi

sleep $CHECK_INTERVAL
ATTEMPT=$((ATTEMPT + 1))
done
# Use Python CLI for check waiting (testable, maintainable)
cd bot-repo
if aieng-bot wait-checks --repo "$REPO" --pr-number "$PR_NUMBER" --max-wait-minutes 15 --check-interval 30; then
echo "checks_completed=true" >> $GITHUB_OUTPUT
else
EXIT_CODE=$?
echo "checks_completed=false" >> $GITHUB_OUTPUT

# Provide helpful messages based on exit code
case $EXIT_CODE in
1)
echo ""
echo "❌ Some checks failed. Will not trigger auto-merge."
echo " The monitor workflow will re-attempt fixes on its next run."
;;
2)
echo ""
echo "⏱️ Timeout: Checks still running after 15 minutes"
echo " Will not trigger auto-merge now."
echo " The monitor workflow (runs every 6h) will merge when checks complete."
;;
3)
echo ""
echo "⚠️ No checks found on PR. This might indicate:"
echo " - Repository has no CI configured"
echo " - Checks haven't started yet"
;;
*)
echo "❌ Error checking PR status (exit code: $EXIT_CODE)"
;;
esac
fi
env:
GH_TOKEN: ${{ secrets.ORG_ACCESS_TOKEN }}

Expand Down
109 changes: 109 additions & 0 deletions src/aieng_bot/_cli/commands/wait_checks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"""CLI command for waiting on PR checks to complete."""

import os
import sys
import traceback

import click

from ...check_waiter import CheckStatus, CheckWaiter
from ...utils.logging import log_error, log_info, log_success


@click.command(name="wait-checks")
@click.option(
"--repo",
required=True,
help="Repository name in owner/repo format (e.g., VectorInstitute/aieng-bot)",
)
@click.option(
"--pr-number",
required=True,
type=int,
help="PR number to wait for",
)
@click.option(
"--max-wait-minutes",
default=15,
type=int,
help="Maximum time to wait in minutes (default: 15)",
)
@click.option(
"--check-interval",
default=30,
type=int,
help="Seconds between status checks (default: 30)",
)
def wait_checks(
repo: str,
pr_number: int,
max_wait_minutes: int,
check_interval: int,
) -> None:
r"""Wait for PR checks to complete.

Monitors check status and waits until all checks complete successfully,
fail, or timeout is reached. Filters out stale/malformed checks.

Exit codes:
0 - All checks completed successfully
1 - Some checks failed
2 - Timeout waiting for checks
3 - No checks found on PR
4 - Error occurred

Examples:
\b
# Wait for checks on PR #123
aieng-bot wait-checks --repo VectorInstitute/repo-name --pr-number 123

\b
# Wait up to 30 minutes with 60s intervals
aieng-bot wait-checks --repo VectorInstitute/repo-name --pr-number 123 \\
--max-wait-minutes 30 --check-interval 60

"""
try:
gh_token = os.environ.get("GH_TOKEN")
if not gh_token:
log_error("GH_TOKEN environment variable not set")
sys.exit(4)

log_info(f"Waiting for checks on {repo}#{pr_number}...")
log_info(
f"Max wait: {max_wait_minutes} minutes, check interval: {check_interval}s"
)

waiter = CheckWaiter(
repo=repo,
pr_number=pr_number,
gh_token=gh_token,
max_wait_seconds=max_wait_minutes * 60,
check_interval_seconds=check_interval,
)

result = waiter.wait()

log_info(f"Status: {result.status}")
log_info(f"Elapsed: {result.elapsed_seconds:.1f}s ({result.attempts} checks)")

if result.status == CheckStatus.COMPLETED:
log_success(result.message)
sys.exit(0)
elif result.status == CheckStatus.FAILED:
log_error(result.message)
sys.exit(1)
elif result.status == CheckStatus.TIMEOUT:
log_error(result.message)
sys.exit(2)
elif result.status == CheckStatus.NO_CHECKS:
log_error(result.message)
sys.exit(3)
else:
log_error(f"Unexpected status: {result.status}")
sys.exit(4)

except Exception as e:
log_error(f"Failed to wait for checks: {e}")
traceback.print_exc()
sys.exit(4)
2 changes: 2 additions & 0 deletions src/aieng_bot/_cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from .commands.fix import fix
from .commands.metrics import metrics
from .commands.queue import queue
from .commands.wait_checks import wait_checks
from .utils import get_version


Expand Down Expand Up @@ -112,6 +113,7 @@ def cli(ctx: click.Context, no_banner: bool) -> None:
cli.add_command(fix)
cli.add_command(metrics)
cli.add_command(queue)
cli.add_command(wait_checks)


if __name__ == "__main__":
Expand Down
5 changes: 5 additions & 0 deletions src/aieng_bot/check_waiter/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""Check waiter module for monitoring PR check status."""

from .waiter import CheckStatus, CheckWaiter, WaitResult

__all__ = ["CheckStatus", "CheckWaiter", "WaitResult"]
Loading