@7n/rules 1.32.1 → 1.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.33.0] - 2026-07-20
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Per-категорійний автодетект плагінів: явний непорожній plugins (лише з @7n/rules-`category`-*) домішує автодетектом категорії, яких у ньому немає
|
|
8
|
+
|
|
9
|
+
## [1.32.2] - 2026-07-20
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- doc-files: згенеровано файлові доки для `k8s/dremio_logging/main.mjs` і `skills/taze/js/migration-cache.mjs`, що доти ламали tree-wide doc-files/check; вручну виправлено фабрикації локальної моделі (розбиті backtick-маркери, невірні твердження про fail-safe/scope кешу міграцій)
|
|
14
|
+
|
|
3
15
|
## [1.32.1] - 2026-07-20
|
|
4
16
|
|
|
5
17
|
### Changed
|
package/bin/n-rules.js
CHANGED
|
@@ -365,9 +365,11 @@ async function readConfig(paths = {}) {
|
|
|
365
365
|
}
|
|
366
366
|
}
|
|
367
367
|
|
|
368
|
-
// Плагіни: явне поле plugins конфігу
|
|
369
|
-
//
|
|
370
|
-
|
|
368
|
+
// Плагіни: явне поле plugins конфігу (з per-категорійним backfill автодетекту — ADR
|
|
369
|
+
// 260719-2154) або повний автодетект, коли поля нема; sync-контекст — з установкою devDep.
|
|
370
|
+
// Результат записуємо у конфіг (нижче), щоб hook/lint не залежали від детекту й мережі.
|
|
371
|
+
const declaredPlugins = Array.isArray(rawConfig?.plugins) ? rawConfig.plugins : null
|
|
372
|
+
const resolvedPlugins = resolvePluginList(cwd(), rawConfig)
|
|
371
373
|
const rulesDirs = resolveRulesDirs(cwd(), rawConfig, bundledRulesDir).map(d => d.rulesDir)
|
|
372
374
|
const { names: availableRules } = await aggregateRuleSources(rulesDirs)
|
|
373
375
|
const availableSkills = await discoverBundledSkillNames(bundledSkillsDir)
|
|
@@ -442,9 +444,17 @@ async function readConfig(paths = {}) {
|
|
|
442
444
|
if (merged['disable-skills']?.length) {
|
|
443
445
|
normalized['disable-skills'] = merged['disable-skills']
|
|
444
446
|
}
|
|
445
|
-
if (!('plugins' in parsedConfig)
|
|
446
|
-
|
|
447
|
-
|
|
447
|
+
if (!('plugins' in parsedConfig)) {
|
|
448
|
+
if (resolvedPlugins.length > 0) {
|
|
449
|
+
normalized.plugins = resolvedPlugins
|
|
450
|
+
console.log(`🔌 Автодетект плагінів: ${resolvedPlugins.join(', ')} — записано у ${CONFIG_FILE}\n`)
|
|
451
|
+
}
|
|
452
|
+
} else if (declaredPlugins && JSON.stringify(resolvedPlugins) !== JSON.stringify(declaredPlugins)) {
|
|
453
|
+
// per-категорійний backfill (resolvePluginList, ADR 260719-2154) додав плагін
|
|
454
|
+
// категорії, відсутньої в явному plugins — фіксуємо результат у конфізі.
|
|
455
|
+
normalized.plugins = resolvedPlugins
|
|
456
|
+
const added = resolvedPlugins.filter(p => !declaredPlugins.includes(p))
|
|
457
|
+
console.log(`🔌 Доповнено plugins у ${CONFIG_FILE} (${added.join(', ')}) — категорія не була задекларована\n`)
|
|
448
458
|
}
|
|
449
459
|
return sortConfigIdArrays(normalized)
|
|
450
460
|
}
|
|
@@ -465,7 +475,7 @@ async function readConfig(paths = {}) {
|
|
|
465
475
|
$schema: CONFIG_SCHEMA_URL,
|
|
466
476
|
rules: autoDetectedRules.rules,
|
|
467
477
|
skills: autoDetectedSkills.skills,
|
|
468
|
-
...(
|
|
478
|
+
...(resolvedPlugins.length > 0 && { plugins: resolvedPlugins })
|
|
469
479
|
})
|
|
470
480
|
await writeFile(configPath, `${JSON.stringify(defaultConfig, null, 2)}\n`, 'utf8')
|
|
471
481
|
console.log(
|
package/package.json
CHANGED
|
@@ -7,23 +7,23 @@ docgen:
|
|
|
7
7
|
model: openai-codex/gpt-5.4-mini
|
|
8
8
|
tier: cloud-min
|
|
9
9
|
score: 95
|
|
10
|
-
issues: anchor-miss:(k8s.mdc),judge:inaccurate:0.
|
|
10
|
+
issues: anchor-miss:(k8s.mdc),judge:inaccurate:0.98
|
|
11
11
|
judgeModel: openai-codex/gpt-5.4-mini
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
## Огляд
|
|
15
15
|
|
|
16
|
-
`
|
|
16
|
+
Перевіряє `zookeeper.yaml` на надто гучний `root level` у вбудованому `logback.xml` і формує звіт про порушення з маркером `zk-logback-root-level`. `zkLogbackRootLevelViolation` і `lint` працюють як read-only, не змінюють ФС чи БД, і поводяться fail-safe: помилки перехоплюються, назовні винятки не виходять.
|
|
17
17
|
|
|
18
18
|
## Поведінка
|
|
19
19
|
|
|
20
|
-
- `zkLogbackRootLevelViolation` — перевіряє, чи вбудований `logback.xml` у
|
|
21
|
-
- `lint` — проходить по вказаних
|
|
20
|
+
- `zkLogbackRootLevelViolation` — перевіряє, чи вбудований `logback.xml` у `zookeeper.yaml` має допустимий `root level` (`warn`, `error`, `off`); якщо блоку немає — не чіпає файл, якщо рівень надто гучний або відсутній — повертає текст порушення з посиланням на `k8s.mdc`.
|
|
21
|
+
- `lint` — проходить по вказаних файлах, безпечно читає їх як `zookeeper.yaml`-кандидати, фіксує порушення для `k8s.dremio_logging` і не перериває перевірку на помилках читання; для кожного знайденого випадку додає повідомлення з маркером `zk-logback-root-level`.
|
|
22
22
|
|
|
23
23
|
## Публічний API
|
|
24
24
|
|
|
25
|
-
- zkLogbackRootLevelViolation —
|
|
26
|
-
- lint — запускає `k8s.dremio_logging` для ZooKeeper
|
|
25
|
+
- zkLogbackRootLevelViolation — знаходить у Helm-темплейті ZooKeeper вбудований `logback.xml`, де `root` заданий занадто гучно або взагалі відсутній; пропускає, якщо рівень уже `warn`, `error` чи `off`
|
|
26
|
+
- lint — запускає `k8s.dremio_logging` для ZooKeeper-темплейту на рівні окремого файла, без змін у вмісті
|
|
27
27
|
|
|
28
28
|
## Гарантії поведінки
|
|
29
29
|
|
|
@@ -3,7 +3,7 @@ type: JS Module
|
|
|
3
3
|
title: resolve-plugins.mjs
|
|
4
4
|
resource: npm/scripts/lib/resolve-plugins.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 2bac9b49
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
Резолв плагінів @7n/rules: визначає, які пакети-плагіни активні у проєкті, де їхні `rules/`-каталоги, які capabilities вони надають і які handlers надають.
|
|
@@ -2,10 +2,16 @@
|
|
|
2
2
|
* Резолв плагінів `@7n/rules`: які пакети-плагіни активні у проєкті, де їхні `rules/`,
|
|
3
3
|
* які capabilities вони дають і які handlers надають.
|
|
4
4
|
*
|
|
5
|
-
* Джерело правди — поле `plugins: string[]` у `.n-rules.json`
|
|
6
|
-
*
|
|
7
|
-
* (
|
|
8
|
-
*
|
|
5
|
+
* Джерело правди — поле `plugins: string[]` у `.n-rules.json`. Явний `[]` = «плагіни
|
|
6
|
+
* вимкнено» (автодетект не застосовується). Якщо поля немає взагалі — повний автодетект
|
|
7
|
+
* (`detectPluginsFromRepo`). Якщо `plugins` непорожній і складається **виключно** з пакетів
|
|
8
|
+
* за конвенцією `@7n/rules-<category>-<name>` (напр. `ci`, `lang`) — автодетект домішує
|
|
9
|
+
* лише ті категорії, яких у списку немає (ADR `260719-2154-per-category-автодетект-плагінів`);
|
|
10
|
+
* будь-який сторонній (не `@7n/rules-*`) пакет у списку вимикає backfill повністю — змішаний
|
|
11
|
+
* чи повністю кастомний список означає ручне керування, без сюрпризів. Файлові сигнали
|
|
12
|
+
* автодетекту: `.github/workflows/*.yml` → `@7n/rules-ci-github`; `azure-pipelines.yml` →
|
|
13
|
+
* `@7n/rules-ci-azure`, а без них — fallback за `repository.url` кореневого package.json
|
|
14
|
+
* (`github.com` / `dev.azure.com`).
|
|
9
15
|
*
|
|
10
16
|
* Установка: `ensurePluginInstalled` — плагін стає devDependency через `bun add -d` (bun сам
|
|
11
17
|
* резолвить актуальну версію; зміна видима у diff package.json). Фейл установки (offline,
|
|
@@ -153,18 +159,91 @@ export function detectPluginsFromRepo(projectRoot) {
|
|
|
153
159
|
return out
|
|
154
160
|
}
|
|
155
161
|
|
|
162
|
+
/** Naming convention плагінів, які розпізнає автодетект: `@7n/rules-<category>-<name>`. */
|
|
163
|
+
const PLUGIN_CATEGORY_RE = /^@7n\/rules-([a-z0-9]+)-/u
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Категорія плагіна за naming convention `@7n/rules-<category>-<name>` (напр. `ci`, `lang`).
|
|
167
|
+
* `null` — пакет поза цією конвенцією (сторонній/кастомний плагін); такий пакет ніколи не
|
|
168
|
+
* зʼявляється сам через автодетект і, якщо присутній у явному `config.plugins`, вимикає
|
|
169
|
+
* per-категорійний backfill для всього списку (див. `resolvePluginList`).
|
|
170
|
+
* @param {string} pkg npm-ім'я плагіна
|
|
171
|
+
* @returns {string | null} категорія або null
|
|
172
|
+
*/
|
|
173
|
+
export function pluginCategory(pkg) {
|
|
174
|
+
const m = PLUGIN_CATEGORY_RE.exec(pkg)
|
|
175
|
+
return m ? m[1] : null
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Усі категорії, які реально може повернути `detectPluginsFromRepo` — для short-circuit коли declared вже покриває все. */
|
|
179
|
+
const ALL_KNOWN_CATEGORIES = new Set(
|
|
180
|
+
[...Object.values(KNOWN_CI_PLUGINS), ...Object.values(KNOWN_LANG_PLUGINS).map(l => l.pkg)]
|
|
181
|
+
.map(pkg => pluginCategory(pkg))
|
|
182
|
+
.filter(c => c !== null)
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
/** Кеш `resolvePluginList` на процес: `root::declared-json` → результат (щоб не дублювати warning). */
|
|
186
|
+
const PLUGIN_LIST_CACHE = new Map()
|
|
187
|
+
|
|
156
188
|
/**
|
|
157
|
-
* Список плагінів проєкту: явний `config.plugins`
|
|
189
|
+
* Список плагінів проєкту: явний `config.plugins` або автодетект.
|
|
190
|
+
*
|
|
191
|
+
* Явний `plugins` непорожній і складається **виключно** з пакетів `@7n/rules-<category>-*` —
|
|
192
|
+
* автодетект домішує лише категорії, відсутні в списку (ADR
|
|
193
|
+
* `260719-2154-per-category-автодетект-плагінів`); категорія, присутня хоч одним пакетом,
|
|
194
|
+
* лишається зафіксованою користувачем. Якщо `plugins` містить хоча б один сторонній
|
|
195
|
+
* (не `@7n/rules-*`) пакет — це сигнал ручного керування, backfill вимикається повністю
|
|
196
|
+
* (як і раніше: список повертається як є). Явний `[]` — «плагіни вимкнено», без backfill.
|
|
197
|
+
* Поле відсутнє взагалі — повний автодетект.
|
|
198
|
+
*
|
|
199
|
+
* Результат кешується на процес за `(projectRoot, declared)` — виклик з `resolvePlugins`
|
|
200
|
+
* (через `resolveRulesDirs` тощо) і прямий виклик у sync-CLI інакше дублювали б і файловий
|
|
201
|
+
* скан, і warning про backfill.
|
|
158
202
|
* @param {string} projectRoot корінь репозиторію
|
|
159
203
|
* @param {{ plugins?: unknown } | null | undefined} config розпарсений `.n-rules.json` (може бути відсутній)
|
|
204
|
+
* @param {{ quiet?: boolean }} [options] `quiet:true` — без warning-у про плагін, доданий автодетектом (hot-path);
|
|
205
|
+
* ігнорується при cache hit — warning друкується щонайбільше раз на `(root, declared)` за процес
|
|
160
206
|
* @returns {string[]} npm-імена плагінів
|
|
161
207
|
*/
|
|
162
|
-
export function resolvePluginList(projectRoot, config) {
|
|
208
|
+
export function resolvePluginList(projectRoot, config, options = {}) {
|
|
209
|
+
const root = resolve(projectRoot)
|
|
163
210
|
const declared = config?.plugins
|
|
164
|
-
|
|
165
|
-
|
|
211
|
+
const cacheKey = `${root}::${Array.isArray(declared) ? JSON.stringify(declared) : '∅'}`
|
|
212
|
+
const cached = PLUGIN_LIST_CACHE.get(cacheKey)
|
|
213
|
+
if (cached) return cached
|
|
214
|
+
|
|
215
|
+
const result = computePluginList(root, declared, options)
|
|
216
|
+
PLUGIN_LIST_CACHE.set(cacheKey, result)
|
|
217
|
+
return result
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Обчислення `resolvePluginList` без кешу (винесено окремо, щоб кеш-обгортка лишалась тонкою).
|
|
222
|
+
* @param {string} root абсолютний корінь репозиторію (вже пройшов через `resolve()`)
|
|
223
|
+
* @param {unknown} declared сире значення `config.plugins`
|
|
224
|
+
* @param {{ quiet?: boolean }} options прокинуті опції виклику
|
|
225
|
+
* @returns {string[]} npm-імена плагінів
|
|
226
|
+
*/
|
|
227
|
+
function computePluginList(root, declared, options) {
|
|
228
|
+
if (!Array.isArray(declared)) return detectPluginsFromRepo(root)
|
|
229
|
+
|
|
230
|
+
const names = declared.filter(p => typeof p === 'string' && p.trim() !== '')
|
|
231
|
+
if (names.length === 0) return names
|
|
232
|
+
|
|
233
|
+
const declaredCategories = new Set(names.map(pkg => pluginCategory(pkg)))
|
|
234
|
+
// Хоч один сторонній пакет у списку — не вгадуємо намір, повертаємо як є.
|
|
235
|
+
if (declaredCategories.has(null)) return names
|
|
236
|
+
// Усі відомі категорії вже покриті явним списком — не марнуємо файлові сигнали.
|
|
237
|
+
if ([...ALL_KNOWN_CATEGORIES].every(c => declaredCategories.has(c))) return names
|
|
238
|
+
|
|
239
|
+
const missing = detectPluginsFromRepo(root).filter(pkg => !declaredCategories.has(pluginCategory(pkg)))
|
|
240
|
+
if (missing.length > 0 && options.quiet !== true) {
|
|
241
|
+
const word = missing.length > 1 ? 'плагіни' : 'плагін'
|
|
242
|
+
console.warn(
|
|
243
|
+
`⚠️ Додано автодетектом ${word} ${missing.join(', ')} — категорія не задекларована явно в .n-rules.json\n`
|
|
244
|
+
)
|
|
166
245
|
}
|
|
167
|
-
return
|
|
246
|
+
return [...names, ...missing]
|
|
168
247
|
}
|
|
169
248
|
|
|
170
249
|
/**
|
|
@@ -240,7 +319,7 @@ function readPluginManifest(packageRoot) {
|
|
|
240
319
|
*/
|
|
241
320
|
export function resolvePlugins(projectRoot, config, options = {}) {
|
|
242
321
|
const root = resolve(projectRoot)
|
|
243
|
-
const names = resolvePluginList(root, config)
|
|
322
|
+
const names = resolvePluginList(root, config, { quiet: options.quiet })
|
|
244
323
|
const cacheKey = `${root}${names.join(',')}${options.allowInstall !== false}`
|
|
245
324
|
const cached = RESOLVE_CACHE.get(cacheKey)
|
|
246
325
|
if (cached) return cached
|
|
@@ -341,4 +420,5 @@ export function getHandlers(projectRoot, config, point) {
|
|
|
341
420
|
/** Скидає кеш резолву (для тестів). */
|
|
342
421
|
export function clearPluginResolveCache() {
|
|
343
422
|
RESOLVE_CACHE.clear()
|
|
423
|
+
PLUGIN_LIST_CACHE.clear()
|
|
344
424
|
}
|
|
@@ -13,26 +13,25 @@ docgen:
|
|
|
13
13
|
|
|
14
14
|
## Огляд
|
|
15
15
|
|
|
16
|
-
Модуль
|
|
16
|
+
Модуль керує кешем уже відомих міграцій на диску (`~/.cache/n-rules/taze-migrations` за замовчуванням) — кеш персистентний і спільний для всіх repo/worktree на машині, не обмежений одним прогоном. `migrationCacheKey` формує ключ для запису, `readMigrationCache` і `writeMigrationCache` працюють із кешованими даними, а `withKnownMigrationNotes` підставляє вже відомі відомості там, де це доречно. Читання fail-safe (`readMigrationCache` перехоплює помилки й повертає `null` замість винятку); запис (`writeMigrationCache`) винятків не перехоплює.
|
|
17
17
|
|
|
18
18
|
## Поведінка
|
|
19
19
|
|
|
20
|
-
- `DEFAULT_CACHE_DIR` —
|
|
21
|
-
- `migrationCacheKey` — перетворює
|
|
22
|
-
- `readMigrationCache` — читає кешований запис міграції для
|
|
23
|
-
- `writeMigrationCache` — зберігає
|
|
24
|
-
- `withKnownMigrationNotes` — додає до промпта підсумок
|
|
20
|
+
- `DEFAULT_CACHE_DIR` — задає спільний каталог кешу міграцій для всіх репозиторіїв на цій машині.
|
|
21
|
+
- `migrationCacheKey` — перетворює пару версій пакета на безпечний крос-репо ключ кешу.
|
|
22
|
+
- `readMigrationCache` — читає кешований запис міграції для конкретної пари версій; якщо запису немає або він пошкоджений, повертає `null` без помилки.
|
|
23
|
+
- `writeMigrationCache` — зберігає запис про вже проаналізовану міграцію в кеш, щоб не повторювати той самий аналіз у наступних прогонових середовищах.
|
|
24
|
+
- `withKnownMigrationNotes` — додає до промпта підсумок відомої міграції з кешу й підказує пропустити повторне дослідження та перейти до перевірки в поточному проєкті.
|
|
25
25
|
|
|
26
26
|
## Публічний API
|
|
27
27
|
|
|
28
|
-
- DEFAULT_CACHE_DIR — Спільний каталог кешу міграцій
|
|
29
|
-
- migrationCacheKey — Перетворює
|
|
30
|
-
- readMigrationCache — Повертає
|
|
31
|
-
- writeMigrationCache — Записує результат
|
|
32
|
-
- withKnownMigrationNotes — Додає до prompt короткий підсумок знайденої міграції
|
|
28
|
+
- DEFAULT_CACHE_DIR — Спільний каталог для кешу міграцій на цій машині, незалежний від конкретного repo чи worktree.
|
|
29
|
+
- migrationCacheKey — Перетворює `pkg`, `from` і `to` на безпечну назву файла для спільного кешу.
|
|
30
|
+
- readMigrationCache — Повертає збережені дані про вже відому міграцію для тієї самої пари версій, або `null`, якщо запису немає чи він пошкоджений.
|
|
31
|
+
- writeMigrationCache — Записує результат вже розібраної міграції в кеш, щоб наступний repo з тим самим оновленням не починав дослідження заново.
|
|
32
|
+
- withKnownMigrationNotes — Додає до prompt короткий підсумок знайденої міграції і пропускає початковий етап з пошуком у CHANGELOG та diff.
|
|
33
33
|
|
|
34
34
|
## Гарантії поведінки
|
|
35
35
|
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
- Кешує результати в межах одного прогону.
|
|
36
|
+
- `readMigrationCache` перехоплює помилки читання/парсингу й повертає `null` замість винятку (fail-safe); `writeMigrationCache` винятків не перехоплює.
|
|
37
|
+
- Кеш персистентний на диску і спільний для всіх repo/worktree на машині — не обмежений одним прогоном.
|