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
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
exclude: '^{{cookiecutter\.project_name}}/.*'
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: c59bba8fb259db0fec2bbb77ad8ba51ea7341b56 # frozen: v0.15.20
rev: 2eeb5678de71a00c0902cbda7105d328432f72cb # frozen: v0.16.8
hooks:
- id: ruff-check
types_or: [ python, pyi, jupyter ]
Expand All @@ -24,7 +24,7 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/lycheeverse/lychee
rev: 3d6c1b6bb8d648d6e2949d7e5bd0c9e20d8b41dd # frozen: nightly
rev: 4e065481e8571d0b270c5f4e2332b74cc8342758 # frozen: nightly
hooks:
- id: lychee-docker
# Keep all other configs in the config file
Expand All @@ -40,7 +40,7 @@ repos:
- id: shellcheck
args: [ -x, --source-path=SCRIPTDIR ]
- repo: https://github.com/trufflesecurity/trufflehog
rev: 00155c9dc586f34d189adc83d3ac2698c2ec551f # frozen: v3.95.8
rev: f714bf454f350590f4a24c3ddb1aef02c35bf5b6 # frozen: v3.97.5
hooks:
- id: trufflehog
# Resolve the repo root via git-common-dir so this works in both normal repos and worktrees
Expand All @@ -53,7 +53,7 @@ repos:
hooks:
- id: openapi-spec-validator
- repo: https://github.com/streetsidesoftware/cspell-cli
rev: ea11f9efc0bec520073405bc30552da887ba71bc # frozen: v10.0.1
rev: 058f44e47719e80d8f1e597a235310b60e937860 # frozen: v10.2.0
hooks:
- id: cspell
types: [markdown]
Expand Down
8 changes: 4 additions & 4 deletions {{cookiecutter.project_name}}/.pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
exclude: '^{{ cookiecutter.project_name }}/.*'
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: c59bba8fb259db0fec2bbb77ad8ba51ea7341b56 # frozen: v0.15.20
rev: 2eeb5678de71a00c0902cbda7105d328432f72cb # frozen: v0.16.8
hooks:
- id: ruff-check
types_or: [ python, pyi, jupyter ]
Expand All @@ -24,7 +24,7 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/lycheeverse/lychee
rev: 3d6c1b6bb8d648d6e2949d7e5bd0c9e20d8b41dd # frozen: nightly
rev: 4e065481e8571d0b270c5f4e2332b74cc8342758 # frozen: nightly
hooks:
- id: lychee-docker
# Keep all other configs in the config file
Expand All @@ -40,7 +40,7 @@ repos:
- id: shellcheck
args: [ -x, --source-path=SCRIPTDIR ]
- repo: https://github.com/trufflesecurity/trufflehog
rev: 00155c9dc586f34d189adc83d3ac2698c2ec551f # frozen: v3.95.8
rev: f714bf454f350590f4a24c3ddb1aef02c35bf5b6 # frozen: v3.97.5
hooks:
- id: trufflehog
# Resolve the repo root via git-common-dir so this works in both normal repos and worktrees
Expand All @@ -53,7 +53,7 @@ repos:
hooks:
- id: openapi-spec-validator
- repo: https://github.com/streetsidesoftware/cspell-cli
rev: ea11f9efc0bec520073405bc30552da887ba71bc # frozen: v10.0.1
rev: 058f44e47719e80d8f1e597a235310b60e937860 # frozen: v10.2.0
hooks:
- id: cspell
types: [markdown]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

import sys
from pathlib import Path
from typing import Literal, Optional
from typing import Literal

import yaml
from pydantic import (
Expand Down Expand Up @@ -82,10 +82,10 @@ class InfrastructureDependency(BaseModel):
"search_engine",
]
]
local_runtime: Optional[Literal["compose", "process", "managed", "mock"]] = Field(
local_runtime: Literal["compose", "process", "managed", "mock"] | None = Field(
None, description="How to accommodate the infrastructure locally"
)
version: Optional[str] = Field(
version: str | None = Field(
None, pattern=r"^(0|[1-9]\d*)\.(0|[1-9]\d*)(\.(0|[1-9]\d*))?$"
)

Expand All @@ -94,7 +94,7 @@ class ServiceDependency(BaseModel):
model_config = ConfigDict(strict=True)

name: str
version: Optional[str] = Field(None, pattern=r"\b[0-9a-f]{40}\b")
version: str | None = Field(None, pattern=r"\b[0-9a-f]{40}\b")


class DependsOnConfig(BaseModel):
Expand Down Expand Up @@ -137,7 +137,7 @@ class DevelopConfig(BaseModel):
model_config = ConfigDict(strict=True)

software: SoftwareConfig
depends_on: Optional[DependsOnConfig] = Field(
depends_on: DependsOnConfig | None = Field(
default_factory=DependsOnConfig,
description="The dependencies of this service during development",
)
Expand All @@ -147,7 +147,7 @@ class PublishConfig(BaseModel):
model_config = ConfigDict(strict=True)

publish_type: Literal["docker", "oci", "archive", "package", "binary", "none"]
depends_on: Optional[DependsOnConfig] = Field(
depends_on: DependsOnConfig | None = Field(
default_factory=DependsOnConfig,
description="The dependencies of this service in order to build and publish",
)
Expand All @@ -166,7 +166,7 @@ class DeployConfig(BaseModel):
"ansible",
"none",
]
depends_on: Optional[DependsOnConfig] = Field(
depends_on: DependsOnConfig | None = Field(
default_factory=DependsOnConfig,
description="The dependencies of this service in order to deploy",
)
Expand Down Expand Up @@ -194,7 +194,7 @@ class OperationsScheduleConfig(BaseModel):
uptime: Literal["continuous", "on_demand", "scheduled", "business_hours"] = Field(
"continuous", description="The uptime expectations"
)
days: Optional[
days: (
list[
Literal[
"Monday",
Expand All @@ -206,49 +206,50 @@ class OperationsScheduleConfig(BaseModel):
"Sunday",
]
]
] = None
start: Optional[str] = None # RFC3339 timestamp
end: Optional[str] = None # RFC3339 timestamp
| None
) = None
start: str | None = None # RFC3339 timestamp
end: str | None = None # RFC3339 timestamp

@field_validator("days", "start", "end", mode="before")
@classmethod
def validate_scheduled_fields(cls, value, info):
if info.data.get("uptime") == "scheduled":
if not value:
raise ValueError(
f'{info.field_name} must be specified if uptime is set to "scheduled"'
)
if info.data.get("uptime") == "scheduled" and not value:
raise ValueError(
f'{info.field_name} must be specified if uptime is set to "scheduled"'
)
return value

@model_validator(mode="after")
def validate_no_extra_fields(self):
if self.uptime != "scheduled":
if self.days is not None or self.start is not None or self.end is not None:
raise ValueError(
'days, start, and end should only be set if uptime is "scheduled"'
)
if self.uptime != "scheduled" and (
self.days is not None or self.start is not None or self.end is not None
):
raise ValueError(
'days, start, and end should only be set if uptime is "scheduled"'
)
return self


class RuntimeEnvironmentConfig(BaseModel):
model_config = ConfigDict(strict=True)

authorization: dict[Literal["public"], bool]
service_route_prefix: Optional[str] = Field(
service_route_prefix: str | None = Field(
None,
description="The service-specific route prefix, like data for /api/data/example and /api/data",
)
stage_name: Optional[str] = Field(
stage_name: str | None = Field(
None,
description="The service-specific stage name, like current or v1; typically used for REST APIs",
)
uptime_sla: float = Field(95.0, description="The uptime agreement")
uptime_slo: float = Field(99.0, description="The uptime objective")
operations_schedule: Optional[OperationsScheduleConfig] = Field(
operations_schedule: OperationsScheduleConfig | None = Field(
default_factory=lambda: OperationsScheduleConfig(uptime="on_demand"),
description="The operations schedule",
)
depends_on: Optional[DependsOnConfig] = Field(
depends_on: DependsOnConfig | None = Field(
default_factory=DependsOnConfig,
description="Dependencies for the specific environment",
)
Expand Down Expand Up @@ -347,11 +348,11 @@ class ServiceSchema(BaseModel):
develop: DevelopConfig = Field(
description="Context used when updating the service's software, tests, documentation, IaC, or other related information."
)
publish: Optional[PublishConfig] = Field(
publish: PublishConfig | None = Field(
None,
description="Details regarding how and where artifacts are published, such as container images and in-toto attestations",
)
deploy: Optional[DeployConfig] = Field(
deploy: DeployConfig | None = Field(
None, description="Details regarding how and where to deploy a service"
)
runtime: RuntimeConfig = Field(
Expand All @@ -373,14 +374,15 @@ def check_lifecycle_constraints(self):
"archived",
]

if self.deploy and self.deploy.environments:
if (
lifecycle in production_restricted_lifecycles
and "production" in self.deploy.environments
):
raise ValueError(
f"Service {self.name}'s lifecycle of '{lifecycle}' is not allowed to deploy to production."
)
if (
self.deploy
and self.deploy.environments
and lifecycle in production_restricted_lifecycles
and "production" in self.deploy.environments
):
raise ValueError(
f"Service {self.name}'s lifecycle of '{lifecycle}' is not allowed to deploy to production."
)
return self


Expand All @@ -407,8 +409,11 @@ def validate_service_definition(file_path: Path) -> bool:
location = " -> ".join(str(loc) for loc in error["loc"])
print(f" - {location}: {error['msg']}")
return False
except Exception as e:
print(f"Unexpected error: {e}")
except OSError as e:
print(f"Error reading {file_path}: {e}")
return False
except TypeError as e:
print(f"Error: {file_path} must contain a YAML mapping at the top level: {e}")
Comment thread
JonZeolla marked this conversation as resolved.
return False


Expand Down
4 changes: 3 additions & 1 deletion {{cookiecutter.project_name}}/tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
"""

import json
import tomllib
import os
import subprocess
import tomllib
from pathlib import Path

import pytest
Expand Down Expand Up @@ -132,6 +132,7 @@ def test_docker_image():
process = subprocess.run(
["docker", "run", "--rm", f"{image_name}:latest"],
capture_output=True,
check=False,
cwd=project_root,
)
assert process.returncode == 1, (
Expand All @@ -154,6 +155,7 @@ def test_docker_image():
process = subprocess.run(
command,
capture_output=True,
check=False,
cwd=project_root,
)
assert process.returncode == expected_exit, (
Expand Down
7 changes: 4 additions & 3 deletions {{cookiecutter.project_name}}/tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ def test_main_function():
"""Test that main() raises NotImplementedError"""
from main import main

with patch("sys.argv", ["main"]):
with pytest.raises(NotImplementedError):
main()
with patch("sys.argv", ["main"]), pytest.raises(NotImplementedError):
main()


@pytest.mark.unit
Expand All @@ -46,6 +45,7 @@ def test_main_as_script():
result = subprocess.run(
[sys.executable, str(main_path)],
capture_output=True,
check=False,
text=True,
)

Expand All @@ -62,6 +62,7 @@ def test_main_as_script_version():
result = subprocess.run(
[sys.executable, str(main_path), "--version"],
capture_output=True,
check=True,
text=True,
)

Expand Down
12 changes: 6 additions & 6 deletions {{cookiecutter.project_name}}/tests/test_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
import pytest

from {{ cookiecutter.project_slug }} import (
__maintainer__,
__project_name__,
__version__,
{%- if cookiecutter.license != 'NONE' %}
__license__,
{%- endif %}
{%- if cookiecutter.license == 'NONE' %}
__copyright__,
{%- endif %}
{%- if cookiecutter.license != 'NONE' %}
__license__,
{%- endif %}
__maintainer__,
__project_name__,
__version__,
)


Expand Down
Loading