diff --git a/.dockerignore b/.dockerignore
index 0736aa4..21fa16c 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,5 +1,13 @@
**/__pycache__
**/lost+found
+**/.terraform
+**/.terraform.lock.hcl
+**/terraform.tfstate
+**/terraform.tfstate.backup
+**/terraform.tfvars
+**/terraform.tfvars.json
+**/*.auto.tfvars
+**/*.auto.tfvars.json
*.md
.*
Dockerfile
diff --git a/Dockerfile b/Dockerfile
index c55d540..7295cb8 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -4,6 +4,7 @@ ARG DEBIAN_FRONTEND=noninteractive
ARG OUTPUT_BASE_DIR=/data
ENV OUTPUT_BASE_DIR=${OUTPUT_BASE_DIR}
WORKDIR /app
+ARG OPENTOFU_VERSION=1.10.6
RUN apt-get update \
&& apt-get install --assume-yes --no-install-recommends \
curl \
@@ -11,12 +12,11 @@ RUN apt-get update \
gnupg \
libpq-dev \
software-properties-common \
- && curl https://apt.releases.hashicorp.com/gpg | gpg --dearmor > /usr/share/keyrings/hashicorp-archive-keyring.gpg \
- && gpg --no-default-keyring --keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg --fingerprint \
- && echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/hashicorp.list \
- && apt-get update \
- && apt-get install --assume-yes --no-install-recommends \
- terraform \
+ unzip \
+ && curl -fsSL https://get.opentofu.org/install-opentofu.sh -o /tmp/install-opentofu.sh \
+ && chmod +x /tmp/install-opentofu.sh \
+ && /tmp/install-opentofu.sh --install-method standalone --opentofu-version "${OPENTOFU_VERSION}" \
+ && rm /tmp/install-opentofu.sh \
&& rm -rf /var/lib/apt/lists/*
COPY ./requirements/common.txt requirements/common.txt
RUN python3 -m pip install --no-cache-dir --upgrade pip setuptools \
diff --git a/README.md b/README.md
index 71cc209..38fdd35 100644
--- a/README.md
+++ b/README.md
@@ -115,7 +115,7 @@ Staging environment complete URL [https://stage.my-project-name.com]:
Production environment complete URL [https://www.my-project-name.com]:
Do you want Traefik to generate SSL certificates? [Y/n]:
Let's Encrypt certificates email: info@my-organization-email.com
-Do you want to use Redis? [y/N]:
+Do you want to use Valkey? [y/N]:
Kubernetes cluster DigitalOcean region [fra1]:
Database cluster DigitalOcean region [fra1]:
Database cluster node size [db-s-1vcpu-2gb]:
@@ -322,16 +322,16 @@ else use
`--project-url-monitoring=https://logs.example.org/`
-#### Redis
+#### Valkey
-For enabling redis integration the following arguments are needed:
+For enabling valkey integration the following arguments are needed:
-`--use-redis`
-`--digitalocean-redis-cluster-region=fra1`
-`--digitalocean-redis-cluster-node-size=db-s-1vcpu-2gb`
+`--use-valkey`
+`--digitalocean-valkey-cluster-region=fra1`
+`--digitalocean-valkey-cluster-node-size=db-s-1vcpu-2gb`
Disabled args
-`--no-redis`
+`--no-valkey`
### ☸️ Other Kubernetes
@@ -354,9 +354,9 @@ Disabled args
`--postgres-persistent-volume-claim-capacity=""`
`--postgres-persistent-volume-host-path={{postgres-persistent-volume-host-path}}`
-#### Redis
+#### Valkey
-`--redis-image=redis:6.2`
+`--valkey-image=valkey/valkey:8`
### 🦊 GitLab
diff --git a/bootstrap/collector.py b/bootstrap/collector.py
index 04a3238..9f86116 100755
--- a/bootstrap/collector.py
+++ b/bootstrap/collector.py
@@ -13,11 +13,12 @@
BACKEND_TYPE_CHOICES,
BACKEND_TYPE_DEFAULT,
CLUSTERS_DEFAULT,
+ CORE_PROVIDER_AWS,
CORE_PROVIDER_CHOICES,
CORE_PROVIDER_DIGITALOCEAN,
DIGITALOCEAN_DATABASE_CLUSTER_NODE_SIZE_DEFAULT,
- DIGITALOCEAN_REDIS_CLUSTER_NODE_SIZE_DEFAULT,
DIGITALOCEAN_SPACES_REGION_DEFAULT,
+ DIGITALOCEAN_VALKEY_CLUSTER_NODE_SIZE_DEFAULT,
EMPTY_SERVICE_TYPE,
ENV_NAMES,
ENV_TO_CLUSTER_DEFAULT,
@@ -68,6 +69,8 @@ class Collector:
clusters: list[str] | None = None
cluster_core_providers: dict[str, list[str]] | None = None
env_to_cluster: dict[str, str] | None = None
+ aws_role_arn: str | None = None
+ aws_region: str | None = None
project_domain: str | None = None
subdomain_dev: str | None = None
subdomain_stage: str | None = None
@@ -86,10 +89,10 @@ class Collector:
postgres_persistent_volume_capacity: str | None = None
postgres_persistent_volume_claim_capacity: str | None = None
postgres_persistent_volume_host_path: str | None = None
- use_redis: bool | None = None
- redis_image: str | None = None
- digitalocean_redis_cluster_region: str | None = None
- digitalocean_redis_cluster_node_size: str | None = None
+ use_valkey: bool | None = None
+ valkey_image: str | None = None
+ digitalocean_valkey_cluster_region: str | None = None
+ digitalocean_valkey_cluster_node_size: str | None = None
sentry_org: str | None = None
sentry_url: str | None = None
sentry_auth_token: str | None = None
@@ -130,10 +133,11 @@ def collect(self):
self.set_service_dir()
self.set_backend_service()
self.set_frontend_service()
- self.set_use_redis()
+ self.set_use_valkey()
self.set_terraform()
self.set_vault()
self.set_clusters()
+ self.set_aws()
self.set_envs()
self.set_domain_and_urls()
self.set_letsencrypt()
@@ -201,11 +205,11 @@ def set_frontend_service(self):
separator="",
)
- def set_use_redis(self):
- """Set the use Redis option."""
- if self.use_redis is None:
- self.use_redis = click.confirm(
- warning("Do you want to use Redis?"), default=False
+ def set_use_valkey(self):
+ """Set the use Valkey option."""
+ if self.use_valkey is None:
+ self.use_valkey = click.confirm(
+ warning("Do you want to use Valkey?"), default=False
)
def set_terraform(self):
@@ -289,6 +293,21 @@ def set_clusters(self):
if p.strip().lower() in CORE_PROVIDER_CHOICES
]
+ def set_aws(self):
+ """Set AWS-specific options when AWS is among the chosen core providers."""
+ uses_aws = any(
+ CORE_PROVIDER_AWS in providers
+ for providers in (self.cluster_core_providers or {}).values()
+ )
+ if not uses_aws:
+ return
+ self.aws_role_arn = self.aws_role_arn or click.prompt(
+ "AWS IAM role ARN for GitLab OIDC federation"
+ )
+ self.aws_region = self.aws_region or click.prompt(
+ "AWS region", default=AWS_S3_REGION_DEFAULT
+ )
+
def set_envs(self):
"""Set the environment-to-cluster mapping (one cluster slug per environment)."""
self.env_to_cluster = self.env_to_cluster or {}
@@ -388,15 +407,15 @@ def set_digitalocean(self):
default=DIGITALOCEAN_DATABASE_CLUSTER_NODE_SIZE_DEFAULT,
)
)
- if self.use_redis:
- if self.digitalocean_redis_cluster_region is None:
- self.digitalocean_redis_cluster_region = click.prompt(
- "Redis cluster DigitalOcean region", default="fra1"
+ if self.use_valkey:
+ if self.digitalocean_valkey_cluster_region is None:
+ self.digitalocean_valkey_cluster_region = click.prompt(
+ "Valkey cluster DigitalOcean region", default="fra1"
)
- if self.digitalocean_redis_cluster_node_size is None:
- self.digitalocean_redis_cluster_node_size = click.prompt(
- "Redis cluster node size",
- default=DIGITALOCEAN_REDIS_CLUSTER_NODE_SIZE_DEFAULT,
+ if self.digitalocean_valkey_cluster_node_size is None:
+ self.digitalocean_valkey_cluster_node_size = click.prompt(
+ "Valkey cluster node size",
+ default=DIGITALOCEAN_VALKEY_CLUSTER_NODE_SIZE_DEFAULT,
)
def set_digitalocean_token(self):
@@ -572,6 +591,8 @@ def get_runner(self):
clusters=self.clusters,
cluster_core_providers=self.cluster_core_providers,
env_to_cluster=self.env_to_cluster,
+ aws_role_arn=self.aws_role_arn,
+ aws_region=self.aws_region,
project_domain=self.project_domain,
subdomain_dev=self.subdomain_dev,
subdomain_stage=self.subdomain_stage,
@@ -590,10 +611,10 @@ def get_runner(self):
postgres_persistent_volume_capacity=self.postgres_persistent_volume_capacity,
postgres_persistent_volume_claim_capacity=self.postgres_persistent_volume_claim_capacity,
postgres_persistent_volume_host_path=self.postgres_persistent_volume_host_path,
- use_redis=self.use_redis,
- redis_image=self.redis_image,
- digitalocean_redis_cluster_region=self.digitalocean_redis_cluster_region,
- digitalocean_redis_cluster_node_size=self.digitalocean_redis_cluster_node_size,
+ use_valkey=self.use_valkey,
+ valkey_image=self.valkey_image,
+ digitalocean_valkey_cluster_region=self.digitalocean_valkey_cluster_region,
+ digitalocean_valkey_cluster_node_size=self.digitalocean_valkey_cluster_node_size,
sentry_org=self.sentry_org,
sentry_url=self.sentry_url,
backend_sentry_dsn=self.backend_sentry_dsn,
diff --git a/bootstrap/constants.py b/bootstrap/constants.py
index ef14b19..5b891a3 100755
--- a/bootstrap/constants.py
+++ b/bootstrap/constants.py
@@ -1,5 +1,6 @@
"""Web project initialization CLI constants."""
+import os
from pathlib import Path
BASE_DIR = Path(__file__).parent.parent
@@ -32,12 +33,21 @@
# Subrepos
BACKEND_TEMPLATE_URLS = {
- "django": "https://github.com/20tab/django-continuous-delivery"
+ "django": os.environ.get(
+ "BACKEND_TEMPLATE_URL_DJANGO",
+ "https://github.com/20tab/django-continuous-delivery",
+ ),
}
FRONTEND_TEMPLATE_URLS = {
- "nextjs": "https://github.com/20tab/nextjs-continuous-delivery",
- "nextjs-light": "https://github.com/20tab/nextjs-light-continuous-delivery",
+ "nextjs": os.environ.get(
+ "FRONTEND_TEMPLATE_URL_NEXTJS",
+ "https://github.com/20tab/nextjs-continuous-delivery",
+ ),
+ "nextjs-light": os.environ.get(
+ "FRONTEND_TEMPLATE_URL_NEXTJS_LIGHT",
+ "https://github.com/20tab/nextjs-light-continuous-delivery",
+ ),
}
SUBREPOS_DIR = Path(__file__).parent.parent / ".subrepos"
@@ -62,7 +72,7 @@
DIGITALOCEAN_DATABASE_CLUSTER_NODE_SIZE_DEFAULT = "db-s-1vcpu-2gb"
-DIGITALOCEAN_REDIS_CLUSTER_NODE_SIZE_DEFAULT = "db-s-1vcpu-2gb"
+DIGITALOCEAN_VALKEY_CLUSTER_NODE_SIZE_DEFAULT = "db-s-1vcpu-2gb"
DIGITALOCEAN_SPACES_REGION_DEFAULT = "fra1"
diff --git a/bootstrap/runner.py b/bootstrap/runner.py
index 5df3d4c..c9f1f1c 100644
--- a/bootstrap/runner.py
+++ b/bootstrap/runner.py
@@ -78,6 +78,8 @@ class Runner:
clusters: list[str] | None = None
cluster_core_providers: dict[str, list[str]] | None = None
env_to_cluster: dict[str, str] | None = None
+ aws_role_arn: str | None = None
+ aws_region: str | None = None
python_version: str = PYTHON_VERSION_DEFAULT
minos_platform_image: str = MINOS_PLATFORM_IMAGE
minos_service_image: str = MINOS_SERVICE_IMAGE
@@ -101,10 +103,10 @@ class Runner:
postgres_persistent_volume_capacity: str | None = None
postgres_persistent_volume_claim_capacity: str | None = None
postgres_persistent_volume_host_path: str | None = None
- use_redis: bool = False
- redis_image: str | None = None
- digitalocean_redis_cluster_region: str | None = None
- digitalocean_redis_cluster_node_size: str | None = None
+ use_valkey: bool = False
+ valkey_image: str | None = None
+ digitalocean_valkey_cluster_region: str | None = None
+ digitalocean_valkey_cluster_node_size: str | None = None
sentry_org: str | None = None
sentry_url: str | None = None
sentry_auth_token: str | None = None
@@ -253,6 +255,11 @@ def collect_gitlab_variables_secrets(self):
self.digitalocean_token and self.register_gitlab_group_variables(
("DIGITALOCEAN_TOKEN", self.digitalocean_token, True)
)
+ if self.aws_role_arn:
+ self.register_gitlab_group_variables(
+ ("AWS_ROLE_ARN", self.aws_role_arn, False, False),
+ ("AWS_DEFAULT_REGION", self.aws_region, False, False),
+ )
if "s3" in self.media_storage:
self.register_gitlab_group_variables(
("S3_ACCESS_ID", self.s3_access_id, True),
@@ -349,6 +356,13 @@ def collect_vault_secrets(self):
def init_service(self):
"""Initialize the service."""
click.echo(info("...cookiecutting the service"))
+ core_providers = sorted(
+ {
+ provider
+ for providers in (self.cluster_core_providers or {}).values()
+ for provider in providers
+ }
+ )
cookiecutter(
os.path.dirname(os.path.dirname(__file__)),
extra_context={
@@ -367,7 +381,7 @@ def init_service(self):
"project_name": self.project_name,
"project_slug": self.project_slug,
"python_version": self.python_version,
- "resources": {"envs": self.envs},
+ "resources": {"envs": self.envs, "core_providers": core_providers},
"service_slug": self.service_slug,
"terraform_backend": self.terraform_backend,
"terraform_cloud_organization": self.terraform_cloud_organization,
@@ -480,6 +494,9 @@ def init_terraform_cloud(self):
"TF_VAR_project_name": self.project_name,
"TF_VAR_project_slug": self.project_slug,
"TF_VAR_terraform_cloud_token": self.terraform_cloud_token,
+ # Serialize workspace creation: tfe provider races on tfe_project readiness
+ # when creating multiple workspaces in parallel, returning sporadic 422s.
+ "TF_CLI_ARGS_apply": "-parallelism=1",
}
self.run_terraform("terraform-cloud", env)
@@ -503,8 +520,8 @@ def init_vault(self):
def get_terraform_module_params(self, module_name, env):
"""Return Terraform parameters for the given module."""
return (
- Path(__file__).parent.parent / "terraform" / module_name,
- self.logs_dir / self.service_slug / "terraform" / module_name,
+ Path(__file__).parent.parent / "tofu" / module_name,
+ self.logs_dir / self.service_slug / "tofu" / module_name,
terraform_dir := self.terraform_dir / self.service_slug / module_name,
{
**env,
@@ -521,7 +538,7 @@ def run_terraform_init(self, cwd, env, logs_dir, state_path):
init_stderr_path = logs_dir / "init-stderr.log"
init_process = subprocess.run(
[
- "terraform",
+ "tofu",
"init",
"-backend-config",
f"path={state_path.resolve()}",
@@ -550,7 +567,7 @@ def run_terraform_apply(self, cwd, env, logs_dir):
apply_stdout_path = logs_dir / "apply-stdout.log"
apply_stderr_path = logs_dir / "apply-stderr.log"
apply_process = subprocess.run(
- ["terraform", "apply", "-auto-approve", "-input=false", "-no-color"],
+ ["tofu", "apply", "-auto-approve", "-input=false", "-no-color"],
capture_output=True,
cwd=cwd,
env=dict(**env, TF_LOG_PATH=str(apply_log_path.resolve())),
@@ -575,7 +592,7 @@ def run_terraform_destroy(self, cwd, env, logs_dir):
destroy_stderr_path = logs_dir / "destroy-stderr.log"
destroy_process = subprocess.run(
[
- "terraform",
+ "tofu",
"destroy",
"-auto-approve",
"-input=false",
@@ -601,7 +618,7 @@ def get_terraform_outputs(self, cwd, env, outputs):
"""Get Terraform outputs."""
return {
output_name: subprocess.run(
- ["terraform", "output", "-raw", output_name],
+ ["tofu", "output", "-raw", output_name],
capture_output=True,
cwd=cwd,
env=env,
@@ -644,7 +661,7 @@ def init_subrepo(self, service_slug, template_url, **kwargs):
"""Initialize a subrepo using the given template and options."""
subrepo_dir = str((SUBREPOS_DIR / service_slug).resolve())
shutil.rmtree(subrepo_dir, ignore_errors=True)
- subprocess.run(
+ clone = subprocess.run(
[
"git",
"clone",
@@ -653,6 +670,9 @@ def init_subrepo(self, service_slug, template_url, **kwargs):
"-q",
]
)
+ if clone.returncode != 0:
+ click.echo(error(f"Failed to clone {service_slug} subrepo from {template_url}"))
+ raise BootstrapError
options = {
"env_to_cluster": self.env_to_cluster,
"gid": self.gid,
@@ -681,20 +701,25 @@ def init_subrepo(self, service_slug, template_url, **kwargs):
"terraform_cloud_hostname": self.terraform_cloud_hostname,
"terraform_cloud_organization_create": False,
"terraform_cloud_organization": self.terraform_cloud_organization,
+ "terraform_cloud_project_create": False,
"terraform_cloud_token": self.terraform_cloud_token,
"terraform_dir": str(self.terraform_dir.resolve()),
"uid": self.uid,
- "use_redis": self.use_redis,
+ "use_valkey": self.use_valkey,
"vault_url": self.vault_url,
"vault_token": self.vault_token,
**kwargs,
}
- subprocess.run(
+ deps = subprocess.run(
["python", "-m", "pip", "install", "-r", "requirements/common.txt"],
capture_output=True,
cwd=subrepo_dir,
)
- subprocess.run(
+ if deps.returncode != 0:
+ click.echo(error(f"Failed to install {service_slug} subrepo dependencies"))
+ click.echo(deps.stderr.decode("utf-8", "replace"))
+ raise BootstrapError
+ runner = subprocess.run(
[
"python",
"-c",
@@ -702,6 +727,9 @@ def init_subrepo(self, service_slug, template_url, **kwargs):
],
cwd=subrepo_dir,
)
+ if runner.returncode != 0:
+ click.echo(error(f"Subrepo {service_slug} bootstrap failed"))
+ raise BootstrapError
def change_output_owner(self):
"""Change the owner of the output directory recursively."""
@@ -735,25 +763,29 @@ def run(self):
if self.vault_url:
self.init_vault()
frontend_template_url = FRONTEND_TEMPLATE_URLS.get(self.frontend_type)
- if frontend_template_url:
- self.init_subrepo(
- self.frontend_service_slug,
- frontend_template_url,
- internal_backend_url=self.backend_service_slug
- and (f"http://{self.backend_service_slug}:{self.backend_service_port}")
- or None,
- internal_service_port=self.frontend_service_port,
- sentry_dsn=self.frontend_sentry_dsn,
- )
backend_template_url = BACKEND_TEMPLATE_URLS.get(self.backend_type)
- if backend_template_url:
- self.init_subrepo(
- self.backend_service_slug,
- backend_template_url,
- internal_service_port=self.backend_service_port,
- media_storage=self.media_storage,
- python_version=self.python_version,
- sentry_dsn=self.backend_sentry_dsn,
- )
+ try:
+ if frontend_template_url:
+ self.init_subrepo(
+ self.frontend_service_slug,
+ frontend_template_url,
+ internal_backend_url=self.backend_service_slug
+ and (f"http://{self.backend_service_slug}:{self.backend_service_port}")
+ or None,
+ internal_service_port=self.frontend_service_port,
+ sentry_dsn=self.frontend_sentry_dsn,
+ )
+ if backend_template_url:
+ self.init_subrepo(
+ self.backend_service_slug,
+ backend_template_url,
+ internal_service_port=self.backend_service_port,
+ media_storage=self.media_storage,
+ python_version=self.python_version,
+ sentry_dsn=self.backend_sentry_dsn,
+ )
+ except BootstrapError:
+ self.reset_terraform()
+ raise
self.change_output_owner()
self.cleanup()
diff --git a/cookiecutter.json b/cookiecutter.json
index 0689e9d..b6dcc5d 100644
--- a/cookiecutter.json
+++ b/cookiecutter.json
@@ -39,7 +39,8 @@
"cluster_slug": "main",
"host": ""
}
- ]
+ ],
+ "core_providers": ["aws", "digitalocean"]
},
"_extensions": ["cookiecutter.extensions.SlugifyExtension"]
}
diff --git a/requirements/common.in b/requirements/common.in
index 0013375..1291d57 100644
--- a/requirements/common.in
+++ b/requirements/common.in
@@ -1,5 +1,6 @@
-click~=8.1.0
-cookiecutter~=2.5.0
+click~=8.3.0
+cookiecutter~=2.6.0
pydantic~=1.10.0
python-slugify~=8.0.0
+uv~=0.9.0
validators~=0.20.0
diff --git a/requirements/common.txt b/requirements/common.txt
index 95b2974..305f0ad 100644
--- a/requirements/common.txt
+++ b/requirements/common.txt
@@ -1,335 +1,444 @@
-arrow==1.3.0 \
- --hash=sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80 \
- --hash=sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85
+arrow==1.4.0 \
+ --hash=sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205 \
+ --hash=sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7
# via cookiecutter
-binaryornot==0.4.4 \
- --hash=sha256:359501dfc9d40632edc9fac890e19542db1a287bbcfa58175b66658392018061 \
- --hash=sha256:b8b71173c917bddcd2c16070412e369c3ed7f0528926f70cac18a6c97fd563e4
+binaryornot==0.6.0 \
+ --hash=sha256:900adfd5e1b821255ba7e63139b0396b14c88b9286e74e03b6f51e0200331337 \
+ --hash=sha256:cc8d57cfa71d74ff8c28a7726734d53a851d02fad9e3a5581fb807f989f702f0
# via cookiecutter
-certifi==2023.11.17 \
- --hash=sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1 \
- --hash=sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474
+certifi==2026.4.22 \
+ --hash=sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a \
+ --hash=sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580
# via requests
-chardet==5.2.0 \
- --hash=sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7 \
- --hash=sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970
- # via binaryornot
-charset-normalizer==3.3.2 \
- --hash=sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027 \
- --hash=sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087 \
- --hash=sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786 \
- --hash=sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8 \
- --hash=sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09 \
- --hash=sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185 \
- --hash=sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574 \
- --hash=sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e \
- --hash=sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519 \
- --hash=sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898 \
- --hash=sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269 \
- --hash=sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3 \
- --hash=sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f \
- --hash=sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6 \
- --hash=sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8 \
- --hash=sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a \
- --hash=sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73 \
- --hash=sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc \
- --hash=sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714 \
- --hash=sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2 \
- --hash=sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc \
- --hash=sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce \
- --hash=sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d \
- --hash=sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e \
- --hash=sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6 \
- --hash=sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269 \
- --hash=sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96 \
- --hash=sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d \
- --hash=sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a \
- --hash=sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4 \
- --hash=sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77 \
- --hash=sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d \
- --hash=sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0 \
- --hash=sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed \
- --hash=sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068 \
- --hash=sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac \
- --hash=sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25 \
- --hash=sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8 \
- --hash=sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab \
- --hash=sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26 \
- --hash=sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2 \
- --hash=sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db \
- --hash=sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f \
- --hash=sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5 \
- --hash=sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99 \
- --hash=sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c \
- --hash=sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d \
- --hash=sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811 \
- --hash=sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa \
- --hash=sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a \
- --hash=sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03 \
- --hash=sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b \
- --hash=sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04 \
- --hash=sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c \
- --hash=sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001 \
- --hash=sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458 \
- --hash=sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389 \
- --hash=sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99 \
- --hash=sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985 \
- --hash=sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537 \
- --hash=sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238 \
- --hash=sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f \
- --hash=sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d \
- --hash=sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796 \
- --hash=sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a \
- --hash=sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143 \
- --hash=sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8 \
- --hash=sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c \
- --hash=sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5 \
- --hash=sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5 \
- --hash=sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711 \
- --hash=sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4 \
- --hash=sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6 \
- --hash=sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c \
- --hash=sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7 \
- --hash=sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4 \
- --hash=sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b \
- --hash=sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae \
- --hash=sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12 \
- --hash=sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c \
- --hash=sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae \
- --hash=sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8 \
- --hash=sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887 \
- --hash=sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b \
- --hash=sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4 \
- --hash=sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f \
- --hash=sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5 \
- --hash=sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33 \
- --hash=sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519 \
- --hash=sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561
+charset-normalizer==3.4.7 \
+ --hash=sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc \
+ --hash=sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c \
+ --hash=sha256:07d9e39b01743c3717745f4c530a6349eadbfa043c7577eef86c502c15df2c67 \
+ --hash=sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4 \
+ --hash=sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0 \
+ --hash=sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c \
+ --hash=sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5 \
+ --hash=sha256:12a6fff75f6bc66711b73a2f0addfc4c8c15a20e805146a02d147a318962c444 \
+ --hash=sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153 \
+ --hash=sha256:14265bfe1f09498b9d8ec91e9ec9fa52775edf90fcbde092b25f4a33d444fea9 \
+ --hash=sha256:16d971e29578a5e97d7117866d15889a4a07befe0e87e703ed63cd90cb348c01 \
+ --hash=sha256:177a0ba5f0211d488e295aaf82707237e331c24788d8d76c96c5a41594723217 \
+ --hash=sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b \
+ --hash=sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c \
+ --hash=sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a \
+ --hash=sha256:1dc8b0ea451d6e69735094606991f32867807881400f808a106ee1d963c46a83 \
+ --hash=sha256:1efde3cae86c8c273f1eb3b287be7d8499420cf2fe7585c41d370d3e790054a5 \
+ --hash=sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7 \
+ --hash=sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb \
+ --hash=sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c \
+ --hash=sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1 \
+ --hash=sha256:2cd4a60d0e2fb04537162c62bbbb4182f53541fe0ede35cdf270a1c1e723cc42 \
+ --hash=sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab \
+ --hash=sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df \
+ --hash=sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e \
+ --hash=sha256:320ade88cfb846b8cd6b4ddf5ee9e80ee0c1f52401f2456b84ae1ae6a1a5f207 \
+ --hash=sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18 \
+ --hash=sha256:36836d6ff945a00b88ba1e4572d721e60b5b8c98c155d465f56ad19d68f23734 \
+ --hash=sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38 \
+ --hash=sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110 \
+ --hash=sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18 \
+ --hash=sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44 \
+ --hash=sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d \
+ --hash=sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48 \
+ --hash=sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e \
+ --hash=sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5 \
+ --hash=sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d \
+ --hash=sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53 \
+ --hash=sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790 \
+ --hash=sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c \
+ --hash=sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b \
+ --hash=sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116 \
+ --hash=sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d \
+ --hash=sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10 \
+ --hash=sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6 \
+ --hash=sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2 \
+ --hash=sha256:6370e8686f662e6a3941ee48ed4742317cafbe5707e36406e9df792cdb535776 \
+ --hash=sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a \
+ --hash=sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265 \
+ --hash=sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008 \
+ --hash=sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943 \
+ --hash=sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374 \
+ --hash=sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246 \
+ --hash=sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e \
+ --hash=sha256:6e0d51f618228538a3e8f46bd246f87a6cd030565e015803691603f55e12afb5 \
+ --hash=sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616 \
+ --hash=sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15 \
+ --hash=sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41 \
+ --hash=sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960 \
+ --hash=sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752 \
+ --hash=sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e \
+ --hash=sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72 \
+ --hash=sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7 \
+ --hash=sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8 \
+ --hash=sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b \
+ --hash=sha256:813c0e0132266c08eb87469a642cb30aaff57c5f426255419572aaeceeaa7bf4 \
+ --hash=sha256:82b271f5137d07749f7bf32f70b17ab6eaabedd297e75dce75081a24f76eb545 \
+ --hash=sha256:84c018e49c3bf790f9c2771c45e9313a08c2c2a6342b162cd650258b57817706 \
+ --hash=sha256:8751d2787c9131302398b11e6c8068053dcb55d5a8964e114b6e196cf16cb366 \
+ --hash=sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb \
+ --hash=sha256:87fad7d9ba98c86bcb41b2dc8dbb326619be2562af1f8ff50776a39e55721c5a \
+ --hash=sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e \
+ --hash=sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00 \
+ --hash=sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f \
+ --hash=sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a \
+ --hash=sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1 \
+ --hash=sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66 \
+ --hash=sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356 \
+ --hash=sha256:a6c5863edfbe888d9eff9c8b8087354e27618d9da76425c119293f11712a6319 \
+ --hash=sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4 \
+ --hash=sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad \
+ --hash=sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d \
+ --hash=sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5 \
+ --hash=sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7 \
+ --hash=sha256:aef65cd602a6d0e0ff6f9930fcb1c8fec60dd2cfcb6facaf4bdb0e5873042db0 \
+ --hash=sha256:af21eb4409a119e365397b2adbaca4c9ccab56543a65d5dbd9f920d6ac29f686 \
+ --hash=sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34 \
+ --hash=sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49 \
+ --hash=sha256:bb8cc7534f51d9a017b93e3e85b260924f909601c3df002bcdb58ddb4dc41a5c \
+ --hash=sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1 \
+ --hash=sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e \
+ --hash=sha256:bd9b23791fe793e4968dba0c447e12f78e425c59fc0e3b97f6450f4781f3ee60 \
+ --hash=sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0 \
+ --hash=sha256:c0f081d69a6e58272819b70288d3221a6ee64b98df852631c80f293514d3b274 \
+ --hash=sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d \
+ --hash=sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0 \
+ --hash=sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae \
+ --hash=sha256:c593052c465475e64bbfe5dbd81680f64a67fdc752c56d7a0ae205dc8aeefe0f \
+ --hash=sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d \
+ --hash=sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe \
+ --hash=sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3 \
+ --hash=sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393 \
+ --hash=sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1 \
+ --hash=sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af \
+ --hash=sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44 \
+ --hash=sha256:d61f00a0869d77422d9b2aba989e2d24afa6ffd552af442e0e58de4f35ea6d00 \
+ --hash=sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c \
+ --hash=sha256:dca4bbc466a95ba9c0234ef56d7dd9509f63da22274589ebd4ed7f1f4d4c54e3 \
+ --hash=sha256:dd915403e231e6b1809fe9b6d9fc55cf8fb5e02765ac625d9cd623342a7905d7 \
+ --hash=sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd \
+ --hash=sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e \
+ --hash=sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b \
+ --hash=sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8 \
+ --hash=sha256:e5f4d355f0a2b1a31bc3edec6795b46324349c9cb25eed068049e4f472fb4259 \
+ --hash=sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859 \
+ --hash=sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46 \
+ --hash=sha256:e80c8378d8f3d83cd3164da1ad2df9e37a666cdde7b1cb2298ed0b558064be30 \
+ --hash=sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b \
+ --hash=sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46 \
+ --hash=sha256:ed065083d0898c9d5b4bbec7b026fd755ff7454e6e8b73a67f8c744b13986e24 \
+ --hash=sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a \
+ --hash=sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24 \
+ --hash=sha256:f22dec1690b584cea26fade98b2435c132c1b5f68e39f5a0b7627cd7ae31f1dc \
+ --hash=sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215 \
+ --hash=sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063 \
+ --hash=sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832 \
+ --hash=sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6 \
+ --hash=sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79 \
+ --hash=sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464
# via requests
-click==8.1.7 \
- --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \
- --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de
+click==8.3.3 \
+ --hash=sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2 \
+ --hash=sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613
# via
# -r requirements/common.in
# cookiecutter
-cookiecutter==2.5.0 \
- --hash=sha256:8aa2f12ed11bc05628651e9dc4353a10571dd9908aaaaeec959a2b9ea465a5d2 \
- --hash=sha256:e61e9034748e3f41b8bd2c11f00d030784b48711c4d5c42363c50989a65331ec
+cookiecutter==2.6.0 \
+ --hash=sha256:a54a8e37995e4ed963b3e82831072d1ad4b005af736bb17b99c2cbd9d41b6e2d \
+ --hash=sha256:db21f8169ea4f4fdc2408d48ca44859349de2647fbe494a9d6c3edfc0542c21c
# via -r requirements/common.in
-decorator==5.1.1 \
- --hash=sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330 \
- --hash=sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186
+decorator==5.2.1 \
+ --hash=sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360 \
+ --hash=sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a
# via validators
-idna==3.6 \
- --hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \
- --hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
+idna==3.13 \
+ --hash=sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242 \
+ --hash=sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3
# via requests
-jinja2==3.1.3 \
- --hash=sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa \
- --hash=sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90
+jinja2==3.1.6 \
+ --hash=sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d \
+ --hash=sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67
# via cookiecutter
-markdown-it-py==3.0.0 \
- --hash=sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1 \
- --hash=sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb
+markdown-it-py==4.0.0 \
+ --hash=sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147 \
+ --hash=sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3
# via rich
-markupsafe==2.1.3 \
- --hash=sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e \
- --hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \
- --hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431 \
- --hash=sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686 \
- --hash=sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c \
- --hash=sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559 \
- --hash=sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc \
- --hash=sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb \
- --hash=sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939 \
- --hash=sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c \
- --hash=sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0 \
- --hash=sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4 \
- --hash=sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9 \
- --hash=sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575 \
- --hash=sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba \
- --hash=sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d \
- --hash=sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd \
- --hash=sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3 \
- --hash=sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00 \
- --hash=sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155 \
- --hash=sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac \
- --hash=sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52 \
- --hash=sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f \
- --hash=sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8 \
- --hash=sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b \
- --hash=sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007 \
- --hash=sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24 \
- --hash=sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea \
- --hash=sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198 \
- --hash=sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0 \
- --hash=sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee \
- --hash=sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be \
- --hash=sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2 \
- --hash=sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1 \
- --hash=sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707 \
- --hash=sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6 \
- --hash=sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c \
- --hash=sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58 \
- --hash=sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823 \
- --hash=sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779 \
- --hash=sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636 \
- --hash=sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c \
- --hash=sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad \
- --hash=sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee \
- --hash=sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc \
- --hash=sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2 \
- --hash=sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48 \
- --hash=sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7 \
- --hash=sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e \
- --hash=sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b \
- --hash=sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa \
- --hash=sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5 \
- --hash=sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e \
- --hash=sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb \
- --hash=sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9 \
- --hash=sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57 \
- --hash=sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc \
- --hash=sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc \
- --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2 \
- --hash=sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11
+markupsafe==3.0.3 \
+ --hash=sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f \
+ --hash=sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a \
+ --hash=sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf \
+ --hash=sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19 \
+ --hash=sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf \
+ --hash=sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c \
+ --hash=sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175 \
+ --hash=sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219 \
+ --hash=sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb \
+ --hash=sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6 \
+ --hash=sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab \
+ --hash=sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26 \
+ --hash=sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1 \
+ --hash=sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce \
+ --hash=sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218 \
+ --hash=sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634 \
+ --hash=sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695 \
+ --hash=sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad \
+ --hash=sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73 \
+ --hash=sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c \
+ --hash=sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe \
+ --hash=sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa \
+ --hash=sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559 \
+ --hash=sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa \
+ --hash=sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37 \
+ --hash=sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758 \
+ --hash=sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f \
+ --hash=sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8 \
+ --hash=sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d \
+ --hash=sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c \
+ --hash=sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97 \
+ --hash=sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a \
+ --hash=sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19 \
+ --hash=sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9 \
+ --hash=sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9 \
+ --hash=sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc \
+ --hash=sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2 \
+ --hash=sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4 \
+ --hash=sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354 \
+ --hash=sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50 \
+ --hash=sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698 \
+ --hash=sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9 \
+ --hash=sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b \
+ --hash=sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc \
+ --hash=sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115 \
+ --hash=sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e \
+ --hash=sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485 \
+ --hash=sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f \
+ --hash=sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12 \
+ --hash=sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025 \
+ --hash=sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009 \
+ --hash=sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d \
+ --hash=sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b \
+ --hash=sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a \
+ --hash=sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5 \
+ --hash=sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f \
+ --hash=sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d \
+ --hash=sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1 \
+ --hash=sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287 \
+ --hash=sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6 \
+ --hash=sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f \
+ --hash=sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581 \
+ --hash=sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed \
+ --hash=sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b \
+ --hash=sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c \
+ --hash=sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026 \
+ --hash=sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8 \
+ --hash=sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676 \
+ --hash=sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6 \
+ --hash=sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e \
+ --hash=sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d \
+ --hash=sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d \
+ --hash=sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01 \
+ --hash=sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7 \
+ --hash=sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419 \
+ --hash=sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795 \
+ --hash=sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1 \
+ --hash=sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5 \
+ --hash=sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d \
+ --hash=sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42 \
+ --hash=sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe \
+ --hash=sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda \
+ --hash=sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e \
+ --hash=sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737 \
+ --hash=sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523 \
+ --hash=sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591 \
+ --hash=sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc \
+ --hash=sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a \
+ --hash=sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50
# via jinja2
mdurl==0.1.2 \
--hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \
--hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba
# via markdown-it-py
-pydantic==1.10.13 \
- --hash=sha256:1740068fd8e2ef6eb27a20e5651df000978edce6da6803c2bef0bc74540f9548 \
- --hash=sha256:210ce042e8f6f7c01168b2d84d4c9eb2b009fe7bf572c2266e235edf14bacd80 \
- --hash=sha256:32c8b48dcd3b2ac4e78b0ba4af3a2c2eb6048cb75202f0ea7b34feb740efc340 \
- --hash=sha256:3ecea2b9d80e5333303eeb77e180b90e95eea8f765d08c3d278cd56b00345d01 \
- --hash=sha256:4b03e42ec20286f052490423682016fd80fda830d8e4119f8ab13ec7464c0132 \
- --hash=sha256:4c5370a7edaac06daee3af1c8b1192e305bc102abcbf2a92374b5bc793818599 \
- --hash=sha256:56e3ff861c3b9c6857579de282ce8baabf443f42ffba355bf070770ed63e11e1 \
- --hash=sha256:5a1f9f747851338933942db7af7b6ee8268568ef2ed86c4185c6ef4402e80ba8 \
- --hash=sha256:5e08865bc6464df8c7d61439ef4439829e3ab62ab1669cddea8dd00cd74b9ffe \
- --hash=sha256:61d9dce220447fb74f45e73d7ff3b530e25db30192ad8d425166d43c5deb6df0 \
- --hash=sha256:654db58ae399fe6434e55325a2c3e959836bd17a6f6a0b6ca8107ea0571d2e17 \
- --hash=sha256:678bcf5591b63cc917100dc50ab6caebe597ac67e8c9ccb75e698f66038ea953 \
- --hash=sha256:6cf25c1a65c27923a17b3da28a0bdb99f62ee04230c931d83e888012851f4e7f \
- --hash=sha256:75ac15385a3534d887a99c713aa3da88a30fbd6204a5cd0dc4dab3d770b9bd2f \
- --hash=sha256:75b297827b59bc229cac1a23a2f7a4ac0031068e5be0ce385be1462e7e17a35d \
- --hash=sha256:7d6f6e7305244bddb4414ba7094ce910560c907bdfa3501e9db1a7fd7eaea127 \
- --hash=sha256:84bafe2e60b5e78bc64a2941b4c071a4b7404c5c907f5f5a99b0139781e69ed8 \
- --hash=sha256:854223752ba81e3abf663d685f105c64150873cc6f5d0c01d3e3220bcff7d36f \
- --hash=sha256:8ae5dd6b721459bfa30805f4c25880e0dd78fc5b5879f9f7a692196ddcb5a580 \
- --hash=sha256:8ef467901d7a41fa0ca6db9ae3ec0021e3f657ce2c208e98cd511f3161c762c6 \
- --hash=sha256:968ac42970f57b8344ee08837b62f6ee6f53c33f603547a55571c954a4225691 \
- --hash=sha256:97cce3ae7341f7620a0ba5ef6cf043975cd9d2b81f3aa5f4ea37928269bc1b87 \
- --hash=sha256:9849f031cf8a2f0a928fe885e5a04b08006d6d41876b8bbd2fc68a18f9f2e3fd \
- --hash=sha256:9f00790179497767aae6bcdc36355792c79e7bbb20b145ff449700eb076c5f96 \
- --hash=sha256:b87326822e71bd5f313e7d3bfdc77ac3247035ac10b0c0618bd99dcf95b1e687 \
- --hash=sha256:b97c1fac8c49be29486df85968682b0afa77e1b809aff74b83081cc115e52f33 \
- --hash=sha256:bc0898c12f8e9c97f6cd44c0ed70d55749eaf783716896960b4ecce2edfd2d69 \
- --hash=sha256:c553f6a156deb868ba38a23cf0df886c63492e9257f60a79c0fd8e7173537653 \
- --hash=sha256:c636925f38b8db208e09d344c7aa4f29a86bb9947495dd6b6d376ad10334fb78 \
- --hash=sha256:c958d053453a1c4b1c2062b05cd42d9d5c8eb67537b8d5a7e3c3032943ecd261 \
- --hash=sha256:d3a3c792a58e1622667a2837512099eac62490cdfd63bd407993aaf200a4cf1f \
- --hash=sha256:e31647d85a2013d926ce60b84f9dd5300d44535a9941fe825dc349ae1f760df9 \
- --hash=sha256:e70ca129d2053fb8b728ee7d1af8e553a928d7e301a311094b8a0501adc8763d \
- --hash=sha256:efff03cc7a4f29d9009d1c96ceb1e7a70a65cfe86e89d34e4a5f2ab1e5693737 \
- --hash=sha256:f59ef915cac80275245824e9d771ee939133be38215555e9dc90c6cb148aaeb5 \
- --hash=sha256:f8e81fc5fb17dae698f52bdd1c4f18b6ca674d7068242b2aff075f588301bbb0
+pydantic==1.10.26 \
+ --hash=sha256:0141f4bafe5eda539d98c9755128a9ea933654c6ca4306b5059fc87a01a38573 \
+ --hash=sha256:0d8f6087bf697dec3bf7ffcd7fe8362674f16519f3151789f33cbe8f1d19fc15 \
+ --hash=sha256:0e4451951a9a93bf9a90576f3e25240b47ee49ab5236adccb8eff6ac943adf0f \
+ --hash=sha256:116233e53889bcc536f617e38c1b8337d7fa9c280f0fd7a4045947515a785637 \
+ --hash=sha256:15b13b9f8ba8867095769e1156e0d7fbafa1f65b898dd40fd1c02e34430973cb \
+ --hash=sha256:1a4e3062b71ab1d5df339ba12c48f9ed5817c5de6cb92a961dd5c64bb32e7b96 \
+ --hash=sha256:1ae7913bb40a96c87e3d3f6fe4e918ef53bf181583de4e71824360a9b11aef1c \
+ --hash=sha256:2c1b0b914be31671000ca25cf7ea17fcaaa68cfeadf6924529c5c5aa24b7ab1f \
+ --hash=sha256:36d9e46b588aaeb1dcd2409fa4c467fe0b331f3cc9f227b03a7a00643704e962 \
+ --hash=sha256:4482b299874dabb88a6c3759e3d85c6557c407c3b586891f7d808d8a38b66b9c \
+ --hash=sha256:465ad8edb29b15c10b779b16431fe8e77c380098badf6db367b7a1d3e572cf53 \
+ --hash=sha256:468d5b9cacfcaadc76ed0a4645354ab6f263ec01a63fb6d05630ea1df6ae453f \
+ --hash=sha256:502b9d30d18a2dfaf81b7302f6ba0e5853474b1c96212449eb4db912cb604b7d \
+ --hash=sha256:6b40730cc81d53d515dc0b8bb5c9b43fadb9bed46de4a3c03bd95e8571616dba \
+ --hash=sha256:71cde228bc0600cf8619f0ee62db050d1880dcc477eba0e90b23011b4ee0f314 \
+ --hash=sha256:80e6be6272839c8a7641d26ad569ab77772809dd78f91d0068dc0fc97f071945 \
+ --hash=sha256:8154c13f58d4de5d3a856bb6c909c7370f41fb876a5952a503af6b975265f4ba \
+ --hash=sha256:81ce3c8616d12a7be31b4aadfd3434f78f6b44b75adbfaec2fe1ad4f7f999b8c \
+ --hash=sha256:8be08b5cfe88e58198722861c7aab737c978423c3a27300911767931e5311d0d \
+ --hash=sha256:8c6aa39b494c5af092e690127c283d84f363ac36017106a9e66cb33a22ac412e \
+ --hash=sha256:9858ed44c6bea5f29ffe95308db9e62060791c877766c67dd5f55d072c8612b5 \
+ --hash=sha256:a943ce8e00ad708ed06a1d9df5b4fd28f5635a003b82a4908ece6f24c0b18464 \
+ --hash=sha256:ac1089f723e2106ebde434377d31239e00870a7563245072968e5af5cc4d33df \
+ --hash=sha256:ad7025ca324ae263d4313998e25078dcaec5f9ed0392c06dedb57e053cc8086b \
+ --hash=sha256:bc5c91a3b3106caf07ac6735ec6efad8ba37b860b9eb569923386debe65039ad \
+ --hash=sha256:c3bbb9c0eecdf599e4db9b372fa9cc55be12e80a0d9c6d307950a39050cb0e37 \
+ --hash=sha256:c3cfdd361addb6eb64ccd26ac356ad6514cee06a61ab26b27e16b5ed53108f77 \
+ --hash=sha256:c43ad70dc3ce7787543d563792426a16fd7895e14be4b194b5665e36459dd917 \
+ --hash=sha256:cc2e3fe7bc4993626ef6b6fa855defafa1d6f8996aa1caef2deb83c5ac4d043a \
+ --hash=sha256:ce3293b86ca9f4125df02ff0a70be91bc7946522467cbd98e7f1493f340616ba \
+ --hash=sha256:d95a76cf503f0f72ed7812a91de948440b2bf564269975738a4751e4fadeb572 \
+ --hash=sha256:dcb5a7318fb43189fde6af6f21ac7149c4bcbcfffc54bc87b5becddc46084847 \
+ --hash=sha256:dd40a99c358419910c85e6f5d22f9c56684c25b5e7abc40879b3b4a52f34ae90 \
+ --hash=sha256:dde599e0388e04778480d57f49355c9cc7916de818bf674de5d5429f2feebfb6 \
+ --hash=sha256:eb664305ffca8a9766a8629303bb596607d77eae35bb5f32ff9245984881b638 \
+ --hash=sha256:f7ae36fa0ecef8d39884120f212e16c06bb096a38f523421278e2f39c1784546 \
+ --hash=sha256:f8af0507bf6118b054a9765fb2e402f18a8b70c964f420d95b525eb711122d62
# via -r requirements/common.in
-pygments==2.17.2 \
- --hash=sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c \
- --hash=sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367
+pygments==2.20.0 \
+ --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \
+ --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176
# via rich
-python-dateutil==2.8.2 \
- --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \
- --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9
+python-dateutil==2.9.0.post0 \
+ --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \
+ --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427
# via arrow
-python-slugify==8.0.1 \
- --hash=sha256:70ca6ea68fe63ecc8fa4fcf00ae651fc8a5d02d93dcd12ae6d4fc7ca46c4d395 \
- --hash=sha256:ce0d46ddb668b3be82f4ed5e503dbc33dd815d83e2eb6824211310d3fb172a27
+python-slugify==8.0.4 \
+ --hash=sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8 \
+ --hash=sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856
# via
# -r requirements/common.in
# cookiecutter
-pyyaml==6.0.1 \
- --hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \
- --hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \
- --hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \
- --hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \
- --hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \
- --hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \
- --hash=sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595 \
- --hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \
- --hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \
- --hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \
- --hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \
- --hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \
- --hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \
- --hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \
- --hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \
- --hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \
- --hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \
- --hash=sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6 \
- --hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \
- --hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \
- --hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \
- --hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \
- --hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \
- --hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \
- --hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \
- --hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \
- --hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \
- --hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \
- --hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \
- --hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \
- --hash=sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd \
- --hash=sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3 \
- --hash=sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0 \
- --hash=sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515 \
- --hash=sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c \
- --hash=sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c \
- --hash=sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924 \
- --hash=sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34 \
- --hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \
- --hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \
- --hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \
- --hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \
- --hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \
- --hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \
- --hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \
- --hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \
- --hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \
- --hash=sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585 \
- --hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \
- --hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f
+pyyaml==6.0.3 \
+ --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \
+ --hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \
+ --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \
+ --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \
+ --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \
+ --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \
+ --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \
+ --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \
+ --hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \
+ --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \
+ --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \
+ --hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \
+ --hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \
+ --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \
+ --hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \
+ --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \
+ --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \
+ --hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \
+ --hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \
+ --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \
+ --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \
+ --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \
+ --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \
+ --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \
+ --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \
+ --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \
+ --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \
+ --hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \
+ --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \
+ --hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \
+ --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \
+ --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \
+ --hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \
+ --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \
+ --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \
+ --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \
+ --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \
+ --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \
+ --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \
+ --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \
+ --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \
+ --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \
+ --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \
+ --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \
+ --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \
+ --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \
+ --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \
+ --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \
+ --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \
+ --hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \
+ --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \
+ --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \
+ --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \
+ --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \
+ --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \
+ --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \
+ --hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \
+ --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \
+ --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \
+ --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \
+ --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \
+ --hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \
+ --hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \
+ --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \
+ --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \
+ --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \
+ --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \
+ --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \
+ --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \
+ --hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \
+ --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \
+ --hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \
+ --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0
# via cookiecutter
-requests==2.31.0 \
- --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
- --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
+requests==2.33.1 \
+ --hash=sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517 \
+ --hash=sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a
# via cookiecutter
-rich==13.7.0 \
- --hash=sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa \
- --hash=sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235
+rich==15.0.0 \
+ --hash=sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb \
+ --hash=sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36
# via cookiecutter
-six==1.16.0 \
- --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
- --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
+six==1.17.0 \
+ --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \
+ --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81
# via python-dateutil
text-unidecode==1.3 \
--hash=sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8 \
--hash=sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93
# via python-slugify
-types-python-dateutil==2.8.19.20240106 \
- --hash=sha256:1f8db221c3b98e6ca02ea83a58371b22c374f42ae5bbdf186db9c9a76581459f \
- --hash=sha256:efbbdc54590d0f16152fa103c9879c7d4a00e82078f6e2cf01769042165acaa2
- # via arrow
-typing-extensions==4.9.0 \
- --hash=sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783 \
- --hash=sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd
+typing-extensions==4.15.0 \
+ --hash=sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466 \
+ --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548
# via pydantic
-urllib3==2.1.0 \
- --hash=sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3 \
- --hash=sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54
+tzdata==2026.2 \
+ --hash=sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10 \
+ --hash=sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7
+ # via arrow
+urllib3==2.6.3 \
+ --hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \
+ --hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4
# via requests
+uv==0.9.30 \
+ --hash=sha256:03ebd4b22769e0a8d825fa09d038e31cbab5d3d48edf755971cb0cec7920ab95 \
+ --hash=sha256:0b3b3700ecf64a09a07fd04d10ec35f0973ec15595d38bbafaa0318252f7e31f \
+ --hash=sha256:180e8070b8c438b9a3fb3fde8a37b365f85c3c06e17090f555dc68fdebd73333 \
+ --hash=sha256:2faf84e1f3b6fc347a34c07f1291d11acf000b0dd537a61d541020f22b17ccd9 \
+ --hash=sha256:33e50f208e01a0c20b3c5f87d453356a5cbcfd68f19e47a28b274cd45618881c \
+ --hash=sha256:4125a9aa2a751e1589728f6365cfe204d1be41499148ead44b6180b7df576f27 \
+ --hash=sha256:4366dd740ac9ad3ec50a58868a955b032493bb7d7e6ed368289e6ced8bbc70f3 \
+ --hash=sha256:5e7a6fa7a3549ce893cf91fe4b06629e3e594fc1dca0a6050aba2ea08722e964 \
+ --hash=sha256:62d7e408d41e392b55ffa4cf9b07f7bbd8b04e0929258a42e19716c221ac0590 \
+ --hash=sha256:6dc65c24f5b9cdc78300fa6631368d3106e260bbffa66fb1e831a318374da2df \
+ --hash=sha256:6ec38ae29aa83a37c6e50331707eac8ecc90cf2b356d60ea6382a94de14973be \
+ --hash=sha256:74e94c65d578657db94a753d41763d0364e5468ec0d368fb9ac8ddab0fb6e21f \
+ --hash=sha256:777ecd117cf1d8d6bb07de8c9b7f6c5f3e802415b926cf059d3423699732eb8c \
+ --hash=sha256:7fde83a5b5ea027315223c33c30a1ab2f2186910b933d091a1b7652da879e230 \
+ --hash=sha256:88a2190810684830a1ba4bb1cf8fb06b0308988a1589559404259d295260891c \
+ --hash=sha256:93049ba3c41fa2cc38b467cb78ef61b2ddedca34b6be924a5481d7750c8111c6 \
+ --hash=sha256:a5467dddae1cd5f4e093f433c0f0d9a0df679b92696273485ec91bbb5a8620e6 \
+ --hash=sha256:b176fc2937937dd81820445cb7e7e2e3cd1009a003c512f55fa0ae10064c8a38 \
+ --hash=sha256:f295604fee71224ebe2685a0f1f4ff7a45c77211a60bd57133a4a02056d7c775
+ # via -r requirements/common.in
validators==0.20.0 \
--hash=sha256:24148ce4e64100a2d5e267233e23e7afeb55316b47d30faae7eb6e7292bc226a
# via -r requirements/common.in
diff --git a/start.py b/start.py
index 5e215bd..d8933ba 100755
--- a/start.py
+++ b/start.py
@@ -75,10 +75,10 @@
@click.option("--postgres-persistent-volume-capacity")
@click.option("--postgres-persistent-volume-claim-capacity")
@click.option("--postgres-persistent-volume-host-path")
-@click.option("--use-redis/--no-redis", is_flag=True, default=None)
-@click.option("--redis-image")
-@click.option("--digitalocean-redis-cluster-region")
-@click.option("--digitalocean-redis-cluster-node-size")
+@click.option("--use-valkey/--no-valkey", is_flag=True, default=None)
+@click.option("--valkey-image")
+@click.option("--digitalocean-valkey-cluster-region")
+@click.option("--digitalocean-valkey-cluster-node-size")
@click.option("--sentry-org")
@click.option("--sentry-url")
@click.option("--sentry-auth-token")
diff --git a/tests/test_collector.py b/tests/test_collector.py
index 59cbc31..35dd82e 100755
--- a/tests/test_collector.py
+++ b/tests/test_collector.py
@@ -178,21 +178,21 @@ def test_frontend_service_invalid(self):
self.assertEqual(collector.frontend_type, "nextjs")
self.assertEqual(collector.frontend_service_slug, "frontend")
- def test_use_redis_from_input(self):
- """Test setting the `use_redis` flag from user input."""
+ def test_use_valkey_from_input(self):
+ """Test setting the `use_valkey` flag from user input."""
collector = Collector()
- self.assertIsNone(collector.use_redis)
+ self.assertIsNone(collector.use_valkey)
with mock_input("y"):
- collector.set_use_redis()
- self.assertTrue(collector.use_redis)
+ collector.set_use_valkey()
+ self.assertTrue(collector.use_valkey)
- def test_use_redis_from_options(self):
- """Test setting the `use_redis` flag from user input."""
- collector = Collector(use_redis=False)
- self.assertFalse(collector.use_redis)
+ def test_use_valkey_from_options(self):
+ """Test setting the `use_valkey` flag from user input."""
+ collector = Collector(use_valkey=False)
+ self.assertFalse(collector.use_valkey)
with mock.patch("bootstrap.collector.click.confirm") as mocked_confirm:
- collector.set_use_redis()
- self.assertFalse(collector.use_redis)
+ collector.set_use_valkey()
+ self.assertFalse(collector.use_valkey)
mocked_confirm.assert_not_called()
def test_terraform_backend_from_default(self):
@@ -490,7 +490,7 @@ def test_deployment_aws_only(self):
def test_digitalocean_default(self):
"""Test setting the Digitalocean options from default."""
- collector = Collector(use_redis=False)
+ collector = Collector(use_valkey=False)
collector.set_digitalocean_token = mock.MagicMock()
self.assertFalse(collector._digitalocean_enabled)
self.assertIsNone(collector.digitalocean_domain_create)
@@ -511,7 +511,7 @@ def test_digitalocean_default(self):
def test_digitalocean_input(self):
"""Test setting the Digitalocean options from input."""
- collector = Collector(use_redis=True)
+ collector = Collector(use_valkey=True)
collector.set_digitalocean_token = mock.MagicMock()
self.assertFalse(collector._digitalocean_enabled)
self.assertIsNone(collector.digitalocean_domain_create)
@@ -526,7 +526,7 @@ def test_digitalocean_input(self):
"database-cluster-region",
"db-size",
"",
- "redis-cluster-size",
+ "valkey-cluster-size",
):
collector.set_digitalocean()
self.assertEqual(collector._digitalocean_enabled, True)
@@ -539,17 +539,17 @@ def test_digitalocean_input(self):
collector.digitalocean_database_cluster_region, "database-cluster-region"
)
self.assertEqual(collector.digitalocean_database_cluster_node_size, "db-size")
- self.assertEqual(collector.digitalocean_redis_cluster_region, "fra1")
+ self.assertEqual(collector.digitalocean_valkey_cluster_region, "fra1")
self.assertEqual(
- collector.digitalocean_redis_cluster_node_size, "redis-cluster-size"
+ collector.digitalocean_valkey_cluster_node_size, "valkey-cluster-size"
)
def test_digitalocean_options(self):
"""Test setting the Digitalocean options from options."""
collector = Collector(
- use_redis=True,
- digitalocean_redis_cluster_region="fra2",
- digitalocean_redis_cluster_node_size="size",
+ use_valkey=True,
+ digitalocean_valkey_cluster_region="fra2",
+ digitalocean_valkey_cluster_node_size="size",
digitalocean_domain_create=False,
digitalocean_dns_records_create=True,
digitalocean_k8s_cluster_region="k8s-cluster-region-from-options",
@@ -975,7 +975,7 @@ def test_get_runner(self):
project_url_prod="https://www.test.com",
project_url_stage="https://stage.test.com",
terraform_backend="terraform-cloud",
- use_redis=False,
+ use_valkey=False,
)
collector._service_dir = Path(".")
runner = collector.get_runner()
@@ -989,7 +989,7 @@ def test_get_runner(self):
self.assertEqual(runner.project_url_prod, "https://www.test.com")
self.assertEqual(runner.project_url_stage, "https://stage.test.com")
self.assertEqual(runner.terraform_backend, "terraform-cloud")
- self.assertEqual(runner.use_redis, False)
+ self.assertEqual(runner.use_valkey, False)
def test_collect(self):
"""Test collect options."""
@@ -1000,7 +1000,7 @@ def test_collect(self):
collector.set_service_dir = mock.MagicMock()
collector.set_backend_service = mock.MagicMock()
collector.set_frontend_service = mock.MagicMock()
- collector.set_use_redis = mock.MagicMock()
+ collector.set_use_valkey = mock.MagicMock()
collector.set_terraform = mock.MagicMock()
collector.set_vault = mock.MagicMock()
collector.set_clusters = mock.MagicMock()
@@ -1019,7 +1019,7 @@ def test_collect(self):
collector.set_service_dir.assert_called_once()
collector.set_backend_service.assert_called_once()
collector.set_frontend_service.assert_called_once()
- collector.set_use_redis.assert_called_once()
+ collector.set_use_valkey.assert_called_once()
collector.set_terraform.assert_called_once()
collector.set_vault.assert_called_once()
collector.set_clusters.assert_called_once()
diff --git a/terraform/gitlab/main.tf b/tofu/gitlab/main.tf
similarity index 100%
rename from terraform/gitlab/main.tf
rename to tofu/gitlab/main.tf
diff --git a/terraform/gitlab/outputs.tf b/tofu/gitlab/outputs.tf
similarity index 100%
rename from terraform/gitlab/outputs.tf
rename to tofu/gitlab/outputs.tf
diff --git a/terraform/gitlab/variables.tf b/tofu/gitlab/variables.tf
similarity index 100%
rename from terraform/gitlab/variables.tf
rename to tofu/gitlab/variables.tf
diff --git a/terraform/terraform-cloud/main.tf b/tofu/terraform-cloud/main.tf
similarity index 100%
rename from terraform/terraform-cloud/main.tf
rename to tofu/terraform-cloud/main.tf
diff --git a/terraform/terraform-cloud/variables.tf b/tofu/terraform-cloud/variables.tf
similarity index 100%
rename from terraform/terraform-cloud/variables.tf
rename to tofu/terraform-cloud/variables.tf
diff --git a/terraform/vault/main.tf b/tofu/vault/main.tf
similarity index 98%
rename from terraform/vault/main.tf
rename to tofu/vault/main.tf
index 4e135e5..8b4a116 100644
--- a/terraform/vault/main.tf
+++ b/tofu/vault/main.tf
@@ -5,7 +5,7 @@ terraform {
required_providers {
vault = {
source = "hashicorp/vault"
- version = "~> 4.2.0"
+ version = "~> 5.0"
}
}
}
diff --git a/terraform/vault/variables.tf b/tofu/vault/variables.tf
similarity index 92%
rename from terraform/vault/variables.tf
rename to tofu/vault/variables.tf
index 2e50720..e70fc5f 100644
--- a/terraform/vault/variables.tf
+++ b/tofu/vault/variables.tf
@@ -9,8 +9,8 @@ variable "project_slug" {
}
variable "secrets" {
- description = "The secrets."
- type = map(map(any))
+ description = "The secrets keyed by Vault KV path."
+ type = any
default = {}
}
diff --git a/{{cookiecutter.project_dirname}}/.env_template b/{{cookiecutter.project_dirname}}/.env_template
index ca25700..4d1f0cb 100644
--- a/{{cookiecutter.project_dirname}}/.env_template
+++ b/{{cookiecutter.project_dirname}}/.env_template
@@ -1,31 +1,32 @@
{% if cookiecutter.backend_type != 'none' %}# backend
CACHE_URL=locmem://
-DATABASE_URL=postgres://postgres:postgres@postgres:5432/{{ cookiecutter.project_slug }}
DJANGO_ADMINS=admin,errors@example.org
-DJANGO_ALLOWED_HOSTS=localhost,backend
+DJANGO_ALLOWED_HOSTS=localhost,{{ cookiecutter.backend_service_slug }}
DJANGO_CONFIGURATION=Local
DJANGO_DEBUG=True
DJANGO_DEFAULT_FROM_EMAIL=info@example.org
DJANGO_SECRET_KEY=__SECRETKEY__
DJANGO_SERVER_EMAIL=server@example.org
-DJANGO_SESSION_COOKIE_DOMAIN=localhost
DJANGO_SUPERUSER_EMAIL=info@example.org
DJANGO_SUPERUSER_PASSWORD={{ cookiecutter.project_slug }}
DJANGO_SUPERUSER_USERNAME=${USER}
-EMAIL_URL=console:///
+EMAIL_URL=smtp://@mail:1025
PYTHONBREAKPOINT=IPython.core.debugger.set_trace
PYTHONDEVMODE=""
PYTHONTRACEMALLOC=0
-{% endif %}{% if cookiecutter.frontend_type != 'none' %}# frontend
-# FRONTEND_DOCKER_FILE=docker/remote.Dockerfile
-# INTERNAL_BACKEND_URL=http://{{ cookiecutter.backend_service_slug }}:{{ cookiecutter.backend_service_port }}
-# PROJECT_URL=https://localhost:8443
-# REACT_ENVIRONMENT=Production
-{% endif %}{% if cookiecutter.use_pact == "true" %}# pact
-PACT_BROKER_URL=https://user:password@broker.pact.com
-PACT_PROVIDER_NAME={{ cookiecutter.project_slug }}-{{ cookiecutter.backend_service_slug }}
-{% endif %}# {{ cookiecutter.service_slug }}
-COMPOSE_FILE=docker-compose.yaml
+# database
+POSTGRES_DB={{ cookiecutter.project_slug }}
+POSTGRES_HOST=db
+POSTGRES_PASSWORD=postgres
+POSTGRES_PORT=5432
+POSTGRES_USER=postgres
+{% endif %}# platform
+{% if cookiecutter.backend_type != 'none' %}BACKEND_DOMAIN={{ cookiecutter.backend_service_slug }}.localhost
+{% endif %}{% if cookiecutter.frontend_type != 'none' %}FRONTEND_DOMAIN={{ cookiecutter.frontend_service_slug }}.localhost
+{% endif %}MAIL_DOMAIN=mail.localhost
+COMPOSE_FILE=compose.yaml:compose.local.yaml
+COMPOSE_PROJECT_NAME={{ cookiecutter.project_slug }}
+EXTERNAL_PORT=8443
# user
# GROUP_ID=1000
# USER_ID=1000
diff --git a/{{cookiecutter.project_dirname}}/.gitignore b/{{cookiecutter.project_dirname}}/.gitignore
index f973267..4572552 100644
--- a/{{cookiecutter.project_dirname}}/.gitignore
+++ b/{{cookiecutter.project_dirname}}/.gitignore
@@ -24,9 +24,6 @@ kubeconfig.yaml
{{ cookiecutter.backend_service_slug }}/{% endif %}{% if cookiecutter.frontend_type != 'none' %}
{{ cookiecutter.frontend_service_slug }}/{% endif %}
-# Locals
-*.local.yaml
-
# Backup
*.sql
*.dump
diff --git a/{{cookiecutter.project_dirname}}/.gitlab-ci.yml b/{{cookiecutter.project_dirname}}/.gitlab-ci.yml
index bf139d4..ced8715 100644
--- a/{{cookiecutter.project_dirname}}/.gitlab-ci.yml
+++ b/{{cookiecutter.project_dirname}}/.gitlab-ci.yml
@@ -34,15 +34,15 @@ before_script:
.core:
parallel:
matrix:
- - CORE_PROVIDER: ["aws", "digitalocean"]
- id_tokens:
+ - CORE_PROVIDER: [{% for p in cookiecutter.resources.core_providers %}"{{ p }}"{% if not loop.last %}, {% endif %}{% endfor %}]
+ id_tokens:{% if "aws" in cookiecutter.resources.core_providers %}
GITLAB_OIDC_TOKEN:
- aud: https://gitlab.com
+ aud: https://gitlab.com{% endif %}
VAULT_ID_TOKEN:
aud: ${VAULT_ADDR}
image: {{ cookiecutter.minos_platform_image }}
- variables:
- AWS_WEB_IDENTITY_TOKEN_FILE: "/tmp/web_identity_token"
+ variables:{% if "aws" in cookiecutter.resources.core_providers %}
+ AWS_WEB_IDENTITY_TOKEN_FILE: "/tmp/web_identity_token"{% endif %}
GITLAB_TOFU_ROOT_DIR: "${CI_PROJECT_DIR}/tofu/core/${CORE_PROVIDER}"
TF_WORKSPACE: "${PROJECT_SLUG}_platform_${CLUSTER}_core_${CORE_PROVIDER}"
TOFU_ROOT_PATH: "tofu/core/${CORE_PROVIDER}"
diff --git a/{{cookiecutter.project_dirname}}/Makefile b/{{cookiecutter.project_dirname}}/Makefile
index 4e4b58c..afe21f0 100644
--- a/{{cookiecutter.project_dirname}}/Makefile
+++ b/{{cookiecutter.project_dirname}}/Makefile
@@ -24,7 +24,7 @@ pull: ## Pull develop on all services
s =
.PHONY: rebuild
rebuild: ## Rebuild container
- docker-compose stop $(s) && docker-compose rm -f $(s) && docker-compose up -d --build $(s)
+ docker compose stop $(s) && docker compose rm -f $(s) && docker compose up -d --build $(s)
help:
@echo "[Help] Makefile list commands:"
diff --git a/{{cookiecutter.project_dirname}}/compose.local.yaml b/{{cookiecutter.project_dirname}}/compose.local.yaml
new file mode 100644
index 0000000..e1a4d41
--- /dev/null
+++ b/{{cookiecutter.project_dirname}}/compose.local.yaml
@@ -0,0 +1,32 @@
+services:
+{%- if cookiecutter.backend_type != "none" %}
+ {{ cookiecutter.backend_service_slug }}:
+ build:
+ context: ./{{ cookiecutter.backend_service_slug }}
+ command: sleep infinity
+ entrypoint: ""
+ healthcheck:
+ test: ["CMD", "true"]
+ ports:
+ - "${BACKEND_LOCAL_PORT:-8005}:{{ cookiecutter.backend_service_port }}"
+ volumes:
+ - ./{{ cookiecutter.backend_service_slug }}:/app
+ - ~/.claude:/home/${USER}/.claude
+ - ~/.ssh:/home/${USER}/.ssh-localhost:ro
+{%- endif %}
+{%- if cookiecutter.frontend_type != "none" %}
+
+ {{ cookiecutter.frontend_service_slug }}:
+ build:
+ context: ./{{ cookiecutter.frontend_service_slug }}
+ command: sleep infinity
+ entrypoint: ""
+ healthcheck:
+ test: ["CMD", "true"]
+ ports:
+ - "${FRONTEND_LOCAL_PORT:-3005}:{{ cookiecutter.frontend_service_port }}"
+ volumes:
+ - ./{{ cookiecutter.frontend_service_slug }}:/app
+ - ~/.claude:/home/${USER}/.claude
+ - ~/.ssh:/home/${USER}/.ssh-localhost:ro
+{%- endif %}
diff --git a/{{cookiecutter.project_dirname}}/compose.yaml b/{{cookiecutter.project_dirname}}/compose.yaml
new file mode 100644
index 0000000..15245a3
--- /dev/null
+++ b/{{cookiecutter.project_dirname}}/compose.yaml
@@ -0,0 +1,130 @@
+services:
+{%- if cookiecutter.backend_type != "none" %}
+ {{ cookiecutter.backend_service_slug }}:
+ build:
+ args:
+ USER: ${USER:-appuser}
+ context: ./{{ cookiecutter.backend_service_slug }}
+ target: ${BACKEND_BUILD_TARGET:-local}
+ depends_on:
+ db:
+ condition: service_healthy
+ environment:
+ - CACHE_URL
+ - DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:${POSTGRES_PORT}/${POSTGRES_DB}
+ - DJANGO_ADMINS
+ - DJANGO_ALLOWED_HOSTS
+ - DJANGO_CONFIGURATION
+ - DJANGO_DEBUG
+ - DJANGO_DEFAULT_FROM_EMAIL
+ - DJANGO_SECRET_KEY
+ - DJANGO_SERVER_EMAIL
+ - EMAIL_URL
+ - INTERNAL_SERVICE_PORT={{ cookiecutter.backend_service_port }}
+ - PYTHONBREAKPOINT
+ - PYTHONDEVMODE
+ - PYTHONTRACEMALLOC
+ healthcheck:
+ interval: 3s
+ retries: 30
+ test: curl --fail --get http://{{ cookiecutter.backend_service_slug }}:{{ cookiecutter.backend_service_port }}/health/ || exit 1
+ timeout: 3s
+ restart: unless-stopped
+ user: ${USER:-appuser}
+ volumes:
+ - ./{{ cookiecutter.backend_service_slug }}:/app
+ - ./proxy/tls/cert.pem:/certs/cert.pem:ro
+{%- endif %}
+{%- if cookiecutter.frontend_type != "none" %}
+
+ {{ cookiecutter.frontend_service_slug }}:
+ build:
+ args:
+ GROUP_ID: ${GROUP_ID:-1000}
+ USER_ID: ${USER_ID:-1000}
+ USER: ${USER:-appuser}
+ context: ./{{ cookiecutter.frontend_service_slug }}
+ dockerfile: ${FRONTEND_DOCKER_FILE:-docker/local.Dockerfile}
+ {%- if cookiecutter.backend_type != "none" %}
+ depends_on:
+ {{ cookiecutter.backend_service_slug }}:
+ condition: service_healthy
+ {%- endif %}
+ environment:
+ - INTERNAL_SERVICE_PORT={{ cookiecutter.frontend_service_port }}
+ - NEXT_PUBLIC_PROJECT_URL=https://{{ cookiecutter.frontend_service_slug }}.localhost:${EXTERNAL_PORT:-8443}
+ {%- if cookiecutter.backend_type != "none" %}
+ - INTERNAL_BACKEND_URL=http://{{ cookiecutter.backend_service_slug }}:{{ cookiecutter.backend_service_port }}
+ - NEXT_PUBLIC_BACKEND_URL=https://{{ cookiecutter.backend_service_slug }}.localhost:${EXTERNAL_PORT:-8443}
+ {%- endif %}
+ extra_hosts:
+ {%- if cookiecutter.backend_type != "none" %}
+ - "${BACKEND_DOMAIN:-{{ cookiecutter.backend_service_slug }}.localhost}:host-gateway"
+ {%- endif %}
+ healthcheck:
+ test: wget -O- -q http://{{ cookiecutter.frontend_service_slug }}:{{ cookiecutter.frontend_service_port }}/api/healthz || exit 1
+ retries: 5
+ start_period: 5s
+ user: ${USER:-appuser}
+ volumes:
+ - ./{{ cookiecutter.frontend_service_slug }}:/app
+ - ./proxy/tls/cert.pem:/certs/cert.pem:ro
+ - /app/node_modules
+{%- endif %}
+{%- if cookiecutter.backend_type == "django" %}
+
+ db:
+ environment:
+ - POSTGRES_DB
+ - POSTGRES_INITDB_ARGS=--no-sync
+ - POSTGRES_PASSWORD
+ - POSTGRES_USER
+ healthcheck:
+ interval: 3s
+ retries: 30
+ test: ["CMD", "pg_isready", "-U", "postgres"]
+ timeout: 3s
+ image: postgres:17-bookworm
+ volumes:
+ - db_data:/var/lib/postgresql/data
+{%- endif %}
+
+ mail:
+ environment:
+ - MP_SMTP_AUTH_ALLOW_INSECURE=true
+ - MP_SMTP_AUTH_ACCEPT_ANY=true
+ healthcheck:
+ interval: 3s
+ retries: 30
+ test: ["CMD", "/mailpit", "readyz"]
+ timeout: 3s
+ image: axllent/mailpit:latest
+ volumes:
+ - mail_data:/data
+
+ proxy:
+ command:
+ - --configFile=/traefik/config/static.yaml
+ environment:
+ {%- if cookiecutter.backend_type != "none" %}
+ - BACKEND_DOMAIN=${BACKEND_DOMAIN:-{{ cookiecutter.backend_service_slug }}.localhost}
+ {%- endif %}
+ {%- if cookiecutter.frontend_type != "none" %}
+ - FRONTEND_DOMAIN=${FRONTEND_DOMAIN:-{{ cookiecutter.frontend_service_slug }}.localhost}
+ {%- endif %}
+ - MAIL_DOMAIN=${MAIL_DOMAIN:-mail.localhost}
+ healthcheck:
+ retries: 5
+ start_period: 5s
+ test: ["CMD", "traefik", "healthcheck", "--ping"]
+ image: traefik:v3.5
+ ports:
+ - "${EXTERNAL_PORT:-8443}:8443"
+ volumes:
+ - ./proxy/:/traefik/:ro
+
+volumes:
+{%- if cookiecutter.backend_type == "django" %}
+ db_data: {}
+{%- endif %}
+ mail_data: {}
diff --git a/{{cookiecutter.project_dirname}}/proxy/config/dynamic.yaml b/{{cookiecutter.project_dirname}}/proxy/config/dynamic.yaml
new file mode 100644
index 0000000..390bd89
--- /dev/null
+++ b/{{cookiecutter.project_dirname}}/proxy/config/dynamic.yaml
@@ -0,0 +1,37 @@
+http:
+ routers:
+{%- if cookiecutter.backend_type != "none" %}
+ {{ cookiecutter.backend_service_slug }}:
+ rule: Host(`{{ '{{' }}env "BACKEND_DOMAIN"{{ '}}' }}`)
+ service: {{ cookiecutter.backend_service_slug }}
+{%- endif %}
+{%- if cookiecutter.frontend_type != "none" %}
+ {{ cookiecutter.frontend_service_slug }}:
+ rule: Host(`{{ '{{' }}env "FRONTEND_DOMAIN"{{ '}}' }}`)
+ service: {{ cookiecutter.frontend_service_slug }}
+{%- endif %}
+ mailbox:
+ rule: Host(`{{ '{{' }}env "MAIL_DOMAIN"{{ '}}' }}`)
+ service: mail
+ services:
+{%- if cookiecutter.backend_type != "none" %}
+ {{ cookiecutter.backend_service_slug }}:
+ loadBalancer:
+ servers:
+ - url: "http://{{ cookiecutter.backend_service_slug }}:{{ cookiecutter.backend_service_port }}/"
+{%- endif %}
+{%- if cookiecutter.frontend_type != "none" %}
+ {{ cookiecutter.frontend_service_slug }}:
+ loadBalancer:
+ servers:
+ - url: "http://{{ cookiecutter.frontend_service_slug }}:{{ cookiecutter.frontend_service_port }}/"
+{%- endif %}
+ mail:
+ loadBalancer:
+ servers:
+ - url: "http://mail:8025/"
+
+tls:
+ certificates:
+ - certFile: /traefik/tls/cert.pem
+ keyFile: /traefik/tls/key.pem
diff --git a/{{cookiecutter.project_dirname}}/proxy/config/static.yaml b/{{cookiecutter.project_dirname}}/proxy/config/static.yaml
new file mode 100644
index 0000000..f5eae5b
--- /dev/null
+++ b/{{cookiecutter.project_dirname}}/proxy/config/static.yaml
@@ -0,0 +1,18 @@
+api:
+ debug: true
+
+entryPoints:
+ websecure:
+ address: ":8443"
+ http:
+ tls:
+ options:
+
+log:
+ level: INFO
+
+ping: true
+
+providers:
+ file:
+ filename: /traefik/config/dynamic.yaml
diff --git a/{{cookiecutter.project_dirname}}/proxy/tls/cert.pem b/{{cookiecutter.project_dirname}}/proxy/tls/cert.pem
new file mode 100644
index 0000000..7ee612d
--- /dev/null
+++ b/{{cookiecutter.project_dirname}}/proxy/tls/cert.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICNzCCAd2gAwIBAgIUIRZV5mB7wTOB7HSQCt7qp5/DDp0wCgYIKoZIzj0EAwIw
+FDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI2MDMyNjIzNTQ1M1oXDTI3MDQyODIz
+NTQ1M1owFDESMBAGA1UEAwwJbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0D
+AQcDQgAEMf5RdsTbPnOEHMsaE9hR223fWfb/8yhzKa+JDs/2r+6TiHZF+m2D7HHK
+/2Ek4zalUPWaxPg8pEEICbjvSKO0y6OCAQswggEHMB0GA1UdDgQWBBQy4sa1S0Ip
+K3mXYe3iuIqznT0+HzAfBgNVHSMEGDAWgBQy4sa1S0IpK3mXYe3iuIqznT0+HzCB
+kQYDVR0RBIGJMIGGgg9hZG1pbi5sb2NhbGhvc3SCDWFwaS5sb2NhbGhvc3SCDmF1
+dGgubG9jYWxob3N0gg1hd3MubG9jYWxob3N0ghRiYWNrb2ZmaWNlLmxvY2FsaG9z
+dIIObWFpbC5sb2NhbGhvc3SCEHNlYXJjaC5sb2NhbGhvc3SCDXd3dy5sb2NhbGhv
+c3QwDwYDVR0TAQH/BAUwAwEB/zATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8E
+BAMCBaAwCgYIKoZIzj0EAwIDSAAwRQIhANPaQ+gyytWQ84db8DpiMTseW7c9Su/D
+FYON+az3Y0qBAiBQx3dRJ3EQcB8kkdAU87pymnFEQ7tVvDtZ3BlP15Yw1A==
+-----END CERTIFICATE-----
diff --git a/{{cookiecutter.project_dirname}}/proxy/tls/key.pem b/{{cookiecutter.project_dirname}}/proxy/tls/key.pem
new file mode 100644
index 0000000..fc4d043
--- /dev/null
+++ b/{{cookiecutter.project_dirname}}/proxy/tls/key.pem
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgxQ2F4JEEsrt0Qi9z
+07brP08fSewqW5SmmS8pd051pdyhRANCAAQx/lF2xNs+c4QcyxoT2FHbbd9Z9v/z
+KHMpr4kOz/av7pOIdkX6bYPsccr/YSTjNqVQ9ZrE+DykQQgJuO9Io7TL
+-----END PRIVATE KEY-----