spectrum-cli 5.1.0 → 6.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.
- package/CHANGELOG.md +15 -0
- package/README.md +99 -24
- package/package.json +1 -1
- package/src/changelog/commands.js +1 -1
- package/src/changelog/open-release.js +9 -1
- package/src/chart/deploy.js +3 -3
- package/src/chart/start.js +2 -2
- package/src/chart/verify.js +5 -5
- package/src/cli/command-executor.js +33 -4
- package/src/cli/commands.js +22 -1
- package/src/cli/help.js +4 -1
- package/src/cli/run-action.js +1 -1
- package/src/git/index.js +1 -1
- package/src/hotfix/checks.js +27 -4
- package/src/hotfix/close.js +3 -3
- package/src/hotfix/start.js +2 -2
- package/src/preflight/helmrelease.js +11 -5
- package/src/preflight/source.js +13 -14
- package/src/preflight/tags.js +9 -1
- package/src/release/start.js +14 -9
- package/src/sbom/populate.js +3 -3
- package/src/security/repair.js +2 -2
- package/src/security/tracking.js +33 -29
- package/src/token/rotate.js +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,21 @@ This is the changelog of the `spectrum-cli` npm package. Its version comes from
|
|
|
9
9
|
`package.json`; the release commands use Git tags and `CHANGELOG.md` in the
|
|
10
10
|
**target project** and do not update that project's `package.json`.
|
|
11
11
|
|
|
12
|
+
## 🚀 [6.0.0] - 2026-10-29
|
|
13
|
+
|
|
14
|
+
### Breaking change
|
|
15
|
+
|
|
16
|
+
- У `spectrum chart start` короткий флаг `-n` теперь означает `--no-verify` и отключает preflight. Для запуска без ожидания публикации чарта используйте `--no-wait`.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- Во всех 21 командах с действием доступен `-n, --no-verify`: команда пропускает preflight и проверку обновлений CLI, сохраняя чтение необходимых входных данных и обработку ошибок операций. Поддерживается сочетание `-ns`; `--dry` и `--no-verify` несовместимы.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- README содержит полный список команд, аргументов и опций; справка вложенных команд использует общий формат и показывает актуальные флаги.
|
|
25
|
+
- Добавлены проверки разбора флагов у всех команд, соответствия README регистрации команд и выполнения release/hotfix без preflight на временных Git origins, а также обхода preflight автоматизации.
|
|
26
|
+
|
|
12
27
|
## 🚀 [5.1.0]
|
|
13
28
|
|
|
14
29
|
### Changed
|
package/README.md
CHANGED
|
@@ -18,7 +18,8 @@ spectrum --help
|
|
|
18
18
|
в npm Registry. Если доступна новая версия, показывает текущую и новую версии и
|
|
19
19
|
команду `spectrum upgrade`. Она запускает `npm install -g spectrum-cli`.
|
|
20
20
|
Проверка ограничена 200 мс; недоступность Registry не влияет на выполнение
|
|
21
|
-
команды и не выводит ошибку.
|
|
21
|
+
команды и не выводит ошибку. С `--dry` или `--no-verify` проверка обновлений
|
|
22
|
+
не выполняется.
|
|
22
23
|
|
|
23
24
|
### 🔧 Из исходников
|
|
24
25
|
|
|
@@ -42,6 +43,9 @@ spectrum --help
|
|
|
42
43
|
# Показать текущую версию CLI
|
|
43
44
|
spectrum -v # или spectrum --version
|
|
44
45
|
|
|
46
|
+
# Обновить установленный CLI
|
|
47
|
+
spectrum upgrade
|
|
48
|
+
|
|
45
49
|
# 🚀 Управление релизами
|
|
46
50
|
spectrum release start [--stable] # Схлопнуть fragments и атомарно отправить release commit в dev и main/master
|
|
47
51
|
spectrum release deploy # Создать стабильный тег release/X.Y.Z из заголовка CHANGELOG
|
|
@@ -54,7 +58,7 @@ spectrum hotfix close # После stable pipeline: main/master
|
|
|
54
58
|
|
|
55
59
|
# 📈 Теги chart
|
|
56
60
|
spectrum chart start # Создать тег по CHANGELOG.md чарта и дождаться публикации в Helm-registry
|
|
57
|
-
spectrum chart start -
|
|
61
|
+
spectrum chart start --no-wait # Не ждать публикации после отправки тега
|
|
58
62
|
spectrum chart start --force # Разрешить версию не выше последнего remote-тега, если ее еще нет в registry и Git
|
|
59
63
|
spectrum chart deploy # Обновить helmrelease.yaml до последнего chart-тега и запушить
|
|
60
64
|
spectrum chart deploy --instances sd,cbch # Обновить только выбранные инстансы instances/<name>
|
|
@@ -67,12 +71,42 @@ spectrum changelog write # Выравнять заголовки C
|
|
|
67
71
|
|
|
68
72
|
# 🔑 GitLab токены
|
|
69
73
|
spectrum token rotate # Пролить SC_BOT_PAT в CI variables
|
|
74
|
+
|
|
75
|
+
# CI: Next.js, SBOM и security
|
|
76
|
+
spectrum nextjs licenses
|
|
77
|
+
spectrum sbom catalog resolve
|
|
78
|
+
spectrum sbom catalog populate
|
|
79
|
+
spectrum security scan
|
|
80
|
+
spectrum security track
|
|
81
|
+
spectrum security reconcile
|
|
82
|
+
spectrum security repair
|
|
70
83
|
```
|
|
71
84
|
|
|
72
|
-
|
|
85
|
+
По умолчанию команды с действием показывают, какая предпроверка успешно пройдена;
|
|
73
86
|
при ошибке выводят её имя и причину. Независимые предпроверки продолжают
|
|
74
87
|
выполняться после ошибки; зависимые проверки без нужных данных помечаются как
|
|
75
88
|
невыполненные. Шаги команды запускаются, только если все проверки прошли.
|
|
89
|
+
|
|
90
|
+
`-n, --no-verify` доступен во всех 21 командах с действием: пропускает
|
|
91
|
+
preflight и проверку обновлений CLI, затем запускает действия команды.
|
|
92
|
+
Чтение необходимых входных данных (версии, fragments, пути, конфигурация,
|
|
93
|
+
токены) остаётся частью подготовки к действию. Ошибки чтения и самих операций
|
|
94
|
+
по-прежнему останавливают выполнение. Флаг не отменяет выбор типа fragment,
|
|
95
|
+
запрос токенов, подтверждение публикации chart или ожидание registry.
|
|
96
|
+
Для отключения ожидания у `chart start` отдельно укажите `--no-wait`.
|
|
97
|
+
Прежний короткий флаг `chart start -n` теперь означает `--no-verify`.
|
|
98
|
+
Release/hotfix используют локальные теги и сохранённые refs `origin/*`
|
|
99
|
+
для определения базовой версии, без preflight fetch и сверки с сервером.
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
spectrum release start -n
|
|
103
|
+
spectrum chart start --no-verify --no-wait
|
|
104
|
+
spectrum security repair -ns
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`--dry` и `--no-verify` несовместимы: CLI завершится с ошибкой до действий.
|
|
108
|
+
Без `--no-verify` сохраняются обычные предпроверки.
|
|
109
|
+
|
|
76
110
|
`-s, --silence` скрывает сообщения об успешно пройденных предпроверках.
|
|
77
111
|
Ошибки проверок и итоговое сообщение команды остаются видны; флаг можно
|
|
78
112
|
совмещать с `-d, --dry`.
|
|
@@ -88,8 +122,10 @@ GitLab; `token rotate -d` запрашивает токены и проверя
|
|
|
88
122
|
| --- | --- | --- |
|
|
89
123
|
| `-d` | `--dry` | Все команды с действием: только предпроверки |
|
|
90
124
|
| `-s` | `--silence` | Все команды с действием: скрыть успешные предпроверки |
|
|
91
|
-
| `-f` | `--force` | `chart start`: разрешить версию не больше последнего тега на `origin`;
|
|
92
|
-
| `-n` | `--no-
|
|
125
|
+
| `-f` | `--force` | `chart start`: разрешить версию не больше последнего тега на `origin`; остальные preflight сохраняются, если не задан `--no-verify` |
|
|
126
|
+
| `-n` | `--no-verify` | Все команды с действием: пропустить preflight и сразу выполнить действия |
|
|
127
|
+
| — | `--no-wait` | `chart start`: не ждать появления версии в Helm-registry после push; по умолчанию команда ждёт |
|
|
128
|
+
| — | `--stable` | `release start`: переход с 0.x на 1.0.0 |
|
|
93
129
|
| `-i <names>` | `--instances <names>` | `chart deploy`: выбрать инстансы через запятую |
|
|
94
130
|
| `-h` | `--help` | Справка CLI и команд |
|
|
95
131
|
| `-v` | `--version` | Версия CLI |
|
|
@@ -100,6 +136,9 @@ GitLab; `token rotate -d` запрашивает токены и проверя
|
|
|
100
136
|
# Справка по релизам
|
|
101
137
|
spectrum release --help
|
|
102
138
|
|
|
139
|
+
# Справка по хотфиксам
|
|
140
|
+
spectrum hotfix --help
|
|
141
|
+
|
|
103
142
|
# Справка по changelog
|
|
104
143
|
spectrum changelog --help
|
|
105
144
|
|
|
@@ -108,6 +147,11 @@ spectrum chart --help
|
|
|
108
147
|
|
|
109
148
|
# Справка по токенам
|
|
110
149
|
spectrum token --help
|
|
150
|
+
|
|
151
|
+
# Справка по CI-командам
|
|
152
|
+
spectrum nextjs --help
|
|
153
|
+
spectrum sbom catalog --help
|
|
154
|
+
spectrum security --help
|
|
111
155
|
```
|
|
112
156
|
|
|
113
157
|
## ✨ Особенности
|
|
@@ -141,6 +185,10 @@ spectrum-cli/
|
|
|
141
185
|
│ ├── hotfix/ # Проверки и этапы hotfix
|
|
142
186
|
│ ├── token/ # Конфиг, GitLab API и ротация
|
|
143
187
|
│ ├── integrations/ # Общие функции внешних сервисов
|
|
188
|
+
│ ├── automation/ # Общие операции ввода-вывода для CI
|
|
189
|
+
│ ├── nextjs/ # LICENSE standalone-пакетов
|
|
190
|
+
│ ├── sbom/ # Поиск и публикация OCI-каталогов
|
|
191
|
+
│ └── security/ # Аудит, задачи и исправления
|
|
144
192
|
├── __tests__/ # Модульные и интеграционные тесты
|
|
145
193
|
├── package.json
|
|
146
194
|
├── install.sh
|
|
@@ -153,26 +201,45 @@ spectrum-cli/
|
|
|
153
201
|
|
|
154
202
|
### Команды
|
|
155
203
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
|
161
|
-
|
|
|
162
|
-
| `spectrum
|
|
163
|
-
| `spectrum
|
|
164
|
-
| `spectrum
|
|
165
|
-
| `spectrum
|
|
166
|
-
| `spectrum
|
|
204
|
+
Ниже перечислены все команды с действием из `spectrum --help` и справки
|
|
205
|
+
вложенных групп. Каждая поддерживает `-n, --no-verify`, `-d, --dry` и
|
|
206
|
+
`-s, --silence`.
|
|
207
|
+
|
|
208
|
+
| Команда | Аргументы и дополнительные опции | Назначение |
|
|
209
|
+
| --- | --- | --- |
|
|
210
|
+
| `spectrum upgrade` | — | Обновить CLI через npm |
|
|
211
|
+
| `spectrum release start` | `--stable` | Собрать fragments и отправить release commit в dev и main/master |
|
|
212
|
+
| `spectrum release deploy` | — | Создать и отправить стабильный тег `release/X.Y.Z` |
|
|
213
|
+
| `spectrum release close` | — | Свести main/master в dev |
|
|
214
|
+
| `spectrum hotfix start` | — | Подготовить hotfix CHANGELOG локально |
|
|
215
|
+
| `spectrum hotfix deploy` | — | Отправить тег `hotfix/X.Y.Z` после merge в production |
|
|
216
|
+
| `spectrum hotfix close` | — | Свести production в dev после stable pipeline |
|
|
217
|
+
| `spectrum changelog append` | `<message>` | Создать changelog fragment |
|
|
218
|
+
| `spectrum changelog check` | — | Проверить CHANGELOG.md и fragments |
|
|
219
|
+
| `spectrum changelog write` | — | Нормализовать заголовки и отформатировать CHANGELOG.md |
|
|
220
|
+
| `spectrum chart start` | `-f, --force`, `--no-wait` | Создать и отправить chart-тег, дождаться registry |
|
|
221
|
+
| `spectrum chart deploy` | `-i, --instances <names>` | Обновить helmrelease и опубликовать изменения |
|
|
222
|
+
| `spectrum chart verify` | `<source_path>` | Сравнить ingress paths с маршрутами Next.js |
|
|
223
|
+
| `spectrum token rotate` | — | Пролить `SC_BOT_PAT` в CI variables |
|
|
224
|
+
| `spectrum nextjs licenses` | — | Сохранить LICENSE compiled-пакетов в standalone |
|
|
225
|
+
| `spectrum sbom catalog resolve` | — | Найти OCI-каталоги зависимостей образа |
|
|
226
|
+
| `spectrum sbom catalog populate` | — | Построить и опубликовать OCI-каталоги |
|
|
227
|
+
| `spectrum security scan` | — | Проверить npm-зависимости и runtime |
|
|
228
|
+
| `spectrum security track` | — | Найти или создать задачи YouTrack |
|
|
229
|
+
| `spectrum security reconcile` | — | Запустить tracker и получить номера задач |
|
|
230
|
+
| `spectrum security repair` | — | Исправить зависимости, проверить и создать MR |
|
|
167
231
|
|
|
168
232
|
### 🛡️ Preflight-проверки по командам
|
|
169
233
|
|
|
234
|
+
Этот список описывает запуск без `--no-verify`.
|
|
235
|
+
|
|
170
236
|
- `spectrum release start`: чистая и актуальная dev-ветка, `dev` содержит `origin/main` или `origin/master`, стабильный тег, достижимый из production-ветки, согласованная опубликованная история `CHANGELOG.md` и не более одного открытого релиза. При первом запуске нужны валидные fragments и свободная версия; повторный дополняет открытый раздел без повышения версии. `package.json` не читается.
|
|
171
237
|
- `spectrum release deploy`: чистая и актуальная main/master, стабильная версия `X.Y.Z` из верхнего заголовка `CHANGELOG.md`, отсутствие этой версии среди локальных и remote-тегов всех трёх форм. Команда не создает RC-теги.
|
|
172
238
|
- `spectrum release close`: чистая и актуальная main/master, версия из верхнего заголовка `CHANGELOG.md` и remote stable-тег поддерживаемой формы, указывающий на текущий commit.
|
|
173
239
|
- `spectrum changelog append <message>`: `git-repo`, `changelog-exists`, валидные ID задачи, git identity и тип fragment. Команда не изменяет общий `CHANGELOG.md`.
|
|
174
|
-
- `spectrum changelog check`: `git-repo`, `changelog-exists`, `changelog-prettier-check`, наличие и формат всех changelog fragments.
|
|
175
|
-
- `spectrum
|
|
240
|
+
- `spectrum changelog check`: `git-repo`, `changelog-exists`, `changelog-prettier-check`, наличие и формат всех changelog fragments. С `--no-verify` команда завершается без проверок.
|
|
241
|
+
- `spectrum changelog write`: `git-repo`, `changelog-exists`, доступность Prettier.
|
|
242
|
+
- `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 нужен `SC_DEVELOPER_PAT` (PAT со scope `api` и доступом к проекту; роль Owner не требуется); ошибка доступа блокирует команду. Право отправки chart-тега определяется отдельно правилами protected tags. `-f, --force` обходит только проверку повышения версии относительно последнего remote-тега. После push команда по умолчанию ждёт версию в registry; `--no-wait` отключает ожидание.
|
|
176
243
|
|
|
177
244
|
Для обратной совместимости chart-команды принимают `SC_OWNER_PAT`, если
|
|
178
245
|
`SC_DEVELOPER_PAT` не задана или пуста. `SC_OWNER_PAT` deprecated: поддержка
|
|
@@ -180,8 +247,8 @@ spectrum-cli/
|
|
|
180
247
|
выводит предупреждение с просьбой перейти на `SC_DEVELOPER_PAT`. Если заданы обе
|
|
181
248
|
переменные, используется `SC_DEVELOPER_PAT` без предупреждения.
|
|
182
249
|
|
|
183
|
-
- `spectrum chart deploy`: `git-repo`, `clean-working-tree`, `on-main-branch` (текущая ветка `main`), `remote-origin` (настроен `origin`), `remote-reachable` (доступен `origin`), `single-chart`, `helmrelease-files` (найдены `helmrelease.yaml`).
|
|
184
|
-
- `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`.
|
|
250
|
+
- `spectrum chart deploy`: `git-repo`, `clean-working-tree`, `on-main-branch` (текущая ветка `main/master`), `branch-up-to-date`, `remote-origin` (настроен `origin`), `remote-reachable` (доступен `origin`), `single-chart`, `helmrelease-files` (найдены `helmrelease.yaml`).
|
|
251
|
+
- `spectrum chart verify <source_path>`: `git-repo`, `branch-up-to-date`, `single-values-yaml` (ровно один `charts/**/values.yaml`), `values-ingress-sections` (есть `ingress.paths.api/pages/assets`), `source-path-directory`, `next-project`, `build-command-support`.
|
|
185
252
|
- `spectrum token rotate`: `load-config` (есть валидный `~/.config/spectrum-cli/config.yaml`), `ask-tokens` (owner PAT и `SC_BOT_PAT` только в памяти), `check-access` (все группы и проекты доступны owner PAT).
|
|
186
253
|
|
|
187
254
|
## 🔄 Workflow релиза
|
|
@@ -323,7 +390,7 @@ CI-часть реализует подключенный release component, а
|
|
|
323
390
|
3. Читает версию из верхнего заголовка `charts/<chart-name>/CHANGELOG.md` и проверяет SemVer
|
|
324
391
|
4. Собирает тег `chart-<name>-<version>`
|
|
325
392
|
5. Проверяет, что такого тега еще нет локально и на `origin`, а версия отсутствует в Helm-registry
|
|
326
|
-
6. Создает тег и пушит его в `origin`, затем ждёт появления версии в registry;
|
|
393
|
+
6. Создает тег и пушит его в `origin`, затем ждёт появления версии в registry; `--no-wait` отключает ожидание
|
|
327
394
|
|
|
328
395
|
### `spectrum chart deploy`
|
|
329
396
|
|
|
@@ -581,16 +648,17 @@ registry push: эти права проверяются соответствую
|
|
|
581
648
|
[YouTrack My User Profile](https://www.jetbrains.com/help/youtrack/devportal/resource-api-users-me.html),
|
|
582
649
|
[YouTrack project fields](https://www.jetbrains.com/help/youtrack/devportal/resource-api-admin-projects-projectID-customFields.html).
|
|
583
650
|
|
|
584
|
-
###
|
|
651
|
+
### No-verify, dry и silence для команд автоматизации
|
|
585
652
|
|
|
586
653
|
Все семь команд `nextjs licenses`, `sbom catalog resolve/populate` и
|
|
587
|
-
`security scan/track/reconcile/repair` поддерживают `-d, --dry` и
|
|
588
|
-
`-s, --silence`, включая
|
|
654
|
+
`security scan/track/reconcile/repair` поддерживают `-n, --no-verify`, `-d, --dry` и
|
|
655
|
+
`-s, --silence`, включая сочетания `-ns` и `-ds`.
|
|
589
656
|
|
|
590
657
|
```sh
|
|
591
658
|
spectrum nextjs licenses --dry
|
|
592
659
|
spectrum sbom catalog populate --dry --silence
|
|
593
660
|
spectrum security repair -ds
|
|
661
|
+
spectrum security repair --no-verify --silence
|
|
594
662
|
```
|
|
595
663
|
|
|
596
664
|
`--dry` выполняет предпроверки настроек, файлов, инструментов и доступов,
|
|
@@ -607,3 +675,10 @@ Git/проектов; SBOM preflight может загрузить закреп
|
|
|
607
675
|
Ошибки, предупреждения, результаты команды и итог dry остаются видимыми.
|
|
608
676
|
Это тот же смысл флага, что у release/hotfix команд. Без dry выполняются
|
|
609
677
|
обычные действия и сохраняются прежние коды завершения 0/1/2.
|
|
678
|
+
|
|
679
|
+
`--no-verify` пропускает предварительные проверки доступов GitLab/YouTrack,
|
|
680
|
+
registry и соответствия tracker ref. Команда сразу переходит к своим действиям:
|
|
681
|
+
аудиту, копированию LICENSE, работе с каталогами, задачами или исправлениям.
|
|
682
|
+
Загрузка входных отчётов, аутентификация для операций, проверка целостности
|
|
683
|
+
артефактов и проверочные команды после исправления остаются частью действия.
|
|
684
|
+
Пропущенные preflight не получают статус «проверено» в отчёте.
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@ function changelogAppend(message) {
|
|
|
9
9
|
checks: [
|
|
10
10
|
{ name: 'git-repo', run: requireGitRepo },
|
|
11
11
|
{ name: 'changelog-exists', run: () => requireFileExists(CHANGELOG_FILE) },
|
|
12
|
-
{ name: 'prepare-fragment', dependsOn: ['git-repo', 'changelog-exists'], run: () => prepareChangelogEntry(message) }
|
|
12
|
+
{ name: 'prepare-fragment', prepare: true, dependsOn: ['git-repo', 'changelog-exists'], run: () => prepareChangelogEntry(message) }
|
|
13
13
|
],
|
|
14
14
|
steps: [
|
|
15
15
|
{ name: 'write-fragment', run: appendPreparedChangelogEntry }
|
|
@@ -41,6 +41,14 @@ function requireReleaseState(context) {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
function readLocalReleaseState(context) {
|
|
45
|
+
const local = splitOpenRelease(fs.readFileSync(CHANGELOG_FILE, 'utf8'), context.stableVersion);
|
|
46
|
+
return { ok: true, data: {
|
|
47
|
+
openReleaseVersion: local.open?.version || null,
|
|
48
|
+
releaseChangelogState: { local, production: { ...local, open: null, entries: [] } }
|
|
49
|
+
} };
|
|
50
|
+
}
|
|
51
|
+
|
|
44
52
|
function requireReleaseFragments(context) {
|
|
45
53
|
try {
|
|
46
54
|
// Empty is valid only for an already prepared release, including retry after a failed push.
|
|
@@ -77,4 +85,4 @@ function appendOpenRelease(context) {
|
|
|
77
85
|
}
|
|
78
86
|
}
|
|
79
87
|
|
|
80
|
-
module.exports = { resolveReleaseState, requireReleaseState, requireReleaseFragments, appendOpenRelease };
|
|
88
|
+
module.exports = { resolveReleaseState, readLocalReleaseState, requireReleaseState, requireReleaseFragments, appendOpenRelease };
|
package/src/chart/deploy.js
CHANGED
|
@@ -30,10 +30,10 @@ function chartDeploy(options = {}) {
|
|
|
30
30
|
{ name: 'branch-up-to-date', run: requireCurrentBranchUpToDateWithRemote },
|
|
31
31
|
{ name: 'remote-origin', run: requireRemoteOrigin },
|
|
32
32
|
{ name: 'remote-reachable', run: requireRemoteReachable },
|
|
33
|
-
{ name: 'single-chart', run: requireSingleChart },
|
|
34
|
-
{ name: 'helmrelease-files', run: requireHelmReleaseFiles },
|
|
33
|
+
{ name: 'single-chart', prepare: true, run: requireSingleChart },
|
|
34
|
+
{ name: 'helmrelease-files', prepare: true, run: requireHelmReleaseFiles },
|
|
35
35
|
{
|
|
36
|
-
name: 'filter-instances',
|
|
36
|
+
name: 'filter-instances', prepare: true,
|
|
37
37
|
run: (ctx) => {
|
|
38
38
|
const requested = parseInstancesOption(instancesOption);
|
|
39
39
|
if (requested.length === 0) {
|
package/src/chart/start.js
CHANGED
|
@@ -15,9 +15,9 @@ function chartStart(options = {}) {
|
|
|
15
15
|
{ name: 'clean-working-tree', run: requireCleanWorkingTree },
|
|
16
16
|
{ name: 'on-main-branch', run: requireOnMainBranch },
|
|
17
17
|
{ name: 'branch-up-to-date', run: requireCurrentBranchUpToDateWithRemote },
|
|
18
|
-
{ name: 'single-chart', run: requireSingleChart },
|
|
18
|
+
{ name: 'single-chart', prepare: true, run: requireSingleChart },
|
|
19
19
|
{
|
|
20
|
-
name: 'chart-changelog-version',
|
|
20
|
+
name: 'chart-changelog-version', prepare: true,
|
|
21
21
|
requires: ['chartFilePath'],
|
|
22
22
|
run: (ctx) => requireChartChangelogVersion(path.dirname(ctx.chartFilePath))
|
|
23
23
|
},
|
package/src/chart/verify.js
CHANGED
|
@@ -3,7 +3,7 @@ const path = require('path');
|
|
|
3
3
|
const { execSync } = require('child_process');
|
|
4
4
|
const { logSuccess, logError, colors } = require('../common/utils');
|
|
5
5
|
const { runCommand } = require('../cli/command-executor');
|
|
6
|
-
const { requireGitRepo, requireCurrentBranchUpToDateWithRemote, requireSingleValuesYaml, requireIngressPathSections, requireSourcePathDirectory, requireNextProject, requireBuildCommandSupport } = require('../preflight');
|
|
6
|
+
const { requireGitRepo, requireCurrentBranchUpToDateWithRemote, requireSingleValuesYaml, readIngressPathSections, requireIngressPathSections, requireSourcePathDirectory, readSourceProject, requireNextProject, requireBuildCommandSupport } = require('../preflight');
|
|
7
7
|
const { normalizeList, normalizeToBeList, collectRoutesFromFilesystem, collectRoutesFromBuildArtifacts } = require('./routes');
|
|
8
8
|
const FIXED_ASSET_PATHS = [
|
|
9
9
|
'/_next/image$',
|
|
@@ -96,10 +96,10 @@ function chartVerify(sourcePath) {
|
|
|
96
96
|
checks: [
|
|
97
97
|
{ name: 'git-repo', run: requireGitRepo },
|
|
98
98
|
{ name: 'branch-up-to-date', run: requireCurrentBranchUpToDateWithRemote },
|
|
99
|
-
{ name: 'single-values-yaml', run: requireSingleValuesYaml },
|
|
100
|
-
{ name: 'values-ingress-sections', requires: ['valuesYamlPath'], run: (ctx) => requireIngressPathSections(ctx.valuesYamlPath) },
|
|
101
|
-
{ name: 'source-path-directory', run: () => requireSourcePathDirectory(sourcePath) },
|
|
102
|
-
{ name: 'next-project', requires: ['sourcePath'], run: (ctx) => requireNextProject(ctx.sourcePath) },
|
|
99
|
+
{ name: 'single-values-yaml', prepare: true, run: requireSingleValuesYaml },
|
|
100
|
+
{ name: 'values-ingress-sections', prepare: (ctx) => readIngressPathSections(ctx.valuesYamlPath), requires: ['valuesYamlPath'], run: (ctx) => requireIngressPathSections(ctx.valuesYamlPath) },
|
|
101
|
+
{ name: 'source-path-directory', prepare: true, run: () => requireSourcePathDirectory(sourcePath) },
|
|
102
|
+
{ name: 'next-project', prepare: (ctx) => readSourceProject(ctx.sourcePath), requires: ['sourcePath'], run: (ctx) => requireNextProject(ctx.sourcePath) },
|
|
103
103
|
{ name: 'build-command-support', requires: ['sourcePath'], run: (ctx) => requireBuildCommandSupport(ctx.sourcePath) }
|
|
104
104
|
],
|
|
105
105
|
steps: [
|
|
@@ -59,7 +59,14 @@ function describePreflight(checkName, context) {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
function withCommandOptions(options, action) {
|
|
62
|
-
|
|
62
|
+
if (options?.dry && options?.verify === false) {
|
|
63
|
+
throw new Error('--dry и --no-verify нельзя использовать вместе.');
|
|
64
|
+
}
|
|
65
|
+
return commandContext.run({
|
|
66
|
+
dry: Boolean(options?.dry),
|
|
67
|
+
silence: Boolean(options?.silence),
|
|
68
|
+
verify: options?.verify !== false
|
|
69
|
+
}, action);
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
function withDryRun(dry, action) {
|
|
@@ -70,18 +77,22 @@ function isDryRun() {
|
|
|
70
77
|
return Boolean(commandContext.getStore()?.dry);
|
|
71
78
|
}
|
|
72
79
|
|
|
80
|
+
function isVerificationSkipped() {
|
|
81
|
+
return commandContext.getStore()?.verify === false;
|
|
82
|
+
}
|
|
83
|
+
|
|
73
84
|
function reportDryRun(_name) {
|
|
74
85
|
logSuccess('✅', 'Все предпроверки пройдены, проблем нет. Действия не выполнялись.');
|
|
75
86
|
}
|
|
76
87
|
|
|
77
88
|
function reportPreflight(_commandName, checkName, description = PREFLIGHT_DESCRIPTIONS[checkName] || checkName) {
|
|
78
|
-
if (!commandContext.getStore()?.silence) {
|
|
89
|
+
if (!isVerificationSkipped() && !commandContext.getStore()?.silence) {
|
|
79
90
|
logSuccess('🔎', 'Проверено (%s): %s.', checkName, description);
|
|
80
91
|
}
|
|
81
92
|
}
|
|
82
93
|
|
|
83
94
|
function reportNoPreflights(_name) {
|
|
84
|
-
logSuccess('🔎', 'Предпроверок нет.');
|
|
95
|
+
if (!isVerificationSkipped()) logSuccess('🔎', 'Предпроверок нет.');
|
|
85
96
|
}
|
|
86
97
|
|
|
87
98
|
function normalizeCheckResult(result, fallbackReason) {
|
|
@@ -120,6 +131,24 @@ async function runCommand(spec) {
|
|
|
120
131
|
let failedPreflights = 0;
|
|
121
132
|
const results = new Map();
|
|
122
133
|
for (const check of checks) {
|
|
134
|
+
// Some checks also read inputs needed by the action. In no-verify mode
|
|
135
|
+
// only their explicit preparation runs, without policy checks or reports.
|
|
136
|
+
if (isVerificationSkipped()) {
|
|
137
|
+
if (!check.prepare) continue;
|
|
138
|
+
const prepare = check.prepare === true ? check.run : check.prepare;
|
|
139
|
+
let result;
|
|
140
|
+
try {
|
|
141
|
+
result = normalizeCheckResult(await prepare(context), 'Не удалось получить данные команды.');
|
|
142
|
+
} catch (error) {
|
|
143
|
+
result = { ok: false, reason: error?.message || String(error) };
|
|
144
|
+
}
|
|
145
|
+
if (!result.ok) {
|
|
146
|
+
logError('❌', 'Не удалось подготовить команду (%s): %s', check.name, result.reason);
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
if (result.data) Object.assign(context, result.data);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
123
152
|
const checkName = check.name || 'unknown-check';
|
|
124
153
|
const failedDependencies = (check.dependsOn || []).filter((dependency) => results.get(dependency) !== true);
|
|
125
154
|
const missingInputs = (check.requires || []).filter((key) => context[key] === undefined || context[key] === null);
|
|
@@ -178,4 +207,4 @@ async function runCommand(spec) {
|
|
|
178
207
|
return true;
|
|
179
208
|
}
|
|
180
209
|
|
|
181
|
-
module.exports = { runCommand, withCommandOptions, withDryRun, isDryRun, reportDryRun, reportPreflight, reportNoPreflights };
|
|
210
|
+
module.exports = { runCommand, withCommandOptions, withDryRun, isDryRun, isVerificationSkipped, reportDryRun, reportPreflight, reportNoPreflights };
|
package/src/cli/commands.js
CHANGED
|
@@ -20,6 +20,7 @@ program
|
|
|
20
20
|
program
|
|
21
21
|
.command("upgrade")
|
|
22
22
|
.description("Обновить Spectrum CLI через npm install -g spectrum-cli")
|
|
23
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
23
24
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
24
25
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
25
26
|
.action((command) => runAction(upgrade, command));
|
|
@@ -33,6 +34,7 @@ nextjsCmd
|
|
|
33
34
|
.command("licenses")
|
|
34
35
|
.description("Сохранить LICENSE встроенных пакетов в standalone")
|
|
35
36
|
.allowExcessArguments(false)
|
|
37
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
36
38
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
37
39
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
38
40
|
.action((command) => runAutomationAction(nextjs.licenses, command));
|
|
@@ -50,6 +52,7 @@ catalogCmd
|
|
|
50
52
|
.command("resolve")
|
|
51
53
|
.description("Найти проверенные OCI-каталоги для образа приложения")
|
|
52
54
|
.allowExcessArguments(false)
|
|
55
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
53
56
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
54
57
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
55
58
|
.action((command) => runAutomationAction(sbom.resolve, command));
|
|
@@ -58,6 +61,7 @@ catalogCmd
|
|
|
58
61
|
.command("populate")
|
|
59
62
|
.description("Построить и опубликовать каталог из официальных исходников")
|
|
60
63
|
.allowExcessArguments(false)
|
|
64
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
61
65
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
62
66
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
63
67
|
.action((command) => runAutomationAction(sbom.populate, command));
|
|
@@ -71,6 +75,7 @@ securityCmd
|
|
|
71
75
|
.command("scan")
|
|
72
76
|
.description("Проверить production npm-зависимости и runtime-образ")
|
|
73
77
|
.allowExcessArguments(false)
|
|
78
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
74
79
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
75
80
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
76
81
|
.action((command) => runAutomationAction(security.scan, command));
|
|
@@ -79,6 +84,7 @@ securityCmd
|
|
|
79
84
|
.command("track")
|
|
80
85
|
.description("Создать или найти YouTrack-задачи в центральном pipeline")
|
|
81
86
|
.allowExcessArguments(false)
|
|
87
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
82
88
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
83
89
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
84
90
|
.action((command) => runAutomationAction(security.track, command));
|
|
@@ -87,6 +93,7 @@ securityCmd
|
|
|
87
93
|
.command("reconcile")
|
|
88
94
|
.description("Дождаться центрального tracker и получить номера задач")
|
|
89
95
|
.allowExcessArguments(false)
|
|
96
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
90
97
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
91
98
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
92
99
|
.action((command) => runAutomationAction(security.reconcile, command));
|
|
@@ -95,6 +102,7 @@ securityCmd
|
|
|
95
102
|
.command("repair")
|
|
96
103
|
.description("Исправить, проверить и создать отдельный MR на проектную задачу")
|
|
97
104
|
.allowExcessArguments(false)
|
|
105
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
98
106
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
99
107
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
100
108
|
.action((command) => runAutomationAction(security.repair, command));
|
|
@@ -108,6 +116,7 @@ releaseCmd
|
|
|
108
116
|
.command("start")
|
|
109
117
|
.description("Схлопнуть fragments и атомарно отправить release commit в dev и main/master")
|
|
110
118
|
.option("--stable", "Явно перейти с 0.x на первый стабильный релиз 1.0.0")
|
|
119
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
111
120
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
112
121
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
113
122
|
.action((command) => runAction(release.releaseStart, command));
|
|
@@ -115,6 +124,7 @@ releaseCmd
|
|
|
115
124
|
releaseCmd
|
|
116
125
|
.command("close")
|
|
117
126
|
.description("Свести стабильный релиз из main/master в dev")
|
|
127
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
118
128
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
119
129
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
120
130
|
.action((command) => runAction(release.releaseClose, command));
|
|
@@ -122,6 +132,7 @@ releaseCmd
|
|
|
122
132
|
releaseCmd
|
|
123
133
|
.command("deploy")
|
|
124
134
|
.description("Создать и отправить только стабильный тег release/X.Y.Z")
|
|
135
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
125
136
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
126
137
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
127
138
|
.action((command) => runAction(git.gitCreateTagAndPush, command));
|
|
@@ -130,16 +141,19 @@ const hotfixCmd = program.command("hotfix").description("Изолированн
|
|
|
130
141
|
|
|
131
142
|
hotfixCmd.command("start")
|
|
132
143
|
.description("Подготовить 🩹 CHANGELOG на hotfix/* или main/master, без commit, push и merge")
|
|
144
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
133
145
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
134
146
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
135
147
|
.action((command) => runAction(hotfix.hotfixStart, command));
|
|
136
148
|
hotfixCmd.command("deploy")
|
|
137
149
|
.description("После merge хотфикса отправить stable-тег с main/master")
|
|
150
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
138
151
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
139
152
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
140
153
|
.action((command) => runAction(hotfix.hotfixDeploy, command));
|
|
141
154
|
hotfixCmd.command("close")
|
|
142
155
|
.description("После stable pipeline свести production в dev")
|
|
156
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
143
157
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
144
158
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
145
159
|
.action((command) => runAction(hotfix.hotfixClose, command));
|
|
@@ -152,6 +166,7 @@ const changelogCmd = program
|
|
|
152
166
|
changelogCmd
|
|
153
167
|
.command("append <message>")
|
|
154
168
|
.description("Создать changelog fragment с номером задачи из ветки")
|
|
169
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
155
170
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
156
171
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
157
172
|
.action((message, command) => runAction(() => changelog.changelogAppend(message), command));
|
|
@@ -159,6 +174,7 @@ changelogCmd
|
|
|
159
174
|
changelogCmd
|
|
160
175
|
.command("check")
|
|
161
176
|
.description("Проверить CHANGELOG.md и changelog fragments")
|
|
177
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
162
178
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
163
179
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
164
180
|
.action((command) => runAction(changelog.changelogCheck, command));
|
|
@@ -166,6 +182,7 @@ changelogCmd
|
|
|
166
182
|
changelogCmd
|
|
167
183
|
.command("write")
|
|
168
184
|
.description("Привести заголовки CHANGELOG.md к формату релиза и запустить Prettier")
|
|
185
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
169
186
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
170
187
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
171
188
|
.action((command) => runAction(changelog.changelogWrite, command));
|
|
@@ -180,7 +197,8 @@ chartCmd
|
|
|
180
197
|
.description("Создать и отправить chart-тег по верхней версии CHANGELOG.md чарта")
|
|
181
198
|
.allowExcessArguments(false)
|
|
182
199
|
.option("-f, --force", "Разрешить версию не больше последней версии тега на origin")
|
|
183
|
-
.option("
|
|
200
|
+
.option("--no-wait", "Не ждать публикации версии чарта в Helm-registry")
|
|
201
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
184
202
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
185
203
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
186
204
|
.action((command) => runAction(chart.chartStart, command));
|
|
@@ -188,6 +206,7 @@ chartCmd
|
|
|
188
206
|
chartCmd
|
|
189
207
|
.command("verify <source_path>")
|
|
190
208
|
.description("Проверить пути ingress chart относительно исходников Next.js")
|
|
209
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
191
210
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
192
211
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
193
212
|
.action((sourcePath, command) => runAction(() => chart.chartVerify(sourcePath), command));
|
|
@@ -196,6 +215,7 @@ chartCmd
|
|
|
196
215
|
.command("deploy")
|
|
197
216
|
.description("Задеплоить последнюю версию chart в файлы helmrelease")
|
|
198
217
|
.option("-i, --instances <names>", "Список инстансов через запятую (по умолчанию все)")
|
|
218
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
199
219
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
200
220
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
201
221
|
.action((command) => runAction(chart.chartDeploy, command));
|
|
@@ -207,6 +227,7 @@ const tokenCmd = program
|
|
|
207
227
|
tokenCmd
|
|
208
228
|
.command("rotate")
|
|
209
229
|
.description("Пролить SC_BOT_PAT в CI variables через owner PAT")
|
|
230
|
+
.option("-n, --no-verify", "Выполнить команду без предпроверок")
|
|
210
231
|
.option("-d, --dry", "Запустить только предпроверки")
|
|
211
232
|
.option("-s, --silence", "Скрыть сообщения об успешно пройденных предпроверках")
|
|
212
233
|
.action((command) => runAction(token.tokenRotate, command));
|
package/src/cli/help.js
CHANGED
|
@@ -23,7 +23,9 @@ program.configureHelp({
|
|
|
23
23
|
if (commands.length > 0) {
|
|
24
24
|
output += "Команды:\n";
|
|
25
25
|
commands.forEach((cmd) => {
|
|
26
|
-
const nameAndArgs =
|
|
26
|
+
const nameAndArgs = helper.subcommandTerm
|
|
27
|
+
? helper.subcommandTerm(cmd)
|
|
28
|
+
: cmd.name() + cmd.usage().replace(/^[^\s]+\s*/, " ");
|
|
27
29
|
const aliases = cmd.aliases();
|
|
28
30
|
const description = cmd.description();
|
|
29
31
|
|
|
@@ -50,6 +52,7 @@ program.configureHelp({
|
|
|
50
52
|
return output;
|
|
51
53
|
},
|
|
52
54
|
});
|
|
55
|
+
for (const command of program.commands || []) configureHelp(command);
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
module.exports = { configureHelp };
|
package/src/cli/run-action.js
CHANGED
package/src/git/index.js
CHANGED
|
@@ -86,7 +86,7 @@ function gitCreateTagAndPush() {
|
|
|
86
86
|
{ name: 'clean-working-tree', run: requireCleanWorkingTree },
|
|
87
87
|
{ name: 'on-main-branch', run: requireOnMainBranch },
|
|
88
88
|
{ name: 'branch-up-to-date', run: requireCurrentBranchUpToDateWithRemote },
|
|
89
|
-
{ name: 'changelog-release-version', run: requireChangelogReleaseVersion },
|
|
89
|
+
{ name: 'changelog-release-version', prepare: true, run: requireChangelogReleaseVersion },
|
|
90
90
|
{
|
|
91
91
|
name: 'tag-missing',
|
|
92
92
|
requires: ['version'],
|
package/src/hotfix/checks.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const preflight = require('../preflight');
|
|
4
|
-
const { logError } = require('../common/utils');
|
|
5
|
-
const { reportPreflight } = require('../cli/command-executor');
|
|
4
|
+
const { logError, getMainBranch, getDevelopBranch } = require('../common/utils');
|
|
5
|
+
const { reportPreflight, isVerificationSkipped } = require('../cli/command-executor');
|
|
6
6
|
const { upVersion, compareVersions } = require('../common/version');
|
|
7
7
|
const { STABLE_TAG_PATTERNS, parseStableTag, stableTagNames } = require('../release/stable-tags');
|
|
8
8
|
const { git, exists, ancestor, readAt } = require('./git');
|
|
9
9
|
function preflightReport(commandName) {
|
|
10
10
|
let failures = 0;
|
|
11
11
|
return {
|
|
12
|
-
check(name, action, description, unavailableReason) {
|
|
12
|
+
check(name, action, description, unavailableReason, prepare = false) {
|
|
13
|
+
if (isVerificationSkipped()) {
|
|
14
|
+
// Reading/assembling action inputs is still required.
|
|
15
|
+
return { ok: true, value: prepare ? action() : undefined };
|
|
16
|
+
}
|
|
13
17
|
if (unavailableReason) {
|
|
14
18
|
logError('❌', 'Предпроверка не выполнена (%s): %s', name, unavailableReason);
|
|
15
19
|
failures += 1;
|
|
@@ -30,6 +34,20 @@ function preflightReport(commandName) {
|
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
function context(mode, checks) {
|
|
37
|
+
if (isVerificationSkipped()) {
|
|
38
|
+
const mainBranch = getMainBranch();
|
|
39
|
+
const head = git('rev-parse', 'HEAD').trim();
|
|
40
|
+
if (mode !== 'start') {
|
|
41
|
+
const version = preflight.getChangelogReleaseVersion();
|
|
42
|
+
if (!version) throw new Error('Не удалось прочитать версию из CHANGELOG.md.');
|
|
43
|
+
return { repositoryRootOk: true, mainBranch, head, stable: version, target: version };
|
|
44
|
+
}
|
|
45
|
+
const result = preflight.readLocalStableVersion();
|
|
46
|
+
if (!result.ok) throw new Error(result.reason);
|
|
47
|
+
const stable = result.data.stableVersion;
|
|
48
|
+
return { repositoryRootOk: true, mainBranch, head, stable,
|
|
49
|
+
productionText: fs.readFileSync('CHANGELOG.md', 'utf8'), target: upVersion(stable, 'patch') };
|
|
50
|
+
}
|
|
33
51
|
const root = checks.check('repository-root', () => {
|
|
34
52
|
const root = git('rev-parse', '--show-toplevel').trim();
|
|
35
53
|
if (path.relative(fs.realpathSync(root), fs.realpathSync(process.cwd())) !== '') {
|
|
@@ -122,6 +140,11 @@ function assertOriginUnchanged(ctx) {
|
|
|
122
140
|
}
|
|
123
141
|
}
|
|
124
142
|
|
|
143
|
+
function integrationContext() {
|
|
144
|
+
const dev = getDevelopBranch();
|
|
145
|
+
return { dev, devRef: `refs/remotes/origin/${dev}` };
|
|
146
|
+
}
|
|
147
|
+
|
|
125
148
|
function currentFragments() {
|
|
126
149
|
if (!preflight.findChangelogFragmentFiles().length) return [];
|
|
127
150
|
const result = preflight.requireChangelogFragments();
|
|
@@ -141,4 +164,4 @@ function run(action) {
|
|
|
141
164
|
}
|
|
142
165
|
}
|
|
143
166
|
|
|
144
|
-
module.exports = { preflightReport, context, assertTagAvailable, assertOriginUnchanged, currentFragments, run };
|
|
167
|
+
module.exports = { preflightReport, context, integrationContext, assertTagAvailable, assertOriginUnchanged, currentFragments, run };
|
package/src/hotfix/close.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const { logSuccess } = require('../common/utils');
|
|
2
|
-
const { isDryRun, reportDryRun } = require('../cli/command-executor');
|
|
2
|
+
const { isDryRun, isVerificationSkipped, reportDryRun } = require('../cli/command-executor');
|
|
3
3
|
const { parseChangelog } = require('../changelog/hotfix');
|
|
4
4
|
const { git, exists, ancestor } = require('./git');
|
|
5
|
-
const { preflightReport, context, assertOriginUnchanged, run } = require('./checks');
|
|
5
|
+
const { preflightReport, context, integrationContext, assertOriginUnchanged, run } = require('./checks');
|
|
6
6
|
function hotfixClose() {
|
|
7
7
|
return run(() => {
|
|
8
8
|
const checks = preflightReport('hotfix close');
|
|
@@ -15,7 +15,7 @@ function hotfixClose() {
|
|
|
15
15
|
}
|
|
16
16
|
}, `опубликованный тег ${tag} указывает на текущий commit`,
|
|
17
17
|
ctx.stable && ctx.productionText && ctx.remoteTags && ctx.head ? null : 'не определены stable-тег или production commit');
|
|
18
|
-
const integration = checks.check('integration-history', () => {
|
|
18
|
+
const integration = isVerificationSkipped() ? { value: integrationContext() } : checks.check('integration-history', () => {
|
|
19
19
|
const dev = ['develop', 'dev'].find((name) => exists(`refs/remotes/origin/${name}`));
|
|
20
20
|
if (!dev) throw new Error('Не найдена integration-ветка origin/dev или origin/develop.');
|
|
21
21
|
const devRef = `refs/remotes/origin/${dev}`;
|
package/src/hotfix/start.js
CHANGED
|
@@ -21,13 +21,13 @@ function hotfixStart() {
|
|
|
21
21
|
ctx.target ? null : 'не определена целевая версия хотфикса');
|
|
22
22
|
const fragmentsResult = checks.check('changelog-fragments', currentFragments,
|
|
23
23
|
(value) => `проверены ${value.length} patch fragments`,
|
|
24
|
-
ctx.repositoryRootOk ? null : 'не пройдена проверка корня репозитория');
|
|
24
|
+
ctx.repositoryRootOk ? null : 'не пройдена проверка корня репозитория', true);
|
|
25
25
|
let original;
|
|
26
26
|
const changelog = checks.check('changelog-state', () => {
|
|
27
27
|
original = fs.readFileSync(CHANGELOG_FILE, 'utf8');
|
|
28
28
|
return prepareChangelog({ text: original, ...ctx, fragments: fragmentsResult.value });
|
|
29
29
|
}, `CHANGELOG.md допускает подготовку хотфикса ${ctx.target}`,
|
|
30
|
-
ctx.target && fragmentsResult.ok ? null : 'не определена версия хотфикса или не проверены fragments');
|
|
30
|
+
ctx.target && fragmentsResult.ok ? null : 'не определена версия хотфикса или не проверены fragments', true);
|
|
31
31
|
checks.check('origin-unchanged', () => assertOriginUnchanged(ctx),
|
|
32
32
|
'production-ветка на origin не изменилась во время проверки',
|
|
33
33
|
ctx.mainBranch && ctx.mainSha ? null : 'не проверена история production');
|
|
@@ -167,7 +167,7 @@ function extractYamlList(content, sectionName) {
|
|
|
167
167
|
return values;
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
function
|
|
170
|
+
function readIngressPathSections(valuesYamlPath) {
|
|
171
171
|
if (!valuesYamlPath || !fs.existsSync(valuesYamlPath)) {
|
|
172
172
|
return fail(`Обязательный файл "${valuesYamlPath}" не существует.`);
|
|
173
173
|
}
|
|
@@ -183,6 +183,14 @@ function requireIngressPathSections(valuesYamlPath) {
|
|
|
183
183
|
const pages = extractYamlList(content, 'pages');
|
|
184
184
|
const assets = extractYamlList(content, 'assets');
|
|
185
185
|
|
|
186
|
+
return ok({ valuesIngressPaths: { api, pages, assets } });
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function requireIngressPathSections(valuesYamlPath) {
|
|
190
|
+
const result = readIngressPathSections(valuesYamlPath);
|
|
191
|
+
if (!result.ok) return result;
|
|
192
|
+
const { api, pages, assets } = result.data.valuesIngressPaths;
|
|
193
|
+
|
|
186
194
|
if (api.length === 0) {
|
|
187
195
|
return fail(`Отсутствует или пуст ingress.paths.api в "${valuesYamlPath}".`);
|
|
188
196
|
}
|
|
@@ -193,9 +201,7 @@ function requireIngressPathSections(valuesYamlPath) {
|
|
|
193
201
|
return fail(`Отсутствует или пуст ingress.paths.assets в "${valuesYamlPath}".`);
|
|
194
202
|
}
|
|
195
203
|
|
|
196
|
-
return
|
|
197
|
-
valuesIngressPaths: { api, pages, assets }
|
|
198
|
-
});
|
|
204
|
+
return result;
|
|
199
205
|
}
|
|
200
206
|
|
|
201
|
-
module.exports = { findValuesYamlFiles, findHelmReleaseFiles, requireHelmReleaseFiles, requireSingleValuesYaml, extractYamlList, requireIngressPathSections };
|
|
207
|
+
module.exports = { findValuesYamlFiles, findHelmReleaseFiles, requireHelmReleaseFiles, requireSingleValuesYaml, extractYamlList, readIngressPathSections, requireIngressPathSections };
|
package/src/preflight/source.js
CHANGED
|
@@ -28,6 +28,17 @@ function hasNextConfig(sourcePath) {
|
|
|
28
28
|
return files.some((fileName) => fs.existsSync(path.join(sourcePath, fileName)));
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
function readSourceProject(sourcePath) {
|
|
32
|
+
return ok({
|
|
33
|
+
sourcePackageJsonPath: path.join(sourcePath, 'package.json'),
|
|
34
|
+
sourcePackageManager: fs.existsSync(path.join(sourcePath, 'yarn.lock'))
|
|
35
|
+
? 'yarn'
|
|
36
|
+
: fs.existsSync(path.join(sourcePath, 'bun.lockb')) ? 'bun' : 'npm',
|
|
37
|
+
sourceHasLockfile: ['package-lock.json', 'yarn.lock', 'bun.lockb']
|
|
38
|
+
.some((file) => fs.existsSync(path.join(sourcePath, file)))
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
31
42
|
function requireNextProject(sourcePath) {
|
|
32
43
|
if (!sourcePath) {
|
|
33
44
|
return fail('Требуется путь к исходникам.');
|
|
@@ -53,19 +64,7 @@ function requireNextProject(sourcePath) {
|
|
|
53
64
|
return fail(`"${sourcePath}" не похож на проект Next.js.`);
|
|
54
65
|
}
|
|
55
66
|
|
|
56
|
-
return
|
|
57
|
-
sourcePackageJsonPath: pkgPath,
|
|
58
|
-
sourcePackageManager: fs.existsSync(path.join(sourcePath, 'yarn.lock'))
|
|
59
|
-
? 'yarn'
|
|
60
|
-
: fs.existsSync(path.join(sourcePath, 'bun.lockb'))
|
|
61
|
-
? 'bun'
|
|
62
|
-
: 'npm',
|
|
63
|
-
sourceHasLockfile: Boolean(
|
|
64
|
-
fs.existsSync(path.join(sourcePath, 'package-lock.json')) ||
|
|
65
|
-
fs.existsSync(path.join(sourcePath, 'yarn.lock')) ||
|
|
66
|
-
fs.existsSync(path.join(sourcePath, 'bun.lockb'))
|
|
67
|
-
)
|
|
68
|
-
});
|
|
67
|
+
return readSourceProject(sourcePath);
|
|
69
68
|
}
|
|
70
69
|
|
|
71
70
|
function requireBuildCommandSupport(sourcePath) {
|
|
@@ -92,4 +91,4 @@ function requireBuildCommandSupport(sourcePath) {
|
|
|
92
91
|
return ok({ sourceBuildScript: pkg.scripts.build });
|
|
93
92
|
}
|
|
94
93
|
|
|
95
|
-
module.exports = { requireSourcePathDirectory, requireNextProject, requireBuildCommandSupport };
|
|
94
|
+
module.exports = { requireSourcePathDirectory, readSourceProject, requireNextProject, requireBuildCommandSupport };
|
package/src/preflight/tags.js
CHANGED
|
@@ -65,6 +65,14 @@ function requireLatestStableVersion() {
|
|
|
65
65
|
return ok({ stableVersion });
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
// Read the cached baseline for execution without fetching or verifying origin.
|
|
69
|
+
function readLocalStableVersion() {
|
|
70
|
+
const names = execSilent(`git tag --merged origin/${getMainBranch()} --list "release/*" "hotfix/*" "v*"`);
|
|
71
|
+
const versions = String(names || '').split('\n').map((tag) => parseStableTag(tag.trim())?.version)
|
|
72
|
+
.filter(Boolean).sort(compareStableVersions);
|
|
73
|
+
return versions.length ? ok({ stableVersion: versions.at(-1) }) : fail('Не удалось прочитать локальную stable-версию.');
|
|
74
|
+
}
|
|
75
|
+
|
|
68
76
|
function requireStableTagAtHead(version) {
|
|
69
77
|
if (!STABLE_SEMVER_PATTERN.test(String(version || ''))) return fail('Требуется stable-версия X.Y.Z.');
|
|
70
78
|
const head = execSilent('git rev-parse HEAD');
|
|
@@ -136,4 +144,4 @@ function requireReleaseVersionAvailable(version) {
|
|
|
136
144
|
return ok();
|
|
137
145
|
}
|
|
138
146
|
|
|
139
|
-
module.exports = { requireNoPendingRelease, requireLatestStableVersion, requireStableTagAtHead, requireYouTrackTask, requireTagMissing, requireReleaseVersionAvailable };
|
|
147
|
+
module.exports = { requireNoPendingRelease, requireLatestStableVersion, readLocalStableVersion, requireStableTagAtHead, requireYouTrackTask, requireTagMissing, requireReleaseVersionAvailable };
|
package/src/release/start.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const { changelogBuildRelease, changelogRemoveFragments } = require('../changelog');
|
|
2
|
-
const { requireReleaseState, requireReleaseFragments, appendOpenRelease } = require('../changelog/open-release');
|
|
3
|
-
const { runCommand } = require('../cli/command-executor');
|
|
2
|
+
const { requireReleaseState, readLocalReleaseState, requireReleaseFragments, appendOpenRelease } = require('../changelog/open-release');
|
|
3
|
+
const { runCommand, isVerificationSkipped } = require('../cli/command-executor');
|
|
4
|
+
const { getMainBranch, getDevelopBranch } = require('../common/utils');
|
|
5
|
+
const { readLocalStableVersion } = require('../preflight/tags');
|
|
4
6
|
const { requireGitRepo, requireCleanWorkingTree, requireCurrentBranchUpToDateWithRemote, requireMainAndDevBranches, requireOnDevBranch, requireDevContainsRemoteMain, requireReleaseVersionAvailable, requireLatestStableVersion, requireFileExists, requireChangelogFormatted } = require('../preflight');
|
|
5
7
|
const { CHANGELOG_FILE } = require('../changelog/config');
|
|
6
8
|
const { detectBumpType, resolveReleaseVersion } = require('./version');
|
|
@@ -12,16 +14,17 @@ function releaseStart(options = {}) {
|
|
|
12
14
|
{ name: 'git-repo', run: requireGitRepo },
|
|
13
15
|
{ name: 'clean-working-tree', run: requireCleanWorkingTree },
|
|
14
16
|
{ name: 'branch-up-to-date', run: requireCurrentBranchUpToDateWithRemote },
|
|
15
|
-
{ name: 'main-and-dev-branches', run: requireMainAndDevBranches
|
|
17
|
+
{ name: 'main-and-dev-branches', run: requireMainAndDevBranches,
|
|
18
|
+
prepare: () => ({ ok: true, data: { mainBranch: getMainBranch(), devBranch: getDevelopBranch() } }) },
|
|
16
19
|
{ name: 'on-dev-branch', run: requireOnDevBranch },
|
|
17
20
|
{ name: 'dev-contains-main', run: requireDevContainsRemoteMain },
|
|
18
|
-
{ name: 'stable-version', run: requireLatestStableVersion },
|
|
21
|
+
{ name: 'stable-version', prepare: readLocalStableVersion, run: requireLatestStableVersion },
|
|
19
22
|
{ name: 'changelog-exists', run: () => requireFileExists(CHANGELOG_FILE) },
|
|
20
|
-
{ name: 'release-state', requires: ['mainBranch', 'stableVersion'], run: requireReleaseState },
|
|
23
|
+
{ name: 'release-state', prepare: readLocalReleaseState, requires: ['mainBranch', 'stableVersion'], run: requireReleaseState },
|
|
21
24
|
{ name: 'changelog-prettier-check', run: requireChangelogFormatted },
|
|
22
|
-
{ name: 'changelog-fragments', run: requireReleaseFragments },
|
|
25
|
+
{ name: 'changelog-fragments', prepare: true, run: requireReleaseFragments },
|
|
23
26
|
{
|
|
24
|
-
name: 'detect-bump-type',
|
|
27
|
+
name: 'detect-bump-type', prepare: true,
|
|
25
28
|
requires: ['changelogFragments', 'stableVersion'],
|
|
26
29
|
run: (ctx) => {
|
|
27
30
|
if (options.stable && (!ctx.stableVersion.startsWith('0.') ||
|
|
@@ -35,8 +38,10 @@ function releaseStart(options = {}) {
|
|
|
35
38
|
return { ok: false, reason: 'Не удалось вычислить release-версию от последнего стабильного тега.' };
|
|
36
39
|
}
|
|
37
40
|
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
if (!isVerificationSkipped()) {
|
|
42
|
+
const versionCheck = requireReleaseVersionAvailable(resolved.newVersion);
|
|
43
|
+
if (!versionCheck.ok) return versionCheck;
|
|
44
|
+
}
|
|
40
45
|
return { ok: true, data: resolved };
|
|
41
46
|
}
|
|
42
47
|
}
|
package/src/sbom/populate.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Central catalogue writer. Protected pipeline/resource_group remain in CI.
|
|
2
2
|
const io = require("../automation");
|
|
3
|
-
const { isDryRun } = require("../cli/command-executor");
|
|
3
|
+
const { isDryRun, isVerificationSkipped } = require("../cli/command-executor");
|
|
4
4
|
const { required, checkRegistryAccess } = require("../preflight/automation");
|
|
5
5
|
const {
|
|
6
6
|
fs,
|
|
@@ -489,7 +489,7 @@ class Publisher {
|
|
|
489
489
|
let code = 2;
|
|
490
490
|
try {
|
|
491
491
|
const requested = versions(this.env.NEXT_CATALOG_VERSIONS || "[]");
|
|
492
|
-
ensure(
|
|
492
|
+
if (!isVerificationSkipped()) ensure(
|
|
493
493
|
this.env.CI_COMMIT_BRANCH === "main" &&
|
|
494
494
|
this.env.CI_COMMIT_REF_PROTECTED === "true",
|
|
495
495
|
"Updater requires protected main",
|
|
@@ -526,7 +526,7 @@ class Publisher {
|
|
|
526
526
|
],
|
|
527
527
|
{ input: this.env.NEXUS_PASSWORD },
|
|
528
528
|
);
|
|
529
|
-
report.preflight = await (
|
|
529
|
+
if (!isVerificationSkipped()) report.preflight = await (
|
|
530
530
|
this.deps.checkRegistryAccess || checkRegistryAccess
|
|
531
531
|
)(this.repository, {
|
|
532
532
|
probe: () => this.descriptor(this.tagFor(requested[0])),
|
package/src/security/repair.js
CHANGED
|
@@ -18,7 +18,7 @@ const {
|
|
|
18
18
|
checkGitlabAccess,
|
|
19
19
|
checkExecutables,
|
|
20
20
|
} = require("../preflight/automation");
|
|
21
|
-
const { isDryRun, reportPreflight } = require("../cli/command-executor");
|
|
21
|
+
const { isDryRun, isVerificationSkipped, reportPreflight } = require("../cli/command-executor");
|
|
22
22
|
const escape = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
23
23
|
const pkgName = /^(?:@[a-z0-9._-]+\/)?[a-z0-9._-]+(?![\s\S])/;
|
|
24
24
|
const tuple = (value) => value.split(".").map(Number);
|
|
@@ -506,7 +506,7 @@ class Repairer extends Scanner {
|
|
|
506
506
|
"Incomplete scan/tracking; automatic repair is blocked",
|
|
507
507
|
);
|
|
508
508
|
const api = this.deps.api || glab(this.env);
|
|
509
|
-
report.preflight = {
|
|
509
|
+
if (!isVerificationSkipped()) report.preflight = {
|
|
510
510
|
...report.preflight,
|
|
511
511
|
gitlab_repair: await (this.deps.checkGitlabAccess || checkGitlabAccess)(
|
|
512
512
|
this.env,
|
package/src/security/tracking.js
CHANGED
|
@@ -12,7 +12,7 @@ const { encoded } = require("../sbom/schema");
|
|
|
12
12
|
const { glab, projectPath, pages, API } = require("./runtime");
|
|
13
13
|
const { HIGH } = require("./findings");
|
|
14
14
|
const { Tracker } = require("./tracker");
|
|
15
|
-
const { isDryRun, reportPreflight } = require("../cli/command-executor");
|
|
15
|
+
const { isDryRun, isVerificationSkipped, reportPreflight } = require("../cli/command-executor");
|
|
16
16
|
const {
|
|
17
17
|
required,
|
|
18
18
|
httpsEndpoint,
|
|
@@ -25,7 +25,7 @@ async function track({
|
|
|
25
25
|
deps = {},
|
|
26
26
|
dry = isDryRun(),
|
|
27
27
|
} = {}) {
|
|
28
|
-
ensure(
|
|
28
|
+
if (!isVerificationSkipped()) ensure(
|
|
29
29
|
env.NIGHTLY_EXPECTED_COMPONENT_SHA &&
|
|
30
30
|
env.NIGHTLY_EXPECTED_COMPONENT_SHA === env.CI_COMMIT_SHA,
|
|
31
31
|
"Tracker must run the same commit as the included component",
|
|
@@ -38,7 +38,7 @@ async function track({
|
|
|
38
38
|
]);
|
|
39
39
|
httpsEndpoint(env.YOUTRACK_URL, "YOUTRACK_URL");
|
|
40
40
|
const api = deps.api || glab(env);
|
|
41
|
-
await (deps.checkGitlabAccess || checkGitlabAccess)(
|
|
41
|
+
if (!isVerificationSkipped()) await (deps.checkGitlabAccess || checkGitlabAccess)(
|
|
42
42
|
env,
|
|
43
43
|
[env.NIGHTLY_CONSUMER_PROJECT],
|
|
44
44
|
{ api },
|
|
@@ -46,8 +46,6 @@ async function track({
|
|
|
46
46
|
const consumer = projectPath(env.NIGHTLY_CONSUMER_PROJECT),
|
|
47
47
|
jobId = env.NIGHTLY_CONSUMER_SCAN_JOB;
|
|
48
48
|
ensure(/^\d+(?![\s\S])/.test(jobId || ""), "Invalid consumer job ID");
|
|
49
|
-
const job = await api.get(consumer + "/jobs/" + jobId),
|
|
50
|
-
pipeline = await api.get(consumer + "/pipelines/" + job.pipeline.id);
|
|
51
49
|
const report = json(
|
|
52
50
|
await api.get(
|
|
53
51
|
consumer +
|
|
@@ -58,32 +56,36 @@ async function track({
|
|
|
58
56
|
),
|
|
59
57
|
),
|
|
60
58
|
cfg = report.config;
|
|
61
|
-
ensure(
|
|
59
|
+
if (!isVerificationSkipped()) ensure(
|
|
62
60
|
report.schemaVersion === 1 &&
|
|
63
61
|
String(cfg.project_id) === env.NIGHTLY_CONSUMER_PROJECT,
|
|
64
62
|
"Consumer report identity mismatch",
|
|
65
63
|
);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
64
|
+
if (!isVerificationSkipped()) {
|
|
65
|
+
const job = await api.get(consumer + "/jobs/" + jobId);
|
|
66
|
+
const pipeline = await api.get(consumer + "/pipelines/" + job.pipeline.id);
|
|
67
|
+
ensure(
|
|
68
|
+
pipeline.source === "schedule" &&
|
|
69
|
+
pipeline.ref === cfg.branch &&
|
|
70
|
+
pipeline.sha === cfg.sha &&
|
|
71
|
+
String(pipeline.id) === String(cfg.pipeline_id),
|
|
72
|
+
"Tracking is restricted to matching scheduled branch pipelines",
|
|
73
|
+
);
|
|
74
|
+
const branch = await api.get(
|
|
75
|
+
consumer + "/repository/branches/" + encoded(cfg.branch),
|
|
76
|
+
);
|
|
77
|
+
ensure(
|
|
78
|
+
branch.protected,
|
|
79
|
+
"Nightly automation requires a protected production branch",
|
|
80
|
+
);
|
|
81
|
+
}
|
|
80
82
|
const base = httpsEndpoint(env.YOUTRACK_URL, "YOUTRACK_URL");
|
|
81
83
|
const youtrack = new API(base + "/api", {
|
|
82
84
|
Authorization: "Bearer " + env.YOUTRACK_PRIVATE_TOKEN,
|
|
83
85
|
});
|
|
84
86
|
const tracker = deps.tracker || new Tracker(youtrack, base, env);
|
|
85
87
|
try {
|
|
86
|
-
report.preflight = {
|
|
88
|
+
if (!isVerificationSkipped()) report.preflight = {
|
|
87
89
|
youtrack: await (deps.checkYouTrackAccess || checkYouTrackAccess)(
|
|
88
90
|
env,
|
|
89
91
|
cfg,
|
|
@@ -137,7 +139,7 @@ async function reconcile({
|
|
|
137
139
|
"CI_PROJECT_ID",
|
|
138
140
|
]);
|
|
139
141
|
const api = deps.api || glab(env);
|
|
140
|
-
report.preflight = {
|
|
142
|
+
if (!isVerificationSkipped()) report.preflight = {
|
|
141
143
|
gitlab: await (deps.checkGitlabAccess || checkGitlabAccess)(
|
|
142
144
|
env,
|
|
143
145
|
[env.NIGHTLY_TRACKER_PROJECT],
|
|
@@ -164,13 +166,15 @@ async function reconcile({
|
|
|
164
166
|
);
|
|
165
167
|
ref = names.at(-1);
|
|
166
168
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
169
|
+
if (!isVerificationSkipped()) {
|
|
170
|
+
const commit = await api.get(
|
|
171
|
+
target + "/repository/commits/" + encoded(ref),
|
|
172
|
+
);
|
|
173
|
+
ensure(
|
|
174
|
+
commit.id === sha,
|
|
175
|
+
"Tracker reference does not match the included component",
|
|
176
|
+
);
|
|
177
|
+
}
|
|
174
178
|
reportPreflight(
|
|
175
179
|
"security reconcile",
|
|
176
180
|
"tracker-reference",
|
package/src/token/rotate.js
CHANGED
|
@@ -144,8 +144,8 @@ function tokenRotate() {
|
|
|
144
144
|
return runCommand({
|
|
145
145
|
name: 'token rotate',
|
|
146
146
|
checks: [
|
|
147
|
-
{ name: 'load-config', run: loadOrCreateConfig },
|
|
148
|
-
{ name: 'ask-tokens', requires: ['tokenTtlMonths', 'bot'], run: askTokens },
|
|
147
|
+
{ name: 'load-config', prepare: true, run: loadOrCreateConfig },
|
|
148
|
+
{ name: 'ask-tokens', prepare: true, requires: ['tokenTtlMonths', 'bot'], run: askTokens },
|
|
149
149
|
{ name: 'check-access', requires: ['targets', 'origin', 'ownerToken'], run: checkTargetsAccess }
|
|
150
150
|
],
|
|
151
151
|
steps: [
|