Repository navigation
174 lines (162 loc) · 6.18 KB
/
Copy pathpython-uv.yml
File metadata and controls
174 lines (162 loc) · 6.18 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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
name: Python (uv)
on:
workflow_call:
inputs:
python-version:
description: "Python version to use"
type: string
default: "3.14"
uv-version:
description: "uv version (empty = latest)"
type: string
default: ""
working-directory:
description: "Directory containing pyproject.toml"
type: string
default: "."
runs-on:
description: "Runner label(s) as a JSON array string (parsed with fromJSON). Pass '[\"self-hosted\", \"linux\", \"x64\"]' to use the self-hosted pool."
type: string
default: '["ubuntu-latest"]'
run-lint:
description: "Run ruff format + check"
type: boolean
default: true
run-typecheck:
description: "Run mypy"
type: boolean
default: true
run-tests:
description: "Run pytest"
type: boolean
default: true
test-command:
description: "Override the default pytest invocation"
type: string
default: "uv run pytest --cov --cov-report=xml --cov-report=term"
extras:
description: "uv sync extras (comma-separated, e.g. 'dev,test')"
type: string
default: "dev"
upload-coverage:
description: "Upload coverage.xml as an artifact"
type: boolean
default: true
upload-artifacts:
description: "Master switch for uploading artifacts to GitHub Actions storage. Set false to conserve the account-wide Actions storage quota; per-artifact toggles still apply when true."
type: boolean
default: true
artifact-retention-days:
description: "Days to retain uploaded artifacts. Lower values free the account-wide Actions storage quota sooner."
type: number
default: 7
run-pip-audit:
description: "Run pip-audit against the resolved lockfile"
type: boolean
default: false
pip-audit-args:
description: "Extra args for pip-audit (e.g. '--ignore-vuln GHSA-…')"
type: string
default: ""
run-bandit:
description: "Run bandit static security scan"
type: boolean
default: false
bandit-args:
description: "Extra args for bandit (default scans the working-directory recursively)"
type: string
default: "-r ."
deps-reader-client-id:
description: |
GitHub App client ID for private-dep access. When set, a scoped
installation token is minted (via setup-deps-reader) and git's
credential helper is wired before `uv sync`, so private git deps
(e.g. a `[tool.uv.sources]` entry pointing at a private repo)
resolve over HTTPS. Pair with deps-reader-repositories and the
DEPS_READER_PRIVATE_KEY secret. Empty = no private-deps step.
type: string
default: ""
deps-reader-repositories:
description: "Comma-separated repo names (no owner) the token is scoped to. Required when deps-reader-client-id is set."
type: string
default: ""
secrets:
DEPS_READER_PRIVATE_KEY:
description: "Private key for the deps-reader GitHub App (required when deps-reader-client-id is set)"
required: false
jobs:
ci:
runs-on: ${{ fromJSON(inputs.runs-on) }}
timeout-minutes: 30
defaults:
run:
working-directory: ${{ inputs.working-directory }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# Private-deps access (opt-in). Referenced by full path + floating major
# tag because a relative `./.github/actions/...` inside a reusable would
# resolve against the *consumer's* checkout, not this repo. The composite
# configures git's global credential helper, so `uv sync` (which shells
# out to git for git deps) can reach private repos regardless of checkout.
- name: Configure private-deps access
if: inputs.deps-reader-client-id != ''
uses: nkg/github-actions/.github/actions/setup-deps-reader@v3
with:
app-client-id: ${{ inputs.deps-reader-client-id }}
app-private-key: ${{ secrets.DEPS_READER_PRIVATE_KEY }}
repositories: ${{ inputs.deps-reader-repositories }}
- name: Install uv
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
version: ${{ inputs.uv-version }}
enable-cache: true
cache-dependency-glob: ${{ inputs.working-directory }}/uv.lock
- name: Set up Python
run: uv python install ${{ inputs.python-version }}
- name: Install dependencies
run: |
if [ -n "${{ inputs.extras }}" ]; then
EXTRAS_ARGS=""
IFS=',' read -ra EXTRAS <<< "${{ inputs.extras }}"
for e in "${EXTRAS[@]}"; do
EXTRAS_ARGS="$EXTRAS_ARGS --extra $e"
done
# shellcheck disable=SC2086
uv sync --frozen $EXTRAS_ARGS
else
uv sync --frozen
fi
- name: Lint (ruff)
if: inputs.run-lint
run: |
uv run ruff format --check .
uv run ruff check .
- name: Type check (mypy)
if: inputs.run-typecheck
run: uv run mypy .
- name: Test (pytest)
if: inputs.run-tests
run: ${{ inputs.test-command }}
- name: Upload coverage
if: inputs.run-tests && inputs.upload-coverage && inputs.upload-artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-${{ inputs.python-version }}
path: ${{ inputs.working-directory }}/coverage.xml
if-no-files-found: ignore
retention-days: ${{ inputs.artifact-retention-days }}
- name: pip-audit
if: inputs.run-pip-audit
env:
EXTRA: ${{ inputs.pip-audit-args }}
run: |
# pip-audit reads the resolved env; uv sync above populated .venv.
# shellcheck disable=SC2086
uvx pip-audit $EXTRA
- name: bandit
if: inputs.run-bandit
env:
ARGS: ${{ inputs.bandit-args }}
run: |
# shellcheck disable=SC2086
uvx bandit $ARGS