Imported from IgorAIvanov/altera03 (
skills/src/framework-release/SKILL.md). Install upstream withnpx skills add IgorAIvanov/altera03 --skill framework-release. Copyright stays with the author.
Реліз пакета фреймворку
Скіл про цей репозиторій, а не про застосунок на фреймворку. У пакет
@altera/skills не їде.
Джерело істини — одне, пінів десять
Версія живе в <пакет>/deno.json → version. Але вона повторюється пінами в
трьох файлах, і кожен пін — це ^<та сама версія>:
| файл | піни |
|---|---|
create/template/deno.json |
@altera/client ×2 (@altera/client і @client/), @altera/server ×2 (пакет і /sql), @altera/tools ×3 (дві задачі + @altera/tools/), @altera/skills ×1 |
create/deno.json |
@altera/skills ×1 |
tools/deno.json |
@altera/server ×1 |
create/template/Dockerfile, create/template/docker-compose.yml |
@altera/tools ×1 кожен (оновлення рішення — точна версія, без ^) |
Правило одне: пін = ^ + поточна версія пакета. Нічого вирішувати не треба,
тому це стереже проба scripts/version-pins_test.ts (у deno task test:unit).
Вона падає з файлом і номером рядка. Якщо додаєш новий пін — він теж має пройти
цю пробу, інакше сенс перевірки зникає.
Що рухається разом
Ці зв'язки проба не перевіряє — вони змістовні, і рішення тут твоє:
client→skills. Скіли описують публічну поверхню клієнта. ЗмінивModelListBase,renderField, контракт$root— правь скіл і піднімайskillsтим самим релізом. Відсталий скіл радить те, чого в клієнті вже немає.client→vite. Мажорна версіяviteу шаблоні мусить збігатися з тією, з якою зібрано клієнт: пресет імпортує vite повнимnpm:-специфікатором. Дві мажорні версії в застосунку — і той екземпляр, що виконується, мовчки ігнорує опції, написані для іншого. Це теж під пробою (npm-піни шаблону = кореневі).server→tools.tools/deno.jsonпінить@altera/server: там лишилисяconfigFromEnv,hashPassword,findProductionMarker. Підняв мажор сервера — піднімай пін і публікуй tools, інакше в застосунку опиняться дві версії сервера (scaffold:verifyце ловить окремим кроком).server→ SQL ядра. Змінивserver/sql/**/db/*.sql— виконайdeno task core:sql, інакше в пакет поїде старий текст. Розсинхрон ловить проба, але тільки якщо її запустити.
Рівень версії
До 1.0 мінор — це «щось зламалося для споживача», патч — «ні»:
- патч (
0.4.0 → 0.4.1): виправлення, новий необов'язковий параметр, правка тексту скіла; - мінор (
0.4.0 → 0.5.0): обов'язковий аргумент, прибраний експорт, змінена сигнатура, змінена структура шаблону scaffold; - нова можливість без ламання — патч, якщо споживачеві не треба нічого міняти.
Приклад мінору: assembleSqlPackage дістав обов'язковий coreSql, а CLI
прибрали — жоден старий виклик не працює, тож tools пішов з 0.3.3 на 0.4.0.
Порядок публікації
server → client → tools → skills → create
За залежностями: tools пінить server, create імпортує skills, шаблон
всередині create пінить усе разом. Порядок зашитий у
.github/workflows/publish.yml; додаєш новий пакет — став його перед тим,
хто на нього посилається.
Перед публікацією
Спершу — CHANGELOG.md у корені. Це ЄДИНИЙ канал, яким до прикладника
доходить «що змінилося і що доведеться поправити руками»: скіли кажуть, як робити
зараз, а docs/ і цей CLAUDE.md у застосунок не їдуть узагалі. Файл вбудовується
в @altera/skills (skills:build) і лягає в корінь застосунку як
FRAMEWORK-CHANGELOG.md при skills:sync.
Розділ на реліз, і в ньому два підрозділи — «Потребує дії» (те, що доведеться робити руками: міграція коду, змінене умовчання, збірка, що тепер падає) і «Змінилося саме». Правки шаблону scaffold ідуть окремим підрозділом «Тільки для нових застосунків»: наявні застосунки їх не отримають ніколи, тож рядок для ручного додавання треба навести прямо.
Забути цей крок не можна мовчки — проба в test:unit звіряє вбудований текст із
файлом на диску, тож пропущений skills:build вона впіймає; але сам ЗМІСТ ніхто
не перевірить, крім того, хто робить реліз.
deno task skills:build # після правки CHANGELOG.md
deno task check:deps
deno task test:unit # тут і піни, і свіжість generated-файлів
deno task scaffold:verify:local
:local тут не примха: шаблон пінить діапазон, якого в реєстрі ще немає, тож
звичайний прогін впав би на нерозв'язному пінові, не дійшовши до збірки. А
публікація на JSR незворотна — знайти дефект після неї пізно.
Плюс сухий прогін по кожному пакету, що змінився:
cd <пакет> && deno publish --dry-run
Він показує склад пакета — заразом видно, чи не поїхало зайве (джерела скілів, дерево шаблону, проби).
Сама публікація
Workflow Publish to JSR запускається САМ — після зеленого CI на master. Тобто публікація це наслідок пуша: пуш → CI → зелений → у реєстр їдуть пакети з піднятим номером. Окремої дії немає, і чекати від тебе її не треба.
Кнопка на сторінці Actions лишилася другим входом — для випадку, коли реліз упав не через код (реєстр лежить, мережа). На цьому шляху зелений CI перевіряється явно, через API.
Наслідок, з яким треба рахуватися: реліз їде ПОЇЗДОМ до пуша, а не після.
Бамп першим комітом і правки наступними — законно, бо публікується head пуша й
забирає все; а от притримати недописане ПІСЛЯ пуша не можна — воно лишиться без
номера, бо свій уже витрачений. Кожен опублікований номер workflow позначає
тегом jsr/<пакет>@<версія> — це точка відліку для перевірки нижче.
Публікуються лише пакети, чиєї версії ще немає в реєстрі. Бамп у коміті і є командою на реліз; решта тихо пропускається, тому запуск ідемпотентний. Зворотний бік: забутий бамп = мовчазний пропуск. Якщо після релізу поведінка не змінилася — перше, що перевіряти, це чи піднята версія.
Кнопка публікує КОМІТ, а не намір. Пуш, зроблений після натискання, у цей реліз не потрапляє — і це не просто «поїде наступним разом»: номери версій із того коміту вже в реєстрі, з тим змістом, що був. Правка, дописана після, лишається без номера назавжди, бо свій він уже витрачений.
Так сталося з ledger 0.20.0: server підняли вже після запуску, і в реєстр пішли
tools@0.13.12, skills@0.1.23, create@0.7.22 з піном @altera/server@^0.19.0.
Опубліковане при цьому було ЦІЛЕ й робоче — розійшовся лише репозиторій, у якого
ті самі номери означали вже інше. Post-release verify саме це й показав:
Could not find version of '@altera/server' that matches '^0.20.0'.
Ліки — фікс-реліз, у якому кожен спалений номер піднімається, навіть якщо в пакеті не змінилося нічого, крім піна. Перевірити, що саме пішло, можна не здогадуючись:
deno eval "const m = await (await fetch('https://jsr.io/@altera/server/meta.json')).json(); console.log(m.latest)"
а зміст опублікованого — тим самим fetch по конкретному файлу
(https://jsr.io/@altera/skills/0.1.23/skills.generated.ts). Це швидше, ніж
відновлювати з логів CI, який коміт було взято.
Після публікації
Крок Post-release scaffold verify ставить пакети вже з реєстру й проганяє
skills:sync опублікованим @altera/skills. Червоний одразу — найпевніше кеш
meta.json ще не розійшовся, і вбудовані повтори (0/60/120/120 с) це покривають.
Червоний після повторів означає не «публікацію скасовано» — версія на JSR уже
назавжди, — а потрібен фікс-реліз.
Пастки, що вже стріляли
-
Правка після релізу вимагає БАМПУ, і
:localцього не побачить. Він підміняє залежності вихідниками, де все завжди свіже, — тож зміна, яка нікуди не поїхала, проходить зелено, а реєстр віддає застосункам старий вміст. Такclientпростояв два дні з трьома доданими експортами (tab-url,ui-dialog,ui-remark) і старим номером0.12.3: кожен свіжий застосунок падав наUnknown export './tabs/tab-url.ts', а побачив це лише post-release verify — коли решта пакетів уже пішла в реєстр незворотно.Тепер це окремий крок
scaffold:verify— «вміст проти реєстру»: якщо номер уdeno.jsonдорівнює опублікованійlatest, а в каталог пакета лягли коміти ПІСЛЯ опублікованого стану, перевірка падає й називає їх. Проби з рахунку виключені (вони й так не їдуть у пакет).Точка відліку — тег публікації
jsr/<пакет>@<версія>(його ставить workflow), а не коміт, який підняв версію. Різниця вистрелила першою ж версією перевірки: реліз законно їде поїздом (бамп першим, пуш усього разом), публікується head — і «коміти після бампа» лежали в реєстрі, а перевірка червоніла на здоровому релізі, причому лише ПІСЛЯ публікації: доти номера в реєстрі не було, і вона мовчала. Для версій без тега фолбек — час публікації з реєстру (підозрілі лише коміти, вчинені після createdAt із запасом на годинники).Міряється саме ІСТОРІЄЮ, а не байтами опублікованого, і це не лінощі: JSR переписує специфікатори імпорту при публікації (
"lit"→"npm:lit@^3.3.1"), тож жоден вихідник із голим імпортом не збігається сам із собою. Звідси й вимога до CI: джобscaffoldбереfetch-depth: 0— з одним комітом крок чесно каже, що пропущений, і гейт стає декоративним. -
Новий запис в
exportsпідводить модуль під slow types. Доти файл був внутрішнім, і правила публічного API його не стосувалися; щойно він з'явився вexports, JSR вимагає явний тип повернення з КОЖНОЇ експортованої функції. Так@altera/server@0.6.2упав наstringifyPrintTemplateValueуprint-template.ts, який роками жив без анотації. Те саме з новим файлом у пакеті:/// <reference lib="deno.ns" />уtools/заборонений (banned-triple-slash-directives) — уscripts/він доречний, і звідти переїжджає копіюванням.Ловить це рівно
deno publish --dry-runпо кожному зміненому пакету — жодна з решти перевірок (deno check,check:deps,test:unit,scaffold:verify) slow types не бачить. Пропустити цей крок — значить дізнатися про помилку від workflow, уже запушивши реліз. -
Ліцензію перевіряє тільки реєстр, і вже після пуша. JSR приймає ОДИН упізнаваний ідентифікатор SPDX; складений вираз —
"license": "MIT AND OFL-1.1", цілком законний за стандартом, — відхиляється:error: Failed to publish @altera/server@0.25.3 Caused by: The license specified in the "license" field of your configuration file, or in the LICENSE file was not recognized.Ціна непропорційна розміру:
deno publish --dry-runліцензію не дивиться ВЗАГАЛІ (він показує склад пакета й ловить slow types — і все), решта перевірок тим паче. Тобто зелено скрізь, а публікація падає на першому ж пакеті — і не їде НІЧОГО, бо цикл іде за залежностями й спиняється наserver. Так згорів реліз 0.25.3.Те, що пакет везе не лише свій код (субсети шрифтів під OFL у
server), говориться вTHIRD-PARTY-NOTICES.md— його реєстр не читає й відхилити не може. ПолеlicenseлишаєтьсяMIT. Тепер це під пробоюscripts/version-pins_test.ts(«ліцензія пакета — один ідентифікатор»). -
Специфікатор без версії в задачах шаблону (
deno run jsr:@altera/tools/...) бере довільну версію з реєстру: у застосунок приїхавtools@0.3.0замість0.3.3. Піни в задачах обов'язкові. -
Неоголошена залежність.
toolsімпортував@altera/serverу п'яти модулях, не маючи його вimports. JSR зафіксував при публікації те, що стояло у воркспейсі (@0.3), — мовчки й назавжди. У базу поїхала схема однієї версії, а читав її рантайм іншої:column "must_change_password" does not existна чистій базі. Кожен імпорт має бути в карті імпортів пакета. -
Застарілий
template.generated.ts.@altera/create@0.1.4пішов у реєстр із версіями фреймворку, піднятими в дереві шаблону, але не перезібраними в мапу.deno task scaffold:templateпісля кожної правки шаблону; свіжість перевіряєscaffold:verify.