Skip to content

jonathanvanleeuwen/lib_template

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

49 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

lib_template

A cookiecutter template for Python libraries with modern CI/CD setup.

Features

  • Automated testing on PR using GitHub Actions
  • Pre-commit hooks for code quality (ruff, isort, trailing whitespace, etc.)
  • Semantic release using GitHub Actions
  • Automatic code coverage report in README
  • Automatic wheel build and GitHub Release publishing
  • Modern Python packaging with pyproject.toml

Notes Workflows trigger when a branch is merged into main! To install, please follow all the instructions in this readme. The workflows require a PAT set as secret (see GitHub Repository Setup section for instructions). See the notes on how to create semantic releases at the bottom of the README.

If you followed all the steps, whenever a PR is merged into main, the workflows are triggered and should:

  • Run pre-commit checks (fail fast on code quality issues)
  • Ensure that tests pass (before merge)
  • Create a code coverage report and commit that to the bottom of the README
  • Create a semantic release (if you follow the semantic release pattern) and automatically update the version number of your code
  • Build a wheel and publish it as a GitHub Release asset

Install

Cookiecutter template:

  • Cd to your new library location
    • cd /your/new/library/path/
  • Install cookiecutter using pip
    • pip install cookiecutter
  • Run the cookiecutter template from this GitHub repo
    • cookiecutter https://github.com/jonathanvanleeuwen/lib_template
  • Fill in your new library values (including your GitHub username for CODEOWNERS)
  • Create new virtual environment
    • python -m venv .venv
  • Activate the environment and install library with dev dependencies
    • pip install -e ".[dev]"
  • Install pre-commit hooks
    • pip install pre-commit
    • pre-commit install
  • Run pre-commit on all files (important for initial commit!)
    • pre-commit run --all-files
  • Check proper install by running tests
    • pytest

Turn the new local cookiecutter code into a git repo

Open git bash

cd C:/your/code/directory

To init the repository, add all files and commit

git init
git add *
git add .github
git add .gitignore
git add .pre-commit-config.yaml
git commit -m "fix: Initial commit"

To add the new git repository to your GitHub:

  • Log in to your account.
  • Click the new repository button in the top-right. You'll have an option there to initialize the repository with a README file, but don't. Leave the repo empty
  • Give the new repo the same name you gave your repo with the cookiecutter
  • Click the "Create repository" button.

Now we want to make sure we are using main as main branch name and push the code to GitHub

git remote add origin https://github.com/username/new_repo_name.git
git branch -M main
git push -u origin main

πŸ”’ GitHub Repository Setup

Complete these steps in order to enable the CI/CD pipeline.

Step 1: Create the Release Token (PAT)

The workflow needs a Personal Access Token to push to the protected main branch.

Create a Fine-Grained PAT (Recommended - More Secure)

  1. Go to GitHub Settings β†’ Developer settings β†’ Personal access tokens β†’ Fine-grained tokens
  2. Click "Generate new token"
  3. Configure the token:
    • Token name: RELEASE_TOKEN_YOUR_REPO_NAME
    • Expiration: Choose an appropriate duration (recommend 90 days, set a reminder to rotate)
    • Repository access: Select "Only select repositories" β†’ choose your repository
    • Permissions:
      • Contents: Read and write (for pushing commits and tags)
      • Metadata: Read-only (automatically selected)
  4. Click "Generate token"
  5. Copy the token immediately - you won't see it again!

Alternative: Classic PAT (Simpler but Broader Access)

If fine-grained tokens don't work for your use case:

  1. Go to GitHub Settings β†’ Developer settings β†’ Personal access tokens β†’ Tokens (classic)
  2. Click "Generate new token (classic)"
  3. Configure:
    • Note: RELEASE_TOKEN
    • Expiration: Set an appropriate duration
    • Scopes: Select repo (full control of private repositories)
  4. Click "Generate token" and copy it

Step 2: Add the Token as a Repository Secret

  1. Go to your repository on GitHub

  2. Navigate to Settings β†’ Secrets and variables β†’ Actions

  3. Click "New repository secret"

  4. Configure:

    • Name: RELEASE_TOKEN
    • Secret: Paste your copied PAT
  5. Click "Add secret"

Step 3: Configure Branch Protection with Rulesets

GitHub Rulesets provide modern, flexible branch protection. The PAT allows the workflow to bypass these rules while humans must go through PRs.

  1. Go to your repository β†’ Settings β†’ Rules β†’ Rulesets

  2. Click "New ruleset" β†’ "New branch ruleset"

  3. Configure the ruleset:

    • Ruleset name: Protect main
    • Enforcement status: Active
    • Target branches: Click "Add target" β†’ "Include by pattern" β†’ enter main
  4. Enable these rules:

    • βœ… Restrict deletions - Prevent branch deletion
    • βœ… Require a pull request before merging
      • Required approvals: 1 (or more)
      • βœ… Dismiss stale pull request approvals when new commits are pushed
      • βœ… Require conversation resolution before merging
    • βœ… Require status checks to pass
      • βœ… Require branches to be up to date before merging
      • Add status checks:
        • ci / Run Pre-commit Checks
        • ci / Lint with Ruff
        • ci / Run Tests with Pytest
    • βœ… Block force pushes
  5. Click "Create"

Note: Status checks appear as ci / <Job Name> because they run inside the reusable workflow. You may need to run a PR first before these checks appear in the dropdown.

Security Model

This setup provides security through multiple layers:

Protection What it prevents
CODEOWNERS Requires your approval for any workflow changes
Required PRs No direct pushes to main (humans must use PRs)
Required reviews At least one approval needed for every change
Status checks Tests must pass before merge
PAT as secret Token only accessible to workflows, not forks

Why is the PAT safe?

  • The PAT is stored as a secret, never exposed in logs (GitHub auto-masks it)
  • Forks cannot access repository secrets
  • Any attempt to modify workflows to steal the PAT requires your explicit approval via CODEOWNERS
  • The PAT can only push; it cannot change branch protection rules

Semantic Release

https://python-semantic-release.readthedocs.io/en/latest/

The workflows are triggered when you merge into main!

When committing use the following format for your commit message:

  • patch (0.0.X):
    fix: commit message
    
  • minor (0.X.0):
    feat: commit message
    
  • major/breaking (X.0.0) - add the breaking change on the third line:
    feat: commit message
    
    BREAKING CHANGE: description of breaking change
    

Installation Options (for generated libraries)

Libraries created with this template support multiple installation methods: Note that the @v1.0.0 can be updated for a specific version, or removed for the newest version

Configure git credentials (more secure, recommended)

This method doesn't expose your token in command history:

# Store credentials in git (one-time setup)
git config --global credential.helper store

# Then install normally - git will prompt for credentials once
pip install "git+https://github.com/username/repo_name.git@v1.1.0"
# When prompted: username = your GitHub username, password = your PAT

Option 1: Install from Private GitHub Release (Recommended)

# Using pip with a personal access token
pip install "git+https://YOUR_TOKEN@github.com/username/repo_name.git@v1.0.0"

# Using uv
uv pip install "git+https://YOUR_TOKEN@github.com/username/repo_name.git@v1.0.0"

Option 2: Install from Wheel File in Repository

After cloning, install the wheel file from the dist/ directory:

pip install repo_name/dist/repo_name-1.0.0-py3-none-any.whl

Option 3: Install from Source (Clone Repository)

git clone https://github.com/username/repo_name.git
cd repo_name
pip install -e ".[dev]"

Option4: Building a Wheel File Locally

pip install build
python -m build --wheel
# The wheel will be created in the dist/ directory

Option 5: Add to requirements.txt or pyproject.toml

In requirements.txt:

# Using git+https (requires GH_TOKEN environment variable to be set)
repo_name @ git+https://github.com/username/repo_name.git@v1.1.0

In pyproject.toml (for projects using PEP 621):

[project]
dependencies = [
    "repo_name @ git+https://github.com/username/repo_name.git@v1.1.0",
]

Note: When installing from requirements.txt with a private repo, ensure your git credentials are configured (see Option 1, Step 2, Option B above).

Coverage report

About

Template repo with automated testing and versioning

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages