Skip to content

Commit c2609df

Browse files
authored
CoReason Python package template
feat: Create best-in-class Python package template This commit introduces a complete, production-ready Python package template with a modern, secure, and standardized structure. Key Features: * **Dependency Management:** Uses Poetry for standardized package and dependency management. * **Code Quality:** Implements a comprehensive, self-validating `pre-commit` suite, including Ruff, Mypy, and `hadolint`, to ensure code quality and consistency. * **Containerization:** Provides an optimized, secure, multi-stage `Dockerfile` that runs as a non-root user. The runtime stage adds the non-root user's local bin to the `PATH` for script accessibility. * **CI/CD:** Establishes a robust GitHub Actions workflow that includes: * Linting (via `pre-commit`) * Testing (across multiple Python versions) * Docker image vulnerability scanning (via Trivy) * **Security:** Pins all third-party GitHub Actions to their full commit SHA to mitigate supply chain risks. * **Efficiency:** Implements a robust caching strategy for Poetry dependencies using `actions/cache` and a local `.venv` to accelerate CI builds. * **Licensing:** Updates the license to the Prosperity Public License 3.0.0, establishing a dual-licensing model (non-commercial and commercial). * **Metadata:** Updates the package author to "CoReason, Inc."
1 parent 40e1db7 commit c2609df

13 files changed

Lines changed: 837 additions & 73 deletions

File tree

‎.dockerignore‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.git/
2+
.venv/
3+
__pycache__/
4+
.pytest_cache/
5+
.mypy_cache/
6+
docs/
7+
.github/
8+
README.md
9+
.pre-commit-config.yaml

‎.github/workflows/ci.yml‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- develop
8+
pull_request:
9+
branches:
10+
- main
11+
- develop
12+
13+
permissions:
14+
contents: read
15+
16+
concurrency:
17+
group: ${{ github.workflow }}-${{ github.ref }}
18+
cancel-in-progress: true
19+
20+
jobs:
21+
lint:
22+
runs-on: ubuntu-latest
23+
steps:
24+
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493
25+
- uses: actions/setup-python@cfd55ca82492758d853442341ad4d8010466803a
26+
with:
27+
python-version: '3.12'
28+
- name: Run pre-commit
29+
uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd
30+
31+
test:
32+
needs: lint
33+
runs-on: ubuntu-latest
34+
strategy:
35+
matrix:
36+
python-version: ["3.10", "3.11", "3.12"]
37+
steps:
38+
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493
39+
- name: Set up Python ${{ matrix.python-version }}
40+
uses: actions/setup-python@cfd55ca82492758d853442341ad4d8010466803a
41+
with:
42+
python-version: ${{ matrix.python-version }}
43+
44+
- name: Install Poetry
45+
uses: snok/install-poetry@ff8a7d7de27005376176819789742a2280cc35e2
46+
47+
- name: Configure Poetry
48+
run: poetry config virtualenvs.in-project true
49+
50+
- name: Cache dependencies
51+
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830
52+
with:
53+
path: .venv
54+
key: v1-${{ runner.os }}-python-${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}
55+
restore-keys: |
56+
v1-${{ runner.os }}-python-${{ matrix.python-version }}-
57+
58+
- name: Install dependencies
59+
run: poetry install --no-interaction --no-root
60+
61+
- name: Run tests
62+
run: poetry run pytest

‎.github/workflows/docker.yml‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Docker
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- develop
8+
9+
permissions:
10+
contents: read
11+
packages: write
12+
13+
concurrency:
14+
group: ${{ github.workflow }}-${{ github.ref }}
15+
cancel-in-progress: true
16+
17+
jobs:
18+
build-scan-push:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout repository
22+
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493
23+
24+
- name: Log in to the Container registry
25+
uses: docker/login-action@9788b0c4429711fb0def2b5cb23a4bB5Dff6c30a
26+
with:
27+
registry: ghcr.io
28+
username: ${{ github.actor }}
29+
password: ${{ secrets.GITHUB_TOKEN }}
30+
31+
- name: Set up QEMU
32+
uses: docker/setup-qemu-action@68827325e0b33c7199093565ac3b62264dc64a97
33+
34+
- name: Set up Docker Buildx
35+
uses: docker/setup-buildx-action@d70bba72b6f31a22640103738a088e5d3c8b4104
36+
37+
- name: Build and push
38+
uses: docker/build-push-action@2cddeafc873d6113b789a7164923793f63101131
39+
with:
40+
context: .
41+
push: true
42+
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
43+
cache-from: type=gha
44+
cache-to: type=gha,mode=max
45+
46+
- name: Scan for vulnerabilities
47+
uses: aquasecurity/trivy-action@678a23d8ab761c56f6f59508935c1054363d11b3
48+
with:
49+
image-ref: 'ghcr.io/${{ github.repository }}:${{ github.sha }}'
50+
format: 'table'
51+
exit-code: '1'
52+
ignore-unfixed: true
53+
vuln-type: 'os,library'
54+
severity: 'CRITICAL,HIGH'

‎.gitignore‎

Lines changed: 11 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Byte-compiled / optimized / DLL files
22
__pycache__/
3-
*.py[codz]
3+
*.py[cod]
44
*$py.class
55

66
# C extensions
@@ -20,7 +20,6 @@ parts/
2020
sdist/
2121
var/
2222
wheels/
23-
share/python-wheels/
2423
*.egg-info/
2524
.installed.cfg
2625
*.egg
@@ -46,10 +45,9 @@ htmlcov/
4645
nosetests.xml
4746
coverage.xml
4847
*.cover
49-
*.py.cover
48+
*.py,cover
5049
.hypothesis/
5150
.pytest_cache/
52-
cover/
5351

5452
# Translations
5553
*.mo
@@ -72,7 +70,6 @@ instance/
7270
docs/_build/
7371

7472
# PyBuilder
75-
.pybuilder/
7673
target/
7774

7875
# Jupyter Notebook
@@ -83,48 +80,28 @@ profile_default/
8380
ipython_config.py
8481

8582
# pyenv
86-
# For a library or package, you might want to ignore these files since the code is
87-
# intended to run in multiple environments; otherwise, check them in:
88-
# .python-version
83+
.python-version
8984

9085
# pipenv
9186
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
9287
# However, in case of collaboration, if having platform-specific dependencies or dependencies
93-
# having no cross-platform support, pipenv may install dependencies that don't work, or not
94-
# install all needed dependencies.
88+
# from different sources is not a concern, Pipfile.lock also may be ignored.
9589
#Pipfile.lock
9690

97-
# UV
98-
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99-
# This is especially recommended for binary packages to ensure reproducibility, and is more
100-
# commonly ignored for libraries.
101-
#uv.lock
102-
10391
# poetry
10492
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105-
# This is especially recommended for binary packages to ensure reproducibility, and is more
106-
# commonly ignored for libraries.
107-
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
93+
# This is especially true if you want to ensure deterministic builds.
94+
# However, in some cases, it may be desirable to ignore them.
10895
#poetry.lock
109-
#poetry.toml
11096

11197
# pdm
11298
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113-
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
114-
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
11599
#pdm.lock
116-
#pdm.toml
117-
.pdm-python
118-
.pdm-build/
119-
120-
# pixi
121-
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
122-
#pixi.lock
123-
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
124-
# in the .venv directory. It is recommended not to include this directory in version control.
125-
.pixi
126-
127-
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
100+
# pdm stores its cache in the specified location, which is ~/.pdm/cache by default.
101+
# It might be desirable to ignore it if you use a different cache directory.
102+
#.pdm-cache/
103+
104+
# PEP 582; used by pdm
128105
__pypackages__/
129106

130107
# Celery stuff
@@ -136,7 +113,6 @@ celerybeat.pid
136113

137114
# Environments
138115
.env
139-
.envrc
140116
.venv
141117
env/
142118
venv/
@@ -167,41 +143,3 @@ dmypy.json
167143

168144
# Cython debug symbols
169145
cython_debug/
170-
171-
# PyCharm
172-
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
173-
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
174-
# and can be added to the global gitignore or merged into this file. For a more nuclear
175-
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
176-
#.idea/
177-
178-
# Abstra
179-
# Abstra is an AI-powered process automation framework.
180-
# Ignore directories containing user credentials, local state, and settings.
181-
# Learn more at https://abstra.io/docs
182-
.abstra/
183-
184-
# Visual Studio Code
185-
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
186-
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
187-
# and can be added to the global gitignore or merged into this file. However, if you prefer,
188-
# you could uncomment the following to ignore the entire vscode folder
189-
# .vscode/
190-
191-
# Ruff stuff:
192-
.ruff_cache/
193-
194-
# PyPI configuration file
195-
.pypirc
196-
197-
# Cursor
198-
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
199-
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
200-
# refer to https://docs.cursor.com/context/ignore-files
201-
.cursorignore
202-
.cursorindexingignore
203-
204-
# Marimo
205-
marimo/_static/
206-
marimo/_lsp/
207-
__marimo__/

‎.pre-commit-config.yaml‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
repos:
2+
- repo: https://github.com/pre-commit/pre-commit-hooks
3+
rev: v4.6.0
4+
hooks:
5+
- id: trailing-whitespace
6+
- id: end-of-file-fixer
7+
- id: check-yaml
8+
- id: check-toml
9+
- id: check-json
10+
- repo: https://github.com/astral-sh/ruff-pre-commit
11+
rev: v0.14.2
12+
hooks:
13+
- id: ruff
14+
args: [--fix, --exit-non-zero-on-fix]
15+
- id: ruff-format
16+
- repo: https://github.com/pre-commit/mirrors-mypy
17+
rev: v1.18.2
18+
hooks:
19+
- id: mypy
20+
- repo: https://github.com/AleksaC/hadolint-py
21+
rev: v2.14.0
22+
hooks:
23+
- id: hadolint

‎CI_CD_STRATEGY.md‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# CI/CD Strategy
2+
3+
This document outlines the CI/CD architecture for this project, including the Docker strategy and security measures.
4+
5+
## CI/CD Architecture
6+
7+
The CI/CD pipeline is built using GitHub Actions and is divided into two workflows: `ci.yml` and `docker.yml`.
8+
9+
- **`ci.yml`**: This workflow is triggered on `push` and `pull_request` events to the `main` and `develop` branches. It consists of two jobs:
10+
1. **`lint`**: This job runs the `pre-commit` suite to ensure all code adheres to the defined quality and style standards.
11+
2. **`test`**: This job runs the `pytest` suite across a matrix of Python versions (3.10, 3.11, and 3.12) to ensure the code is working as expected. It depends on the `lint` job, so it will only run if the linting passes.
12+
13+
- **`docker.yml`**: This workflow is triggered on `push` events to the `main` and `develop` branches. It builds, scans, and pushes a Docker image to the GitHub Container Registry.
14+
15+
## Docker Strategy
16+
17+
The `Dockerfile` is a multi-stage build to create a lean and secure production image.
18+
19+
- **Stage 1 (Builder)**: This stage installs Poetry, exports the project dependencies to a `requirements.txt` file, and installs them. It also installs the application itself.
20+
- **Stage 2 (Runtime)**: This stage uses a slim Python base image, creates a non-root user, and copies the installed dependencies and application from the builder stage. This results in a smaller and more secure final image.
21+
22+
## Security Measures
23+
24+
- **Principle of Least Privilege (PoLP)**: The GitHub Actions workflows are configured with the minimum required permissions.
25+
- **SHA Pinning**: All third-party GitHub Actions are pinned to their full commit SHA to prevent supply chain attacks.
26+
- **Vulnerability Scanning**: The `docker.yml` workflow includes a step to scan the Docker image for vulnerabilities using Trivy. The workflow will fail if any `CRITICAL` or `HIGH` severity vulnerabilities are found.
27+
- **Non-Root User**: The `Dockerfile` creates and runs the application as a non-root user to reduce the attack surface.

‎Dockerfile‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Stage 1: Builder
2+
FROM python:3.12-slim AS builder
3+
4+
# Install poetry
5+
RUN pip install --no-cache-dir poetry==1.8.2
6+
7+
# Set the working directory
8+
WORKDIR /app
9+
10+
# Copy the project files and install dependencies
11+
COPY pyproject.toml poetry.lock* ./
12+
COPY src/ ./src/
13+
14+
# Export dependencies and install them, then install the project
15+
RUN poetry export -f requirements.txt --output requirements.txt --without-hashes && \
16+
pip install --no-cache-dir --prefix="/install" -r requirements.txt && \
17+
poetry install --no-dev
18+
19+
20+
# Stage 2: Runtime
21+
FROM python:3.12-slim AS runtime
22+
23+
# Create a non-root user
24+
RUN useradd --create-home --shell /bin/bash appuser
25+
USER appuser
26+
27+
# Add user's local bin to PATH
28+
ENV PATH="/home/appuser/.local/bin:${PATH}"
29+
30+
# Set the working directory
31+
WORKDIR /home/appuser/app
32+
33+
# Copy the installed dependencies from the builder stage
34+
COPY --from=builder /install /usr/local
35+
36+
# Copy the application source code from the builder stage
37+
COPY --from=builder /app/src/my_python_project ./my_python_project
38+
39+
# Set the PYTHONPATH to include the installed packages
40+
ENV PYTHONPATH="/home/appuser/app"

0 commit comments

Comments
 (0)