From b793b7806bb817dd77c61609543fa87a5a029319 Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Sun, 22 Feb 2026 18:41:29 +0300 Subject: [PATCH] Migrate from task to invoke --- inv | 3 ++ tasks.py | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100755 inv create mode 100644 tasks.py diff --git a/inv b/inv new file mode 100755 index 0000000..b94ce8a --- /dev/null +++ b/inv @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +exec uv run inv "$@" diff --git a/tasks.py b/tasks.py new file mode 100644 index 0000000..3ff730f --- /dev/null +++ b/tasks.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +"""Invoke tasks — замена Taskfile.yml""" + +import os +import subprocess + +from invoke.context import Context +from invoke.tasks import task + +HOSTS_FILE = "production.yml" +AUTHELIA_DOCKER = "docker run --rm -v $PWD:/data authelia/authelia:4.39.4 authelia" + + +def _yq(query: str) -> str: + result = subprocess.run( + ["yq", query, HOSTS_FILE], capture_output=True, text=True, check=True + ) + return result.stdout.strip() + + +def _remote_user() -> str: + return _yq(".ungrouped.hosts.server.ansible_user") + + +def _remote_host() -> str: + return _yq(".ungrouped.hosts.server.ansible_host") + + +@task +def install_roles(ctx: Context) -> None: + """Установить ansible-galaxy roles""" + ctx.run("uv run ansible-galaxy role install --role-file requirements.yml --force") + + +@task +def pl(ctx: Context, args: str = "") -> None: + """Запустить плейбуки (передать файл плейбука через --args)""" + ctx.run(f"uv run ansible-playbook -i production.yml --diff {args}") + + +@task +def ssh(ctx: Context) -> None: + """SSH на удалённый сервер""" + ctx.run(f"ssh {_remote_user()}@{_remote_host()}", pty=True) + + +@task +def btop(ctx: Context) -> None: + """Запустить btop на удалённом сервере""" + ctx.run(f"ssh {_remote_user()}@{_remote_host()} -t btop", pty=True) + + +@task +def encrypt(ctx: Context, args: str = "") -> None: + """Зашифровать файлы через ansible-vault""" + ctx.run(f"uv run ansible-vault encrypt {args}") + + +@task +def decrypt(ctx: Context, args: str = "") -> None: + """Расшифровать файлы через ansible-vault""" + ctx.run(f"uv run ansible-vault decrypt {args}") + + +@task +def authelia_cli(ctx: Context, args: str = "") -> None: + """Запустить authelia CLI в docker""" + ctx.run(f"{AUTHELIA_DOCKER} {args}") + + +@task +def authelia_validate_config(ctx: Context) -> None: + """Отрендерить конфиг authelia из шаблона и проверить его""" + dest = "temp/configuration.yml" + try: + ctx.run( + "uv run ansible localhost" + " --module-name template" + f' --args "src=files/authelia/configuration.template.yml dest={dest}"' + " --extra-vars @vars/secrets.yml" + " --extra-vars @files/authelia/secrets.yml" + ) + ctx.run(f"{AUTHELIA_DOCKER} validate-config --config /data/{dest}") + finally: + ctx.run(f"rm -f {dest}", warn=True) + + +@task +def authelia_gen_random_string(ctx: Context, length: int = 10) -> None: + """Сгенерировать случайную alphanumeric-строку""" + ctx.run(f"{AUTHELIA_DOCKER} crypto rand --length {length} --charset alphanumeric") + + +@task +def authelia_gen_secret_and_hash(ctx: Context, length: int = 72) -> None: + """Сгенерировать случайный секрет и его pbkdf2-sha512 хэш""" + ctx.run( + f"{AUTHELIA_DOCKER} crypto hash generate pbkdf2" + f" --variant sha512 --random --random.length {length} --random.charset rfc3986" + ) + + +@task +def format_py_files(ctx: Context) -> None: + """Отформатировать Python-файлы через Black в docker""" + uid = os.getuid() + gid = os.getgid() + ctx.run( + f"docker run --rm -u {uid}:{gid} -v $PWD:/app -w /app" + " pyfound/black:latest_release black ." + )