Path-scope the live LocalStack job; drop the stale greenlet pin

The localstack-integration job installs the AWS CLI, pulls the image, and
starts a real container against the auth token -- weight unrelated PRs
shouldn't pay. A `changes` job (dorny/paths-filter) now gates it on
LocalStack paths for pull_request events, and `check` allows the skip so
the required aggregate still passes. A run that actually fires still votes,
so a live failure keeps blocking merges as before; push/tag events run it
unconditionally.

Also remove the test-floor greenlet --exclude-newer-package pin: greenlet
3.5.3 has shipped Linux wheels since 2026-06-26, main never carried the
pin, and its test-floor is green without it.
This commit is contained in:
David SF
2026-07-17 12:35:57 -05:00
parent f6a51760e4
commit b0788b12c4
2 changed files with 46 additions and 8 deletions
+32 -7
View File
@@ -21,6 +21,27 @@ permissions:
contents: read
jobs:
# Detect whether a pull request touches LocalStack. The live integration job
# installs the AWS CLI, pulls the LocalStack image, and starts a real
# container against an external auth token -- weight that only pays off when
# LocalStack code or its CI wiring changes. Only runs on pull_request; on
# push/tag events localstack-integration runs unconditionally.
changes:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
outputs:
localstack: ${{ steps.filter.outputs.localstack }}
steps:
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: filter
with:
filters: |
localstack:
- 'pydantic_ai_harness/localstack/**'
- 'tests/localstack/**'
- 'integration_tests/localstack/**'
- '.github/workflows/main.yml'
lint:
runs-on: ubuntu-latest
steps:
@@ -109,12 +130,7 @@ jobs:
# The lock validation must be off here: this job deviates from the lock
# on purpose, and under UV_LOCKED=1 the lowest-direct resolution fails
# because uv refuses to write the lowest-resolved lockfile it produces.
# greenlet 3.5.3 was published without Linux wheels, which makes uv's
# install phase fail on Ubuntu runners after resolution succeeds.
- run: >
uv sync --resolution lowest-direct
--exclude-newer-package greenlet=2026-06-25T00:00:00Z
--group dev --all-extras
- run: uv sync --resolution lowest-direct --group dev --all-extras
env:
UV_LOCKED: '0'
- run: uv run --no-sync pytest
@@ -154,6 +170,11 @@ jobs:
- run: uv run --no-sync pytest
localstack-integration:
needs: [changes]
# Skip on pull requests that leave LocalStack untouched; always run on
# push/tag events so releases exercise the live path. `always()` lets this
# evaluate even though `changes` is skipped on non-pull_request events.
if: ${{ always() && (github.event_name != 'pull_request' || needs.changes.outputs.localstack == 'true') }}
runs-on: ubuntu-latest
name: LocalStack integration tests
# The auth token secret lives in this environment; binding the job to it also
@@ -216,11 +237,15 @@ jobs:
check:
if: always()
needs: [lint, test, test-floor, test-latest, coverage, localstack-integration]
needs: [changes, lint, test, test-floor, test-latest, coverage, localstack-integration]
runs-on: ubuntu-latest
steps:
- uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2
with:
# localstack-integration is skipped on pull requests that don't touch
# LocalStack, and `changes` is skipped on push/tag events. Both still
# vote (block the merge) when they actually run and fail.
allowed-skips: changes, localstack-integration
jobs: ${{ toJSON(needs) }}
release:
+14 -1
View File
@@ -38,10 +38,23 @@ def test_localstack_ci_gates_the_aggregate_check() -> None:
# The aggregate `check` job must depend on localstack-integration so a live
# test failure blocks merges rather than passing silently.
needs = next(line for line in lines if line.strip().startswith('needs: [lint, test'))
needs = next(line for line in lines if line.strip().startswith('needs: [') and 'coverage' in line)
assert 'localstack-integration' in needs
def test_localstack_integration_is_scoped_to_localstack_changes() -> None:
lines = _workflow_lines()
# On a pull request the live job runs only when LocalStack paths change, so
# unrelated PRs don't pull the image, install the AWS CLI, or spend the token.
assert any('needs.changes.outputs.localstack' in line for line in lines)
assert any("- 'pydantic_ai_harness/localstack/**'" in line for line in lines)
# A skipped live job must not fail the required aggregate check; a job that
# actually runs and fails still votes (see allowed-skips semantics).
assert any('allowed-skips: changes, localstack-integration' in line for line in lines)
def test_localstack_ci_scopes_the_auth_token_to_the_test_step() -> None:
lines = _workflow_lines()