Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: "CI - linters and tests"
name: "🧪 CI - linters and tests"

on:
push:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/conventional-commit-pr-name.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: "Check PR title matches conventional commit specification"
name: "🧪 CI - Check PR title matches conventional commit specification"

on:
pull_request:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: "Release"
name: "🚀 Release"

on:
workflow_dispatch:
Expand Down
23 changes: 23 additions & 0 deletions copier.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,29 @@ project_name_title_case:
default: "{{ sanitized_project_name | replace('-', ' ') | title }}"
when: false

source_catalog_name:
type: str
default: "{{ project_name_snake_case }}"
when: false

description:
type: str
help: Provide a short description of the project

specify_author:
type: bool
default: false
help: Would you like to specify author information?

author_name:
type: str
help: Provide an author name in format "Firstname Lastname"
when: "{{ specify_author }}"

author_email:
type: str
help: Provide the author's email address
when: "{{ specify_author }}"

project_type:
type: str
Expand All @@ -65,6 +77,17 @@ is_lambda_project:
default: "{{ project_type == 'lambda' }}"
when: false

enforce_conventional_commits:
type: bool
default: true
help: Enforce conventional commit messages in Git (needed for automatic GitHub releases)

automatic_gh_release:
type: bool
default: true
help: Enable automatic GitHub releases based on conventional commits
when: "{{ enforce_conventional_commits }}"

tasks:
# Make entrypoint executable if it exists
- [invoke, "chmod +x docker-entrypoint.sh || true", after-copy]
17 changes: 11 additions & 6 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@ default: help
help:
just --list

[group("generation")]
[doc("Runs copier copy using current local implementation")]
copy output=DEFAULT_OUTPUT:
uv run copier copy . {{ output }} --vcs-ref=HEAD

[group("generation")]
[doc("Generates repo from template to output directory")]
copy data_file output=DEFAULT_OUTPUT:
copy_from_file data_file output=DEFAULT_OUTPUT:
uv run copier copy . {{ output }} \
--vcs-ref=HEAD \
--data-file {{ data_file }} \
Expand Down Expand Up @@ -95,18 +100,18 @@ lint_fix:
[group("development")]
[group("generation")]
generation_check_docker data_file=DOCKER_ANSWERS output=DOCKER_OUTPUT:
just copy {{ data_file }} {{ output }}
just copy_from_file {{ data_file }} {{ output }}
just output_all_ff_docker {{ output }}

[group("development")]
[group("generation")]
generation_check data_file=PLAIN_ANSWERS output=PLAIN_OUTPUT:
just copy {{ data_file }} {{ output }}
just copy_from_file {{ data_file }} {{ output }}
just output_all_ff {{ output }}

[group("development")]
[group("generation")]
copy_all:
just copy {{ DOCKER_ANSWERS }} {{ DOCKER_OUTPUT }}
just copy {{ PLAIN_ANSWERS }} {{ PLAIN_OUTPUT }}
just copy {{ LAMBDA_ANSWERS }} {{ LAMBDA_OUTPUT }}
just copy_from_file {{ DOCKER_ANSWERS }} {{ DOCKER_OUTPUT }}
just copy_from_file {{ PLAIN_ANSWERS }} {{ PLAIN_OUTPUT }}
just copy_from_file {{ LAMBDA_ANSWERS }} {{ LAMBDA_OUTPUT }}
2 changes: 1 addition & 1 deletion skeleton/.github/workflows/ci.yml.jinja
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: "CI - linters and tests"
name: "🧪 CI - linters and tests"

on:
push:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: "🚀 Release"

on:
workflow_dispatch:
workflow_run:
workflows: ["CI - linters and tests"]
types:
- completed
branches:
- main

concurrency: # Run release builds sequentially
group: ci-${{ github.ref }}

permissions: # Grant write access to github.token within non-pull_request builds
contents: write

jobs:
release:
name: release
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
steps:
- name: "Check out code"
uses: actions/checkout@v5
with:
fetch-depth: 0
persist-credentials: false

- id: create-release
name: "Prepare release"
uses: mgoltzsche/conventional-release@v0

- name: "Show results"
run: |
if [ "${{ steps.create-release.outputs.publish }}" = "true" ]; then
echo "A release was created."
echo "Version: ${{ steps.create-release.outputs.version }}"
else
echo "No release was created."
fi
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: "Check PR title matches conventional commit specification"
name: "🧪 CI - Check PR title matches conventional commit specification"

on:
pull_request:
Expand Down
6 changes: 3 additions & 3 deletions skeleton/justfile.jinja
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
set dotenv-load

PYTHONPATH := "./{{ project_name_snake_case }}"
PATHS_TO_LINT := "{{ project_name_snake_case }} tests"
PYTHONPATH := "./{{ source_catalog_name }}"
PATHS_TO_LINT := "{{ source_catalog_name }} tests"
TEST_PATH := "tests"
ANSWERS_FILE := ".copier/.copier-answers.copier-python-project.yml"
{%- if is_docker_project %}
Expand Down Expand Up @@ -113,7 +113,7 @@ test file=TEST_PATH: _set_pythonpath
[group("lambda_build")]
build: build_create_env
cd {{ BUILD_PATH }}/pkg && \
zip -r ../{% endraw %}{{ project_name_snake_case }}{% raw %}_{{ CURRENT_DATETIME }}.zip .
zip -r ../{% endraw %}{{ source_catalog_name }}{% raw %}_{{ CURRENT_DATETIME }}.zip .

[group("lambda_build")]
build_create_env: build_generate_requirements build_install
Expand Down
14 changes: 6 additions & 8 deletions skeleton/pyproject.toml.jinja
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
[project]
name = "{{ project_name_kebab_case }}"
version = "0.0.0" # This is a placeholder, version is determined by git tag during CI/CD
version = "0.0.0"{% if automatic_gh_release %} # This is a placeholder, version is determined by git tag and commits{% endif %}
description = "{{ description }}"
authors = [{ name = "{{ author_name }}", email = "{{ author_email }}" }]
authors = [{% if specify_author %}{ name = "{{ author_name }}", email = "{{ author_email }}" }{% endif %}]
requires-python = ">=3.13"
readme = "README.md"
dependencies = [
{%- if is_lambda_project %}
dependencies = [{% if is_lambda_project %}
"aws-lambda-powertools>=3.16.0,<4",
{%- endif %}
]
{% endif %}]

[dependency-groups]
dev = [
Expand Down Expand Up @@ -69,7 +67,7 @@ mark-parentheses = true

[tool.ruff.lint.isort]
order-by-type = false
known-first-party = ["{{ project_name_snake_case }}"]
known-first-party = ["{{ source_catalog_name }}"]

[tool.coverage.report]
exclude_lines = [
Expand All @@ -78,7 +76,7 @@ exclude_lines = [
]

[tool.fawltydeps]
code = ["{{ project_name_snake_case }}"]
code = ["{{ source_catalog_name }}"]
deps = ["pyproject.toml"]
ignore_unused = [
# Dev dependencies. This list should contain ALL of them!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ from typing import Any

from aws_lambda_powertools.utilities.typing import LambdaContext

from {{ project_name_snake_case }}.lambda_function import lambda_handler
from {{ source_catalog_name }}.lambda_function import lambda_handler


def test_lambda_handler(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \

# Adds our application code to the image
COPY . /code
WORKDIR /code/{{ project_name_snake_case }}
WORKDIR /code/{{ source_catalog_name }}

# Sync the project
RUN --mount=type=cache,target=/root/.cache/uv \
Expand Down
2 changes: 2 additions & 0 deletions test_answers/docker.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
author_email: author@email.test
author_name: test-author
description: Service description.
enforce_conventional_commits: false
project_name: test-service
project_type: dockerized
specify_author: true
3 changes: 3 additions & 0 deletions test_answers/lambda.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
author_email: author@email.test
author_name: test-author
automatic_gh_release: false
description: Lambda description.
enforce_conventional_commits: true
project_name: test-lambda
project_type: lambda
specify_author: true
5 changes: 3 additions & 2 deletions test_answers/plain.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
author_email: author@email.test
author_name: test-author
automatic_gh_release: true
description: Service description.
enforce_conventional_commits: true
project_name: test-service
project_type: empty
specify_author: false
89 changes: 80 additions & 9 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,92 @@
from enum import Enum, StrEnum
from pathlib import Path
from typing import Any
from typing import Any, Final

import pytest
from _pytest.fixtures import FixtureRequest


@pytest.fixture()
def answers() -> dict[str, Any]:
"""Fixture to provide default answers for project generation."""
class ProjectType(StrEnum):
EMPTY = "empty"
DOCKERIZED = "dockerized"
AWS_LAMBDA = "lambda"


DEFAULT_ANSWERS = {
"author_email": "author@email.test",
"author_name": "test-author",
"automatic_gh_release": True,
"enforce_conventional_commits": True,
"description": "A test project",
"project_name": "test-project",
"project_type": "dockerized",
"specify_author": True,
}


class ArgSetType(Enum):
NOTSET = 0
DEFAULT = 1


NOTSET: Final = ArgSetType.NOTSET
DEFAULT: Final = ArgSetType.DEFAULT


def create_answers(
author_email: str | ArgSetType = DEFAULT,
author_name: str | ArgSetType = DEFAULT,
automatic_gh_release: bool | ArgSetType = DEFAULT,
description: str | ArgSetType = DEFAULT,
enforce_conventional_commits: bool | ArgSetType = DEFAULT,
project_name: str | ArgSetType = DEFAULT,
project_type: ProjectType | ArgSetType = DEFAULT,
specify_author: bool | ArgSetType = DEFAULT,
) -> dict[str, Any]:
"""
Fixture to easily create different answer sets for tests.
Each argument can be set to:
- A specific value
- DEFAULT to use the default answer
- NOTSET to omit the answer (useful for testing optional questions)
"""
return {
"author_name": "test-author",
"author_email": "author@email.test",
"project_name": "test-project",
"project_type": "dockerized",
"description": "A test project",
key: DEFAULT_ANSWERS[key] if value is DEFAULT else value
for key, value in {
"author_email": author_email,
"author_name": author_name,
"automatic_gh_release": automatic_gh_release,
"description": description,
"enforce_conventional_commits": enforce_conventional_commits,
"project_name": project_name,
"project_type": project_type,
"specify_author": specify_author,
}.items()
if value is not NOTSET
}


@pytest.fixture(
params=[
pytest.param(create_answers(project_type=ProjectType.AWS_LAMBDA), id="lambda"),
pytest.param(create_answers(project_type=ProjectType.DOCKERIZED), id="dockerized"),
pytest.param(create_answers(project_type=ProjectType.EMPTY), id="plain"),
pytest.param(
create_answers(specify_author=False, author_name=NOTSET, author_email=NOTSET),
id="no-author",
),
pytest.param(
create_answers(enforce_conventional_commits=False, automatic_gh_release=NOTSET),
id="no-cc-no-release",
),
],
)
def answers(
request: FixtureRequest,
) -> dict[str, Any]:
return request.param


def determine_file_type(path: Path) -> str:
if not path.suffix:
return path.name
Expand Down
Loading