hotpocket/tasks.py
Tomek Wójcik 80fbbcddf3 BTHLABS-0000: bump-version task
Enough with manual version bumps :D
Co-authored-by: Tomek Wójcik <labs@tomekwojcik.pl>
Co-committed-by: Tomek Wójcik <labs@tomekwojcik.pl>
2025-09-17 20:27:20 +02:00

286 lines
6.8 KiB
Python

# -*- coding: utf-8 -*-
from __future__ import annotations
import base64
import datetime
from invoke import Context, task
from hotpocket_workspace_tools import get_workspace_mode, WorkspaceMode
from hotpocket_workspace_tools.tasks import (
bump_version as tools_bump_version_task,
)
class DockerBuildContext:
def __init__(self,
/,
target='deployment',
version=None,
build='01',
machine=None,
):
self._target = target
self._version = version
self._build = build
self._image_arch = None
self._node_arch = None
def __enter__(self, *args, **kwargs):
return self
def __exit__(self, *args, **kwargs):
pass
@property
def target(self) -> str:
return self._target
@property
def version(self) -> str:
return self._version or datetime.date.today().strftime('%Y%m%d')
@property
def build(self) -> str:
return self._build.zfill(2)
@property
def image_arch(self) -> str:
return self._image_arch
@property
def node_arch(self) -> str:
return self._node_arch
@property
def tag(self) -> str:
return '{target}-{version}-{build}'.format(
target=self.target,
version=self.version,
build=self.build,
)
WORKSPACE_MODE = get_workspace_mode()
ALL_SERVICES = ['apple', 'backend', 'extension', 'packages']
VERSIONED_SERVICES = ['apple', 'backend', 'extension']
def _run_in_service(ctx: Context, service, command, **kwargs):
match WORKSPACE_MODE:
case WorkspaceMode.DOCKER:
return ctx.run(
f'docker compose run --rm -it {service}-management {command}',
)
case WorkspaceMode.METAL:
with ctx.cd(f'services/{service}'):
ctx.run(f'direnv exec . {command}')
def _get_version(ctx: Context, service):
with ctx.cd(f'services/{service}'):
run_result = ctx.run('poetry version -s', hide='out')
return run_result.stdout.strip()
def _get_head_sha(ctx):
run_result = ctx.run('git rev-parse HEAD', hide='out')
return run_result.stdout.strip()
@task
def clean(ctx: Context, service):
_run_in_service(ctx, service, 'inv clean')
@task
def test(ctx: Context, service):
_run_in_service(ctx, service, 'inv test')
@task
def lint(ctx: Context, service):
_run_in_service(ctx, service, 'inv lint')
@task
def typecheck(ctx: Context, service):
_run_in_service(ctx, service, 'inv typecheck')
@task
def shell(ctx: Context, service):
assert WORKSPACE_MODE == WorkspaceMode.DOCKER, (
'Just `cd services/{service}` ;)'
)
_run_in_service(ctx, service, '/bin/bash')
@task
def django_shell(ctx: Context, service):
_run_in_service(ctx, service, 'inv django-shell')
@task
def png_to_data_url(ctx: Context, png_path):
with open(png_path, 'rb') as png_f:
data = png_f.read()
encoded_data = base64.b64encode(data)
print(f'data:image/png;base64,{encoded_data.decode("utf-8")}')
@task
def build(ctx: Context,
service,
context=None,
builder=None,
push=False,
target='deployment',
version=None,
build='01',
image_tag=None,
machine=None,
platform=None,
):
image_tag = image_tag or '{target}.{{short_sha}}'.format(target=target)
short_sha = _get_head_sha(ctx)[:8]
image_tag = image_tag.format(
short_sha=short_sha,
)
with DockerBuildContext(target, version, build, machine) as docker_build_ctx: # noqa: E501
commandline = [
'docker',
(
f'--context {context}'
if context is not None
else ''
),
'buildx build',
(
f'--builder {builder}'
if builder is not None
else ''
),
(
'--push'
if push is True
else ''
),
(
f'--platform {platform}'
if platform is not None
else ''
),
f'-t docker-hosted.nexus.bthlabs.pl/hotpocket/{service}:{docker_build_ctx.tag}', # noqa: E501
f'-f services/{service}/Dockerfile',
f'--build-arg IMAGE_ID={image_tag}',
f'--target {docker_build_ctx.target}',
'services/',
]
ctx.run(' '.join(commandline))
@task
def publish(ctx: Context,
service,
context=None,
target='deployment',
version=None,
build='01',
machine=None,
):
with DockerBuildContext(target, version, build, machine) as docker_build_ctx: # noqa: E501
commandline = [
'docker',
(
f'--context {context}'
if context is not None
else ''
),
'push',
f'docker-hosted.nexus.bthlabs.pl/hotpocket/{service}:{docker_build_ctx.tag}', # noqa: E501
]
ctx.run(' '.join(commandline))
@task
def ci(ctx: Context, service):
_run_in_service(ctx, service, 'inv ci')
@task
def setup(ctx: Context, service=None):
services_to_setup = []
services_to_setup = [*ALL_SERVICES]
if service is not None:
services_to_setup = [service]
for service_to_setup in services_to_setup:
_run_in_service(ctx, service_to_setup, 'inv setup')
@task
def install(ctx: Context, service=None):
services_to_setup = []
services_to_setup = [*ALL_SERVICES]
if service is not None:
services_to_setup = [service]
for service_to_setup in services_to_setup:
_run_in_service(ctx, service_to_setup, 'poetry install')
@task
def lock(ctx: Context, service):
_run_in_service(ctx, service, 'poetry lock --no-update')
@task
def start_cloud(ctx):
ctx.run('docker compose -f docker-compose-cloud.yaml up')
@task
def start_web(ctx: Context, service):
_run_in_service(ctx, service, 'inv start-web')
@task
def start_celery_worker(ctx: Context, service):
_run_in_service(ctx, service, 'inv start-worker')
@task
def start_celery_beat(ctx: Context, service):
_run_in_service(ctx, service, 'inv start-beat')
@task
def start_app(ctx: Context, service, app):
_run_in_service(ctx, service, f'inv start-{app}')
@task
def bump_version(ctx: Context, next_version: str, build: str | None = None):
assert build is not None, '`--build` is required here'
tools_bump_version_task(ctx, next_version, build=build)
for service_to_setup in VERSIONED_SERVICES:
_run_in_service(
ctx,
service_to_setup,
f'inv bump-version {next_version} --build {build}',
)