Skip to content

Commit 07a1b70

Browse files
authored
add dev container and tests (#19)
* add devcontainer * automate site generation and testing
1 parent 22e0459 commit 07a1b70

File tree

10 files changed

+253
-0
lines changed

10 files changed

+253
-0
lines changed

.devcontainer/Dockerfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
ARG IMAGE="python:3.12"
2+
FROM --platform=amd64 mcr.microsoft.com/devcontainers/${IMAGE}
3+
RUN apt-get update \
4+
&& export DEBIAN_FRONTEND=noninteractive \
5+
&& apt-get -y install --no-install-recommends build-essential libssl-dev gdb cmake

.devcontainer/devcontainer.json

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
2+
// README at: https://github.com/devcontainers/templates/tree/main/src/python
3+
{
4+
"name": "Cookie Cutter - Render Engine",
5+
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
6+
"build": {
7+
"dockerfile": "Dockerfile",
8+
"context": "..",
9+
"args": {
10+
"IMAGE": "python:3.12"
11+
}
12+
},
13+
"postCreateCommand": ". .devcontainer/setup.sh",
14+
"customizations": {
15+
"vscode": {
16+
"settings": {
17+
"python.formatting.provider": "charliermarsh.ruff",
18+
"python.testing.pytestEnabled": true,
19+
"python.testing.pytestPath": "pytest",
20+
"python.testing.unittestEnabled": false,
21+
"python.editor.formatOnSave": true,
22+
"python.editor.codeActionsOnSave": {"source.fixAll": true},
23+
"python.testing.pytestArgs": [
24+
"tests"
25+
],
26+
".markdownlint-cli2.ignores": [".gitignore"]
27+
28+
},
29+
"extensions": [
30+
"ms-python.python",
31+
"charliermarsh.ruff",
32+
"GitHub.vscode-github-actions",
33+
"yzhang.markdown-all-in-one",
34+
"DavidAnson.vscode-markdownlint",
35+
"ms-vscode.makefile-tools"
36+
]
37+
}
38+
}
39+
}

.devcontainer/setup.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
python -m pip install --user --upgrade pip
2+
python -m venv .venv
3+
source .venv/bin/activate
4+
python -m pip install pip-tools
5+
python -m piptools compile --upgrade -o requirements.txt requirements.in
6+
python -m pip install -r requirements-dev.txt
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Check Dev Container
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
paths:
7+
- ".devcontainer/**"
8+
- ".github/workflows/devcontainer-ci.yaml"
9+
pull_request:
10+
branches: [ main ]
11+
paths:
12+
- ".devcontainer/**"
13+
- ".github/workflows/devcontainer-ci.yaml"
14+
15+
permissions:
16+
contents: read
17+
18+
jobs:
19+
build:
20+
runs-on: ubuntu-latest
21+
steps:
22+
- uses: actions/checkout@v4
23+
- name: Use Node.js 20.x
24+
uses: actions/setup-node@v4
25+
with:
26+
node-version: 20.x
27+
- run: npm install -g @devcontainers/cli
28+
- run: devcontainer build --config ./.devcontainer/devcontainer.json --workspace-folder "$(pwd)"

.github/workflows/tests.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: PyTest
2+
on:
3+
workflow_call:
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- main
8+
pull_request:
9+
branches:
10+
- main
11+
12+
jobs:
13+
build:
14+
runs-on: ubuntu-latest
15+
strategy:
16+
matrix:
17+
python-version: ["3.10", "3.11", "3.12", "3.13"]
18+
fail-fast: true
19+
steps:
20+
- uses: actions/checkout@v4
21+
- name: Set up Python ${{ matrix.python-version }}
22+
uses: actions/setup-python@v5
23+
with:
24+
python-version: ${{ matrix.python-version }}
25+
- name: Install dependencies
26+
run: python -m pip install --upgrade pip -r requirements-dev.txt
27+
- name: Run tests
28+
run: python tests/test_template.py

requirements-dev.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pip-tools
2+
pytest
3+
4+
-r requirements.txt

requirements.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
cookiecutter

requirements.txt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#
2+
# This file is autogenerated by pip-compile with Python 3.12
3+
# by the following command:
4+
#
5+
# pip-compile --output-file=requirements.txt requirements.in
6+
#
7+
arrow==1.3.0
8+
# via cookiecutter
9+
binaryornot==0.4.4
10+
# via cookiecutter
11+
certifi==2024.8.30
12+
# via requests
13+
chardet==5.2.0
14+
# via binaryornot
15+
charset-normalizer==3.4.0
16+
# via requests
17+
click==8.1.7
18+
# via cookiecutter
19+
cookiecutter==2.6.0
20+
# via -r requirements.in
21+
idna==3.10
22+
# via requests
23+
jinja2==3.1.4
24+
# via cookiecutter
25+
markdown-it-py==3.0.0
26+
# via rich
27+
markupsafe==3.0.2
28+
# via jinja2
29+
mdurl==0.1.2
30+
# via markdown-it-py
31+
pygments==2.18.0
32+
# via rich
33+
python-dateutil==2.9.0.post0
34+
# via arrow
35+
python-slugify==8.0.4
36+
# via cookiecutter
37+
pyyaml==6.0.2
38+
# via cookiecutter
39+
requests==2.32.3
40+
# via cookiecutter
41+
rich==13.9.4
42+
# via cookiecutter
43+
six==1.17.0
44+
# via python-dateutil
45+
text-unidecode==1.3
46+
# via python-slugify
47+
types-python-dateutil==2.9.0.20241206
48+
# via arrow
49+
urllib3==2.2.3
50+
# via requests

tests/pytest.ini

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[pytest]
2+
python_files = test_*.py
3+
python_classes = [A-Z]*Test
4+
python_functions = test_*

tests/test_template.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
"""
2+
Test the Cookiecutter template.
3+
"""
4+
from cookiecutter.generate import generate_context
5+
from cookiecutter.main import cookiecutter
6+
from pathlib import Path
7+
from shlex import split
8+
from subprocess import run
9+
from venv import create
10+
11+
import pytest
12+
13+
14+
@pytest.fixture(scope="session")
15+
def template() -> Path:
16+
""" The template under test.
17+
18+
"""
19+
return Path(__file__).resolve().parents[1]
20+
21+
22+
@pytest.fixture(scope="module")
23+
def tmpdir(tmp_path_factory) -> Path:
24+
""" Test directory.
25+
26+
"""
27+
return tmp_path_factory.mktemp("test_template")
28+
29+
30+
@pytest.fixture(scope="module")
31+
def context(template) -> dict:
32+
""" Template context for testing.
33+
34+
"""
35+
context = generate_context(template.joinpath("cookiecutter.json"))
36+
context["cookiecutter"].update({
37+
"project_slug": "slugify"
38+
})
39+
return context["cookiecutter"]
40+
41+
42+
@pytest.fixture(scope="module")
43+
def project(tmpdir, template, context) -> Path:
44+
""" Create a test project from the Cookiecutter template.
45+
46+
"""
47+
cookiecutter(str(template), no_input=True, output_dir=tmpdir, extra_context=context)
48+
return tmpdir / context["project_slug"]
49+
50+
51+
@pytest.fixture
52+
def python(tmp_path):
53+
""" Create a Python virtual environment for testing.
54+
55+
"""
56+
venv = tmp_path / ".venv"
57+
create(venv, with_pip=True)
58+
return venv / "bin" / "python"
59+
60+
61+
def test_project(project):
62+
""" Verify that the project was created correctly.
63+
64+
"""
65+
# Just a basic sanity test.
66+
assert len(list(project.iterdir())) == 4
67+
return
68+
69+
70+
@pytest.fixture
71+
def install_render_engine_cli(python):
72+
install_cli = "pip install render_engine[cli]"
73+
install_args = split(f"{python} -m {install_cli}")
74+
install_process = run(install_args)
75+
assert install_process.returncode == 0
76+
77+
78+
def test_site_generation(context, project, python, install_render_engine_cli):
79+
generate_site = "render_engine build app:app"
80+
generate_args = split(f"{python} -m {generate_site}")
81+
generate_process = run(generate_args, cwd=project)
82+
assert generate_process.returncode == 0
83+
84+
85+
# Make the script executable.
86+
87+
if __name__ == "__main__":
88+
raise SystemExit(pytest.main([__file__]))

0 commit comments

Comments
 (0)