spectrum-cli 3.2.0 → 4.0.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +23 -1
  2. package/README.md +56 -26
  3. package/index.js +6 -174
  4. package/package.json +4 -4
  5. package/src/changelog/commands.js +44 -0
  6. package/src/changelog/format.js +51 -0
  7. package/src/changelog/fragments.js +223 -0
  8. package/src/{hotfix-changelog.js → changelog/hotfix.js} +2 -2
  9. package/src/changelog/index.js +7 -0
  10. package/src/{open-release.js → changelog/open-release.js} +6 -6
  11. package/src/changelog/release-block.js +80 -0
  12. package/src/chart/deploy.js +216 -0
  13. package/src/chart/helmrelease.js +90 -0
  14. package/src/chart/index.js +30 -0
  15. package/src/chart/metadata.js +114 -0
  16. package/src/chart/registry.js +139 -0
  17. package/src/chart/routes.js +244 -0
  18. package/src/chart/start.js +98 -0
  19. package/src/chart/verify.js +166 -0
  20. package/src/cli/command-executor.js +181 -0
  21. package/src/cli/commands.js +136 -0
  22. package/src/{development.js → cli/development.js} +1 -1
  23. package/src/cli/help.js +55 -0
  24. package/src/cli/run-action.js +28 -0
  25. package/src/{update-check.js → cli/update-check.js} +8 -2
  26. package/src/{git.js → git/index.js} +4 -3
  27. package/src/hotfix/checks.js +144 -0
  28. package/src/hotfix/close.js +46 -0
  29. package/src/hotfix/deploy.js +37 -0
  30. package/src/hotfix/git.js +35 -0
  31. package/src/hotfix/index.js +5 -0
  32. package/src/hotfix/start.js +57 -0
  33. package/src/integrations/gitlab-url.js +16 -0
  34. package/src/preflight/changelog.js +130 -0
  35. package/src/preflight/chart.js +57 -0
  36. package/src/preflight/git.js +133 -0
  37. package/src/preflight/helmrelease.js +201 -0
  38. package/src/preflight/index.js +10 -0
  39. package/src/preflight/result.js +18 -0
  40. package/src/preflight/source.js +95 -0
  41. package/src/preflight/tags.js +139 -0
  42. package/src/release/close.js +39 -0
  43. package/src/release/index.js +7 -0
  44. package/src/{stable-tags.js → release/stable-tags.js} +1 -1
  45. package/src/release/start.js +51 -0
  46. package/src/release/steps.js +83 -0
  47. package/src/release/version.js +20 -0
  48. package/src/token/config.js +163 -0
  49. package/src/token/gitlab-api.js +59 -0
  50. package/src/token/index.js +6 -0
  51. package/src/token/rotate.js +157 -0
  52. package/src/changelog.js +0 -418
  53. package/src/chart.js +0 -1071
  54. package/src/command-executor.js +0 -70
  55. package/src/hotfix.js +0 -208
  56. package/src/preflight.js +0 -796
  57. package/src/release.js +0 -207
  58. package/src/token.js +0 -399
  59. /package/src/{changelog-config.js → changelog/config.js} +0 -0
  60. /package/src/{utils.js → common/utils.js} +0 -0
  61. /package/src/{version.js → common/version.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -5,13 +5,35 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ This is the changelog of the `spectrum-cli` npm package. Its version comes from
9
+ `package.json`; the release commands use Git tags and `CHANGELOG.md` in the
10
+ **target project** and do not update that project's `package.json`.
11
+
12
+ ## 🚀 [4.0.0] - 2026-10-01
13
+
14
+ ### 💥 Breaking change
15
+
16
+ - `spectrum chart create <version>` заменена на `spectrum chart start`: версия берётся из верхнего заголовка `charts/<chart-name>/CHANGELOG.md` и больше не передаётся аргументом.
17
+ - `chart start` по умолчанию ждёт публикации чарта в Helm-registry. Прежний `--wait` заменён на `-n, --no-wait` для запуска без ожидания.
18
+ - Исходные модули перемещены из файлов `src/*.js` в каталоги с `index.js`; прямые импорты файлов со старыми путями требуют обновления.
19
+
20
+ ### 🆕 Added
21
+
22
+ - Все команды с действием поддерживают `-d, --dry` для запуска только предпроверок и `-s, --silence` для скрытия сообщений об успешно пройденных проверках. Для всех флагов доступны короткие формы.
23
+ - Перед созданием chart-тега проверяется, что версия из changelog ещё не существует среди локальных и удалённых Git-тегов и в Helm-registry.
24
+
25
+ ### 🛠 Changed
26
+
27
+ - Предпроверки сообщают, что именно проверено, выполняются все доступные проверки и завершают команду с ошибкой, если хотя бы одна не прошла; имя запущенной команды из сообщений убрано.
28
+ - Код CLI и тесты распределены по тематическим модулям; документация по установке, релизам и безопасности приведена в соответствие с текущим кодом.
29
+
8
30
  ## 🚀 [3.2.0] - 2026-10-01
9
31
 
10
32
  ### 🆕 Added
11
33
 
12
34
  - Добавлена команда `spectrum changelog write`: выравнивает заголовки релизов и хотфиксов и форматирует `CHANGELOG.md` через Prettier.
13
35
  - Добавлена команда `spectrum upgrade` для глобального обновления CLI через npm.
14
- - Перед выполнением команд CLI проверяет наличие новой версии в npm с таймаутом 200 мс и выводит версии и команду обновления; ошибка проверки не прерывает команду.
36
+ - Перед выполнением команд с действием CLI проверяет наличие новой версии в npm с таймаутом 200 мс и выводит версии и команду обновления; ошибка проверки не прерывает команду. `--help` и `--version` проверку не запускают.
15
37
 
16
38
  ### 🪲 Fixed
17
39
 
package/README.md CHANGED
@@ -40,7 +40,7 @@ npm install
40
40
  # Показать справку
41
41
  spectrum --help
42
42
  # Показать текущую версию CLI
43
- spectrum -v | --version
43
+ spectrum -v # или spectrum --version
44
44
 
45
45
  # 🚀 Управление релизами
46
46
  spectrum release start # Схлопнуть fragments и атомарно отправить release commit в dev и main/master
@@ -53,9 +53,9 @@ spectrum hotfix deploy # После merge MR: stable-тег на
53
53
  spectrum hotfix close # После stable pipeline: main/master → dev
54
54
 
55
55
  # 📈 Теги chart
56
- spectrum chart create 1.2.3 # Создать и запушить chart-$name-$version (требует запись в CHANGELOG.md чарта)
57
- spectrum chart create 1.2.3 --wait # После пуша тега дождаться публикации версии в Helm-registry
58
- spectrum chart create 1.2.2 --force # Разрешить версию не больше последней опубликованной
56
+ spectrum chart start # Создать тег по CHANGELOG.md чарта и дождаться публикации в Helm-registry
57
+ spectrum chart start -n # Не ждать публикации после отправки тега
58
+ spectrum chart start --force # Разрешить версию не выше последнего remote-тега, если ее еще нет в registry и Git
59
59
  spectrum chart deploy # Обновить helmrelease.yaml до последнего chart-тега и запушить
60
60
  spectrum chart deploy --instances sd,cbch # Обновить только выбранные инстансы instances/<name>
61
61
  spectrum chart verify ~/repo # Сравнить ingress paths AS IS vs TO BE для Next.js исходников
@@ -69,6 +69,31 @@ spectrum changelog write # Выравнять заголовки C
69
69
  spectrum token rotate # Пролить GITLAB_PRIVATE_TOKEN в CI variables
70
70
  ```
71
71
 
72
+ Все команды с действием показывают, какая предпроверка успешно пройдена;
73
+ при ошибке выводят её имя и причину. Независимые предпроверки продолжают
74
+ выполняться после ошибки; зависимые проверки без нужных данных помечаются как
75
+ невыполненные. Шаги команды запускаются, только если все проверки прошли.
76
+ `-s, --silence` скрывает сообщения об успешно пройденных предпроверках.
77
+ Ошибки проверок и итоговое сообщение команды остаются видны; флаг можно
78
+ совмещать с `-d, --dry`.
79
+ `-d, --dry` выполняет все предпроверки,
80
+ показывает их результаты и не запускает шаги записи, создания тегов,
81
+ публикации или деплоя. Предпроверки могут обращаться к `origin`, registry и
82
+ GitLab; `token rotate -d` запрашивает токены и проверяет доступ к целям, но не
83
+ изменяет CI variables. Если конфиг токенов отсутствует, `--dry` завершится
84
+ ошибкой и не создаст его. Для `upgrade -d` предпроверок нет: команда сообщает
85
+ об успехе, не запуская npm.
86
+
87
+ | Короткий флаг | Полный флаг | Где действует |
88
+ | --- | --- | --- |
89
+ | `-d` | `--dry` | Все команды с действием: только предпроверки |
90
+ | `-s` | `--silence` | Все команды с действием: скрыть успешные предпроверки |
91
+ | `-f` | `--force` | `chart start`: разрешить версию не больше последнего тега на `origin`; проверки Git-тега и Helm-registry обязательны |
92
+ | `-n` | `--no-wait` | `chart start`: не ждать появления версии в Helm-registry после push; по умолчанию команда ждёт |
93
+ | `-i <names>` | `--instances <names>` | `chart deploy`: выбрать инстансы через запятую |
94
+ | `-h` | `--help` | Справка CLI и команд |
95
+ | `-v` | `--version` | Версия CLI |
96
+
72
97
  ### Справка по командам:
73
98
 
74
99
  ```bash
@@ -103,24 +128,29 @@ spectrum token --help
103
128
 
104
129
  ```
105
130
  spectrum-cli/
106
- ├── index.js # 🚀 Главный CLI интерфейс
107
- ├── .changelog/ # 🧩 Независимые записи для следующего релиза
108
- ├── src/ # 📁 Исходный код
109
- │ ├── utils.js # 🛠️ Утилиты и логирование
110
- │ ├── git.js # 📝 Git операции
111
- │ ├── version.js # 📦 SemVer-хелперы (парсинг, сравнение, бамп)
112
- │ ├── changelog-config.js # 🧩 Типы и правила changelog fragments
113
- │ ├── changelog.js # 📋 Создание и сборка changelog fragments
114
- │ ├── chart.js # 📈 Создание и push chart тегов
115
- │ ├── token.js # 🔑 Ротация GitLab PAT и CI variables
116
- │ ├── development.js # ⚡ Dev команды (внутренние)
117
- │ └── release.js # 🚀 Release процесс
118
- ├── package.json # 📦 Конфигурация проекта
119
- ├── install.sh # 🔧 Автоматический установщик
120
- ├── INSTALL.md # 📋 Инструкции по установке
121
- └── README.md # 📖 Документация
131
+ ├── index.js # Точка входа CLI
132
+ ├── .changelog/ # Записи для следующего релиза
133
+ ├── src/
134
+ │ ├── cli/ # Команды, справка и запуск действий
135
+ │ ├── common/ # Общие утилиты и версии
136
+ │ ├── git/ # Git операции
137
+ │ ├── preflight/ # Независимые предпроверки по областям
138
+ │ ├── changelog/ # Fragments, конфиг и релизные разделы
139
+ │ ├── chart/ # Метаданные, теги, registry, verify и deploy
140
+ │ ├── release/ # Версии и этапы обычного релиза
141
+ │ ├── hotfix/ # Проверки и этапы hotfix
142
+ │ ├── token/ # Конфиг, GitLab API и ротация
143
+ │ ├── integrations/ # Общие функции внешних сервисов
144
+ ├── __tests__/ # Модульные и интеграционные тесты
145
+ ├── package.json
146
+ ├── install.sh
147
+ ├── INSTALL.md
148
+ └── README.md
122
149
  ```
123
150
 
151
+ Входы модулей находятся в `index.js` соответствующих каталогов. Например,
152
+ `require('./src/chart')` загружает `src/chart/index.js`; в корне `src` файлов нет.
153
+
124
154
  ### Команды
125
155
 
126
156
  | Команда | Описание |
@@ -130,7 +160,7 @@ spectrum-cli/
130
160
  | `spectrum release close` | Закрыть релиз |
131
161
  | `spectrum changelog append` | Создать changelog fragment |
132
162
  | `spectrum changelog check` | Проверить changelog и fragments |
133
- | `spectrum chart create` | Создать и запушить chart тег |
163
+ | `spectrum chart start` | Создать и запушить chart тег |
134
164
  | `spectrum chart deploy` | Обновить chart версию в helmrelease |
135
165
  | `spectrum chart verify` | Проверить ingress paths chart |
136
166
  | `spectrum token rotate` | Пролить GITLAB_PRIVATE_TOKEN в CI |
@@ -142,7 +172,7 @@ spectrum-cli/
142
172
  - `spectrum release close`: чистая и актуальная main/master, версия из верхнего заголовка `CHANGELOG.md` и remote stable-тег поддерживаемой формы, указывающий на текущий commit.
143
173
  - `spectrum changelog append <message>`: `git-repo`, `changelog-exists`, валидные ID задачи, git identity и тип fragment. Команда не изменяет общий `CHANGELOG.md`.
144
174
  - `spectrum changelog check`: `git-repo`, `changelog-exists`, `changelog-prettier-check`, наличие и формат всех changelog fragments.
145
- - `spectrum chart create <version>`: `git-repo`, `clean-working-tree`, `on-main-branch`, `valid-semver` (переданный `<version>` — semver), `single-chart` (ровно один `charts/<chart-name>/Chart.yaml`), `tag-missing` (тега `chart-<name>-<version>` нет локально и на `origin`).
175
+ - `spectrum chart start`: `git-repo`, `clean-working-tree`, `on-main-branch`, `single-chart` (ровно один `charts/<chart-name>/Chart.yaml`), валидный верхний SemVer-заголовок в `charts/<chart-name>/CHANGELOG.md`, отсутствие тега `chart-<name>-<version>` локально и на `origin`, отсутствие этой версии чарта в Helm-registry. Для запроса registry нужен `GITLAB_PRIVATE_TOKEN`; ошибка доступа блокирует команду. `-f, --force` обходит только проверку повышения версии относительно последнего remote-тега. После push команда по умолчанию ждёт версию в registry; `-n, --no-wait` отключает ожидание.
146
176
  - `spectrum chart deploy`: `git-repo`, `clean-working-tree`, `on-main-branch` (текущая ветка `main`), `remote-origin` (настроен `origin`), `remote-reachable` (доступен `origin`), `single-chart`, `helmrelease-files` (найдены `helmrelease.yaml`).
147
177
  - `spectrum chart verify <source_path>`: `git-repo`, `single-values-yaml` (ровно один `charts/**/values.yaml`), `values-ingress-sections` (есть `ingress.paths.api/pages/assets`), `source-path-directory`, `next-project`, `build-command-support`.
148
178
  - `spectrum token rotate`: `load-config` (есть валидный `~/.config/spectrum-cli/config.yaml`), `ask-tokens` (owner PAT и `GITLAB_PRIVATE_TOKEN` только в памяти), `check-access` (все группы и проекты доступны owner PAT).
@@ -266,14 +296,14 @@ pipeline и фактического выката выполняется отд
266
296
  revision обязательных образов и копирует точные RC digest без пересборки. Эту
267
297
  CI-часть реализует подключенный release component, а не Spectrum CLI.
268
298
 
269
- ### `spectrum chart create <version>`
299
+ ### `spectrum chart start`
270
300
 
271
301
  1. Ищет `Chart.yaml` в `charts/<chart-name>/Chart.yaml` и читает поле `name`
272
302
  2. Проверяет, что текущая ветка — `main`
273
- 3. Проверяет semver для переданной версии
303
+ 3. Читает версию из верхнего заголовка `charts/<chart-name>/CHANGELOG.md` и проверяет SemVer
274
304
  4. Собирает тег `chart-<name>-<version>`
275
- 5. Проверяет, что такого тега еще нет
276
- 6. Создает тег и пушит его в `origin`
305
+ 5. Проверяет, что такого тега еще нет локально и на `origin`, а версия отсутствует в Helm-registry
306
+ 6. Создает тег и пушит его в `origin`, затем ждёт появления версии в registry; `-n, --no-wait` отключает ожидание
277
307
 
278
308
  ### `spectrum chart deploy`
279
309
 
package/index.js CHANGED
@@ -1,177 +1,9 @@
1
1
  #!/usr/bin/env node
2
- const { Command } = require("commander");
3
- const program = new Command();
4
-
5
- const git = require("./src/git");
6
- const release = require("./src/release");
7
- const hotfix = require("./src/hotfix");
8
- const changelog = require("./src/changelog");
9
- const chart = require("./src/chart");
10
- const token = require("./src/token");
11
- const { checkForUpdates, upgrade } = require("./src/update-check");
12
- const { version: pkgVersion } = require("./package.json");
13
-
14
- async function runAction(action, ...args) {
15
- try {
16
- await checkForUpdates();
17
- } catch {
18
- // A failed update check must not affect the command.
19
- }
20
- try {
21
- const result = await action(...args);
22
- if (!result) {
23
- process.exit(1);
24
- }
25
- } catch (error) {
26
- console.error(`❌ Ошибка: ${error.message}`);
27
- process.exit(1);
28
- }
29
- }
30
-
31
- program
32
- .name("spectrum")
33
- .description("🚀 Spectrum CLI для процесса разработки")
34
- .version(pkgVersion, "-v, --version");
35
-
36
- program
37
- .command("upgrade")
38
- .description("Обновить Spectrum CLI через npm install -g spectrum-cli")
39
- .action(() => runAction(upgrade));
40
-
41
- // Команды релиза
42
- const releaseCmd = program
43
- .command("release")
44
- .description("Команды управления релизом");
45
-
46
- releaseCmd
47
- .command("start")
48
- .description("Схлопнуть fragments и атомарно отправить release commit в dev и main/master")
49
- .action(() => runAction(release.releaseStart));
50
-
51
- releaseCmd
52
- .command("close")
53
- .description("Свести стабильный релиз из main/master в dev")
54
- .action(() => runAction(release.releaseClose));
55
-
56
- releaseCmd
57
- .command("deploy")
58
- .description("Создать и отправить только стабильный тег release/X.Y.Z")
59
- .action(() => runAction(git.gitCreateTagAndPush));
60
-
61
- const hotfixCmd = program.command("hotfix").description("Изолированный цикл срочного patch-релиза");
62
-
63
- hotfixCmd.command("start")
64
- .description("Подготовить 🩹 CHANGELOG на hotfix/* или main/master, без commit, push и merge")
65
- .action(() => runAction(hotfix.hotfixStart));
66
- hotfixCmd.command("deploy")
67
- .description("После merge хотфикса отправить stable-тег с main/master")
68
- .action(() => runAction(hotfix.hotfixDeploy));
69
- hotfixCmd.command("close")
70
- .description("После stable pipeline свести production в dev")
71
- .action(() => runAction(hotfix.hotfixClose));
72
-
73
- // Команды changelog
74
- const changelogCmd = program
75
- .command("changelog")
76
- .description("Команды управления changelog fragments");
77
-
78
- changelogCmd
79
- .command("append <message>")
80
- .description("Создать changelog fragment с номером задачи из ветки")
81
- .action((message) => runAction(() => changelog.changelogAppend(message)));
82
-
83
- changelogCmd
84
- .command("check")
85
- .description("Проверить CHANGELOG.md и changelog fragments")
86
- .action(() => runAction(changelog.changelogCheck));
87
-
88
- changelogCmd
89
- .command("write")
90
- .description("Привести заголовки CHANGELOG.md к формату релиза и запустить Prettier")
91
- .action(() => runAction(changelog.changelogWrite));
92
-
93
- // Команды chart
94
- const chartCmd = program
95
- .command("chart")
96
- .description("Команды управления тегами chart");
97
-
98
- chartCmd
99
- .command("create <version>")
100
- .description("Создать и отправить chart-тег (chart-<name>-<version>)")
101
- .option("--force", "Разрешить версию не больше последней опубликованной")
102
- .option("--wait", "Дождаться публикации версии чарта в Helm-registry")
103
- .action((version, options) => runAction(() => chart.chartCreateTag(version, options)));
104
-
105
- chartCmd
106
- .command("verify <source_path>")
107
- .description("Проверить пути ingress chart относительно исходников Next.js")
108
- .action((sourcePath) => runAction(() => chart.chartVerify(sourcePath)));
109
-
110
- chartCmd
111
- .command("deploy")
112
- .description("Задеплоить последнюю версию chart в файлы helmrelease")
113
- .option("--instances <names>", "Список инстансов через запятую (по умолчанию все)")
114
- .action((options) => runAction(() => chart.chartDeploy(options)));
115
-
116
- const tokenCmd = program
117
- .command("token")
118
- .description("Команды управления GitLab токенами");
119
-
120
- tokenCmd
121
- .command("rotate")
122
- .description("Пролить GITLAB_PRIVATE_TOKEN в CI variables через owner PAT")
123
- .action(() => runAction(token.tokenRotate));
124
-
125
- // Переопределяем help, чтобы показать кастомный формат с алиасами
126
- program.configureHelp({
127
- formatHelp: (cmd, helper) => {
128
- const cmdUsage = helper.commandUsage(cmd);
129
- const cmdDescription = helper.commandDescription(cmd);
130
- const options = helper.visibleOptions(cmd);
131
- const commands = helper.visibleCommands(cmd);
132
-
133
- let output = "";
134
-
135
- // Использование
136
- if (cmdUsage) {
137
- output += `Использование: ${cmdUsage}\n\n`;
138
- }
139
-
140
- // Описание
141
- if (cmdDescription) {
142
- output += `${cmdDescription}\n\n`;
143
- }
144
-
145
- // Команды с алиасами
146
- if (commands.length > 0) {
147
- output += "Команды:\n";
148
- commands.forEach((cmd) => {
149
- const nameAndArgs = cmd.name() + cmd.usage().replace(/^[^\s]+\s*/, " ");
150
- const aliases = cmd.aliases();
151
- const description = cmd.description();
152
-
153
- let cmdLine = ` ${nameAndArgs}`;
154
- if (aliases.length > 0) {
155
- cmdLine = cmdLine.padEnd(25) + `[${aliases.join(", ")}]`;
156
- }
157
- cmdLine = cmdLine.padEnd(40) + description;
158
- output += cmdLine + "\n";
159
- });
160
- output += "\n";
161
- }
162
-
163
- // Опции
164
- if (options.length > 0) {
165
- output += "Опции:\n";
166
- options.forEach((option) => {
167
- const flags = helper.optionTerm(option);
168
- const description = helper.optionDescription(option);
169
- output += ` ${flags.padEnd(25)} ${description}\n`;
170
- });
171
- }
172
-
173
- return output;
174
- },
175
- });
2
+ const { Command } = require('commander');
3
+ const { registerCommands } = require('./src/cli/commands');
4
+ const { configureHelp } = require('./src/cli/help');
176
5
 
6
+ const program = new Command();
7
+ registerCommands(program);
8
+ configureHelp(program);
177
9
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spectrum-cli",
3
- "version": "3.2.0",
3
+ "version": "4.0.0",
4
4
  "description": "🚀 Modern CLI for development workflow automation - release management, versioning, and changelog generation",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -71,9 +71,9 @@
71
71
  "testEnvironment": "node",
72
72
  "collectCoverageFrom": [
73
73
  "index.js",
74
- "src/command-executor.js",
75
- "src/development.js",
76
- "src/utils.js"
74
+ "src/cli/command-executor.js",
75
+ "src/cli/development.js",
76
+ "src/common/utils.js"
77
77
  ],
78
78
  "testMatch": [
79
79
  "**/__tests__/**/*.js",
@@ -0,0 +1,44 @@
1
+ const { runCommand } = require('../cli/command-executor');
2
+ const { requireGitRepo, requireFileExists, requirePrettierAvailable, requireChangelogFormatted, requireChangelogFragments } = require('../preflight');
3
+ const { CHANGELOG_FILE } = require('./config');
4
+ const { prepareChangelogEntry, appendPreparedChangelogEntry } = require('./fragments');
5
+ const { writeCurrentChangelog } = require('./format');
6
+ function changelogAppend(message) {
7
+ return runCommand({
8
+ name: 'changelog append',
9
+ checks: [
10
+ { name: 'git-repo', run: requireGitRepo },
11
+ { name: 'changelog-exists', run: () => requireFileExists(CHANGELOG_FILE) },
12
+ { name: 'prepare-fragment', dependsOn: ['git-repo', 'changelog-exists'], run: () => prepareChangelogEntry(message) }
13
+ ],
14
+ steps: [
15
+ { name: 'write-fragment', run: appendPreparedChangelogEntry }
16
+ ]
17
+ });
18
+ }
19
+
20
+ function changelogCheck() {
21
+ return runCommand({
22
+ name: 'changelog check',
23
+ checks: [
24
+ { name: 'git-repo', run: requireGitRepo },
25
+ { name: 'changelog-exists', run: () => requireFileExists(CHANGELOG_FILE) },
26
+ { name: 'changelog-prettier-check', run: requireChangelogFormatted },
27
+ { name: 'changelog-fragments', run: requireChangelogFragments }
28
+ ]
29
+ });
30
+ }
31
+
32
+ function changelogWrite() {
33
+ return runCommand({
34
+ name: 'changelog write',
35
+ checks: [
36
+ { name: 'git-repo', run: requireGitRepo },
37
+ { name: 'changelog-exists', run: () => requireFileExists(CHANGELOG_FILE) },
38
+ { name: 'prettier-available', run: requirePrettierAvailable }
39
+ ],
40
+ steps: [{ name: 'write-changelog', run: writeCurrentChangelog }]
41
+ });
42
+ }
43
+
44
+ module.exports = { changelogAppend, changelogCheck, changelogWrite };
@@ -0,0 +1,51 @@
1
+ const fs = require('fs');
2
+ const { logError, execCommand } = require('../common/utils');
3
+ const { CHANGELOG_FILE } = require('./config');
4
+ function normalizeChangelogHeadings(text) {
5
+ const version = '(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)';
6
+ const date = '(\\d{4}-\\d{2}-\\d{2}|\\d{4}\\.\\d{2}\\.\\d{2}|\\d{2}\\.\\d{2}\\.\\d{4})';
7
+ const releaseHeading = new RegExp(`^##[ \\t]*(?:(🚀|🩹)[ \\t]*)?\\[${version}\\](?:[ \\t]+-[ \\t]+${date})?[ \\t]*\\r?$`);
8
+ return text.replace(/^##(?!#)[^\n]*$/gm, (heading) => {
9
+ const match = releaseHeading.exec(heading);
10
+ if (!match) throw new Error(`Некорректный заголовок ${CHANGELOG_FILE}: ${heading.trimEnd()}`);
11
+ const marker = match[1] ? `${match[1]} ` : '';
12
+ const releaseVersion = `${match[2]}.${match[3]}.${match[4]}`;
13
+ const releaseDate = match[5] ? ` - ${match[5]}` : '';
14
+ return `## ${marker}[${releaseVersion}]${releaseDate}${heading.endsWith('\r') ? '\r' : ''}`;
15
+ });
16
+ }
17
+
18
+ function writeCurrentChangelog(context) {
19
+ let original;
20
+ let mayHaveChanged = false;
21
+ try {
22
+ original = fs.readFileSync(CHANGELOG_FILE, 'utf8');
23
+ const normalized = normalizeChangelogHeadings(original);
24
+ if (normalized !== original) {
25
+ mayHaveChanged = true;
26
+ fs.writeFileSync(CHANGELOG_FILE, normalized);
27
+ }
28
+ mayHaveChanged = true;
29
+ if (!execCommand(`${context.prettierRunner} --write ${CHANGELOG_FILE}`) ||
30
+ !execCommand(`${context.prettierRunner} --check ${CHANGELOG_FILE}`)) {
31
+ throw new Error('Prettier не смог отформатировать CHANGELOG.md.');
32
+ }
33
+ const formatted = fs.readFileSync(CHANGELOG_FILE, 'utf8');
34
+ if (normalizeChangelogHeadings(formatted) !== formatted) {
35
+ throw new Error('После Prettier заголовки CHANGELOG.md не соответствуют формату релизов.');
36
+ }
37
+ return true;
38
+ } catch (error) {
39
+ if (mayHaveChanged && original !== undefined) {
40
+ try {
41
+ fs.writeFileSync(CHANGELOG_FILE, original);
42
+ } catch (restoreError) {
43
+ logError('❌', 'Не удалось восстановить %s: %s', CHANGELOG_FILE, restoreError.message);
44
+ }
45
+ }
46
+ logError('❌', 'Не удалось отформатировать %s: %s', CHANGELOG_FILE, error.message);
47
+ return false;
48
+ }
49
+ }
50
+
51
+ module.exports = { normalizeChangelogHeadings, writeCurrentChangelog };