ci-cd-playground 0.1.16__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ci_cd_playground-0.1.16/PKG-INFO +297 -0
- ci_cd_playground-0.1.16/README.md +286 -0
- ci_cd_playground-0.1.16/pyproject.toml +68 -0
- ci_cd_playground-0.1.16/pyproject.toml.orig +64 -0
- ci_cd_playground-0.1.16/src/ci_cd_playground/__init__.py +15 -0
- ci_cd_playground-0.1.16/src/ci_cd_playground/app.py +41 -0
- ci_cd_playground-0.1.16/src/ci_cd_playground/py.typed +0 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: ci-cd-playground
|
|
3
|
+
Version: 0.1.16
|
|
4
|
+
Summary: A sample Python project with uv, ruff, pyright, and pytest
|
|
5
|
+
Author: john
|
|
6
|
+
Author-email: john <96323078+JohnStephens1@users.noreply.github.com>
|
|
7
|
+
Requires-Dist: fastapi>=0.162.0
|
|
8
|
+
Requires-Dist: uvicorn>=0.54.0
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# ci-cd-playground
|
|
13
|
+
|
|
14
|
+
A Python project configured with [`uv`](https://docs.astral.sh/uv/), [`ruff`](https://docs.astral.sh/ruff/), [`pyright`](https://microsoft.github.io/pyright/), and [`pytest`](https://docs.pytest.org/), complete with GitHub Actions Continuous Integration (CI), Release automation, and automated publishing to TestPyPI.
|
|
15
|
+
|
|
16
|
+
## Prerequisites
|
|
17
|
+
|
|
18
|
+
- [uv](https://docs.astral.sh/uv/getting-started/installation/) (v0.5+ recommended)
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### 1. Install Dependencies
|
|
23
|
+
|
|
24
|
+
Install all dependencies (including development tools) into a local virtual environment:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
uv sync
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 2. Run Quality Checks & Tests Locally
|
|
31
|
+
|
|
32
|
+
| Tool | Purpose | Command |
|
|
33
|
+
| :--- | :--- | :--- |
|
|
34
|
+
| **Ruff Check** | Linting and code style rules | `uv run ruff check .` |
|
|
35
|
+
| **Ruff Format** | Code formatting verification | `uv run ruff format --check .` |
|
|
36
|
+
| **Pyright** | Static type checking | `uv run pyright` |
|
|
37
|
+
| **Pytest** | Unit test suite | `uv run pytest` |
|
|
38
|
+
| **Build** | Build sdist and wheel | `uv build` |
|
|
39
|
+
|
|
40
|
+
To automatically fix lint violations or format code:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Format code
|
|
44
|
+
uv run ruff format .
|
|
45
|
+
|
|
46
|
+
# Auto-fix linting issues
|
|
47
|
+
uv run ruff check --fix .
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 3. Run the FastAPI Application
|
|
51
|
+
|
|
52
|
+
Start the local development server with auto-reload:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
uv run uvicorn ci_cd_playground:app --reload
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The API endpoints will be accessible at `http://127.0.0.1:8000/` and interactive Swagger docs at `http://127.0.0.1:8000/docs`.
|
|
59
|
+
|
|
60
|
+
### 4. Build Distribution Packages
|
|
61
|
+
|
|
62
|
+
Build both the source distribution (`.tar.gz`) and wheel (`.whl`):
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
uv build
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The output packages will be located in the `dist/` directory.
|
|
69
|
+
|
|
70
|
+
## GitHub Actions Workflows
|
|
71
|
+
|
|
72
|
+
The repository organizes continuous integration and deployment into modular, decoupled workflows:
|
|
73
|
+
|
|
74
|
+
### 1. Continuous Integration (CI) — [`.github/workflows/ci.yml`](.github/workflows/ci.yml)
|
|
75
|
+
|
|
76
|
+
Runs automatically on pushes and pull requests to `main`, and functions as a **reusable workflow** (`workflow_call`):
|
|
77
|
+
|
|
78
|
+
1. **Lint & Format (Ruff)**: Verifies formatting and runs linting rules.
|
|
79
|
+
2. **Type Check (Pyright)**: Validates static typing rules across `src/` and `tests/`.
|
|
80
|
+
3. **Matrix Tests (Pytest)**: Runs the test suite across supported Python versions (`3.10`, `3.11`, `3.12`, `3.13`) using `astral-sh/setup-uv`.
|
|
81
|
+
4. **Security Check (zizmor)**: Audits GitHub Actions workflows for security vulnerabilities and permissions issues.
|
|
82
|
+
5. **Build (uv build)**: Builds source distributions and wheels, performs a smoke test by installing and importing the wheel in an isolated environment, and uploads build artifacts (`dist-packages`).
|
|
83
|
+
|
|
84
|
+
### 2. Container Security — [`.github/workflows/container-security.yml`](.github/workflows/container-security.yml)
|
|
85
|
+
|
|
86
|
+
Runs on pull requests to `main` and via `workflow_call`:
|
|
87
|
+
- Builds the runtime Docker image.
|
|
88
|
+
- Scans for vulnerabilities with Trivy using repository ignore configurations.
|
|
89
|
+
|
|
90
|
+
### 3. Docker Build & Publish — [`.github/workflows/docker-publish.yml`](.github/workflows/docker-publish.yml)
|
|
91
|
+
|
|
92
|
+
A **reusable workflow** (`workflow_call`) that encapsulates container lifecycle tasks:
|
|
93
|
+
1. Builds the production image with `APP_VERSION` and `GIT_SHA` build arguments.
|
|
94
|
+
2. Scans the container with Trivy for CRITICAL and HIGH severity issues.
|
|
95
|
+
3. Starts the container locally and verifies `/health` response.
|
|
96
|
+
4. Logs into GitHub Container Registry (GHCR) and publishes the tagged images (`${GITHUB_SHA}` and optional custom/release tag).
|
|
97
|
+
|
|
98
|
+
### 4. Deploy Service — [`.github/workflows/deploy.yml`](.github/workflows/deploy.yml)
|
|
99
|
+
|
|
100
|
+
A **reusable deployment workflow** (`workflow_call`) that unifies Render deployment logic for all environments:
|
|
101
|
+
1. Configures the target GitHub `environment` (e.g. `staging` or `production`), enabling environment protection rules, approvals, and secret isolation.
|
|
102
|
+
2. Triggers Render deploy via REST API for the given service ID and image tag.
|
|
103
|
+
3. Polls Render deployment status every 30s until `live` or failed.
|
|
104
|
+
4. Verifies service availability via `/health` and validates that `/version` is running the expected commit SHA.
|
|
105
|
+
|
|
106
|
+
### 5. Continuous Delivery (CD) — [`.github/workflows/cd.yml`](.github/workflows/cd.yml)
|
|
107
|
+
|
|
108
|
+
Triggers automatically on pushes to `main` and via manual `workflow_dispatch`:
|
|
109
|
+
1. **CI Validation & Build**: Calls `ci.yml` to verify quality gates.
|
|
110
|
+
2. **Docker Publish**: Calls `docker-publish.yml` to build, scan, test, and push the image to GHCR.
|
|
111
|
+
3. **Deploy to Staging**: Automatically calls `deploy.yml` with `staging` settings and validates staging health.
|
|
112
|
+
4. **Deploy to Production**: Calls `deploy.yml` with `production` settings. Can be gated with GitHub Environment manual approval rules or targeted selectively via manual dispatch.
|
|
113
|
+
|
|
114
|
+
### 6. Package Build & Provenance (SLSA Level 3) — [`.github/workflows/package-build.yml`](.github/workflows/package-build.yml)
|
|
115
|
+
|
|
116
|
+
A **trusted reusable builder workflow** (`workflow_call`) that enforces isolation and build platform integrity to meet **SLSA v1.0 Build Level 3**:
|
|
117
|
+
1. **Isolated Build**: Checks out the target commit and builds both distributions (`uv build --no-sources`) strictly once.
|
|
118
|
+
2. **Artifact Identification**: Computes cryptographic SHA-256 digests for both the wheel (`.whl`) and sdist (`.tar.gz`), generating `checksums/SHA256SUMS`.
|
|
119
|
+
3. **Individual SBOM Generation (Syft)**: Scans the actual built distributions independently with Syft to generate:
|
|
120
|
+
- Machine-readable SPDX 2.3 JSON: `package-wheel.spdx.json` and `package-sdist.spdx.json`
|
|
121
|
+
- Human-readable summary tables: `package-wheel.txt` and `package-sdist.txt`
|
|
122
|
+
4. **SLSA Build Provenance Attestation**: Generates cryptographically signed SLSA provenance via `actions/attest` backed by Sigstore, bound to both distribution digests and exported to `attestations/package-provenance.intoto.jsonl`.
|
|
123
|
+
5. **Individual SBOM Attestations**: Cryptographically signs each distribution's SBOM with `actions/attest`, binding the wheel SBOM to the wheel digest and the sdist SBOM to the sdist digest (`package-wheel-sbom.attestation.json` and `package-sdist-sbom.attestation.json`).
|
|
124
|
+
6. **Immutable Artifact Preservation**: Uploads distributions, SBOMs, attestations, and checksums as GitHub Actions artifacts for downstream pipeline consumption.
|
|
125
|
+
|
|
126
|
+
### 7. Release & PyPI/TestPyPI Publishing — [`.github/workflows/release.yml`](.github/workflows/release.yml)
|
|
127
|
+
|
|
128
|
+
Triggers whenever a version tag matching `v*` (e.g. `v0.1.0`) is pushed to the repository (or via manual `workflow_dispatch`):
|
|
129
|
+
|
|
130
|
+
1. **Validate Release Reference**: Validates git tag syntax, verifies that the tag cryptographically resolves to the target commit SHA, and ensures the git tag matches `project.version` in `pyproject.toml`.
|
|
131
|
+
2. **Build via Trusted Builder**: Invokes the isolated reusable workflow `package-build.yml` to produce immutable distributions, Syft SBOMs, SLSA Level 3 provenance, and SBOM attestations.
|
|
132
|
+
3. **Test Built Distributions**: Installs the built wheel in an isolated `.venv-wheel` and the built sdist in an isolated `.venv-sdist`, verifying installations and import smoke-tests before promotion.
|
|
133
|
+
4. **Comprehensive Automated Verification**:
|
|
134
|
+
- Verifies distribution checksums against `SHA256SUMS`.
|
|
135
|
+
- Verifies SLSA Build Provenance authenticity and subject digests online via GitHub API and offline using the bundled `.intoto.jsonl` file.
|
|
136
|
+
- Verifies individual SBOM attestations online and offline using `--predicate-type https://spdx.dev/Document/v2.3`.
|
|
137
|
+
- Validates that the builder signer workflow strictly matches `package-build.yml` (`--signer-workflow`).
|
|
138
|
+
5. **Publish to TestPyPI**:
|
|
139
|
+
- Publishes verified distributions using Trusted Publishing (OIDC) via `uv publish --index testpypi`.
|
|
140
|
+
- Performs a smoke-test installation in a clean environment from TestPyPI.
|
|
141
|
+
6. **Publish to PyPI**:
|
|
142
|
+
- Gated by the `pypi` environment (with optional environment protection / manual approval rules).
|
|
143
|
+
- Generates PEP 740 digital provenance attestations via `astral-sh/attest-action`.
|
|
144
|
+
- Publishes verified distributions to PyPI via `uv publish`.
|
|
145
|
+
7. **Create GitHub Release**:
|
|
146
|
+
- Creates a GitHub Release with release notes and attaches:
|
|
147
|
+
- `dist/`: Wheel (`.whl`) and source distribution (`.tar.gz`)
|
|
148
|
+
- `sbom/`: SPDX JSON and readable text SBOMs (`package-wheel.spdx.json`, `package-sdist.spdx.json`, `package-wheel.txt`, `package-sdist.txt`)
|
|
149
|
+
- `attestations/`: `package-provenance.intoto.jsonl`, `package-wheel-sbom.attestation.json`, `package-sdist-sbom.attestation.json`
|
|
150
|
+
- `checksums/`: `SHA256SUMS`
|
|
151
|
+
|
|
152
|
+
#### Configuring PyPI & TestPyPI Trusted Publishing
|
|
153
|
+
|
|
154
|
+
1. **TestPyPI**:
|
|
155
|
+
- Add a Pending Publisher on [TestPyPI](https://test.pypi.org/manage/account/publishing/) targeting your GitHub repository and workflow (`release.yml`), with environment name `testpypi`.
|
|
156
|
+
- Create a GitHub Environment named `testpypi` in repository settings (**Settings > Environments**).
|
|
157
|
+
2. **Production PyPI**:
|
|
158
|
+
- Add a Publisher on [PyPI](https://pypi.org/manage/account/publishing/) targeting your GitHub repository and workflow (`release.yml`), with environment name `pypi`.
|
|
159
|
+
- Create a GitHub Environment named `pypi` in repository settings (**Settings > Environments**), optionally configuring required reviewers for manual release sign-off.
|
|
160
|
+
|
|
161
|
+
#### Creating a Release
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
# 1. Bump version in pyproject.toml (e.g. 0.1.15)
|
|
165
|
+
uv version 0.1.15
|
|
166
|
+
|
|
167
|
+
# 2. Commit and tag
|
|
168
|
+
git commit -am "chore: release v0.1.15"
|
|
169
|
+
git tag v0.1.15
|
|
170
|
+
|
|
171
|
+
# 3. Push tag to GitHub to trigger the release workflow
|
|
172
|
+
git push origin v0.1.15
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Consumer Verification Guide
|
|
176
|
+
|
|
177
|
+
Consumers can independently verify the authenticity, integrity, SLSA Build Level 3 provenance, and SBOM attestations of released artifacts without trusting the build workflow's claims.
|
|
178
|
+
|
|
179
|
+
### 1. Verify SHA-256 Checksums
|
|
180
|
+
|
|
181
|
+
Download the release assets into a local directory and verify the distribution digests:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
sha256sum -c SHA256SUMS
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### 2. Verify SLSA Build Level 3 Provenance
|
|
188
|
+
|
|
189
|
+
Using the official [GitHub CLI](https://cli.github.com/) (`gh` v2.97.0+):
|
|
190
|
+
|
|
191
|
+
**Online Verification (via Sigstore transparency log):**
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# Verify wheel provenance
|
|
195
|
+
gh attestation verify ci_cd_playground-0.1.15-py3-none-any.whl \
|
|
196
|
+
--repo <OWNER>/ci-cd-playground \
|
|
197
|
+
--signer-workflow <OWNER>/ci-cd-playground/.github/workflows/package-build.yml
|
|
198
|
+
|
|
199
|
+
# Verify sdist provenance
|
|
200
|
+
gh attestation verify ci_cd_playground-0.1.15.tar.gz \
|
|
201
|
+
--repo <OWNER>/ci-cd-playground \
|
|
202
|
+
--signer-workflow <OWNER>/ci-cd-playground/.github/workflows/package-build.yml
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Offline Verification (using the release attestation bundle):**
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
gh attestation verify ci_cd_playground-0.1.15-py3-none-any.whl \
|
|
209
|
+
--bundle package-provenance.intoto.jsonl \
|
|
210
|
+
--repo <OWNER>/ci-cd-playground \
|
|
211
|
+
--signer-workflow <OWNER>/ci-cd-playground/.github/workflows/package-build.yml
|
|
212
|
+
|
|
213
|
+
gh attestation verify ci_cd_playground-0.1.15.tar.gz \
|
|
214
|
+
--bundle package-provenance.intoto.jsonl \
|
|
215
|
+
--repo <OWNER>/ci-cd-playground \
|
|
216
|
+
--signer-workflow <OWNER>/ci-cd-playground/.github/workflows/package-build.yml
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### 3. Verify Individual SBOM Attestations
|
|
220
|
+
|
|
221
|
+
Verify that the SBOMs were cryptographically signed and bound to the exact distribution files:
|
|
222
|
+
|
|
223
|
+
**Wheel SBOM Attestation:**
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
# Online
|
|
227
|
+
gh attestation verify ci_cd_playground-0.1.15-py3-none-any.whl \
|
|
228
|
+
--repo <OWNER>/ci-cd-playground \
|
|
229
|
+
--signer-workflow <OWNER>/ci-cd-playground/.github/workflows/package-build.yml \
|
|
230
|
+
--predicate-type https://spdx.dev/Document/v2.3
|
|
231
|
+
|
|
232
|
+
# Offline
|
|
233
|
+
gh attestation verify ci_cd_playground-0.1.15-py3-none-any.whl \
|
|
234
|
+
--bundle package-wheel-sbom.attestation.json \
|
|
235
|
+
--repo <OWNER>/ci-cd-playground \
|
|
236
|
+
--signer-workflow <OWNER>/ci-cd-playground/.github/workflows/package-build.yml \
|
|
237
|
+
--predicate-type https://spdx.dev/Document/v2.3
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**Sdist SBOM Attestation:**
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
# Online
|
|
244
|
+
gh attestation verify ci_cd_playground-0.1.15.tar.gz \
|
|
245
|
+
--repo <OWNER>/ci-cd-playground \
|
|
246
|
+
--signer-workflow <OWNER>/ci-cd-playground/.github/workflows/package-build.yml \
|
|
247
|
+
--predicate-type https://spdx.dev/Document/v2.3
|
|
248
|
+
|
|
249
|
+
# Offline
|
|
250
|
+
gh attestation verify ci_cd_playground-0.1.15.tar.gz \
|
|
251
|
+
--bundle package-sdist-sbom.attestation.json \
|
|
252
|
+
--repo <OWNER>/ci-cd-playground \
|
|
253
|
+
--signer-workflow <OWNER>/ci-cd-playground/.github/workflows/package-build.yml \
|
|
254
|
+
--predicate-type https://spdx.dev/Document/v2.3
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### 4. Inspect Software Bill of Materials (SBOM)
|
|
258
|
+
|
|
259
|
+
Inspect the Syft-generated human-readable summary or machine-readable SPDX JSON:
|
|
260
|
+
|
|
261
|
+
```bash
|
|
262
|
+
# Human-readable table summary
|
|
263
|
+
cat package-wheel.txt
|
|
264
|
+
cat package-sdist.txt
|
|
265
|
+
|
|
266
|
+
# Query packages in SPDX JSON with jq
|
|
267
|
+
jq '.packages[] | {name: .name, versionInfo: .versionInfo}' package-wheel.spdx.json
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Project Structure
|
|
271
|
+
|
|
272
|
+
```text
|
|
273
|
+
ci-cd-playground/
|
|
274
|
+
├── .github/
|
|
275
|
+
│ └── workflows/
|
|
276
|
+
│ ├── cd.yml # Continuous delivery pipeline (main -> staging -> production)
|
|
277
|
+
│ ├── ci.yml # Reusable CI workflow (lint, typecheck, test, build)
|
|
278
|
+
│ ├── container-security.yml # Container build & Trivy scan on PRs
|
|
279
|
+
│ ├── deploy.yml # Reusable Render deployment workflow
|
|
280
|
+
│ ├── docker-publish.yml # Reusable Docker build, test & GHCR publish
|
|
281
|
+
│ ├── package-build.yml # Reusable SLSA Level 3 builder (uv build, Syft SBOMs, attestations)
|
|
282
|
+
│ └── release.yml # Release pipeline (validation, build, test, verify, TestPyPI, PyPI, GitHub)
|
|
283
|
+
├── src/
|
|
284
|
+
│ └── ci_cd_playground/
|
|
285
|
+
│ ├── __init__.py # Package entrypoint and sample code
|
|
286
|
+
│ ├── app.py # Minimal FastAPI application
|
|
287
|
+
│ └── py.typed # PEP 561 typing marker
|
|
288
|
+
├── tests/
|
|
289
|
+
│ ├── __init__.py
|
|
290
|
+
│ ├── test_app.py # FastAPI endpoint and factory tests
|
|
291
|
+
│ └── test_ci_test.py # Base unit tests
|
|
292
|
+
├── .gitignore
|
|
293
|
+
├── .python-version # Default local Python version
|
|
294
|
+
├── pyproject.toml # Project configuration, dependencies, and tool settings
|
|
295
|
+
├── uv.lock # Deterministic dependency lockfile
|
|
296
|
+
└── README.md
|
|
297
|
+
```
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# ci-cd-playground
|
|
2
|
+
|
|
3
|
+
A Python project configured with [`uv`](https://docs.astral.sh/uv/), [`ruff`](https://docs.astral.sh/ruff/), [`pyright`](https://microsoft.github.io/pyright/), and [`pytest`](https://docs.pytest.org/), complete with GitHub Actions Continuous Integration (CI), Release automation, and automated publishing to TestPyPI.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- [uv](https://docs.astral.sh/uv/getting-started/installation/) (v0.5+ recommended)
|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
11
|
+
### 1. Install Dependencies
|
|
12
|
+
|
|
13
|
+
Install all dependencies (including development tools) into a local virtual environment:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
uv sync
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### 2. Run Quality Checks & Tests Locally
|
|
20
|
+
|
|
21
|
+
| Tool | Purpose | Command |
|
|
22
|
+
| :--- | :--- | :--- |
|
|
23
|
+
| **Ruff Check** | Linting and code style rules | `uv run ruff check .` |
|
|
24
|
+
| **Ruff Format** | Code formatting verification | `uv run ruff format --check .` |
|
|
25
|
+
| **Pyright** | Static type checking | `uv run pyright` |
|
|
26
|
+
| **Pytest** | Unit test suite | `uv run pytest` |
|
|
27
|
+
| **Build** | Build sdist and wheel | `uv build` |
|
|
28
|
+
|
|
29
|
+
To automatically fix lint violations or format code:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Format code
|
|
33
|
+
uv run ruff format .
|
|
34
|
+
|
|
35
|
+
# Auto-fix linting issues
|
|
36
|
+
uv run ruff check --fix .
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 3. Run the FastAPI Application
|
|
40
|
+
|
|
41
|
+
Start the local development server with auto-reload:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
uv run uvicorn ci_cd_playground:app --reload
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The API endpoints will be accessible at `http://127.0.0.1:8000/` and interactive Swagger docs at `http://127.0.0.1:8000/docs`.
|
|
48
|
+
|
|
49
|
+
### 4. Build Distribution Packages
|
|
50
|
+
|
|
51
|
+
Build both the source distribution (`.tar.gz`) and wheel (`.whl`):
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
uv build
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The output packages will be located in the `dist/` directory.
|
|
58
|
+
|
|
59
|
+
## GitHub Actions Workflows
|
|
60
|
+
|
|
61
|
+
The repository organizes continuous integration and deployment into modular, decoupled workflows:
|
|
62
|
+
|
|
63
|
+
### 1. Continuous Integration (CI) — [`.github/workflows/ci.yml`](.github/workflows/ci.yml)
|
|
64
|
+
|
|
65
|
+
Runs automatically on pushes and pull requests to `main`, and functions as a **reusable workflow** (`workflow_call`):
|
|
66
|
+
|
|
67
|
+
1. **Lint & Format (Ruff)**: Verifies formatting and runs linting rules.
|
|
68
|
+
2. **Type Check (Pyright)**: Validates static typing rules across `src/` and `tests/`.
|
|
69
|
+
3. **Matrix Tests (Pytest)**: Runs the test suite across supported Python versions (`3.10`, `3.11`, `3.12`, `3.13`) using `astral-sh/setup-uv`.
|
|
70
|
+
4. **Security Check (zizmor)**: Audits GitHub Actions workflows for security vulnerabilities and permissions issues.
|
|
71
|
+
5. **Build (uv build)**: Builds source distributions and wheels, performs a smoke test by installing and importing the wheel in an isolated environment, and uploads build artifacts (`dist-packages`).
|
|
72
|
+
|
|
73
|
+
### 2. Container Security — [`.github/workflows/container-security.yml`](.github/workflows/container-security.yml)
|
|
74
|
+
|
|
75
|
+
Runs on pull requests to `main` and via `workflow_call`:
|
|
76
|
+
- Builds the runtime Docker image.
|
|
77
|
+
- Scans for vulnerabilities with Trivy using repository ignore configurations.
|
|
78
|
+
|
|
79
|
+
### 3. Docker Build & Publish — [`.github/workflows/docker-publish.yml`](.github/workflows/docker-publish.yml)
|
|
80
|
+
|
|
81
|
+
A **reusable workflow** (`workflow_call`) that encapsulates container lifecycle tasks:
|
|
82
|
+
1. Builds the production image with `APP_VERSION` and `GIT_SHA` build arguments.
|
|
83
|
+
2. Scans the container with Trivy for CRITICAL and HIGH severity issues.
|
|
84
|
+
3. Starts the container locally and verifies `/health` response.
|
|
85
|
+
4. Logs into GitHub Container Registry (GHCR) and publishes the tagged images (`${GITHUB_SHA}` and optional custom/release tag).
|
|
86
|
+
|
|
87
|
+
### 4. Deploy Service — [`.github/workflows/deploy.yml`](.github/workflows/deploy.yml)
|
|
88
|
+
|
|
89
|
+
A **reusable deployment workflow** (`workflow_call`) that unifies Render deployment logic for all environments:
|
|
90
|
+
1. Configures the target GitHub `environment` (e.g. `staging` or `production`), enabling environment protection rules, approvals, and secret isolation.
|
|
91
|
+
2. Triggers Render deploy via REST API for the given service ID and image tag.
|
|
92
|
+
3. Polls Render deployment status every 30s until `live` or failed.
|
|
93
|
+
4. Verifies service availability via `/health` and validates that `/version` is running the expected commit SHA.
|
|
94
|
+
|
|
95
|
+
### 5. Continuous Delivery (CD) — [`.github/workflows/cd.yml`](.github/workflows/cd.yml)
|
|
96
|
+
|
|
97
|
+
Triggers automatically on pushes to `main` and via manual `workflow_dispatch`:
|
|
98
|
+
1. **CI Validation & Build**: Calls `ci.yml` to verify quality gates.
|
|
99
|
+
2. **Docker Publish**: Calls `docker-publish.yml` to build, scan, test, and push the image to GHCR.
|
|
100
|
+
3. **Deploy to Staging**: Automatically calls `deploy.yml` with `staging` settings and validates staging health.
|
|
101
|
+
4. **Deploy to Production**: Calls `deploy.yml` with `production` settings. Can be gated with GitHub Environment manual approval rules or targeted selectively via manual dispatch.
|
|
102
|
+
|
|
103
|
+
### 6. Package Build & Provenance (SLSA Level 3) — [`.github/workflows/package-build.yml`](.github/workflows/package-build.yml)
|
|
104
|
+
|
|
105
|
+
A **trusted reusable builder workflow** (`workflow_call`) that enforces isolation and build platform integrity to meet **SLSA v1.0 Build Level 3**:
|
|
106
|
+
1. **Isolated Build**: Checks out the target commit and builds both distributions (`uv build --no-sources`) strictly once.
|
|
107
|
+
2. **Artifact Identification**: Computes cryptographic SHA-256 digests for both the wheel (`.whl`) and sdist (`.tar.gz`), generating `checksums/SHA256SUMS`.
|
|
108
|
+
3. **Individual SBOM Generation (Syft)**: Scans the actual built distributions independently with Syft to generate:
|
|
109
|
+
- Machine-readable SPDX 2.3 JSON: `package-wheel.spdx.json` and `package-sdist.spdx.json`
|
|
110
|
+
- Human-readable summary tables: `package-wheel.txt` and `package-sdist.txt`
|
|
111
|
+
4. **SLSA Build Provenance Attestation**: Generates cryptographically signed SLSA provenance via `actions/attest` backed by Sigstore, bound to both distribution digests and exported to `attestations/package-provenance.intoto.jsonl`.
|
|
112
|
+
5. **Individual SBOM Attestations**: Cryptographically signs each distribution's SBOM with `actions/attest`, binding the wheel SBOM to the wheel digest and the sdist SBOM to the sdist digest (`package-wheel-sbom.attestation.json` and `package-sdist-sbom.attestation.json`).
|
|
113
|
+
6. **Immutable Artifact Preservation**: Uploads distributions, SBOMs, attestations, and checksums as GitHub Actions artifacts for downstream pipeline consumption.
|
|
114
|
+
|
|
115
|
+
### 7. Release & PyPI/TestPyPI Publishing — [`.github/workflows/release.yml`](.github/workflows/release.yml)
|
|
116
|
+
|
|
117
|
+
Triggers whenever a version tag matching `v*` (e.g. `v0.1.0`) is pushed to the repository (or via manual `workflow_dispatch`):
|
|
118
|
+
|
|
119
|
+
1. **Validate Release Reference**: Validates git tag syntax, verifies that the tag cryptographically resolves to the target commit SHA, and ensures the git tag matches `project.version` in `pyproject.toml`.
|
|
120
|
+
2. **Build via Trusted Builder**: Invokes the isolated reusable workflow `package-build.yml` to produce immutable distributions, Syft SBOMs, SLSA Level 3 provenance, and SBOM attestations.
|
|
121
|
+
3. **Test Built Distributions**: Installs the built wheel in an isolated `.venv-wheel` and the built sdist in an isolated `.venv-sdist`, verifying installations and import smoke-tests before promotion.
|
|
122
|
+
4. **Comprehensive Automated Verification**:
|
|
123
|
+
- Verifies distribution checksums against `SHA256SUMS`.
|
|
124
|
+
- Verifies SLSA Build Provenance authenticity and subject digests online via GitHub API and offline using the bundled `.intoto.jsonl` file.
|
|
125
|
+
- Verifies individual SBOM attestations online and offline using `--predicate-type https://spdx.dev/Document/v2.3`.
|
|
126
|
+
- Validates that the builder signer workflow strictly matches `package-build.yml` (`--signer-workflow`).
|
|
127
|
+
5. **Publish to TestPyPI**:
|
|
128
|
+
- Publishes verified distributions using Trusted Publishing (OIDC) via `uv publish --index testpypi`.
|
|
129
|
+
- Performs a smoke-test installation in a clean environment from TestPyPI.
|
|
130
|
+
6. **Publish to PyPI**:
|
|
131
|
+
- Gated by the `pypi` environment (with optional environment protection / manual approval rules).
|
|
132
|
+
- Generates PEP 740 digital provenance attestations via `astral-sh/attest-action`.
|
|
133
|
+
- Publishes verified distributions to PyPI via `uv publish`.
|
|
134
|
+
7. **Create GitHub Release**:
|
|
135
|
+
- Creates a GitHub Release with release notes and attaches:
|
|
136
|
+
- `dist/`: Wheel (`.whl`) and source distribution (`.tar.gz`)
|
|
137
|
+
- `sbom/`: SPDX JSON and readable text SBOMs (`package-wheel.spdx.json`, `package-sdist.spdx.json`, `package-wheel.txt`, `package-sdist.txt`)
|
|
138
|
+
- `attestations/`: `package-provenance.intoto.jsonl`, `package-wheel-sbom.attestation.json`, `package-sdist-sbom.attestation.json`
|
|
139
|
+
- `checksums/`: `SHA256SUMS`
|
|
140
|
+
|
|
141
|
+
#### Configuring PyPI & TestPyPI Trusted Publishing
|
|
142
|
+
|
|
143
|
+
1. **TestPyPI**:
|
|
144
|
+
- Add a Pending Publisher on [TestPyPI](https://test.pypi.org/manage/account/publishing/) targeting your GitHub repository and workflow (`release.yml`), with environment name `testpypi`.
|
|
145
|
+
- Create a GitHub Environment named `testpypi` in repository settings (**Settings > Environments**).
|
|
146
|
+
2. **Production PyPI**:
|
|
147
|
+
- Add a Publisher on [PyPI](https://pypi.org/manage/account/publishing/) targeting your GitHub repository and workflow (`release.yml`), with environment name `pypi`.
|
|
148
|
+
- Create a GitHub Environment named `pypi` in repository settings (**Settings > Environments**), optionally configuring required reviewers for manual release sign-off.
|
|
149
|
+
|
|
150
|
+
#### Creating a Release
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
# 1. Bump version in pyproject.toml (e.g. 0.1.15)
|
|
154
|
+
uv version 0.1.15
|
|
155
|
+
|
|
156
|
+
# 2. Commit and tag
|
|
157
|
+
git commit -am "chore: release v0.1.15"
|
|
158
|
+
git tag v0.1.15
|
|
159
|
+
|
|
160
|
+
# 3. Push tag to GitHub to trigger the release workflow
|
|
161
|
+
git push origin v0.1.15
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Consumer Verification Guide
|
|
165
|
+
|
|
166
|
+
Consumers can independently verify the authenticity, integrity, SLSA Build Level 3 provenance, and SBOM attestations of released artifacts without trusting the build workflow's claims.
|
|
167
|
+
|
|
168
|
+
### 1. Verify SHA-256 Checksums
|
|
169
|
+
|
|
170
|
+
Download the release assets into a local directory and verify the distribution digests:
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
sha256sum -c SHA256SUMS
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### 2. Verify SLSA Build Level 3 Provenance
|
|
177
|
+
|
|
178
|
+
Using the official [GitHub CLI](https://cli.github.com/) (`gh` v2.97.0+):
|
|
179
|
+
|
|
180
|
+
**Online Verification (via Sigstore transparency log):**
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# Verify wheel provenance
|
|
184
|
+
gh attestation verify ci_cd_playground-0.1.15-py3-none-any.whl \
|
|
185
|
+
--repo <OWNER>/ci-cd-playground \
|
|
186
|
+
--signer-workflow <OWNER>/ci-cd-playground/.github/workflows/package-build.yml
|
|
187
|
+
|
|
188
|
+
# Verify sdist provenance
|
|
189
|
+
gh attestation verify ci_cd_playground-0.1.15.tar.gz \
|
|
190
|
+
--repo <OWNER>/ci-cd-playground \
|
|
191
|
+
--signer-workflow <OWNER>/ci-cd-playground/.github/workflows/package-build.yml
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**Offline Verification (using the release attestation bundle):**
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
gh attestation verify ci_cd_playground-0.1.15-py3-none-any.whl \
|
|
198
|
+
--bundle package-provenance.intoto.jsonl \
|
|
199
|
+
--repo <OWNER>/ci-cd-playground \
|
|
200
|
+
--signer-workflow <OWNER>/ci-cd-playground/.github/workflows/package-build.yml
|
|
201
|
+
|
|
202
|
+
gh attestation verify ci_cd_playground-0.1.15.tar.gz \
|
|
203
|
+
--bundle package-provenance.intoto.jsonl \
|
|
204
|
+
--repo <OWNER>/ci-cd-playground \
|
|
205
|
+
--signer-workflow <OWNER>/ci-cd-playground/.github/workflows/package-build.yml
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### 3. Verify Individual SBOM Attestations
|
|
209
|
+
|
|
210
|
+
Verify that the SBOMs were cryptographically signed and bound to the exact distribution files:
|
|
211
|
+
|
|
212
|
+
**Wheel SBOM Attestation:**
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
# Online
|
|
216
|
+
gh attestation verify ci_cd_playground-0.1.15-py3-none-any.whl \
|
|
217
|
+
--repo <OWNER>/ci-cd-playground \
|
|
218
|
+
--signer-workflow <OWNER>/ci-cd-playground/.github/workflows/package-build.yml \
|
|
219
|
+
--predicate-type https://spdx.dev/Document/v2.3
|
|
220
|
+
|
|
221
|
+
# Offline
|
|
222
|
+
gh attestation verify ci_cd_playground-0.1.15-py3-none-any.whl \
|
|
223
|
+
--bundle package-wheel-sbom.attestation.json \
|
|
224
|
+
--repo <OWNER>/ci-cd-playground \
|
|
225
|
+
--signer-workflow <OWNER>/ci-cd-playground/.github/workflows/package-build.yml \
|
|
226
|
+
--predicate-type https://spdx.dev/Document/v2.3
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**Sdist SBOM Attestation:**
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
# Online
|
|
233
|
+
gh attestation verify ci_cd_playground-0.1.15.tar.gz \
|
|
234
|
+
--repo <OWNER>/ci-cd-playground \
|
|
235
|
+
--signer-workflow <OWNER>/ci-cd-playground/.github/workflows/package-build.yml \
|
|
236
|
+
--predicate-type https://spdx.dev/Document/v2.3
|
|
237
|
+
|
|
238
|
+
# Offline
|
|
239
|
+
gh attestation verify ci_cd_playground-0.1.15.tar.gz \
|
|
240
|
+
--bundle package-sdist-sbom.attestation.json \
|
|
241
|
+
--repo <OWNER>/ci-cd-playground \
|
|
242
|
+
--signer-workflow <OWNER>/ci-cd-playground/.github/workflows/package-build.yml \
|
|
243
|
+
--predicate-type https://spdx.dev/Document/v2.3
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### 4. Inspect Software Bill of Materials (SBOM)
|
|
247
|
+
|
|
248
|
+
Inspect the Syft-generated human-readable summary or machine-readable SPDX JSON:
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
# Human-readable table summary
|
|
252
|
+
cat package-wheel.txt
|
|
253
|
+
cat package-sdist.txt
|
|
254
|
+
|
|
255
|
+
# Query packages in SPDX JSON with jq
|
|
256
|
+
jq '.packages[] | {name: .name, versionInfo: .versionInfo}' package-wheel.spdx.json
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Project Structure
|
|
260
|
+
|
|
261
|
+
```text
|
|
262
|
+
ci-cd-playground/
|
|
263
|
+
├── .github/
|
|
264
|
+
│ └── workflows/
|
|
265
|
+
│ ├── cd.yml # Continuous delivery pipeline (main -> staging -> production)
|
|
266
|
+
│ ├── ci.yml # Reusable CI workflow (lint, typecheck, test, build)
|
|
267
|
+
│ ├── container-security.yml # Container build & Trivy scan on PRs
|
|
268
|
+
│ ├── deploy.yml # Reusable Render deployment workflow
|
|
269
|
+
│ ├── docker-publish.yml # Reusable Docker build, test & GHCR publish
|
|
270
|
+
│ ├── package-build.yml # Reusable SLSA Level 3 builder (uv build, Syft SBOMs, attestations)
|
|
271
|
+
│ └── release.yml # Release pipeline (validation, build, test, verify, TestPyPI, PyPI, GitHub)
|
|
272
|
+
├── src/
|
|
273
|
+
│ └── ci_cd_playground/
|
|
274
|
+
│ ├── __init__.py # Package entrypoint and sample code
|
|
275
|
+
│ ├── app.py # Minimal FastAPI application
|
|
276
|
+
│ └── py.typed # PEP 561 typing marker
|
|
277
|
+
├── tests/
|
|
278
|
+
│ ├── __init__.py
|
|
279
|
+
│ ├── test_app.py # FastAPI endpoint and factory tests
|
|
280
|
+
│ └── test_ci_test.py # Base unit tests
|
|
281
|
+
├── .gitignore
|
|
282
|
+
├── .python-version # Default local Python version
|
|
283
|
+
├── pyproject.toml # Project configuration, dependencies, and tool settings
|
|
284
|
+
├── uv.lock # Deterministic dependency lockfile
|
|
285
|
+
└── README.md
|
|
286
|
+
```
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "ci-cd-playground"
|
|
3
|
+
version = "0.1.16"
|
|
4
|
+
description = "A sample Python project with uv, ruff, pyright, and pytest"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"fastapi>=0.162.0",
|
|
9
|
+
"uvicorn>=0.54.0",
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
[[project.authors]]
|
|
13
|
+
name = "john"
|
|
14
|
+
email = "96323078+JohnStephens1@users.noreply.github.com"
|
|
15
|
+
|
|
16
|
+
[build-system]
|
|
17
|
+
requires = ["uv_build>=0.12.22,<0.14.0"]
|
|
18
|
+
build-backend = "uv_build"
|
|
19
|
+
|
|
20
|
+
[dependency-groups]
|
|
21
|
+
dev = [
|
|
22
|
+
"httpx2>=2.14.0",
|
|
23
|
+
"pre-commit>=4.6.2",
|
|
24
|
+
"pyright>=1.1.414",
|
|
25
|
+
"pytest>=9.2.0",
|
|
26
|
+
"ruff>=0.17.3",
|
|
27
|
+
"zizmor>=1.30.1",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[[tool.uv.index]]
|
|
31
|
+
name = "testpypi"
|
|
32
|
+
url = "https://test.pypi.org/simple/"
|
|
33
|
+
publish-url = "https://test.pypi.org/legacy/"
|
|
34
|
+
explicit = true
|
|
35
|
+
|
|
36
|
+
[tool.ruff]
|
|
37
|
+
line-length = 88
|
|
38
|
+
target-version = "py310"
|
|
39
|
+
src = ["src"]
|
|
40
|
+
|
|
41
|
+
[tool.ruff.lint]
|
|
42
|
+
select = [
|
|
43
|
+
"E",
|
|
44
|
+
"W",
|
|
45
|
+
"F",
|
|
46
|
+
"I",
|
|
47
|
+
"B",
|
|
48
|
+
"UP",
|
|
49
|
+
]
|
|
50
|
+
ignore = []
|
|
51
|
+
|
|
52
|
+
[tool.ruff.format]
|
|
53
|
+
quote-style = "double"
|
|
54
|
+
indent-style = "space"
|
|
55
|
+
|
|
56
|
+
[tool.pyright]
|
|
57
|
+
include = [
|
|
58
|
+
"src",
|
|
59
|
+
"tests",
|
|
60
|
+
]
|
|
61
|
+
pythonVersion = "3.10"
|
|
62
|
+
typeCheckingMode = "standard"
|
|
63
|
+
|
|
64
|
+
[tool.pytest.ini_options]
|
|
65
|
+
minversion = "7.0"
|
|
66
|
+
testpaths = ["tests"]
|
|
67
|
+
pythonpath = ["src"]
|
|
68
|
+
addopts = "-ra -q"
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "ci-cd-playground"
|
|
3
|
+
version = "0.1.16"
|
|
4
|
+
description = "A sample Python project with uv, ruff, pyright, and pytest"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "john", email = "96323078+JohnStephens1@users.noreply.github.com" }
|
|
8
|
+
]
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"fastapi>=0.162.0",
|
|
12
|
+
"uvicorn>=0.54.0",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
[build-system]
|
|
16
|
+
requires = ["uv_build>=0.12.22,<0.14.0"]
|
|
17
|
+
build-backend = "uv_build"
|
|
18
|
+
|
|
19
|
+
[dependency-groups]
|
|
20
|
+
dev = [
|
|
21
|
+
"httpx2>=2.14.0",
|
|
22
|
+
"pre-commit>=4.6.2",
|
|
23
|
+
"pyright>=1.1.414",
|
|
24
|
+
"pytest>=9.2.0",
|
|
25
|
+
"ruff>=0.17.3",
|
|
26
|
+
"zizmor>=1.30.1",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[[tool.uv.index]]
|
|
30
|
+
name = "testpypi"
|
|
31
|
+
url = "https://test.pypi.org/simple/"
|
|
32
|
+
publish-url = "https://test.pypi.org/legacy/"
|
|
33
|
+
explicit = true
|
|
34
|
+
|
|
35
|
+
[tool.ruff]
|
|
36
|
+
line-length = 88
|
|
37
|
+
target-version = "py310"
|
|
38
|
+
src = ["src"]
|
|
39
|
+
|
|
40
|
+
[tool.ruff.lint]
|
|
41
|
+
select = [
|
|
42
|
+
"E", # pycodestyle errors
|
|
43
|
+
"W", # pycodestyle warnings
|
|
44
|
+
"F", # pyflakes
|
|
45
|
+
"I", # isort
|
|
46
|
+
"B", # flake8-bugbear
|
|
47
|
+
"UP", # pyupgrade
|
|
48
|
+
]
|
|
49
|
+
ignore = []
|
|
50
|
+
|
|
51
|
+
[tool.ruff.format]
|
|
52
|
+
quote-style = "double"
|
|
53
|
+
indent-style = "space"
|
|
54
|
+
|
|
55
|
+
[tool.pyright]
|
|
56
|
+
include = ["src", "tests"]
|
|
57
|
+
pythonVersion = "3.10"
|
|
58
|
+
typeCheckingMode = "standard"
|
|
59
|
+
|
|
60
|
+
[tool.pytest.ini_options]
|
|
61
|
+
minversion = "7.0"
|
|
62
|
+
testpaths = ["tests"]
|
|
63
|
+
pythonpath = ["src"]
|
|
64
|
+
addopts = "-ra -q"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""ci_cd_playground package."""
|
|
2
|
+
|
|
3
|
+
from ci_cd_playground.app import app, create_app
|
|
4
|
+
|
|
5
|
+
__all__ = ["add", "app", "create_app", "hello"]
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def hello(name: str = "world") -> str:
|
|
9
|
+
"""Return a greeting message."""
|
|
10
|
+
return f"Hello, {name}!"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def add(a: int | float, b: int | float) -> int | float:
|
|
14
|
+
"""Add two numbers."""
|
|
15
|
+
return a + b
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""FastAPI application for ci_cd_playground."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from fastapi import FastAPI
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def create_app() -> FastAPI:
|
|
9
|
+
"""Create and configure the FastAPI application."""
|
|
10
|
+
application = FastAPI(
|
|
11
|
+
title="CI/CD Playground API",
|
|
12
|
+
description="A minimal FastAPI application.",
|
|
13
|
+
version="0.1.1",
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
@application.get("/")
|
|
17
|
+
def read_root() -> dict[str, str]:
|
|
18
|
+
"""Root endpoint returning a greeting."""
|
|
19
|
+
return {"message": "Hello, world!"}
|
|
20
|
+
|
|
21
|
+
@application.get("/health")
|
|
22
|
+
def health_check() -> dict[str, str]:
|
|
23
|
+
"""Health check endpoint."""
|
|
24
|
+
return {"status": "ok"}
|
|
25
|
+
|
|
26
|
+
@application.get("/version")
|
|
27
|
+
def version() -> dict[str, str]:
|
|
28
|
+
return {
|
|
29
|
+
"version": os.getenv("APP_VERSION", "unknown"),
|
|
30
|
+
"commit": os.getenv("GIT_SHA", "unknown"),
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@application.get("/hello/{name}")
|
|
34
|
+
def greet(name: str) -> dict[str, str]:
|
|
35
|
+
"""Greeting endpoint accepting a path parameter."""
|
|
36
|
+
return {"message": f"Hello, {name}!"}
|
|
37
|
+
|
|
38
|
+
return application
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
app = create_app()
|
|
File without changes
|