Skip to content

Commit 36a988b

Browse files
committed
Add Docker template bits
1 parent 0f7d63d commit 36a988b

File tree

11 files changed

+163
-10
lines changed

11 files changed

+163
-10
lines changed

cookiecutter.json

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
],
1313
"enable_coverage": "yes",
1414
"enable_pypi_publish": "no",
15+
"enable_container_publish": "no",
1516
"author_name": "",
1617
"author_email": "",
1718
"github_user": ""

hooks/post_gen_project.py

+21
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,36 @@
1+
import itertools
12
import os
23
import shutil
34
import subprocess
45
from typing import Dict, List
56

7+
import yaml
8+
69
TEMPLATE_ONLY_DATA = "cookiecutter_template_data"
710

811

912
def _run(call: List[str], env: Dict[str, str]) -> None:
1013
subprocess.run(call, env=env).check_returncode()
1114

1215

16+
# Remove files and directories for deselected features
17+
with open(
18+
os.path.join(os.getcwd(), TEMPLATE_ONLY_DATA, "feature_paths.yml")
19+
) as f:
20+
feature_paths = yaml.safe_load(f.read())
21+
for rm_path in itertools.chain.from_iterable(
22+
[
23+
feature_data["paths"]
24+
for _, feature_data in feature_paths.items()
25+
if not feature_data["enabled"]
26+
]
27+
):
28+
full_rm_path = os.path.join(os.getcwd(), rm_path)
29+
shutil.rmtree(full_rm_path) if os.path.isdir(full_rm_path) else os.remove(
30+
full_rm_path
31+
)
32+
33+
1334
# Remove template-only data files
1435
shutil.rmtree(os.path.join(os.getcwd(), TEMPLATE_ONLY_DATA))
1536

tests/test_new_cookie.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ def test_new_cookie_create(temp_dir: str) -> None:
4747
.decode("utf-8")
4848
.strip()
4949
), "Untracked files present in template-rendered project"
50-
subprocess.check_call(["poetry", "install"], cwd=project_dir)
50+
5151
subprocess.check_call(
5252
["poetry", "run", "cruft", "diff", "--exit-code"], cwd=project_dir
5353
)
54+
# Install rendered project
55+
subprocess.check_call(["poetry", "install"], cwd=project_dir)
56+
# Run rendered project's tests
57+
subprocess.check_call(["poetry", "run", "poe", "test"], cwd=project_dir)

tests/test_template.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,16 @@ def test_project_license(cookies: Any, project_license: str) -> None:
9595
@pytest.mark.parametrize(
9696
"enable_pypi_publish", [True, False], ids=["pypi", "no pypi"]
9797
)
98+
@pytest.mark.parametrize(
99+
"enable_container_publish",
100+
[True, False],
101+
ids=["container", "no container"],
102+
)
98103
def test_rendered_project(
99-
cookies: Any, enable_coverage: bool, enable_pypi_publish: bool
104+
cookies: Any,
105+
enable_coverage: bool,
106+
enable_pypi_publish: bool,
107+
enable_container_publish: bool,
100108
) -> None:
101109
extra_context = dict(
102110
author_name="Ness",
@@ -134,14 +142,6 @@ def test_rendered_project(
134142
.strip()
135143
), "Untracked files present in template-rendered project"
136144

137-
# Install rendered project
138-
subprocess.check_call(["poetry", "install"], cwd=result.project_path)
139-
140-
# Run rendered project's tests
141-
subprocess.check_call(
142-
["poetry", "run", "poe", "test"], cwd=result.project_path
143-
)
144-
145145

146146
@pytest.mark.parametrize(**ReadmeCases.all_cases().__dict__)
147147
def test_rendered_readme(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.gitignore
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
name: Build
3+
4+
env:
5+
REGISTRY: ghcr.io
6+
IMAGE_NAME: ${{ "{{" }} github.repository }}
7+
RELEASE_PYTHON_VERSION: "3.10"
8+
RELEASE_POETRY_VERSION: "1.2"
9+
10+
on:
11+
pull_request:
12+
push:
13+
branches:
14+
- '*'
15+
tags:
16+
- 'v*'
17+
workflow_dispatch:
18+
19+
jobs:
20+
Container:
21+
runs-on: ubuntu-latest
22+
permissions:
23+
contents: read
24+
packages: write
25+
26+
steps:
27+
- name: 💾 Check out repository
28+
uses: actions/checkout@v3
29+
30+
- name: 🔑 Log in to the container registry
31+
uses: docker/login-action@v2.1.0
32+
with:
33+
registry: ${{ "{{" }} env.REGISTRY }}
34+
username: ${{ "{{" }} github.actor }}
35+
password: ${{ "{{" }} secrets.GITHUB_TOKEN }}
36+
37+
- name: 📡 Collect image metadata
38+
id: meta
39+
uses: docker/metadata-action@v4.1.1
40+
with:
41+
images: ${{ "{{" }} env.REGISTRY }}/${{ "{{" }} env.IMAGE_NAME }}
42+
tags: |
43+
type=ref,event=branch
44+
type=ref,event=pr
45+
type=semver,pattern={{ "{{" }}version}}
46+
type=semver,pattern={{ "{{" }}major}}.{{ "{{" }}minor}}
47+
type=semver,pattern={{ "{{" }}major}},enable=${{ "{{" }} !startsWith(github.ref, 'refs/tags/v0.') }}
48+
type=edge,branch=main
49+
50+
- name: 🐍 Set up Python project with Poetry
51+
uses: ./.github/workflows/actions/python-poetry
52+
with:
53+
python_version: ${{ "{{" }} env.RELEASE_PYTHON_VERSION }}
54+
poetry_version: ${{ "{{" }} env.RELEASE_POETRY_VERSION }}
55+
56+
- name: 🔥 Test
57+
run: poetry run poe test
58+
59+
- name: 📦 Build and publish container image
60+
uses: docker/build-push-action@v3.2.0
61+
with:
62+
context: .
63+
push: ${{ "{{" }} github.event_name != 'pull_request' }}
64+
tags: ${{ "{{" }} steps.meta.outputs.tags }}
65+
labels: ${{ "{{" }} steps.meta.outputs.labels }}
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FROM python:3-alpine
2+
RUN apk add --no-cache tini
3+
COPY docker/entrypoint /
4+
5+
COPY . /python-build
6+
RUN python3 -m pip install /python-build && rm -rf /python-build
7+
8+
ENTRYPOINT ["/sbin/tini", "--"]
9+
CMD ["/entrypoint"]

{{cookiecutter.project_name}}/README.md

+33
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,39 @@
77
[![GitHub stars](https://img.shields.io/github/stars/{{ cookiecutter.github_user }}/{{ cookiecutter.project_name }}?style=social)][repo]
88
{% endif %}{% if cookiecutter.enable_pypi_publish == "yes" and not cookiecutter.github_user %}
99
{% endif %}
10+
{% if cookiecutter.enable_container_publish == "yes" %}## Installation and usage with Docker
11+
12+
Example `docker-compose.yaml`:
13+
14+
```yaml
15+
version: "3.7"
16+
17+
services:
18+
{{ cookiecutter.project_name }}:
19+
image: ghcr.io/{{ cookiecutter.github_user }}{{ cookiecutter.project_name }}:latest
20+
restart: unless-stopped
21+
```
22+
23+
Start the container by running:
24+
25+
```console
26+
docker-compose up -d
27+
```
28+
29+
Debugging information can be viewed in the container log:
30+
31+
```console
32+
docker-compose logs -f
33+
```{% endif %}
34+
{% if cookiecutter.enable_pypi_publish == "yes" %}
35+
## Installation from PyPI
36+
37+
[{{ cookiecutter.project_name }} is available on PyPI][pypi]:
38+
39+
```console
40+
pip install {{ cookiecutter.project_name }}
41+
```
42+
{% endif %}
1043
## Development
1144

1245
### [Poetry][poetry] installation
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
container_publish:
2+
enabled: {{ cookiecutter.enable_container_publish|lower == 'yes' }}
3+
paths:
4+
- ".dockerignore"
5+
- ".github/workflows/container.yml"
6+
- "Dockerfile"
7+
- "docker"
8+
- "docker-compose.yaml"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
version: "3.7"
2+
3+
services:
4+
{{ cookiecutter.project_name }}:
5+
image: ghcr.io/{{ cookiecutter.github_user }}/{{ cookiecutter.project_name }}:latest
6+
restart: unless-stopped
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/sh
2+
3+
set -ex
4+
5+
exec "{{ cookiecutter.project_name }}"

0 commit comments

Comments
 (0)