Lock-файл

From docs/uk/LOCKFILE.md at 4e43843 View on GitHub

ketch.lock — це набір утиліт однієї машини, закріплених на точних релізах. Запишіть його, закомітьте поруч зі своїми dotfiles, і будь-яка інша машина зможе відтворити той самий набір:

ketch lock              # записати ./ketch.lock за тим, що встановлено
ketch lock --check      # чи не розійшлося дерево з ним?
ketch sync              # встановити те, що названо в lock-файлі, у цих версіях

Його JSON Schema, згенерована з типів, у які ketch його читає, — lock.schema.json.

Це не те блокування, яке ketch бере під час роботи, — те є м'ютексом над деревом встановлення, утримується на час виконання команди й звільняється наприкінці.

Який він має вигляд

# ketch.lock — generated by `ketch lock`. Do not edit it by hand:
# run `ketch lock` again instead. Commit it.
version = 1

[[package]]
name = "ripgrep"
source = "github:BurntSushi/ripgrep"
version = "14.1.1"
tag = "14.1.1"
target = "macos-aarch64"
asset = "ripgrep-14.1.1-aarch64-apple-darwin.tar.gz"
sha256 = "4cf9f2741e6c465ffdb7c26f38056a59e2a2544b51f7cc128ef28337eeae4d8e"

[[package]]
name = "fd"
source = "github:sharkdp/fd"
version = "10.2.0"
tag = "v10.2.0"
target = "macos-aarch64"
asset = "fd-v10.2.0-aarch64-apple-darwin.tar.gz"
sha256 = "d3f0e1a5b3ee2b6d0a1a6d0dd6c72fd0e69f6bb7c5b3a2a94f0e5b9a2f8b8c11"
pinned = true

Пакети записуються відсортованими за назвою, тому файл стабільний, а його дифи читабельні. pinned записується лише тоді, коли він дорівнює true, а bin — лише тоді, коли вибір було зроблено.

КлючЩо це
nameназва, під якою пакет встановлено
sourceowner/repo зі схемою — стабільна ідентичність пакета
versionдля людей; насправді розв'язується tag
tagточний реліз, який запитує ketch sync
targetмашина, на якій було знято цей запис
assetфайл релізу, взятий на цьому target
sha256цього файлу
pinnedчи був пакет закріплений на цій версії
binбінарник, вибраний, коли реліз містить кілька файлів із назвою пакета, у вигляді назви файлу (rtok-cli); ketch sync повторює цей вибір, не питаючи, — див. розділ bin у MANIFESTS.md
publishedдля пакета на плаваючому тезі, який не є версією (nightly), — коли було опубліковано зафіксовану збірку; пишеться лише для таких пакетів

Що відтворюване, а що ні

Тег відтворюваний. Кожна машина, що розв'язує той самий тег, отримує той самий реліз. Заради цього його й записують.

Асет і його геш відтворювані лише на тому самому target. Lock-файл, записаний на Apple Silicon, називає tarball aarch64, який машина x86_64 Linux запустити не може. Тому:

  • На машині зі збіжним target sync звіряє завантаження із записаним sha256 і відхиляє його ще до розпакування будь-чого, якщо вони не збігаються. Реліз, підмінений під уже опублікованим тегом, — це саме те, що lock-файл і має виловлювати.
  • На будь-якій іншій машині sync обирає асет, що пасує хосту, і перевіряє його за контрольною сумою, яку публікує джерело, як під час звичайного встановлення. Удавати, що записаний геш досі чинний, означало б дати гарантію, яка непомітно нею не є.
  • Плаваючий тег (nightly, dev) перепубліковують під тією самою назвою, тому сам тег збірку не втримує. На target, що записав lock-файл, її вже фіксує геш. На будь-якій іншій машині sync порівнює published з тим, коли було опубліковано збірку, на яку тег указує зараз, і відхиляє пізнішу ще до завантаження; щоб прийняти її, знову запустіть ketch lock. Якщо джерело не повідомляє, коли опубліковано реліз, лишається тільки геш на target, що записав lock-файл.

Пакет local: відтворюваний лише там, де є його шлях. Lock-файл записує абсолютний шлях, звідки пакет встановлено, і геш того, що там лежало, — байтів файлу, а для бандла .app — дайджест кожного файлу і символьного посилання дерева. Кожен запис обрамлено видом, шляхом, бітами прав і або байтами файлу, або власною ціллю символьного посилання, тож два розташування не можуть поділити один дайджест, а перенацілювання посилання чи зняття +x його змінює. sync перевстановлює з цього шляху й відхиляє його ще до розміщення будь-чого, якщо вміст змінився відтоді, як записано lock-файл. Lock-файл, записаний до цього обрамлення, не збігається з незмінним бандлом; запустіть ketch lock ще раз, щоб записати новий дайджест. На машині без цього шляху запис падає, як будь-яке інше недоступне джерело.

ketch sync

ketch sync                 # встановити відсутнє або те, що стоїть не в тій версії
ketch sync --dry-run       # показати план, нічого не змінюючи
ketch sync --prune         # також видалити пакети, не названі в lock-файлі
ketch sync --file <FILE>   # lock-файл не в ./ketch.lock, а деінде

План читається як диф:

+ jq 1.7.1
~ ripgrep 14.1.0 -> 14.1.1
- httpie not in the lockfile
2 already match

Пакет, не згаданий у lock-файлі, сам по собі не є розбіжністю — lock-файл записує те, що ви хочете, а не обов'язково все, що у вас є, — тому ketch lock --check ігнорує зайве, і видаляє його лише --prune. Видалення може знищити чиюсь роботу, тому спершу ставиться запитання, якщо не передано --yes.

pinned відновлюється після встановлення, тож пакет, записаний у lock-файлі як закріплений, повертається закріпленим, а не тихо оновлюваним.

Що перевіряє ketch

Lock-файл — це файл, який міг написати хтось інший; саме це й означає спільний репозиторій dotfiles. Тому ніщо в ньому не має права обирати шлях у файловій системі. sync запитує джерело на тезі, встановлює його під записаною name — яка має пройти першу перевірку нижче, — і дає звичайному розв'язанню маніфестів вирішити, які бінарники ставити й куди; записаний bin лише вирішує, який із кількох бінарників із назвою пакета отримає посилання, після будь-якого вибору, який уже запам'ятав state.json. Lock-файл закріплює, який реліз, але ніколи — куди він потрапить.

ВідхиляєтьсяЧому
name, яку не можна використати дослівно як один компонент шляхувона зіставляється зі встановленими пакетами й показується вам; назва, яку довелося б переписати, означає не те, що в ній написано
той самий пакет двічівстановити можна було б лише один із них
джерело github:, що не є коректним owner/repoвоно перетворюється на URL
будь-яке інше джерело з порожнім idрозв'язувати нічого
sha256, що не складається з 64 шістнадцяткових символівйого порівнюють зі справжнім дайджестом
bin, порожній або непридатний дослівно як одна назва файлувін називає файл у релізі; значення з роздільником шляху, .. або керівними символами не може цього робити
порожній tagрозв'язувати нічого
published, порожній або з чимось, окрім друкованого ASCIIйого виводять у помилці, що називає зсунутий тег
target, який ketch не розпізнаєвін мовчки перетворює запис на кросплатформний, тож записані асет і геш перестають діяти, а геш, що змінився під тегом, виглядає чистим
невідомий ключключ з одруківкою, який мовчки ігнорується, закріплює не те, що ви написали
version, новіша, ніж розуміє цей ketchоновіться через ketch self upgrade

Один поганий запис провалює весь файл, а не пропускається. На відміну від реєстру, де часткова відповідь краща за жодну, lock-файл, що встановив більшу частину себе, зовсім не був би lock-файлом.

Яка назва розв'язується

sync спершу запитує пакет за name, бо саме так його спочатку знайшли, а те, який рівень маніфестів відповість, вирішує, що отримає посилання й під якими назвами. Розв'язання безпосередньо з github:BurntSushi/ripgrep провалилося б до виведення з репозиторію й могло б дати інші бінарники, ніж запис реєстру, який ви насправді встановили.

Назва використовується лише доти, доки вона означає той самий проєкт. Якщо тепер вона розв'язується в інше джерело, перемагає source з lock-файлу — назва, що змінила власника, не повинна тихо встановити щось інше.

Хай там як, пакет встановлюється під name з lock-файлу. Пакет, встановлений через ketch install --name, повертається під цією назвою, а не під тією, яку вивело б його джерело, тож ketch lock --check узгоджується з машиною, яку щойно налаштував sync.