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
8 changes: 4 additions & 4 deletions .github/DEFAULT_GITHUB_AUTOMATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ This repository uses a default GitHub automation baseline for quality, safety, a
- Bot-authored PRs can be automatically approved when `AUTO_MERGE_BOT_APPROVE=true`
(repository variable) and `AUTO_MERGE_APPROVE_TOKEN` (PAT secret) are configured.
- Required labels (create manually or via the CLI):
```bash
gh label create auto-merge --color 0075ca --description "Squash-merge when all CI gates pass"
gh label create autofix --color e4e669 --description "Auto-merge for automated fix PRs"
```
```bash
gh label create auto-merge --color 0075ca --description "Squash-merge when all CI gates pass"
gh label create autofix --color e4e669 --description "Auto-merge for automated fix PRs"
```
- Human-authored PRs always require at least one human approval regardless of labels.

9. **Dependabot auto-merge**
Expand Down
196 changes: 98 additions & 98 deletions .github/actions/check-auto-merge-eligibility/action.yml
Original file line number Diff line number Diff line change
@@ -1,118 +1,118 @@
name: Check Auto-Merge Eligibility
description: >
Validates all eligibility criteria for automatic PR merging and outputs
whether the PR is eligible together with a human-readable reason.
Checks: not draft, targets main, not a fork, has auto-merge/autofix label,
not blocked/conflicted, no CHANGES_REQUESTED reviews, has an APPROVED review.
Validates all eligibility criteria for automatic PR merging and outputs
whether the PR is eligible together with a human-readable reason.
Checks: not draft, targets main, not a fork, has auto-merge/autofix label,
not blocked/conflicted, no CHANGES_REQUESTED reviews, has an APPROVED review.

inputs:
pr-number:
description: Pull request number to evaluate
required: true
github-token:
description: >
GitHub token with pull-requests:read and checks:read scope.
Defaults to the built-in GITHUB_TOKEN.
required: false
default: ${{ github.token }}
pr-number:
description: Pull request number to evaluate
required: true
github-token:
description: >
GitHub token with pull-requests:read and checks:read scope.
Defaults to the built-in GITHUB_TOKEN.
required: false
default: ${{ github.token }}

outputs:
eligible:
description: "'true' if the PR meets all auto-merge criteria, 'false' otherwise"
value: ${{ steps.check.outputs.eligible }}
reason:
description: >
Human-readable explanation of the eligibility decision.
When eligible=true this is a success message; otherwise it lists failures.
value: ${{ steps.check.outputs.reason }}
eligible:
description: "'true' if the PR meets all auto-merge criteria, 'false' otherwise"
value: ${{ steps.check.outputs.eligible }}
reason:
description: >
Human-readable explanation of the eligibility decision.
When eligible=true this is a success message; otherwise it lists failures.
value: ${{ steps.check.outputs.reason }}

runs:
using: composite
steps:
- name: Evaluate PR eligibility
id: check
shell: bash
env:
GH_TOKEN: ${{ inputs.github-token }}
PR_NUMBER: ${{ inputs.pr-number }}
run: |
set -euo pipefail
using: composite
steps:
- name: Evaluate PR eligibility
id: check
shell: bash
env:
GH_TOKEN: ${{ inputs.github-token }}
PR_NUMBER: ${{ inputs.pr-number }}
run: |
set -euo pipefail

# ---------------------------------------------------------------------------
# Fetch PR metadata via the GitHub CLI.
# ---------------------------------------------------------------------------
pr_json="$(gh pr view "$PR_NUMBER" \
--json number,isDraft,baseRefName,headRepository,labels,mergeStateStatus,reviewDecision,state \
2>&1)" || {
# Sanitize raw gh output: strip ANSI escape codes, keep first line,
# truncate using bash substring (character-aware, avoids splitting
# multi-byte UTF-8 sequences that cut -c can mishandle on some systems).
first_line="$(printf '%s' "${pr_json}" | head -n1 | sed 's/\x1b\[[0-9;]*m//g')"
sanitized="${first_line:0:120}"
echo "eligible=false" >> "$GITHUB_OUTPUT"
printf 'reason=Failed to fetch PR data: %s\n' "${sanitized}" >> "$GITHUB_OUTPUT"
exit 0
}
# ---------------------------------------------------------------------------
# Fetch PR metadata via the GitHub CLI.
# ---------------------------------------------------------------------------
pr_json="$(gh pr view "$PR_NUMBER" \
--json number,isDraft,baseRefName,headRepository,labels,mergeStateStatus,reviewDecision,state \
2>&1)" || {
# Sanitize raw gh output: strip ANSI escape codes, keep first line,
# truncate using bash substring (character-aware, avoids splitting
# multi-byte UTF-8 sequences that cut -c can mishandle on some systems).
first_line="$(printf '%s' "${pr_json}" | head -n1 | sed 's/\x1b\[[0-9;]*m//g')"
sanitized="${first_line:0:120}"
echo "eligible=false" >> "$GITHUB_OUTPUT"
printf 'reason=Failed to fetch PR data: %s\n' "${sanitized}" >> "$GITHUB_OUTPUT"
exit 0
}

# ---------------------------------------------------------------------------
# Extract fields via jq.
# ---------------------------------------------------------------------------
is_draft="$(jq -r '.isDraft' <<< "$pr_json")"
base_ref="$(jq -r '.baseRefName' <<< "$pr_json")"
head_repo="$(jq -r '.headRepository.nameWithOwner // empty' <<< "$pr_json")"
pr_state="$(jq -r '.state' <<< "$pr_json")"
merge_state="$(jq -r '.mergeStateStatus // empty' <<< "$pr_json")"
review_decision="$(jq -r '.reviewDecision // empty' <<< "$pr_json")"
has_label="$(jq -r '
[.labels[].name] | any(. == "auto-merge" or . == "autofix")
' <<< "$pr_json")"
# ---------------------------------------------------------------------------
# Extract fields via jq.
# ---------------------------------------------------------------------------
is_draft="$(jq -r '.isDraft' <<< "$pr_json")"
base_ref="$(jq -r '.baseRefName' <<< "$pr_json")"
head_repo="$(jq -r '.headRepository.nameWithOwner // empty' <<< "$pr_json")"
pr_state="$(jq -r '.state' <<< "$pr_json")"
merge_state="$(jq -r '.mergeStateStatus // empty' <<< "$pr_json")"
review_decision="$(jq -r '.reviewDecision // empty' <<< "$pr_json")"
has_label="$(jq -r '
[.labels[].name] | any(. == "auto-merge" or . == "autofix")
' <<< "$pr_json")"

current_repo="${GITHUB_REPOSITORY:-}"
current_repo="${GITHUB_REPOSITORY:-}"

# ---------------------------------------------------------------------------
# Accumulate failures.
# ---------------------------------------------------------------------------
FAILURES=()
# ---------------------------------------------------------------------------
# Accumulate failures.
# ---------------------------------------------------------------------------
FAILURES=()

[[ "$pr_state" != "OPEN" ]] && \
FAILURES+=("PR state is '${pr_state}' (requires OPEN)")
[[ "$pr_state" != "OPEN" ]] && \
FAILURES+=("PR state is '${pr_state}' (requires OPEN)")

[[ "$is_draft" == "true" ]] && \
FAILURES+=("PR is a draft")
[[ "$is_draft" == "true" ]] && \
FAILURES+=("PR is a draft")

[[ "$base_ref" != "main" ]] && \
FAILURES+=("base branch is '${base_ref}' (requires 'main')")
[[ "$base_ref" != "main" ]] && \
FAILURES+=("base branch is '${base_ref}' (requires 'main')")

if [[ -n "$current_repo" && -n "$head_repo" && "$head_repo" != "$current_repo" ]]; then
FAILURES+=("PR is from a fork (${head_repo}); fork PRs are not eligible")
fi
if [[ -n "$current_repo" && -n "$head_repo" && "$head_repo" != "$current_repo" ]]; then
FAILURES+=("PR is from a fork (${head_repo}); fork PRs are not eligible")
fi

[[ "$has_label" != "true" ]] && \
FAILURES+=("missing 'auto-merge' or 'autofix' label")
[[ "$has_label" != "true" ]] && \
FAILURES+=("missing 'auto-merge' or 'autofix' label")

case "$merge_state" in
DIRTY) FAILURES+=("merge conflicts detected (mergeStateStatus=DIRTY)") ;;
BEHIND) FAILURES+=("branch is behind base branch (mergeStateStatus=BEHIND)") ;;
BLOCKED) FAILURES+=("merge is blocked by required status checks (mergeStateStatus=BLOCKED)") ;;
esac
case "$merge_state" in
DIRTY) FAILURES+=("merge conflicts detected (mergeStateStatus=DIRTY)") ;;
BEHIND) FAILURES+=("branch is behind base branch (mergeStateStatus=BEHIND)") ;;
BLOCKED) FAILURES+=("merge is blocked by required status checks (mergeStateStatus=BLOCKED)") ;;
esac

if [[ "$review_decision" == "CHANGES_REQUESTED" ]]; then
FAILURES+=("has CHANGES_REQUESTED review")
elif [[ "$review_decision" != "APPROVED" ]]; then
FAILURES+=("awaiting required approval (reviewDecision='${review_decision}')")
fi
if [[ "$review_decision" == "CHANGES_REQUESTED" ]]; then
FAILURES+=("has CHANGES_REQUESTED review")
elif [[ "$review_decision" != "APPROVED" ]]; then
FAILURES+=("awaiting required approval (reviewDecision='${review_decision}')")
fi

# ---------------------------------------------------------------------------
# Emit outputs.
# ---------------------------------------------------------------------------
if (( ${#FAILURES[@]} == 0 )); then
echo "eligible=true" >> "$GITHUB_OUTPUT"
echo "reason=All eligibility checks passed." >> "$GITHUB_OUTPUT"
echo "✅ PR #${PR_NUMBER} is eligible for auto-merge."
else
echo "eligible=false" >> "$GITHUB_OUTPUT"
joined="$(printf '%s; ' "${FAILURES[@]}")"
joined="${joined%; }" # trim trailing '; '
printf 'reason=%s\n' "${joined}" >> "$GITHUB_OUTPUT"
echo "❌ PR #${PR_NUMBER} is NOT eligible: ${joined}"
fi
# ---------------------------------------------------------------------------
# Emit outputs.
# ---------------------------------------------------------------------------
if (( ${#FAILURES[@]} == 0 )); then
echo "eligible=true" >> "$GITHUB_OUTPUT"
echo "reason=All eligibility checks passed." >> "$GITHUB_OUTPUT"
echo "✅ PR #${PR_NUMBER} is eligible for auto-merge."
else
echo "eligible=false" >> "$GITHUB_OUTPUT"
joined="$(printf '%s; ' "${FAILURES[@]}")"
joined="${joined%; }" # trim trailing '; '
printf 'reason=%s\n' "${joined}" >> "$GITHUB_OUTPUT"
echo "❌ PR #${PR_NUMBER} is NOT eligible: ${joined}"
fi
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ scipy>=1.18.0

# Model serving & deployment
fastapi>=0.139.0
uvicorn[standard]>=0.50.0
uvicorn[standard]>=0.50.2
pydantic>=2.13.4

# Model export
Expand Down
6 changes: 1 addition & 5 deletions aria-bot/aria_bot/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,7 @@ def _inspect_trailing_whitespace(
results: list[Finding],
) -> str:
lines = cursor.split("\n")
offending_lines = [
index + 1
for index, line in enumerate(lines)
if line != line.rstrip(" \t")
]
offending_lines = [index + 1 for index, line in enumerate(lines) if line != line.rstrip(" \t")]
if not offending_lines:
return cursor

Expand Down
3 changes: 2 additions & 1 deletion aria-bot/aria_bot/from collections.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from collections.abc import Iterable, Mapping, Sequence
from collections.abc import Sequence
from typing import Any


def _as_dict(value: Any) -> dict[str, Any]:
return value if isinstance(value, dict) else {}

Expand Down
4 changes: 1 addition & 3 deletions tests/test_auto_fix_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,7 @@ def test_auto_fix_has_cleanup_step() -> None:
"are accidentally included in the autofix branch"
)

cleanup_step = next(
step for step in steps if step["name"] == "Revert workflow file changes and remove stray files"
)
cleanup_step = next(step for step in steps if step["name"] == "Revert workflow file changes and remove stray files")
assert "detect_output.txt" in cleanup_step["run"], "Must remove stray detect_output.txt"
assert ".github/workflows" in cleanup_step["run"], "Must revert workflow file changes"
assert "git restore" in cleanup_step["run"], "Must use git restore to revert workflow changes"
Expand Down
Loading
Loading