initial commit

This commit is contained in:
root
2025-10-11 17:03:02 +02:00
commit 08dbb6210e
51 changed files with 3420 additions and 0 deletions

View File

@@ -0,0 +1,486 @@
from __future__ import annotations
import pathlib
import shutil
import subprocess
import tempfile
import unittest
from types import TracebackType
from typing import Any, Optional, Type
import tomli
import yaml
from copier import run_copy
def _run_shell_command_in_dir(command: list[str], dir: str) -> tuple[bytes, bytes, int]:
with subprocess.Popen(command, cwd=dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc:
outs, errs = proc.communicate()
returncode = proc.returncode
return outs, errs, returncode
class CopierRenderer:
DEFAULT_PARAMETERS = {
"project_name": "My Little Project",
"author_first_name": "Twilight",
"author_last_name": "Sparkle",
"bitbucket_organization": "MY_LITTLE_BITBUCKET_ORG",
}
def __init__(
self,
temp_source_dir: tempfile.TemporaryDirectory,
parameters: dict[str, str] | None = None,
):
self._temp_source_dir = temp_source_dir
if parameters is None:
self.parameters = {}
else:
self.parameters = parameters
def __enter__(self) -> CopierRenderer:
"""Run when the context is entered. Starts the transaction."""
self._temp_render_dir = tempfile.TemporaryDirectory()
self.render_path = pathlib.Path(self._temp_render_dir.name)
run_copy(
src_path=self._temp_source_dir.name,
dst_path=self.render_path,
vcs_ref="HEAD",
defaults=True,
data=(CopierRenderer.DEFAULT_PARAMETERS | self.parameters),
quiet=True,
)
return self
def __exit__(
self,
exc_type: Optional[Type[BaseException]],
exc_value: Optional[BaseException],
traceback: Optional[TracebackType],
) -> None:
"""Run when the context is exited. Ends the transaction with one of the actions specified in the initializer."""
self._temp_render_dir.cleanup()
def load_yaml(self, file_path: str) -> Any:
return yaml.safe_load((self.render_path / file_path).read_text())
def load_toml(self, file_path: str) -> Any:
return tomli.loads((self.render_path / file_path).read_text())
class TestTemplate(unittest.TestCase):
def setUp(self):
source_dir = pathlib.Path(__file__).parent.parent.parent
self.temp_source_dir = tempfile.TemporaryDirectory()
shutil.copytree(str(source_dir), self.temp_source_dir.name, dirs_exist_ok=True)
return super().setUp()
def tearDown(self):
self.temp_source_dir.cleanup()
return super().tearDown()
def test_dot_python_version(self) -> None:
with CopierRenderer(self.temp_source_dir) as copier_renderer:
self.assertEqual(
"3.12\n",
(copier_renderer.render_path / ".python-version").read_text(),
)
def test_pyproject_toml(self) -> None:
with CopierRenderer(self.temp_source_dir) as copier_renderer:
pyproject = tomli.loads((copier_renderer.render_path / "pyproject.toml").read_text())
self.assertEqual(pyproject["project"]["name"], "sbb-my-little-project")
self.assertEqual(
pyproject["project"]["description"],
"A project created with esta-python-template",
)
self.assertEqual(
pyproject["project"]["authors"],
[
{
"name": "Twilight Sparkle",
"email": "twilight.sparkle@sbb.ch",
}
],
)
self.assertEqual(pyproject["project"]["requires-python"], "~= 3.12.0")
self.assertEqual(
pyproject["project"]["urls"]["repository"],
"https://code.sbb.ch/projects/MY_LITTLE_BITBUCKET_ORG/repos/my-little-project",
)
self.assertEqual(
pyproject["project"]["urls"]["documentation"],
"https://code.sbb.ch/projects/MY_LITTLE_BITBUCKET_ORG/repos/my-little-project/browse/README.md",
)
self.assertEqual(
pyproject["tool"]["poetry"]["packages"],
[{"include": "my_little_project", "from": "src"}],
)
self.assertEqual(
pyproject["project"]["scripts"],
{"entrypoint": "my_little_project.main:cli"},
)
self.assertEqual(pyproject["tool"]["mypy"]["python_version"], "3.12")
def test_with_docker(self) -> None:
with CopierRenderer(
self.temp_source_dir,
{"docker_repository": "esta.docker", "python_version": "3.9"},
) as copier_renderer:
dockerfile = copier_renderer.render_path / "Dockerfile"
# Dockerfile exists
self.assertTrue(dockerfile.is_file())
# Dockerignore exists
self.assertTrue((copier_renderer.render_path / ".dockerignore").is_file())
# FROM-instruction
with dockerfile.open() as fh:
self.assertEqual(
fh.readline(),
"FROM registry-redhat.docker.bin.sbb.ch/rhel9/python-39 AS base\n",
)
# Tekton-Pipeline
tekton_pipeline = copier_renderer.load_yaml("estaTektonPipeline.yaml")
# Tekton-Docker section
self.assertEqual(
tekton_pipeline["docker"],
{"artifactoryDockerRepo": "esta.docker", "caching": True},
)
continuous_build = tekton_pipeline["pipelines"][0]["build"]
snapshot_build = tekton_pipeline["pipelines"][1]["build"]
release_build = tekton_pipeline["pipelines"][2]["build"]
# Check build sections
self.assertEqual(
continuous_build,
{
"sonarScan": {"enabled": True},
"owaspDependencyCheck": {
"enabled": True,
"additionalParams": "--suppression dependency-check-suppressions.xml --disablePyDist --disablePyPkg --failOnCVSS 9",
},
"failOnQualityGateFailure": True,
"buildDockerImage": True,
"deployDockerImage": False,
},
)
self.assertEqual(
snapshot_build,
{
"sonarScan": {"enabled": True},
"owaspDependencyCheck": {
"enabled": True,
"additionalParams": "--suppression dependency-check-suppressions.xml --disablePyDist --disablePyPkg --failOnCVSS 9",
},
"failOnQualityGateFailure": True,
"buildDockerImage": True,
"deployDockerImage": True,
"deployArtifacts": False,
},
)
self.assertEqual(
release_build,
{
"sonarScan": {"enabled": True},
"owaspDependencyCheck": {
"enabled": True,
"additionalParams": "--suppression dependency-check-suppressions.xml --disablePyDist --disablePyPkg --failOnCVSS 9",
},
"failOnQualityGateFailure": True,
"buildDockerImage": True,
"deployArtifacts": True,
"additionalDockerImageTags": ["latest"],
},
)
def test_with_docker_default_python_version(self) -> None:
with CopierRenderer(
self.temp_source_dir,
{"docker_repository": "esta.docker"}, # By not specifying a Python version, the default is taken
) as copier_renderer:
dockerfile = copier_renderer.render_path / "Dockerfile"
# Dockerfile exists
self.assertTrue(dockerfile.is_file())
# Dockerignore exists
self.assertTrue((copier_renderer.render_path / ".dockerignore").is_file())
# FROM-instruction
with dockerfile.open() as fh:
self.assertEqual(
fh.readline(),
"FROM registry-redhat.docker.bin.sbb.ch/rhel9/python-312 AS base\n",
)
def test_without_docker(self) -> None:
with CopierRenderer(self.temp_source_dir, {"docker_repository": "", "python_version": "3.12"}) as copier_renderer:
# Dockerfile does not exist
self.assertFalse((copier_renderer.render_path / "Dockerfile").exists())
# Dockerignore does not exist
self.assertFalse((copier_renderer.render_path / ".dockerignore").exists())
# Tekton-Pipeline
tekton_pipeline = copier_renderer.load_yaml("estaTektonPipeline.yaml")
# Tekton-Docker section
self.assertNotIn("docker", tekton_pipeline)
# Check build sections
for i, pipeline in enumerate(["continuous", "snapshot", "release"]):
with self.subTest(msg=f"Checking pipeline: '{pipeline}'."):
self.assertEqual(
tekton_pipeline["pipelines"][i]["build"],
{
"sonarScan": {"enabled": True},
"owaspDependencyCheck": {
"enabled": True,
"additionalParams": "--suppression dependency-check-suppressions.xml --disablePyDist --disablePyPkg --failOnCVSS 9",
},
"failOnQualityGateFailure": True,
},
)
def test_with_pypi(self) -> None:
with CopierRenderer(self.temp_source_dir, {"pypi_repository": "esta.pypi"}) as copier_renderer:
# Check Tekton-Pipeline
tekton_pipeline = copier_renderer.load_yaml("estaTektonPipeline.yaml")
self.assertEqual(tekton_pipeline["python"], {"targetRepo": "esta.pypi"})
def test_without_pypi(self) -> None:
with CopierRenderer(self.temp_source_dir, {"pypi_repository": ""}) as copier_renderer:
# Check Tekton-Pipeline
tekton_pipeline = copier_renderer.load_yaml("estaTektonPipeline.yaml")
self.assertEqual(tekton_pipeline["python"], {})
def test_pre_commit_hooks_in_template(self) -> None:
with CopierRenderer(self.temp_source_dir) as copier_renderer:
outs, errs, _ = _run_shell_command_in_dir(["git", "init"], dir=str(copier_renderer.render_path))
outs, errs, _ = _run_shell_command_in_dir(["git", "add", "--all"], dir=str(copier_renderer.render_path))
commands = [
["make"],
["poetry", "run", "pre-commit", "run", "--all-files"],
]
for command in commands:
with self.subTest(msg=f"Running {command=}."):
stdout, stderr, returncode = _run_shell_command_in_dir(command=command, dir=str(copier_renderer.render_path))
self.assertEqual(
returncode,
0,
msg=f"\nstdout:\n{stdout.decode()}\nstderr:\n{stderr.decode()}.",
)
def test_directory_names(self) -> None:
with CopierRenderer(self.temp_source_dir, {"project_name": "Funky Grogu"}) as copier_renderer:
self.assertTrue((copier_renderer.render_path / "src" / "funky_grogu").is_dir())
self.assertTrue((copier_renderer.render_path / "tests" / "funky_grogu").is_dir())
def test_poetry_toml(self) -> None:
with CopierRenderer(self.temp_source_dir, {"pypi_repository": "funky-grogu.pypi"}) as copier_renderer:
poetry = tomli.loads((copier_renderer.render_path / "poetry.toml").read_text())
self.assertEqual(
poetry["repositories"]["artifactory"]["url"],
"https://bin.sbb.ch/artifactory/api/pypi/funky-grogu.pypi",
)
def test_with_helm(self) -> None:
with CopierRenderer(
self.temp_source_dir,
{
"name": "my-project",
"description": "My project description",
"helm_repository": "esta.helm.local",
},
) as copier_renderer:
for dir_path in [
"charts",
"charts/my-project",
"charts/my-project/templates",
]:
self.assertTrue((copier_renderer.render_path / dir_path).is_dir())
for file_path in [
"charts/my-project/.helmignore",
"charts/my-project/Chart.yaml",
"charts/my-project/values.yaml",
]:
self.assertTrue((copier_renderer.render_path / file_path).is_file())
chart_yaml = copier_renderer.load_yaml("charts/my-project/Chart.yaml")
self.assertEqual(
chart_yaml,
{
"apiVersion": "v2",
"description": "My project description",
"icon": "http://acme.org/replaceme.jpg",
"name": "my-project",
"type": "application",
"version": "0.0.0",
},
)
# Tekton-Pipeline
tekton_pipeline = copier_renderer.load_yaml("estaTektonPipeline.yaml")
self.assertEqual(
tekton_pipeline["helm"],
{"chartRepository": "esta.helm.local", "linting": True},
)
release_build = tekton_pipeline["pipelines"][2]["build"]
self.assertEqual(
release_build,
{
"sonarScan": {"enabled": True},
"owaspDependencyCheck": {
"enabled": True,
"additionalParams": "--suppression dependency-check-suppressions.xml --disablePyDist --disablePyPkg --failOnCVSS 9",
},
"failOnQualityGateFailure": True,
"packageAndDeployHelmChart": True,
},
)
def test_without_helm(self) -> None:
with CopierRenderer(
self.temp_source_dir,
{
"name": "my-project",
"description": "My project description",
"helm_repository": "",
},
) as copier_renderer:
self.assertFalse((copier_renderer.render_path / "charts").exists())
# Tekton-Pipeline
tekton_pipeline = copier_renderer.load_yaml("estaTektonPipeline.yaml")
self.assertNotIn("helm", tekton_pipeline)
release_build = tekton_pipeline["pipelines"][2]["build"]
self.assertEqual(
release_build,
{
"sonarScan": {"enabled": True},
"owaspDependencyCheck": {
"enabled": True,
"additionalParams": "--suppression dependency-check-suppressions.xml --disablePyDist --disablePyPkg --failOnCVSS 9",
},
"failOnQualityGateFailure": True,
},
)
def test_without_ggshield(self) -> None:
with CopierRenderer(
self.temp_source_dir,
{
"name": "my-project",
"description": "My project description",
"use_ggshield": "False",
},
) as copier_renderer:
# .pre-commit-config.yaml
pre_commit_config = copier_renderer.load_yaml(".pre-commit-config.yaml")
hook_ids = [h["id"] for h in pre_commit_config["repos"][0]["hooks"]]
self.assertNotIn("ggshield", hook_ids)
# estaTektonPipeline.yaml
esta_tekton_pipeline = copier_renderer.load_yaml("estaTektonPipeline.yaml")
for pipeline in esta_tekton_pipeline["pipelines"]:
self.assertNotIn("gitguardian", pipeline["build"])
# .env.example
env_example = (copier_renderer.render_path / ".env.example").read_text()
self.assertNotIn("GITGUARDIAN_API_KEY", env_example)
# Makefile
makefile = (copier_renderer.render_path / "Makefile").read_text()
self.assertNotIn("ggshield_has_key", makefile)
# pyproject.toml
pyproject_toml = (copier_renderer.render_path / "pyproject.toml").read_text()
self.assertNotIn("ggshield", pyproject_toml)
# README.md
readme_md = (copier_renderer.render_path / "README.md").read_text()
self.assertNotIn("GitGuardian", readme_md)
def test_with_ggshield(self) -> None:
with CopierRenderer(
self.temp_source_dir,
{
"name": "my-project",
"description": "My project description",
"use_ggshield": "True",
},
) as copier_renderer:
# .pre-commit-config.yaml
pre_commit_config = copier_renderer.load_yaml(".pre-commit-config.yaml")
self.assertIn(
{
"id": "ggshield",
"name": "ggshield",
"entry": "bash",
"description": "Runs ggshield to detect hardcoded secrets, security vulnerabilities and policy breaks.",
"stages": ["pre-commit"],
"args": ["-c", '[ -n "$CI" ] || ggshield secret scan pre-commit'],
"language": "system",
"pass_filenames": True,
},
pre_commit_config["repos"][0]["hooks"],
)
# estaTektonPipeline.yaml
esta_tekton_pipeline = copier_renderer.load_yaml("estaTektonPipeline.yaml")
for pipeline in esta_tekton_pipeline["pipelines"]:
self.assertEqual(
{"enabled": True, "reportmode": "FAILED"},
pipeline["build"]["gitguardian"],
)
# .env.example
env_example_lines = (copier_renderer.render_path / ".env.example").read_text().splitlines()
for line in [
"# Template for GGShield",
'GITGUARDIAN_API_KEY=""',
'GITGUARDIAN_INSTANCE="https://gitguardian.sbb.ch"',
]:
self.assertIn(line, env_example_lines)
# Makefile
makefile = (copier_renderer.render_path / "Makefile").read_text()
self.assertIn(
"""
ggshield_has_key:
ifeq ($(GITGUARDIAN_API_KEY),)
$(warning No API-Key for GitGuardian was set!)
endif
""",
makefile,
)
# pyproject.toml
pyproject_toml = copier_renderer.load_toml("pyproject.toml")
self.assertIn(
"ggshield",
pyproject_toml["tool"]["poetry"]["group"]["dev"]["dependencies"],
)
# README.md
readme_md = (copier_renderer.render_path / "README.md").read_text()
self.assertIn("### Setup GGShield (GitGuardian)", readme_md)