BTHLABS-63: Production deployment workflow

This commit is contained in:
2025-11-06 20:34:44 +00:00
parent d8bbe57b17
commit e800d0c16c
42 changed files with 786 additions and 97 deletions

View File

@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import annotations
version = '25.10.21'
version = '25.11.06.b0'

View File

@@ -8,6 +8,7 @@ from django.core.management import BaseCommand
import django.db
from hotpocket_backend.apps.accounts.models import Account
from hotpocket_backend.apps.core.conf import settings
LOGGER = logging.getLogger(__name__)
@@ -17,12 +18,12 @@ class Command(BaseCommand):
def add_arguments(self, parser: ArgumentParser):
parser.add_argument(
'username',
help='Username for the Account',
'-u', '--username', default=None,
help='Override username for the Account',
)
parser.add_argument(
'password',
help='Password for the Account',
'-p', '--password', default=None,
help='Override Password for the Account',
)
parser.add_argument(
'-d', '--dry-run', action='store_true', default=False,
@@ -31,10 +32,22 @@ class Command(BaseCommand):
def handle(self, *args, **options):
LOGGER.debug('args=`%s` options=`%s`', args, options)
username = options.get('username')
password = options.get('password')
dry_run = options.get('dry_run', False)
username = options.get('username') or settings.SECRETS.INITIAL_ACCOUNT.username
if not username:
LOGGER.info('Not creating initial Account: empty `username`')
return
password = options.get('password') or settings.SECRETS.INITIAL_ACCOUNT.password
assert password, 'Unable to proceed: empty `password`'
LOGGER.debug(
'Creating initial Account: username=`%s` password=`%s`',
username,
password,
)
with django.db.transaction.atomic():
current_account = Account.objects.filter(username=username).first()
if current_account is not None:

View File

@@ -3,7 +3,13 @@ from __future__ import annotations
import json
from keep_it_secret import AbstractField, LiteralField, Secrets, SecretsField
from keep_it_secret import (
AbstractField,
EnvField,
LiteralField,
Secrets,
SecretsField,
)
class DatabaseSecrets(Secrets):
@@ -84,6 +90,19 @@ class CelerySecrets(Secrets):
result_backend: str = AbstractField.new()
class InitialAccountSecrets(Secrets):
username: str = EnvField.new(
'HOTPOCKET_BACKEND_INITIAL_ACCOUNT_USERNAME',
default=None,
required=False,
)
password: str = EnvField.new(
'HOTPOCKET_BACKEND_INITIAL_ACCOUNT_PASSWORD',
default=None,
required=False,
)
class BaseSecrets(Secrets):
SECRET_KEY: str = AbstractField.new()
@@ -91,3 +110,4 @@ class BaseSecrets(Secrets):
CELERY: CelerySecrets = SecretsField.new(CelerySecrets)
OIDC: OIDCSecrets = SecretsField.new(OIDCSecrets)
INITIAL_ACCOUNT: InitialAccountSecrets = SecretsField.new(InitialAccountSecrets)

View File

@@ -17,6 +17,9 @@ MIDDLEWARE = [
'whitenoise.middleware.WhiteNoiseMiddleware',
]
SESSION_COOKIE_SAMESITE = 'Lax'
SESSION_COOKIE_SECURE = False
STORAGES['staticfiles'] = { # noqa: F405
'BACKEND': 'whitenoise.storage.CompressedManifestStaticFilesStorage',
}

View File

@@ -115,6 +115,8 @@ STATIC_URL = 'static/'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
LOGGING_LEVEL = os.environ.get('HOTPOCKET_BACKEND_LOGGING_LEVEL', 'INFO')
LOG_FORMAT = '%(asctime)s %(levelname)s [%(name)s] [%(request_id)s] (%(funcName)s:%(lineno)s) %(message)s'
LOGGING = {
'version': 1,
@@ -149,17 +151,17 @@ LOGGING = {
'loggers': {
'hotpocket_backend': {
'handlers': ['hotpocket'],
'level': 'INFO',
'level': LOGGING_LEVEL,
'propagate': False,
},
'hotpocket_common': {
'handlers': ['hotpocket'],
'level': 'INFO',
'level': LOGGING_LEVEL,
'propagate': False,
},
'hotpocket_soa': {
'handlers': ['hotpocket'],
'level': 'INFO',
'level': LOGGING_LEVEL,
'propagate': False,
},
'django': {