gitverse-release 3.3.0 → 3.3.2

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/README.md CHANGED
@@ -15,6 +15,8 @@
15
15
  - 🏷️ **Автоматические git теги и коммиты**
16
16
  - 🔍 **Режим dry-run** для безопасного тестирования
17
17
  - ⚙️ **Гибкая конфигурация** через `.gitversereleaserc.json`
18
+ - 🔧 **Автогенерация commitlint конфига** из release конфигурации
19
+ - 🪝 **Хуки beforeCommit** для автоматической подготовки файлов перед релизом
18
20
 
19
21
  ## Установка
20
22
 
@@ -224,6 +226,88 @@ git commit -m "fix(ui): исправлена ошибка компонента"
224
226
  git commit -m "chore: обновлены зависимости"
225
227
  ```
226
228
 
229
+ ## Генерация Commitlint конфига
230
+
231
+ Инструмент может автоматически генерировать `commitlint.config.ts` из вашей release конфигурации, обеспечивая синхронизацию между форматом коммитов и генерацией релизов.
232
+
233
+ ### Быстрый старт
234
+
235
+ ```bash
236
+ # Генерировать commitlint конфиг
237
+ npx gitverse-release generate-commitlint
238
+
239
+ # Preview без записи файла
240
+ npx gitverse-release generate-commitlint --dry-run
241
+
242
+ # С кастомными параметрами
243
+ npx gitverse-release generate-commitlint --format js --output .commitlintrc.js
244
+ ```
245
+
246
+ ### Что генерируется?
247
+
248
+ Команда автоматически извлекает:
249
+ - **Типы коммитов** из `changelog.types` (feat, fix, docs, etc.)
250
+ - **Scopes** из названий пакетов в монорепо + дополнительные scopes
251
+ - **Правила валидации** (max length, case, required fields)
252
+
253
+ ### Конфигурация defaults
254
+
255
+ Добавьте секцию `commitlint` в `.gitversereleaserc.json`:
256
+
257
+ ```json
258
+ {
259
+ "commitlint": {
260
+ "output": "commitlint.config.ts",
261
+ "format": "ts",
262
+ "scopes": ["deps", "ci", "docs"],
263
+ "scopeRequired": false,
264
+ "headerMaxLength": 100,
265
+ "comments": true
266
+ }
267
+ }
268
+ ```
269
+
270
+ Теперь можно просто запускать `npx gitverse-release generate-commitlint` без параметров!
271
+
272
+ ### CLI опции
273
+
274
+ ```bash
275
+ --output <path> Путь для вывода (default: из конфига)
276
+ --format <ts|js|json> Формат файла (default: из конфига)
277
+ --scopes <list> Дополнительные scopes через запятую
278
+ --scope-required Требовать обязательный scope
279
+ --header-max-length <n> Максимальная длина header
280
+ --no-comments Отключить комментарии в файле
281
+ --dry-run Preview без записи
282
+ --verbose Подробный вывод
283
+ ```
284
+
285
+ ### Примеры использования
286
+
287
+ ```bash
288
+ # Использовать настройки из конфига
289
+ npx gitverse-release generate-commitlint
290
+
291
+ # Preview сгенерированного файла
292
+ npx gitverse-release generate-commitlint --dry-run --verbose
293
+
294
+ # Генерировать JS вместо TS
295
+ npx gitverse-release generate-commitlint --format js --output .commitlintrc.js
296
+
297
+ # Добавить дополнительные scopes
298
+ npx gitverse-release generate-commitlint --scopes api,cli,tests
299
+
300
+ # Требовать обязательный scope
301
+ npx gitverse-release generate-commitlint --scope-required
302
+ ```
303
+
304
+ ### Преимущества
305
+
306
+ ✅ **Единый источник истины** - типы и scopes определяются в одном месте
307
+ ✅ **Автоматическая синхронизация** - изменения в release конфиге автоматически отражаются в commitlint
308
+ ✅ **Меньше ошибок** - нет риска рассинхронизации конфигов
309
+ ✅ **CI-friendly** - легко интегрируется в pipelines для проверки актуальности конфига
310
+
227
311
  ## Конфигурация
228
312
 
229
313
  ### Полный пример
@@ -231,6 +315,7 @@ git commit -m "chore: обновлены зависимости"
231
315
  ```json
232
316
  {
233
317
  "git": {
318
+ "beforeCommit": "bun run lint:fix",
234
319
  "commitMessage": "chore(release): v{version} [skip ci]",
235
320
  "tagMessage": "Release {version}",
236
321
  "push": true,
@@ -265,10 +350,54 @@ git commit -m "chore: обновлены зависимости"
265
350
  "monorepo": {
266
351
  "enabled": false,
267
352
  "packages": []
353
+ },
354
+ "commitlint": {
355
+ "output": "commitlint.config.ts",
356
+ "format": "ts",
357
+ "scopes": ["deps", "ci", "docs"],
358
+ "scopeRequired": false,
359
+ "headerMaxLength": 100,
360
+ "comments": true
361
+ }
362
+ }
363
+ ```
364
+
365
+ ### beforeCommit хук
366
+
367
+ Хук `beforeCommit` запускается **перед проверкой чистоты working tree** и позволяет автоматически подготовить файлы перед созданием релиза:
368
+
369
+ ```json
370
+ {
371
+ "git": {
372
+ "beforeCommit": "bun run lint:fix"
268
373
  }
269
374
  }
270
375
  ```
271
376
 
377
+ **Как это работает:**
378
+ 1. Запускается команда `beforeCommit` (например, линтер)
379
+ 2. Если команда выполнилась успешно (exit code 0):
380
+ - Все измененные файлы добавляются в staging area (`git add -A`)
381
+ - Эти изменения войдут в коммит релиза вместе с version/changelog
382
+ 3. Проверяется чистота working tree (только unstaged изменения)
383
+
384
+ **Примеры использования:**
385
+ ```json
386
+ // Форматирование кода
387
+ "beforeCommit": "bun run format"
388
+
389
+ // Линтинг и автофикс
390
+ "beforeCommit": "bun run lint:fix"
391
+
392
+ // Обновление lock файлов
393
+ "beforeCommit": "bun install --frozen-lockfile=false"
394
+
395
+ // Несколько команд
396
+ "beforeCommit": "bun run lint:fix && bun run format"
397
+ ```
398
+
399
+ **Важно:** Если хук завершается с ошибкой (exit code ≠ 0), изменения НЕ добавляются в staging area и релиз продолжается как обычно.
400
+
272
401
  ### Переменные окружения
273
402
 
274
403
  ```bash
@@ -332,19 +461,53 @@ env:
332
461
 
333
462
  ## CLI опции
334
463
 
335
- ```
464
+ ### Основные команды
465
+
466
+ ```bash
467
+ # Создать релиз
336
468
  npx gitverse-release [package] [options]
337
469
 
470
+ # Генерировать commitlint конфиг
471
+ npx gitverse-release generate-commitlint [options]
472
+
473
+ # Создать только GitVerse Release для существующего тега (recovery mode)
474
+ npx gitverse-release create-only --tag <tag> [--package <name>]
475
+ ```
476
+
477
+ ### Опции для релиза
478
+
479
+ ```
338
480
  Аргументы:
339
481
  package Имя пакета для релиза (для монорепозиториев)
340
482
 
341
483
  Опции:
342
484
  --dry-run Запуск без внесения изменений
485
+ --config <path> Путь к конфиг файлу (default: .gitversereleaserc.json)
486
+ --package <name> Имя пакета для релиза (для монорепо)
343
487
  --version <version> Указать конкретную версию
344
- --skip-git-check Пропустить проверку git статуса
488
+ --prerelease [tag] Создать prerelease версию (beta, alpha, rc)
489
+ --no-commit Не создавать git коммит
490
+ --no-tag Не создавать git тег
491
+ --no-push Не пушить в remote
492
+ --no-release Не создавать GitVerse Release
493
+ --verbose Подробный вывод
345
494
  --help Показать справку
346
495
  ```
347
496
 
497
+ ### Опции для generate-commitlint
498
+
499
+ ```
500
+ Опции:
501
+ --output <path> Путь для вывода (default: commitlint.config.ts)
502
+ --format <ts|js|json> Формат файла (default: ts)
503
+ --scopes <scopes> Дополнительные scopes (comma-separated)
504
+ --scope-required Требовать обязательный scope
505
+ --header-max-length <n> Максимальная длина header (default: 100)
506
+ --no-comments Отключить комментарии в файле
507
+ --dry-run Preview без записи
508
+ --verbose Подробный вывод
509
+ ```
510
+
348
511
  ## CI/CD интеграция
349
512
 
350
513
  ### GitVerse Actions
package/dist/cli.js CHANGED
@@ -601,9 +601,9 @@ class q3 {
601
601
  }
602
602
 
603
603
  // ../sdk/dist/client.js
604
- var F = { DELETE: "DELETE", GET: "GET", PATCH: "PATCH", POST: "POST", PUT: "PUT" };
604
+ var J2 = { DELETE: "DELETE", GET: "GET", PATCH: "PATCH", POST: "POST", PUT: "PUT" };
605
605
 
606
- class _ {
606
+ class $ {
607
607
  baseUrl;
608
608
  token;
609
609
  apiVersion;
@@ -615,10 +615,11 @@ class _ {
615
615
  this.token = j5;
616
616
  }
617
617
  extractRateLimitInfo(j5) {
618
- let x2 = j5.get("GitVerse-RateLimit-Limit"), q4 = j5.get("GitVerse-RateLimit-Remaining"), z2 = j5.get("GitVerse-RateLimit-Retry-After"), B2 = j5.get("Gitverse-Ratelimit-Reset");
619
- if (!(x2 && q4 && z2 && B2))
618
+ let x2 = j5.get("GitVerse-RateLimit-Limit"), q4 = j5.get("GitVerse-RateLimit-User-Remaining") || j5.get("GitVerse-RateLimit-Remaining"), z2 = j5.get("GitVerse-RateLimit-Retry-After"), B2 = j5.get("Gitverse-Ratelimit-Reset");
619
+ if (!(x2 && z2))
620
620
  return;
621
- return { limit: Number.parseInt(x2, 10), remaining: Number.parseInt(q4, 10), reset: Number.parseInt(B2, 10), retryAfter: Number.parseInt(z2, 10) };
621
+ let K = Number.parseInt(z2, 10), D = B2 ? Number.parseInt(B2, 10) : Math.floor(Date.now() / 1000) + K, U = q4 ? Number.parseInt(q4, 10) : 0;
622
+ return { limit: Number.parseInt(x2, 10), remaining: U, reset: D, retryAfter: K };
622
623
  }
623
624
  extractApiVersionInfo(j5) {
624
625
  let x2 = j5.get("Gitverse-Api-Version"), q4 = j5.get("Gitverse-Api-Latest-Version"), z2 = j5.get("Gitverse-Api-Deprecation") === "true", B2 = j5.get("Gitverse-Api-Decommissioning");
@@ -635,37 +636,37 @@ class _ {
635
636
  return { apiVersion: q4, rateLimit: x2 };
636
637
  }
637
638
  async request(j5, x2, q4, z2) {
638
- let B2 = j5.startsWith("/") ? j5.slice(1) : j5, Y = `${this.baseUrl}/${B2}`, J2 = new Headers;
639
- if (J2.set("Content-Type", "application/json"), J2.set("Accept", `application/vnd.gitverse.object+json; version=${this.apiVersion}`), this.token)
640
- J2.set("Authorization", `Bearer ${this.token}`);
641
- let Z = { body: q4 ? JSON.stringify(q4) : undefined, headers: J2, method: x2, signal: z2?.signal }, D = await fetch(Y, Z), K = this.extractMetadata(D.headers), N;
639
+ let B2 = j5.startsWith("/") ? j5.slice(1) : j5, K = `${this.baseUrl}/${B2}`, D = new Headers;
640
+ if (D.set("Content-Type", "application/json"), D.set("Accept", `application/vnd.gitverse.object+json; version=${this.apiVersion}`), this.token)
641
+ D.set("Authorization", `Bearer ${this.token}`);
642
+ let U = { body: q4 ? JSON.stringify(q4) : undefined, headers: D, method: x2, signal: z2?.signal }, F = await fetch(K, U), Q = this.extractMetadata(F.headers), S;
642
643
  try {
643
- N = await D.json();
644
+ S = await F.json();
644
645
  } catch {
645
- N = undefined;
646
+ S = undefined;
646
647
  }
647
- if (!D.ok) {
648
- let Q = N?.message || D.statusText;
649
- if (D.status === 429 && K.rateLimit)
650
- throw new k2(Q || "Превышен лимит запросов. Попробуйте позже.", K.rateLimit, K);
651
- throw new j2(D.status, Q, K);
648
+ if (!F.ok) {
649
+ let X = S?.message || F.statusText;
650
+ if (F.status === 429 && Q.rateLimit)
651
+ throw new k2(X || "Превышен лимит запросов. Попробуйте позже.", Q.rateLimit, Q);
652
+ throw new j2(F.status, X, Q);
652
653
  }
653
- return N;
654
+ return S;
654
655
  }
655
656
  get(j5, x2) {
656
- return this.request(j5, F.GET, undefined, x2);
657
+ return this.request(j5, J2.GET, undefined, x2);
657
658
  }
658
659
  post(j5, x2, q4) {
659
- return this.request(j5, F.POST, x2, q4);
660
+ return this.request(j5, J2.POST, x2, q4);
660
661
  }
661
662
  put(j5, x2, q4) {
662
- return this.request(j5, F.PUT, x2, q4);
663
+ return this.request(j5, J2.PUT, x2, q4);
663
664
  }
664
665
  delete(j5, x2, q4) {
665
- return this.request(j5, F.DELETE, x2, q4);
666
+ return this.request(j5, J2.DELETE, x2, q4);
666
667
  }
667
668
  patch(j5, x2, q4) {
668
- return this.request(j5, F.PATCH, x2, q4);
669
+ return this.request(j5, J2.PATCH, x2, q4);
669
670
  }
670
671
  }
671
672
 
@@ -720,7 +721,7 @@ class Z {
720
721
  git;
721
722
  actions;
722
723
  constructor(d2 = {}) {
723
- this.client = new _(d2), this.users = new f(this.client), this.repos = new A(this.client), this.contents = new k4(this.client), this.pulls = new y(this.client), this.forks = new q3(this.client), this.emails = new d(this.client), this.issues = new B(this.client), this.stars = new k3(this.client), this.branches = new g(this.client), this.commits = new j(this.client), this.collaborators = new j3(this.client), this.organizations = new k(this.client), this.teams = new k5(this.client), this.releases = new q2(this.client), this.git = new j4(this.client), this.actions = new x(this.client);
724
+ this.client = new $(d2), this.users = new f(this.client), this.repos = new A(this.client), this.contents = new k4(this.client), this.pulls = new y(this.client), this.forks = new q3(this.client), this.emails = new d(this.client), this.issues = new B(this.client), this.stars = new k3(this.client), this.branches = new g(this.client), this.commits = new j(this.client), this.collaborators = new j3(this.client), this.organizations = new k(this.client), this.teams = new k5(this.client), this.releases = new q2(this.client), this.git = new j4(this.client), this.actions = new x(this.client);
724
725
  }
725
726
  setToken(d2) {
726
727
  return this.client.setToken(d2), this;
@@ -843,6 +844,12 @@ function groupCommitsByType(commits) {
843
844
  }
844
845
  return groups;
845
846
  }
847
+ function filterCommitsForPackage(commits, packageName, isMonorepo) {
848
+ if (!isMonorepo) {
849
+ return commits;
850
+ }
851
+ return commits.filter((commit) => !commit.scope || commit.scope === packageName);
852
+ }
846
853
  function hasBreakingChanges(commits) {
847
854
  return commits.some((commit) => commit.breaking);
848
855
  }
@@ -1139,12 +1146,6 @@ async function isWorkingTreeClean() {
1139
1146
  return false;
1140
1147
  }
1141
1148
  }
1142
- async function addChangedFiles() {
1143
- console.log("\uD83D\uDCE5 Adding changed files to staging area...");
1144
- await git("add -A");
1145
- console.log(`✅ Files added to staging area
1146
- `);
1147
- }
1148
1149
  async function tagExists(tag) {
1149
1150
  try {
1150
1151
  await git(`rev-parse "${tag}"`);
@@ -1384,13 +1385,13 @@ async function updatePackageFiles(pkg, newVersion, changelogEntry) {
1384
1385
  await updateChangelogFile(changelogPath, changelogEntry);
1385
1386
  return changelogPath;
1386
1387
  }
1387
- async function collectCommits(pkg, options, warnings) {
1388
+ async function collectCommits(pkg, options, warnings, isMonorepo) {
1388
1389
  const rawCommits = await getCommitsSinceTag(pkg.tagPrefix, pkg.path);
1389
1390
  if (rawCommits.length === 0 && !options.version) {
1390
1391
  throw new Error(`No commits found since last release for ${pkg.packageName}`);
1391
1392
  }
1392
1393
  const commits = parseCommits(rawCommits);
1393
- const filteredCommits = commits.filter((commit) => !commit.scope || commit.scope === pkg.name);
1394
+ const filteredCommits = filterCommitsForPackage(commits, pkg.name, isMonorepo);
1394
1395
  if (filteredCommits.length === 0 && !options.version) {
1395
1396
  warnings.push("No conventional commits found, but will proceed with patch bump");
1396
1397
  }
@@ -1426,6 +1427,12 @@ async function createGitVerseRelease(options, repoInfo, commits, config, pkg, ve
1426
1427
  }
1427
1428
  async function performGitOperations(config, options, pkg, newVersion, tag, changelogPath) {
1428
1429
  if (config.git.commitChanges && !options.noCommit) {
1430
+ if (config.git.beforeCommit) {
1431
+ const hookSuccess = await runBeforeCommitHook(config.git.beforeCommit);
1432
+ if (!hookSuccess) {
1433
+ console.warn("⚠️ beforeCommit hook failed, proceeding with unformatted files");
1434
+ }
1435
+ }
1429
1436
  const commitMessage = config.git.commitMessage.replace("{{package}}", pkg.name).replace("{{version}}", newVersion);
1430
1437
  await createCommit(commitMessage, [resolve2(pkg.path, "package.json"), changelogPath]);
1431
1438
  }
@@ -1452,18 +1459,12 @@ async function release(packageName, options = {}) {
1452
1459
  const repoInfo = await getRepoInfo();
1453
1460
  const pkg = await resolvePackage(config, packageName);
1454
1461
  result.packageName = pkg.packageName;
1455
- if (!options.dryRun && config.git.beforeCommit) {
1456
- const hookSuccess = await runBeforeCommitHook(config.git.beforeCommit);
1457
- if (hookSuccess) {
1458
- await addChangedFiles();
1459
- }
1460
- }
1461
1462
  if (!(options.dryRun || await isWorkingTreeClean())) {
1462
1463
  throw new Error("Working tree is not clean. Please commit or stash your changes.");
1463
1464
  }
1464
1465
  const currentVersion = await getCurrentVersion(pkg.path);
1465
1466
  result.oldVersion = currentVersion;
1466
- const commits = await collectCommits(pkg, options, result.warnings);
1467
+ const commits = await collectCommits(pkg, options, result.warnings, config.monorepo.enabled);
1467
1468
  const prereleaseTag = resolvePrereleaseTag(options, config);
1468
1469
  const versionBump = calculateVersionBump(currentVersion, commits, options.version, prereleaseTag, config.versioning.preMajorMode);
1469
1470
  result.newVersion = versionBump.newVersion;
@@ -1887,4 +1888,4 @@ Usage: gitverse-release create-only --tag <tag> [--package <name>]`);
1887
1888
  }
1888
1889
  main();
1889
1890
 
1890
- //# debugId=47967437F070163A64756E2164756E21
1891
+ //# debugId=A2D09E9236DE6AFE64756E2164756E21