Invoke: задачи синхронизации общих ролей (roles-pull/push/status)
This commit is contained in:
@@ -115,6 +115,92 @@ def edit_encrypted(ctx: Context, path: str) -> None:
|
|||||||
ctx.run(f"uv run ansible-vault edit {path}", pty=True)
|
ctx.run(f"uv run ansible-vault edit {path}", pty=True)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Синхронизация общих ролей с каноном ansible-roles ---
|
||||||
|
# Блок идентичен во всех репозиториях; различается только SYNCED_ROLES.
|
||||||
|
# Путь к канону — сосед по файловой системе (../ansible-roles/roles),
|
||||||
|
# поэтому не зависит от текущей директории и одинаков везде.
|
||||||
|
|
||||||
|
SHARED_ROLES_DIR = os.path.normpath(
|
||||||
|
os.path.join(
|
||||||
|
os.path.dirname(os.path.abspath(__file__)), "..", "ansible-roles", "roles"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
SYNCED_ROLES = ["eget"]
|
||||||
|
|
||||||
|
|
||||||
|
def _require_canon() -> None:
|
||||||
|
if not os.path.isdir(SHARED_ROLES_DIR):
|
||||||
|
raise Exit(f"Канон не найден: {SHARED_ROLES_DIR}", code=1)
|
||||||
|
|
||||||
|
|
||||||
|
def _roles_to_sync() -> list[str]:
|
||||||
|
"""Роли для синхронизации: из '-- <role> ...' или все из SYNCED_ROLES."""
|
||||||
|
requested = _rest_args()
|
||||||
|
if not requested:
|
||||||
|
return SYNCED_ROLES
|
||||||
|
unknown = [r for r in requested if r not in SYNCED_ROLES]
|
||||||
|
if unknown:
|
||||||
|
raise Exit(f"Роли не в подписке SYNCED_ROLES: {', '.join(unknown)}", code=1)
|
||||||
|
return requested
|
||||||
|
|
||||||
|
|
||||||
|
@task
|
||||||
|
def roles_status(ctx: Context) -> None:
|
||||||
|
"""Сравнить подписанные роли с каноном ansible-roles"""
|
||||||
|
_require_canon()
|
||||||
|
drift = False
|
||||||
|
for role in SYNCED_ROLES:
|
||||||
|
canon = os.path.join(SHARED_ROLES_DIR, role)
|
||||||
|
if not os.path.isdir(canon):
|
||||||
|
print(f"{role}: missing in shared")
|
||||||
|
drift = True
|
||||||
|
elif subprocess.run(["diff", "-rq", f"roles/{role}", canon]).returncode != 0:
|
||||||
|
print(f"{role}: differs")
|
||||||
|
drift = True
|
||||||
|
else:
|
||||||
|
print(f"{role}: ok")
|
||||||
|
if drift:
|
||||||
|
raise Exit("Есть расхождения с каноном", code=1)
|
||||||
|
|
||||||
|
|
||||||
|
@task
|
||||||
|
def roles_pull(ctx: Context) -> None:
|
||||||
|
"""Канон → репозиторий: inv roles-pull [-- <role> ...]"""
|
||||||
|
_require_canon()
|
||||||
|
for role in _roles_to_sync():
|
||||||
|
src = os.path.join(SHARED_ROLES_DIR, role)
|
||||||
|
if not os.path.isdir(src):
|
||||||
|
raise Exit(f"Роль '{role}' отсутствует в каноне: {src}", code=1)
|
||||||
|
os.makedirs(f"roles/{role}", exist_ok=True)
|
||||||
|
ctx.run(f"rsync -a -i --delete {src}/ roles/{role}/")
|
||||||
|
|
||||||
|
|
||||||
|
@task
|
||||||
|
def roles_push(ctx: Context) -> None:
|
||||||
|
"""Репозиторий → канон, с подтверждением при расхождении: inv roles-push [-- <role> ...]"""
|
||||||
|
_require_canon()
|
||||||
|
for role in _roles_to_sync():
|
||||||
|
local = f"roles/{role}"
|
||||||
|
canon = os.path.join(SHARED_ROLES_DIR, role)
|
||||||
|
if not os.path.isdir(local):
|
||||||
|
raise Exit(f"Локальной роли нет: {local}", code=1)
|
||||||
|
differs = (
|
||||||
|
os.path.isdir(canon)
|
||||||
|
and subprocess.run(["diff", "-rq", local, canon]).returncode != 0
|
||||||
|
)
|
||||||
|
if differs:
|
||||||
|
# Защита от затирания более свежего канона устаревшей копией.
|
||||||
|
subprocess.run(["diff", "-r", canon, local])
|
||||||
|
answer = input(
|
||||||
|
f"Канон '{role}' отличается. Перезаписать его копией из этого репо? [y/N] "
|
||||||
|
)
|
||||||
|
if answer.strip().lower() != "y":
|
||||||
|
print(f"{role}: пропущено")
|
||||||
|
continue
|
||||||
|
os.makedirs(canon, exist_ok=True)
|
||||||
|
ctx.run(f"rsync -a -i --delete {local}/ {canon}/")
|
||||||
|
|
||||||
|
|
||||||
# --- Специфичные для этого репозитория задачи ---
|
# --- Специфичные для этого репозитория задачи ---
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user