Инициализация канона общих ролей с ролью eget
This commit is contained in:
@@ -0,0 +1,26 @@
|
|||||||
|
# ansible-roles
|
||||||
|
|
||||||
|
Канон общих Ansible-ролей для изолированных серверных репозиториев
|
||||||
|
(`pet-project-server`, `buckland`, …).
|
||||||
|
|
||||||
|
Это **рабочая директория-канон**, а не сервис. Серверные репозитории
|
||||||
|
держат закоммиченные копии ролей в своём `roles/` и синхронизируются с
|
||||||
|
этим каноном через invoke-задачи (rsync, git здесь не участвует):
|
||||||
|
|
||||||
|
- `inv roles-pull [-- <role>]` — канон → репозиторий;
|
||||||
|
- `inv roles-push [-- <role>]` — репозиторий → канон (с подтверждением
|
||||||
|
при расхождении);
|
||||||
|
- `inv roles-status` — сравнить подписанные роли с каноном.
|
||||||
|
|
||||||
|
Каждый репозиторий объявляет подписку в своём `tasks.py`
|
||||||
|
(`SYNCED_ROLES`). «Пином версии» служит сама закоммиченная копия в
|
||||||
|
репозитории: деплоится ровно то, что лежит в его git.
|
||||||
|
|
||||||
|
Git в этой директории — опциональный бэкап истории канона, он не стоит
|
||||||
|
в рабочей петле синхронизации.
|
||||||
|
|
||||||
|
Спецификация подхода: `pet-project-server/docs/drafts/shared-roles-sync.md`.
|
||||||
|
|
||||||
|
## Роли
|
||||||
|
|
||||||
|
- `eget` — установка утилиты [eget](https://github.com/zyedidia/eget).
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
# eget
|
||||||
|
|
||||||
|
Устанавливает утилиту [eget](https://github.com/zyedidia/eget) и через неё —
|
||||||
|
пиновые бинарники релизов с GitHub (restic, rclone, btop, zellij и т.п.).
|
||||||
|
|
||||||
|
Роль общая для всех серверных репозиториев; канон живёт в `ansible-roles`,
|
||||||
|
копии вендорятся через `inv roles-pull`.
|
||||||
|
|
||||||
|
## Что делает
|
||||||
|
|
||||||
|
1. **Bootstrap eget.** Если нужной версии `eget` нет — качает архив релиза с
|
||||||
|
GitHub напрямую (сам себя eget обновить не может — chicken-and-egg),
|
||||||
|
распаковывает во временную директорию, ставит бинарник и убирает мусор.
|
||||||
|
Идемпотентно: сверяет `eget --version` с `eget_version`.
|
||||||
|
2. **Установка пакетов.** Идёт по декларативному списку `eget_packages` и для
|
||||||
|
каждого вызывает `eget <repo> --tag <tag> --upgrade-only …`.
|
||||||
|
|
||||||
|
## Идемпотентность и статус изменений
|
||||||
|
|
||||||
|
`--upgrade-only` заставляет eget скачивать бинарник, только если релиз новее
|
||||||
|
установленного файла (сравнение по mtime). Когда обновление не нужно, eget
|
||||||
|
пишет в stderr `… requested release is not more recent than current version`
|
||||||
|
и выходит с кодом 0.
|
||||||
|
|
||||||
|
Роль **не** глушит вывод (`--quiet` убран намеренно) и по наличию этой
|
||||||
|
подстроки (`eget_no_upgrade_marker`) выставляет `changed_when`: реальная
|
||||||
|
установка → `changed`, пропуск → `ok`. Ненулевой код выхода eget (реальная
|
||||||
|
ошибка) роняет таск штатно.
|
||||||
|
|
||||||
|
> Ограничения `--upgrade-only` (сравнение по дате релиза vs mtime бинарника, а
|
||||||
|
> не по версии — поэтому это **не** строгий пиннинг тега):
|
||||||
|
>
|
||||||
|
> - Бамп тега на релиз, опубликованный раньше текущей установки, и любой
|
||||||
|
> **даунгрейд** eget сочтёт «не новее» и не поставит (роль покажет `ok`).
|
||||||
|
> На практике теги двигают только вперёд на свежие релизы; откат версии —
|
||||||
|
> вручную (удалить бинарник, затем прогнать роль).
|
||||||
|
> - Если имя бинарника не совпадает с последним сегментом `repo` (напр.
|
||||||
|
> `BurntSushi/ripgrep` → `rg`), eget не находит установленный файл, и роль
|
||||||
|
> будет **переустанавливать пакет каждый прогон** (рапортуя `changed`). Все
|
||||||
|
> текущие пакеты этим не задеты; для таких репозиториев ставь бинарник в
|
||||||
|
> отдельную директорию либо не полагайся на статус `changed`.
|
||||||
|
|
||||||
|
## Переменные
|
||||||
|
|
||||||
|
Полный список и значения по умолчанию — в `defaults/main.yml`.
|
||||||
|
|
||||||
|
| Переменная | По умолчанию | Назначение |
|
||||||
|
|------------|--------------|------------|
|
||||||
|
| `eget_version` | `1.3.4` | Версия самой утилиты eget. |
|
||||||
|
| `eget_install_path` | `/usr/bin/eget` | Куда ставить бинарник eget. |
|
||||||
|
| `eget_download_url` | ссылка на релиз | URL архива eget (собирается из версии). |
|
||||||
|
| `eget_download_checksum` | `""` | Необязательная проверка архива eget (формат `get_url.checksum`). |
|
||||||
|
| `eget_packages_to` | `/usr/local/bin` | Директория для бинарников пакетов по умолчанию. |
|
||||||
|
| `eget_packages` | `[]` | Декларативный список пакетов (см. ниже). |
|
||||||
|
|
||||||
|
### Схема элемента `eget_packages`
|
||||||
|
|
||||||
|
| Ключ | Обяз. | Назначение |
|
||||||
|
|------|:----:|------------|
|
||||||
|
| `repo` | да | `owner/repo` на GitHub, напр. `restic/restic`. |
|
||||||
|
| `tag` | да | Пиновка версии, напр. `v0.18.1`. |
|
||||||
|
| `to` | нет | Целевая директория; по умолчанию `eget_packages_to`. |
|
||||||
|
| `asset` | нет | Один фильтр ассета, напр. `zip` или `^musl` (`^` — анти-матч). |
|
||||||
|
| `assets` | нет | Список фильтров (каждый → свой `--asset`); приоритетнее `asset`. |
|
||||||
|
| `file` | нет | `--file` glob для выбора файла из архива. |
|
||||||
|
|
||||||
|
## Пример
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: "Install eget and pinned binaries"
|
||||||
|
hosts: all
|
||||||
|
tasks:
|
||||||
|
- name: "Install eget and tools"
|
||||||
|
ansible.builtin.import_role:
|
||||||
|
name: eget
|
||||||
|
vars:
|
||||||
|
eget_install_path: "/usr/local/bin/eget"
|
||||||
|
eget_packages_to: "/usr/local/bin"
|
||||||
|
eget_packages:
|
||||||
|
- {repo: "restic/restic", tag: "v0.18.1"}
|
||||||
|
- {repo: "rclone/rclone", tag: "v1.73.4", asset: "zip"}
|
||||||
|
- {repo: "bootandy/dust", tag: "v1.2.4", asset: "gnu"}
|
||||||
|
- {repo: "creativeprojects/resticprofile", tag: "v0.32.0", asset: "^no_self_update"}
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
# defaults file for eget
|
||||||
|
|
||||||
|
# --- Сам eget ---
|
||||||
|
# Версия утилиты eget и путь установки её бинарника.
|
||||||
|
eget_version: "1.3.4"
|
||||||
|
eget_install_path: "/usr/bin/eget"
|
||||||
|
eget_download_url: "https://github.com/zyedidia/eget/releases/download/v{{ eget_version }}/eget-{{ eget_version }}-linux_amd64.tar.gz" # yamllint disable-line rule:line-length
|
||||||
|
|
||||||
|
# Необязательная проверка целостности архива eget при bootstrap.
|
||||||
|
# Формат — как у аргумента checksum модуля get_url
|
||||||
|
# (напр. "sha256:<hex>"). Пусто — проверка пропускается.
|
||||||
|
eget_download_checksum: ""
|
||||||
|
|
||||||
|
# --- Пакеты, устанавливаемые через eget ---
|
||||||
|
# Директория по умолчанию для бинарников пакетов
|
||||||
|
# (переопределяется в eget_packages[*].to).
|
||||||
|
eget_packages_to: "/usr/local/bin"
|
||||||
|
|
||||||
|
# Декларативный список пакетов. Каждый элемент — объект:
|
||||||
|
# repo (обязательно) — "owner/repo" на GitHub, напр. "restic/restic".
|
||||||
|
# tag (обязательно) — пиновка версии, напр. "v0.18.1".
|
||||||
|
# to (опц.) — целевая директория; по умолчанию eget_packages_to.
|
||||||
|
# asset (опц.) — один фильтр ассета, напр. "zip" или "^musl" (^ = анти-матч).
|
||||||
|
# assets (опц.) — список фильтров (каждый добавляет свой --asset); имеет
|
||||||
|
# приоритет над asset, если заданы оба.
|
||||||
|
# file (опц.) — --file glob для выбора файла из архива.
|
||||||
|
# Пример:
|
||||||
|
# eget_packages:
|
||||||
|
# - {repo: "restic/restic", tag: "v0.18.1"}
|
||||||
|
# - {repo: "bootandy/dust", tag: "v1.2.4", asset: "gnu"}
|
||||||
|
eget_packages: []
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
galaxy_info:
|
||||||
|
role_name: eget
|
||||||
|
author: "Anton Vakhrushev"
|
||||||
|
description: "Установка утилиты eget и загрузка через неё пиновых бинарников с GitHub"
|
||||||
|
license: "MIT"
|
||||||
|
min_ansible_version: "2.14"
|
||||||
|
platforms:
|
||||||
|
- name: Ubuntu
|
||||||
|
versions:
|
||||||
|
- all
|
||||||
|
- name: Debian
|
||||||
|
versions:
|
||||||
|
- all
|
||||||
|
galaxy_tags:
|
||||||
|
- eget
|
||||||
|
- binaries
|
||||||
|
- github
|
||||||
|
- cli
|
||||||
|
|
||||||
|
dependencies: []
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
---
|
||||||
|
# Bootstrap самого eget: скачать релиз с GitHub, распаковать, поставить бинарник.
|
||||||
|
# Идемпотентность — по версии уже установленного eget; чужой eget нельзя обновить
|
||||||
|
# им самим (chicken-and-egg), поэтому качаем архив релиза напрямую.
|
||||||
|
|
||||||
|
- name: "Check installed eget version"
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: "{{ eget_install_path }} --version"
|
||||||
|
register: eget_version_check
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
check_mode: false # read-only проба: нужна и в --check, чтобы gate ниже был честным
|
||||||
|
|
||||||
|
- name: "Install eget from GitHub release"
|
||||||
|
when: >-
|
||||||
|
eget_version_check.rc != 0
|
||||||
|
or eget_version not in eget_version_check.stdout
|
||||||
|
block:
|
||||||
|
- name: "Download eget release archive"
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: "{{ eget_download_url }}"
|
||||||
|
dest: "{{ eget_download_dest }}"
|
||||||
|
checksum: "{{ eget_download_checksum or omit }}"
|
||||||
|
mode: "0600"
|
||||||
|
|
||||||
|
- name: "Extract eget release archive"
|
||||||
|
ansible.builtin.unarchive:
|
||||||
|
src: "{{ eget_download_dest }}"
|
||||||
|
dest: "/tmp"
|
||||||
|
remote_src: true
|
||||||
|
|
||||||
|
- name: "Install eget binary"
|
||||||
|
ansible.builtin.copy:
|
||||||
|
src: '{{ (eget_unarchive_dest, "eget") | path_join }}'
|
||||||
|
dest: "{{ eget_install_path }}"
|
||||||
|
mode: "0755"
|
||||||
|
remote_src: true
|
||||||
|
|
||||||
|
always:
|
||||||
|
- name: "Remove eget bootstrap artifacts"
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: absent
|
||||||
|
loop:
|
||||||
|
- "{{ eget_download_dest }}"
|
||||||
|
- "{{ eget_unarchive_dest }}"
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
# tasks file for eget
|
||||||
|
|
||||||
|
- name: "Validate eget_packages entries"
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- item.repo is defined and item.repo is string and item.repo | length > 0
|
||||||
|
- item.tag is defined and item.tag is string and item.tag | length > 0
|
||||||
|
- item.to is not defined or item.to is string
|
||||||
|
- item.file is not defined or item.file is string
|
||||||
|
- item.asset is not defined or item.asset is string
|
||||||
|
- item.assets is not defined or (item.assets is sequence and item.assets is not string)
|
||||||
|
fail_msg: >-
|
||||||
|
Некорректный элемент eget_packages (repo/tag — непустые строки;
|
||||||
|
asset/file/to — строки; assets — список строк): {{ item }}
|
||||||
|
quiet: true
|
||||||
|
loop: "{{ eget_packages }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.repo | default('?') }}"
|
||||||
|
|
||||||
|
- name: "Ensure eget is installed"
|
||||||
|
ansible.builtin.import_tasks: install_eget.yml
|
||||||
|
|
||||||
|
- name: "Install pinned binaries with eget"
|
||||||
|
ansible.builtin.command:
|
||||||
|
argv: "{{ _eget_argv }}"
|
||||||
|
vars:
|
||||||
|
_eget_to: "{{ item.to | default(eget_packages_to) }}"
|
||||||
|
_eget_assets: >-
|
||||||
|
{{ item.assets | default([item.asset] if item.asset is defined else []) }}
|
||||||
|
_eget_argv: >-
|
||||||
|
{{ [eget_install_path, item.repo, '--upgrade-only',
|
||||||
|
'--to=' ~ _eget_to, '--tag=' ~ item.tag]
|
||||||
|
+ (['--file=' ~ item.file] if item.file is defined else [])
|
||||||
|
+ (['--asset'] | product(_eget_assets) | map('join', '=') | list) }}
|
||||||
|
register: eget_install
|
||||||
|
changed_when: eget_no_upgrade_marker not in eget_install.stderr
|
||||||
|
loop: "{{ eget_packages }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.repo }} {{ item.tag }}"
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
# vars file for eget — внутренние значения роли, не предназначены для переопределения.
|
||||||
|
|
||||||
|
# Куда скачивается и распаковывается архив eget при bootstrap.
|
||||||
|
eget_download_dest: '/tmp/{{ eget_download_url | split("/") | last }}'
|
||||||
|
eget_unarchive_dest: '{{ eget_download_dest | regex_replace("(\.tar\.gz|\.zip)$", "") }}'
|
||||||
|
|
||||||
|
# Подстрока из текста ошибки ErrNoUpgrade в eget: печатается в stderr, когда
|
||||||
|
# установленный бинарник уже не старше запрошенного релиза (--upgrade-only).
|
||||||
|
# По ней роль отличает «ничего не делали» от реальной установки.
|
||||||
|
# См. find.go: errors.New("requested release is not more recent than current version").
|
||||||
|
eget_no_upgrade_marker: "not more recent than current version"
|
||||||
Reference in New Issue
Block a user