Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.4.0] - 2026-05-04

### Added
- Placeholder for future updates and new features.
- Added `github` as a new `ci_pipeline` option (closes #44). Selecting GitHub Actions generates `.github/workflows/ci.yml` with `build` and `test` jobs, supporting all four package managers (conda with micromamba, pip, poetry, uv). The GitLab CI option is unchanged.

### Changed
- Updated the README to document the new GitHub Actions CI choice under "Choices explained".

## [1.3.0] - 2026-01-27

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ Feedback and contributions are very welcome! Learn more in the [Contributing](#-
* Select your `ci_pipeline`
- `none` (default): Don't use any CI/CD pipeline.
- `gitlab`: If you plan to use GitLab, this option will add a CI/CD Pipeline definition for [GitLab CI/CD](https://docs.gitlab.com/ee/ci/). The pipeline includes basic steps to build, test and deploy your code. The deployment steps do nothing but echoing a String, as deployment is very project-specific.
- `github`: If you plan to use GitHub, this option will add a workflow for [GitHub Actions](https://docs.github.com/en/actions) at `.github/workflows/ci.yml`. The workflow runs on every push and pull request to `main`/`master` and includes a `build` job (produces a wheel artifact) and a `test` job that runs your pytest suite. All package managers are supported.
* `create_cli` (yes or no): if you plan to build an application with a command line interface (CLI), select *yes* here. This will integrate a template for the CLI into your project - minimal boilerplate guaranteed! (We're leveraging the awesome [typer](https://typer.tiangolo.com/) library for this.)
* `config_file`: select your preferred config format. It is best practice to store your configuration separate from your code, even for small projects, but because there are a gazillion ways to do this, each project seems to reinvents the wheel. We want to provide a few options to set you up with a working configuration:
- `yaml`: use [YAML](https://yaml.org/) as your configuration file format. Easy to read and write, widely adopted, relies on the [PyYAML](https://pyyaml.org/) package.
Expand Down
5 changes: 3 additions & 2 deletions cookiecutter.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"package_manager": ["conda", "pip", "poetry","uv"],
"use_notebooks": ["no", "yes"],
"use_docker": ["no", "yes"],
"ci_pipeline": ["none", "gitlab"],
"ci_pipeline": ["none", "gitlab", "github"],
"create_cli": ["no", "yes"],
"config_file": ["none", "hocon", "yaml"],
"code_formatter": ["none", "black"],
Expand Down Expand Up @@ -41,7 +41,8 @@
"ci_pipeline": {
"__prompt__": "What [bold yellow]CI pipeline[/] would you like to use?",
"none": "None",
"gitlab": "GitLab CI"
"gitlab": "GitLab CI",
"github": "GitHub Actions"
},
"create_cli": {
"__prompt__": "Do you want to create a [bold yellow]CLI[/] for your project?",
Expand Down
10 changes: 9 additions & 1 deletion hooks/post_gen_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,11 @@
".gitlab-ci.yml",
}

files_ci_all = files_ci_gitlab
files_ci_github = {
".github/workflows/ci.yml",
}

files_ci_all = files_ci_gitlab | files_ci_github

folders_editor = [
'.idea__editor',
Expand Down Expand Up @@ -182,8 +186,12 @@ def handle_ci():
ci_pipeline = '{{ cookiecutter.ci_pipeline }}'
if ci_pipeline == "gitlab":
_delete_files(files_ci_all - files_ci_gitlab)
shutil.rmtree(".github", ignore_errors=True)
elif ci_pipeline == "github":
_delete_files(files_ci_all - files_ci_github)
elif ci_pipeline == 'none':
_delete_files(files_ci_all)
shutil.rmtree(".github", ignore_errors=True)


def print_success():
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "at-python-template"
version = "1.3.0"
version = "1.4.0"
description = "This is the official Python Project Template of Alexander Thamm GmbH (AT)"
authors = [
"Christian Baur <christian.baur@alexanderthamm.com>",
Expand Down
33 changes: 31 additions & 2 deletions tests/test_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,5 +231,34 @@ def test_no_ci_pipeline():
settings={
"ci_pipeline": "none"
},
files_non_existent=[".gitlab-ci.yml"]
)
files_non_existent=[".gitlab-ci.yml", ".github/workflows/ci.yml"])


def test_github_pip():
check_project(
settings={
"package_manager": "pip",
"ci_pipeline": "github"
},
files_existent=[".github/workflows/ci.yml"],
files_non_existent=[".gitlab-ci.yml"])


def test_github_conda():
check_project(
settings={
"package_manager": "conda",
"ci_pipeline": "github"
},
files_existent=[".github/workflows/ci.yml"],
files_non_existent=[".gitlab-ci.yml"])


def test_github_poetry():
check_project(
settings={
"package_manager": "poetry",
"ci_pipeline": "github"
},
files_existent=[".github/workflows/ci.yml"],
files_non_existent=[".gitlab-ci.yml"])
140 changes: 140 additions & 0 deletions {{cookiecutter.project_slug}}/.github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
name: CI

on:
push:
branches: ["master", "main"]
pull_request:
branches: ["master", "main"]

{%- if cookiecutter.package_manager == 'conda' %}
env:
CONDA_PKGS_DIRS: ~/.cache/conda
{%- elif cookiecutter.package_manager == 'poetry' %}
env:
POETRY_VERSION: "2.1.1"
POETRY_CACHE_DIR: ~/.cache/poetry
{%- endif %}

jobs:
build:
name: Build package
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
{%- if cookiecutter.package_manager == 'poetry' %}
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
cache: pip
- name: Install Poetry
run: pip install poetry==$POETRY_VERSION
- name: Build wheel
run: poetry build -f wheel
{%- elif cookiecutter.package_manager == 'conda' %}
- name: Set up conda (micromamba)
uses: mamba-org/setup-micromamba@v2
with:
environment-file: environment.yml
init-shell: bash
cache-environment: true
- name: Build wheel
shell: micromamba-shell {0}
run: python setup.py bdist_wheel
{%- elif cookiecutter.package_manager == 'uv' %}
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Build wheel
run: |
uv add --dev build
uv run python -m build --wheel
{%- else %}
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
cache: pip
- name: Build wheel
run: |
pip install setuptools wheel
python setup.py bdist_wheel
{%- endif %}
- name: Upload wheel artifact
uses: actions/upload-artifact@v4
with:
name: "{{ cookiecutter.module_name }}-wheel"
path: dist/{{ cookiecutter.module_name }}-*.whl
retention-days: 7

test:
name: Run tests
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- name: Download wheel artifact
uses: actions/download-artifact@v4
with:
name: "{{ cookiecutter.module_name }}-wheel"
path: dist/
{%- if cookiecutter.package_manager == 'poetry' %}
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
cache: pip
- name: Install Poetry & dependencies
run: |
pip install poetry==$POETRY_VERSION
poetry install --only=main,test
- name: Run tests
run: poetry run pytest tests --cov src --cov-report=xml
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage.xml
{%- elif cookiecutter.package_manager == 'conda' %}
- name: Set up conda (micromamba)
uses: mamba-org/setup-micromamba@v2
with:
environment-file: environment-dev.yml
init-shell: bash
cache-environment: true
- name: Install package and run tests
shell: micromamba-shell {0}
run: |
pip install dist/{{ cookiecutter.module_name }}-*.whl
pytest tests
{%- elif cookiecutter.package_manager == 'uv' %}
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Run tests
run: uv run pytest tests --cov src --cov-report=xml
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage.xml
{%- else %}
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
cache: pip
- name: Install dependencies
run: pip install -r requirements.txt -r requirements-dev.txt
- name: Install wheel
run: pip install dist/{{ cookiecutter.module_name }}-*.whl
- name: Run tests
run: pytest tests --cov src --cov-report=xml
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage.xml
{%- endif %}