-
Notifications
You must be signed in to change notification settings - Fork 0
107 lines (101 loc) · 4.19 KB
/
Copy pathpython-gate.yml
File metadata and controls
107 lines (101 loc) · 4.19 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
# Reusable Python quality gate — the house §3 contract: "CI runs `poe gate` itself".
#
# One self-contained job: checkout -> setup-python-uv composite (uv + Python + sync)
# -> `uv run poe <gate-task>` -> (optional) upload coverage.
#
# The gate composition (ruff lint -> ruff format --check -> mypy --strict ->
# xenon A/A/A -> pytest with the >=90% coverage floor) lives ONCE in each repo's
# pyproject [tool.poe.tasks]. There is deliberately NO coverage-threshold input:
# the gate OWNS the floor (via pytest --cov-fail-under), and CI must not be able to
# pass a looser one — that would silently diverge CI from the local `poe gate`.
#
# Toolchain setup is NOT inlined here — it composes the setup-python-uv composite,
# the same one the bespoke almamesh jobs use. One source for the uv pin + Python idiom.
#
# PIN TO A COMMIT SHA, NOT A TAG. Every `uses:` ref here — including first-party
# hseshadr/ci refs — must name a full 40-char commit SHA; tests/security-policy.sh
# rejects a moving `@ci-vN`. Copy the SHA of the release you want from CHANGELOG.md
# and keep the trailing version comment so Dependabot can bump it. Ready-to-copy
# callers live in examples/.
#
# Caller (one job):
# jobs:
# gate:
# uses: hseshadr/ci/.github/workflows/python-gate.yml@<40-char-sha> # ci-v2.0.1
# with:
# working-directory: backend
# sync-args: "--locked --group dev"
name: Python gate (reusable)
on:
workflow_call:
inputs:
working-directory:
description: "Dir holding pyproject.toml. '.' for a single-package repo; 'backend' for a split repo."
type: string
default: "."
python-version:
description: "Python version to install via uv."
type: string
default: "3.13"
sync-args:
description: >-
Extra args appended to `uv sync` (e.g. '--frozen --all-extras',
'--group dev', '--locked --extra dev'). LOCKED BY DEFAULT: must
include --frozen or --locked; opt out only with the explicit
--allow-unlocked sentinel. Empty input is rejected.
type: string
default: "--locked"
gate-task:
description: "poe task run as the gate."
type: string
default: "gate"
upload-coverage:
description: "Upload the coverage XML to Codecov after the gate."
type: boolean
default: false
coverage-file:
description: "Coverage XML path, relative to working-directory."
type: string
default: "coverage.xml"
runs-on:
type: string
default: "ubuntu-latest"
secrets:
# Optional. Private repos need a token for Codecov uploads; without it the
# upload soft-fails (fail_ci_if_error is false) and never reds the gate.
CODECOV_TOKEN:
required: false
permissions:
contents: read
jobs:
gate:
runs-on: ${{ inputs.runs-on }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Setup Python + uv (+ sync)
uses: hseshadr/ci/.github/actions/setup-python-uv@605e51cbc86f452b56edcf1c9660921da797cbfe # ci-v3.2.1
with:
python-version: ${{ inputs.python-version }}
sync-args: ${{ inputs.sync-args }}
working-directory: ${{ inputs.working-directory }}
# §3: run the exact gate command, not a re-listing of its steps — that is
# what keeps CI and the local `poe gate` mirrored in both directions.
- name: Gate (uv run poe ${{ inputs.gate-task }})
working-directory: ${{ inputs.working-directory }}
env:
POE_GATE_TASK: ${{ inputs.gate-task }}
run: |
if [[ ! "$POE_GATE_TASK" =~ ^[A-Za-z0-9][A-Za-z0-9_-]*$ ]]; then
echo "::error::Invalid poe gate task: $POE_GATE_TASK"
exit 1
fi
uv run poe "$POE_GATE_TASK"
- name: Upload coverage to Codecov
if: ${{ inputs.upload-coverage }}
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
files: ${{ inputs.working-directory }}/${{ inputs.coverage-file }}
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false