Реєстр пакетів
Реєстр — це звичайний репозиторій GitHub. Кожна тека верхнього рівня — це
пакет, і в ній лежить один ketch.toml, що його описує:
ketch-registry/
├── README.md ← не пакет: немає ketch.toml
├── fd/
│ └── ketch.toml
├── jq/
│ └── ketch.toml
└── ripgrep/
└── ketch.toml
Назва теки і є назвою пакета — саме з нею зіставляється ketch install <name>.
Немає індексного файлу, який треба тримати в злагоді з умістом, а
тека без ketch.toml просто не є пакетом, тому в репозиторії
поруч із пакетами можуть лежати README, ліцензія та конфігурація CI.
Файл пакета
# ripgrep/ketch.toml
source = "github:BurntSushi/ripgrep"
description = "Recursively search directories for a regex pattern"
homepage = "https://github.com/BurntSushi/ripgrep"
bin = [{ name = "rg" }]
provides = ["rg"]
source — єдине обов'язкове поле. Решта — ті самі поля, що їх приймає
користувацький маніфест у ~/.ketch/manifests/: MANIFESTS.md містить
повну схему, а crates/ketch-core/src/builtin.toml — робочий приклад кожного поля.
name можна вказати, але вона має збігатися з назвою теки — пакет, який
розходиться зі своєю текою, був би недоступний під назвою, яку оголошує тека,
тому ketch відхиляє його, а не індексує тихо двічі.
Що перевіряє ketch
Запис реєстру — це код, написаний кимось іншим, який зрештою створює файли на диску незнайомої людини, тому його перевіряють, перш ніж йому довіряти:
| Відхиляється | Чому |
|---|---|
невідомий ключ (binary = … замість bin = …) | ключ з одруківкою, який мовчки ігнорується, встановлює не те й ніде не скаржиться |
джерело local: | запис реєстру має називати реліз, який може завантажити будь-хто, а не шлях на диску однієї машини |
name, що розходиться з текою | пакет був би недоступний під назвою, яку оголошує його тека |
name або bin.name, непридатні як назва файлу | name стає каталогом у сховищі, а bin.name — посиланням у ~/.ketch/bin; ../../.zshrc — не бінарник |
запис bin.path або extra_paths, що містить .. | шляхи відраховуються від розпакованого вмісту й мають лишатися всередині нього |
запис bin без name і без path | він не описує нічого, на що можна поставити посилання |
запис provides із пробільними символами | його ніхто не зможе набрати |
strip_prefix більше за 8 | кожен рівень — це лістинг каталогу вмісту, і жоден реальний архів не вкладає обгортки так глибоко |
Тека, що не пройшла перевірку, на машині, де реєстр уже є, потрапляє у звіт і пропускається,
тож один поганий запис ніколи не ламає решту копії.
ketch update також попереджає, коли два пакети претендують на одну назву: кожна тека
коректна сама по собі, але розв'язуватиметься лише одна з них. Клієнти діють
за принципом best-effort щодо вже опублікованого реєстру: вони попереджають і пропускають, але
не відкидають усе дерево.
До злиття ті самі проблеми фатальні. ketch registry validate перетворює
помилки розбору, помилки валідації та конфлікти назв чи аліасів на провалений
запуск, щоб вони не могли потрапити до реєстру.
Manifest::validate застосовується й до ~/.ketch/manifests/*.toml.
Записи реєстру мають додатково пройти правила про local: і назву теки з
таблиці вище — правила, яких користувацькі маніфести ніколи не бачать, — тому маніфест, який
встановлює локально, не обов'язково можна внести до реєстру як є.
Використання
ketch update # завантажити реєстр у ~/.ketch/registry
ketch registry status # вік і джерело локальної копії; без мережі
ketch search fd # шукати в ньому, а також на GitHub
ketch install fd # встановити за назвою
ketch doctor # рядок реєстру містить кількість пакетів, вік і ревізію
Ніщо не завантажує реєстр неявно: назва, що розв'язується сьогодні, розв'язуватиметься
офлайн і завтра, а ketch install owner/repo реєстр узагалі ніколи не потрібен.
ketch update — єдиний спосіб оновлення. ketch registry status і ketch doctor читають локальну копію та registry.meta.toml (ревізію джерела й
час завантаження, зберігаються в корені ketch поруч із маніфестами) і ніколи
не відкривають мережевих з'єднань.
Робочий процес авторів і мейнтейнерів
Точна команда валідації, що запускається з кореня репозиторію реєстру (або з будь-якого дерева з такою самою структурою):
ketch registry validate .
Саме вона має пройти до злиття: кожен ketch.toml розібрано, звірено
з назвою його теки, відхилено, якщо source дорівнює local:, пропущено через
Manifest::validate, а конфлікти назв і аліасів провалюють
запуск.
Щоб іще й офлайн встановити записи, які змінює пул-реквест, покладіть по одному локальному асету на пакет у каталог фікстур — файл із назвою пакета або теку з цією назвою, у якій рівно один файл, — і назвіть пакети:
ketch registry validate . --fixture ./ci/fixtures --changed ripgrep --changed fd
Без --changed встановлюється кожен пакет, що має фікстуру. --changed
без --fixture — помилка. Встановлення виконується в тимчасовому корені ketch
з файлу фікстури, тому джерело github: у записі ніколи не звертається до
мережі. Відсутній бінарник, хибний bin.path або архів, що не
розпаковується, провалюють запуск так само, як конфлікт.
Сумісність
- Локальна валідація (
ketch registry validate) працює за принципом fail-closed. Некоректні або конфліктні записи провалюють запуск, тому їх не можна надіслати в upstream. - Клієнти (
ketch update, пошук, встановлення за назвою) діють за принципом best-effort щодо вже опублікованого поганого реєстру: вони попереджають, пропускають зламану теку й далі розв'язують решту. Вони ніколи не завантажують реєстр як побічний ефект status чи doctor. - Встановлення з фікстури — додатковий доказ для змінених записів. Воно не замінює перевірку конфліктів по всьому дереву.
Від CI для реєстру свідомо відмовилися (plan.md F2): репозиторій ketch-registry
видалив свій єдиний workflow (коміт 5a9bbd6, "no CI is wanted in
this repo"), тож в upstream немає workflow, куди це можна вбудувати. Перевіряйте локально
перед надсиланням, з кореня клону реєстру (або з будь-якого дерева з такою самою
структурою):
ketch registry validate .
Додайте ту саму перевірку як pre-push хук, щоб ніщо неперевірене не залишило машину, — з кореня клону реєстру:
cat > .git/hooks/pre-push <<'EOF'
#!/bin/sh
# Відхилити push, якщо дерево реєстру не проходить валідацію.
ketch registry validate . || exit 1
EOF
chmod +x .git/hooks/pre-push
registry push виконує ті самі перевірки окремого пакета для файлу, який збирається
надіслати. Він не сканує решту реєстру на конфлікти назв — це робить лише
registry validate. Він не виконує офлайн-встановлення з фікстури.
Додавання пакета
ketch.toml у корені проєкту — це той самий файл, що лежав би в його теці в реєстрі,
тому його додавання — одна команда, що запускається з цього кореня:
KETCH_GITHUB_TOKEN=... ketch registry push # завантажити, порівняти, потім пул-реквест
ketch registry push --yes # відповісти на запитання заздалегідь
ketch registry push --dry-run # що було б надіслано, офлайн
ketch registry push --file path/to/ketch.toml # файл в іншому місці
ketch registry push --registry someone/their-registry # реєстр, відмінний від типового
Після того як файл пройшов перевірки, registry push завантажує поточну копію
<name>/ketch.toml з типової гілки реєстру, і що відбувається
далі, залежить від її вмісту:
| Копія в реєстрі | Що робить registry push |
|---|---|
| не існує | одразу відкриває пул-реквест |
| збігається байт у байт | повідомляє unchanged і нічого не відкриває |
| відрізняється | друкує unified diff — копія реєстру як стара сторона, локальний файл як нова — і відкриває пул-реквест з оновленням лише після відповіді так/ні |
Потім він кладе файл у <name>/ketch.toml у гілці з назвою ketch/<name> і відкриває пул-реквест
у типову гілку реєстру. Якщо є права на push у
реєстр, гілка створюється там; без них — у форку реєстру
під вашим обліковим записом, який за потреби створюється. Повторне надсилання оновлює ту саму гілку
й приєднується до вже відкритого пул-реквесту, а не відкриває другий.
--yes відповідає на запитання заздалегідь — для скриптів і CI, де
нема кого спитати, або коли ви вже знаєте, що покаже диф. --dry-run
показує, що було б надіслано, не звертаючись до мережі.
name можна не вказувати у файлі — тоді пакет називається за текою, у якій
він лежить, за тим самим правилом, що застосовує реєстр. Файл надсилається саме в тому вигляді, у якому
написаний, разом із коментарями.
Токен — той самий, що використовують усі інші команди (KETCH_GITHUB_TOKEN або
github_token у config.toml); йому потрібна область repo, а для
fine-grained токена — права на contents і pull requests у реєстрі та, якщо
залучено форк, право його створити.
Пріоритет
Назва розв'язується по черзі:
~/.ketch/manifests/<name>.toml— ваші власні маніфести- завантажений реєстр
- реєстр, вкомпільований у бінарник (
crates/ketch-core/src/builtin.toml) - виведення з
owner/repo
Тож локальний маніфест завжди перемагає, а пакет, курований у реєстрі, перемагає старішу копію, вшиту в ту збірку ketch, яку ви зараз запускаєте.
Використання іншого реєстру
export KETCH_REGISTRY=someone/their-registry
або в ~/.ketch/config.toml:
registry = "someone/their-registry"
Типово використовується pyrlyn/ketch-registry. Приймається лише owner/repo;
завантажується типова гілка репозиторію.