-
Notifications
You must be signed in to change notification settings - Fork 0
119 lines (105 loc) · 5.02 KB
/
Copy pathci.yml
File metadata and controls
119 lines (105 loc) · 5.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# CI for the standalone gcp-pubsub-idempotency repository.
#
# This file's path is meaningful: GitHub Actions only evaluates workflows under a repository's
# own top-level .github/workflows/, so this workflow runs when this directory is the root of
# its own repository. Nested inside a host monorepo it is dormant — a host must provide its
# own path-filtered CI if it wants one.
#
# Two jobs:
# unit — the full backend-contract matrix (Datastore, Redis, PostgreSQL, SQLite,
# in-memory) against every supported Python, on every push/PR.
# functional — the docker-compose end-to-end harness (real Pub/Sub + Datastore emulators,
# a real push subscription). Slow (image builds + 12 scenarios), so it is opt-in:
# add the `functional` label to a PR, or run it manually via workflow_dispatch.
name: CI
on:
push:
branches: [main]
pull_request:
types: [opened, synchronize, reopened, labeled]
workflow_dispatch:
permissions:
contents: read
jobs:
unit:
name: unit (py${{ matrix.python }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python: ['3.10', '3.11', '3.12', '3.13']
# Real backends, never mocked. tests/conftest.py has two paths: if REDIS_HOST, POSTGRES_DSN
# and DATASTORE_EMULATOR_HOST are already set (as they are here) it uses them as-is; only
# when one is missing does it fall back to docker-running its own copies. Redis and
# PostgreSQL are GitHub Actions `services:` (health-checked before `steps:` run); the
# Datastore emulator is not, because a `services:` container only runs its image's default
# ENTRYPOINT/CMD and the emulator needs a custom start command — so it is a plain `docker
# run` step below. Ports/project id (6399/5499/8099, `pubsub-idem-test`) match the constants in
# tests/conftest.py exactly, which is what makes the pre-set-env-vars path apply.
services:
redis:
image: redis:7-alpine
ports: ['6399:6379']
options: >-
--health-cmd "redis-cli ping" --health-interval 5s
--health-timeout 3s --health-retries 20
postgres:
image: postgres:16-alpine
env:
POSTGRES_PASSWORD: pg
POSTGRES_DB: spidem
ports: ['5499:5432']
options: >-
--health-cmd "pg_isready -U postgres" --health-interval 5s
--health-timeout 3s --health-retries 20
env:
REDIS_HOST: 127.0.0.1
REDIS_PORT: '6399'
POSTGRES_DSN: postgresql://postgres:pg@127.0.0.1:5499/spidem
DATASTORE_EMULATOR_HOST: 127.0.0.1:8099
DATASTORE_PROJECT_ID: pubsub-idem-test
GOOGLE_CLOUD_PROJECT: pubsub-idem-test
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python }}
cache: pip
cache-dependency-path: pyproject.toml
- name: Start the Datastore emulator
run: |
docker run -d --rm --name datastore-emulator -p 8099:8099 \
gcr.io/google.com/cloudsdktool/cloud-sdk:emulators \
gcloud beta emulators datastore start --no-store-on-disk \
--consistency=1.0 --host-port=0.0.0.0:8099 --project=pubsub-idem-test
for i in $(seq 1 60); do
curl -sf http://127.0.0.1:8099/ >/dev/null && exit 0
sleep 1
done
echo "datastore emulator did not become ready" && exit 1
# No `dev` extra exists in pyproject.toml — `test` is the one that carries pytest, mypy,
# and every backend driver (google-cloud-datastore, redis, psycopg, functions-framework,
# werkzeug) needed to run the full suite with nothing skipped.
- name: Install '.[test]'
run: pip install -e '.[test]'
# tests/conftest.py's backing-services fixture is session-scoped and autouse, so every
# test in tests/ depends on it — there is no unit-only subset that skips backends. A
# missing backend is a hard pytest.fail here, never a silent skip (see conftest.py's
# docstring and tests/test_backend_contract.py's module docstring).
- name: pytest tests/
run: pytest tests/ -q
- name: mypy --strict src/gcp_pubsub_idempotency
run: mypy --strict src/gcp_pubsub_idempotency
functional:
name: functional (docker compose harness)
if: github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'functional')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
# functional/README.md documents this exact invocation as the headless entry point:
# exit code 0 = all 12 scenarios passed. Build context for the consumer image is the
# library root (the functional/docker-compose.yml uses `..`), so it installs the in-tree
# gcp_pubsub_idempotency, not a published release.
- name: docker compose up (Pub/Sub + Datastore emulators, real push subscription)
working-directory: functional
run: docker compose up --build --abort-on-container-exit --exit-code-from runner