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
2 changes: 1 addition & 1 deletion .github/workflows/pr-title.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
types: [opened, edited, synchronize]

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/static-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
branches: [main, develop]

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

env:
Expand Down Expand Up @@ -41,7 +41,7 @@ jobs:

check:
needs: scope
if: ${{ always() }}
if: ${{ !cancelled() }}
runs-on: ubuntu-latest
timeout-minutes: 90
steps:
Expand All @@ -50,8 +50,8 @@ jobs:
persist-credentials: false
- name: Validate repository metadata and documentation
run: python3 scripts/ci/validate_repository.py
- name: Test CI scope routing
run: python3 scripts/ci/test_ci_scope.py
- name: Test CI contracts
run: python3 -m unittest discover -s scripts/ci -p 'test_*.py'
- name: Validate Terminal-Bench harness contracts
if: ${{ needs.scope.result != 'success' || needs.scope.outputs.harness == 'true' }}
run: |
Expand Down Expand Up @@ -95,7 +95,7 @@ jobs:
sdk:
name: "@astra/sdk (typecheck, test+coverage, build)"
needs: scope
if: ${{ always() && (needs.scope.result != 'success' || needs.scope.outputs.sdk == 'true') }}
if: ${{ !cancelled() && (needs.scope.result != 'success' || needs.scope.outputs.sdk == 'true') }}
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
Expand All @@ -118,7 +118,7 @@ jobs:
web:
name: "web (typecheck, test, build)"
needs: scope
if: ${{ always() && (needs.scope.result != 'success' || needs.scope.outputs.web == 'true') }}
if: ${{ !cancelled() && (needs.scope.result != 'success' || needs.scope.outputs.web == 'true') }}
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
Expand Down
42 changes: 42 additions & 0 deletions .github/workflows/supersede-pr-runs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Supersede stale PR runs

on:
pull_request_target:
branches: [main, develop]
types: [synchronize]

# This controller runs from its trusted workflow revision. It must never check out
# or execute the pull request head while holding permission to cancel runs.
permissions:
actions: write
contents: read

# Do not add concurrency here. Every synchronize event owns one before -> after
# transition; dropping an intermediate controller could leave its prior head
# running after several rapid pushes.

jobs:
cancel-stale-runs:
name: Cancel superseded PR runs
if: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ github.workflow_sha }}
fetch-depth: 1
persist-credentials: false
- name: Cancel runs for earlier PR heads
env:
GH_TOKEN: ${{ github.token }}
HEAD_REF: ${{ github.event.pull_request.head.ref }}
HEAD_REPOSITORY_ID: ${{ github.event.pull_request.head.repo.id }}
REPOSITORY: ${{ github.repository }}
SUPERSEDED_HEAD_SHA: ${{ github.event.before }}
run: >-
python3 scripts/ci/cancel_stale_pr_runs.py
--repository="$REPOSITORY"
--head-repository-id="$HEAD_REPOSITORY_ID"
--head-ref="$HEAD_REF"
--superseded-head-sha="$SUPERSEDED_HEAD_SHA"
12 changes: 6 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
branches: [main, develop]

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

env:
Expand Down Expand Up @@ -63,7 +63,7 @@ jobs:
shard-a:
name: "Test: astra-cli (${{ matrix.segment }})"
needs: scope
if: ${{ always() }}
if: ${{ !cancelled() }}
runs-on: ubuntu-latest
timeout-minutes: 45
env:
Expand Down Expand Up @@ -126,7 +126,7 @@ jobs:
shard-b:
name: "Test: astra-runtime"
needs: scope
if: ${{ always() && (needs.scope.result != 'success' || needs.scope.outputs.test_runtime == 'true') }}
if: ${{ !cancelled() && (needs.scope.result != 'success' || needs.scope.outputs.test_runtime == 'true') }}
runs-on: ubuntu-latest
timeout-minutes: 35
steps:
Expand Down Expand Up @@ -157,7 +157,7 @@ jobs:
shard-c:
name: "Test: turn-core + services + plan"
needs: scope
if: ${{ always() && (needs.scope.result != 'success' || needs.scope.outputs.test_services == 'true') }}
if: ${{ !cancelled() && (needs.scope.result != 'success' || needs.scope.outputs.test_services == 'true') }}
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
Expand All @@ -177,7 +177,7 @@ jobs:
shard-d:
name: "Test: core crates + bridge hooks"
needs: scope
if: ${{ always() && (needs.scope.result != 'success' || needs.scope.outputs.test_core == 'true') }}
if: ${{ !cancelled() && (needs.scope.result != 'success' || needs.scope.outputs.test_core == 'true') }}
runs-on: ubuntu-latest
timeout-minutes: 35
steps:
Expand Down Expand Up @@ -209,7 +209,7 @@ jobs:
test-online:
name: "Test: online (${{ matrix.lane }})"
needs: scope
if: ${{ always() }}
if: ${{ !cancelled() }}
runs-on: ubuntu-latest
timeout-minutes: 45
strategy:
Expand Down
14 changes: 14 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,20 @@ Required check names remain present when their heavy work is skipped, so this
routing is compatible with branch protection and the merge queue. The routing
contract and its tests live in [`scripts/ci/`](scripts/ci/).

For fork pull requests, an update may require maintainer approval before the
replacement test run can enter the normal concurrency group. A separate
trusted-workflow-revision controller cancels active runs for earlier heads
without waiting for replacement-run approval. The controller never checks out
or executes pull request code; the
test workflows remain low-privilege `pull_request` workflows. Normal concurrency
groups use the pull request number, so identically named branches in different
forks remain isolated. Each synchronize event cancels only its exact `before`
head and is intentionally not coalesced, so rapid pushes cannot make an older
controller cancel a newer generation or skip an intermediate cleanup. Jobs that
must run after scope-classification failure use `!cancelled()` rather than
`always()`, preserving fail-safe coverage without making superseded work resist
cancellation.

## Open a pull request

1. Rebase the feature branch on the current `main` branch.
Expand Down
Loading
Loading