@zairakai/js-dev-tools 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.
@@ -134,6 +134,8 @@ variables:
134
134
  secret_detection:
135
135
  stage: security
136
136
  rules:
137
+ - if: $UPDATE_DEPENDENCIES == "true"
138
+ when: never
137
139
  - if: $CI_PIPELINE_SOURCE == "merge_request_event" || $CI_MERGE_REQUEST_ID
138
140
  - if: $CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "develop"
139
141
 
@@ -144,6 +146,8 @@ js:tag-format:
144
146
  stage: security
145
147
  image: ${ALPINE_IMAGE}
146
148
  rules:
149
+ - if: $UPDATE_DEPENDENCIES == "true"
150
+ when: never
147
151
  - if: $CI_COMMIT_TAG
148
152
  script:
149
153
  - |
@@ -166,6 +170,10 @@ js:tag-format:
166
170
  js:install:
167
171
  <<: [*base_job, *cache_node_push]
168
172
  stage: install
173
+ rules:
174
+ - if: $UPDATE_DEPENDENCIES == "true"
175
+ when: never
176
+ - when: on_success
169
177
  image: ${NODE_IMAGE}
170
178
  script:
171
179
  - npm ci --cache .npm --prefer-offline
@@ -184,6 +192,10 @@ js:install:
184
192
  js:validate:markdownlint:
185
193
  <<: [*base_job, *cache_node]
186
194
  stage: validate
195
+ rules:
196
+ - if: $UPDATE_DEPENDENCIES == "true"
197
+ when: never
198
+ - when: on_success
187
199
  image: ${NODE_IMAGE}
188
200
  script:
189
201
  - bash "$JS_DEV_TOOLS_SCRIPTS/markdownlint.sh"
@@ -191,6 +203,10 @@ js:validate:markdownlint:
191
203
  js:validate:shellcheck:
192
204
  <<: *base_job
193
205
  stage: validate
206
+ rules:
207
+ - if: $UPDATE_DEPENDENCIES == "true"
208
+ when: never
209
+ - when: on_success
194
210
  image: ${SHELLCHECK_IMAGE}
195
211
  before_script:
196
212
  - apk add --no-cache bash
@@ -208,6 +224,10 @@ js:validate:shellcheck:
208
224
  js:quality:lint:
209
225
  <<: *job_node
210
226
  stage: quality
227
+ rules:
228
+ - if: $UPDATE_DEPENDENCIES == "true"
229
+ when: never
230
+ - when: on_success
211
231
  script:
212
232
  - bash "$JS_DEV_TOOLS_SCRIPTS/eslint.sh"
213
233
  artifacts:
@@ -221,18 +241,30 @@ js:quality:lint:
221
241
  js:quality:format:
222
242
  <<: *job_node
223
243
  stage: quality
244
+ rules:
245
+ - if: $UPDATE_DEPENDENCIES == "true"
246
+ when: never
247
+ - when: on_success
224
248
  script:
225
249
  - bash "$JS_DEV_TOOLS_SCRIPTS/prettier.sh"
226
250
 
227
251
  js:quality:stylelint:
228
252
  <<: *job_node
229
253
  stage: quality
254
+ rules:
255
+ - if: $UPDATE_DEPENDENCIES == "true"
256
+ when: never
257
+ - when: on_success
230
258
  script:
231
259
  - bash "$JS_DEV_TOOLS_SCRIPTS/stylelint.sh"
232
260
 
233
261
  js:quality:typecheck:
234
262
  <<: *job_node
235
263
  stage: quality
264
+ rules:
265
+ - if: $UPDATE_DEPENDENCIES == "true"
266
+ when: never
267
+ - when: on_success
236
268
  script:
237
269
  - |
238
270
  if [ -f tsconfig.json ] && [ -f node_modules/.bin/tsc ]; then
@@ -244,12 +276,20 @@ js:quality:typecheck:
244
276
  js:quality:knip:
245
277
  <<: *job_node
246
278
  stage: quality
279
+ rules:
280
+ - if: $UPDATE_DEPENDENCIES == "true"
281
+ when: never
282
+ - when: on_success
247
283
  script:
248
284
  - bash "$JS_DEV_TOOLS_SCRIPTS/knip.sh"
249
285
 
250
286
  js:quality:bats:
251
287
  <<: [*base_job, *cache_node]
252
288
  stage: quality
289
+ rules:
290
+ - if: $UPDATE_DEPENDENCIES == "true"
291
+ when: never
292
+ - when: on_success
253
293
  image: ${ALPINE_IMAGE}
254
294
  before_script:
255
295
  - apk add --no-cache bash bats git
@@ -265,6 +305,8 @@ js:quality:outdated:
265
305
  <<: *job_node
266
306
  stage: quality
267
307
  rules:
308
+ - if: $UPDATE_DEPENDENCIES == "true"
309
+ when: never
268
310
  - if: $CI_PIPELINE_SOURCE == "schedule"
269
311
  script:
270
312
  - bash "$JS_DEV_TOOLS_SCRIPTS/outdated.sh"
@@ -280,6 +322,10 @@ js:quality:outdated:
280
322
  js:test:
281
323
  <<: [*base_job, *cache_node]
282
324
  stage: test
325
+ rules:
326
+ - if: $UPDATE_DEPENDENCIES == "true"
327
+ when: never
328
+ - when: on_success
283
329
  image: ${NODE_TEST_IMAGE}
284
330
  script:
285
331
  - CI=true bash "$JS_DEV_TOOLS_SCRIPTS/test.sh"
@@ -314,6 +360,8 @@ js:build:
314
360
  expire_in: 1 week
315
361
  when: on_success
316
362
  rules:
363
+ - if: $UPDATE_DEPENDENCIES == "true"
364
+ when: never
317
365
  - if: $CI_COMMIT_TAG =~ $VERSION_TAG_REGEX
318
366
  - if: $CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "develop"
319
367
  - if: $CI_PIPELINE_SOURCE == "merge_request_event" || $CI_MERGE_REQUEST_ID
@@ -326,6 +374,8 @@ js:publish:
326
374
  stage: publish
327
375
  image: ${NODE_IMAGE}
328
376
  rules:
377
+ - if: $UPDATE_DEPENDENCIES == "true"
378
+ when: never
329
379
  - if: $CI_COMMIT_TAG =~ $VERSION_TAG_REGEX
330
380
  script:
331
381
  - |
@@ -356,6 +406,8 @@ js:release:
356
406
  stage: release
357
407
  image: ${RELEASE_CLI_IMAGE}
358
408
  rules:
409
+ - if: $UPDATE_DEPENDENCIES == "true"
410
+ when: never
359
411
  - if: $CI_COMMIT_TAG =~ $VERSION_TAG_REGEX
360
412
  before_script:
361
413
  - apk add --no-cache bash git jq
@@ -402,6 +454,7 @@ deps:update:
402
454
  script:
403
455
  - node --version
404
456
  - "[ -d node_modules ] || npm ci --no-audit --no-fund"
457
+ - '[ "${JS_DEV_TOOLS_SCRIPTS}" = "scripts" ] || npm install --no-save --no-audit --no-fund "@zairakai/js-dev-tools@latest"'
405
458
  - node "${JS_DEV_TOOLS_SCRIPTS}/update-dependencies.mjs" update
406
459
  rules:
407
460
  - if: $UPDATE_DEPENDENCIES == "true" && $CI_COMMIT_BRANCH == "develop"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zairakai/js-dev-tools",
3
- "version": "3.3.0",
3
+ "version": "3.3.2",
4
4
  "description": "The unified and centralized development toolkit for JavaScript/TypeScript projects - ESLint, Prettier, Stylelint, Knip, Vitest, Git Hooks, and GitLab CI pipeline templates",
5
5
  "keywords": [
6
6
  "dev-tools",
@@ -111,6 +111,23 @@ export function formatChanges(rows) {
111
111
  return lines.join('\n')
112
112
  }
113
113
 
114
+ /** The identity and the key used to sign, from the group variables. It fails when one is missing: no unsigned tag. */
115
+ export function signerIdentity(env) {
116
+ const missing = ['GPG_PRIVATE_KEY_B64', 'RELEASE_SIGNER_NAME', 'RELEASE_SIGNER_EMAIL'].filter((name) => !env[name])
117
+
118
+ if (0 < missing.length) {
119
+ throw new Error(
120
+ `${missing.join(', ')} not set: the commits and the tags are signed, see the handbook (versioning).`
121
+ )
122
+ }
123
+
124
+ return {
125
+ name: env.RELEASE_SIGNER_NAME,
126
+ email: env.RELEASE_SIGNER_EMAIL,
127
+ key: Buffer.from(env.GPG_PRIVATE_KEY_B64, 'base64').toString('utf8'),
128
+ }
129
+ }
130
+
114
131
  /** What `tag` has to do: the version to create, or null (and why). */
115
132
  export function planTag({ config, last, commits, before, after }) {
116
133
  if ('never' === config.tag.when) return { version: null, reason: 'the project is never tagged by the update' }
@@ -179,9 +196,52 @@ function assertEnvironment() {
179
196
  run('git', ['config', '--global', '--add', 'safe.directory', process.cwd()])
180
197
  }
181
198
 
182
- function configureGit() {
183
- run('git', ['config', 'user.name', 'Stanislas Poisson (autoupdate)'])
184
- run('git', ['config', 'user.email', 'contact@stanislas-poisson.fr'])
199
+ /** GPG is not in the image of the CI: add it with the package manager of the image (Alpine or Debian). */
200
+ function ensureGpg() {
201
+ const available = () => {
202
+ try {
203
+ run('gpg', ['--version'])
204
+ return true
205
+ } catch {
206
+ return false
207
+ }
208
+ }
209
+
210
+ if (available()) return
211
+
212
+ for (const [command, args] of [
213
+ ['apk', ['add', '--no-cache', 'gnupg']],
214
+ ['apt-get', ['install', '-y', '--no-install-recommends', 'gnupg']],
215
+ ]) {
216
+ try {
217
+ run(command, args)
218
+ } catch {
219
+ continue
220
+ }
221
+
222
+ if (available()) return
223
+ }
224
+
225
+ throw new Error('gpg is not installed and could not be added: add gnupg to the image of the job.')
226
+ }
227
+
228
+ /** Import the signing key and make Git sign the commits and the tags with it. */
229
+ function configureSigning() {
230
+ const signer = signerIdentity(process.env)
231
+
232
+ ensureGpg()
233
+
234
+ run('gpg', ['--batch', '--import'], { input: signer.key, stdio: ['pipe', 'pipe', 'pipe'] })
235
+ const fingerprint = run('gpg', ['--list-secret-keys', '--with-colons'])
236
+ .split('\n')
237
+ .find((line) => line.startsWith('fpr'))
238
+ .split(':')[9]
239
+
240
+ run('git', ['config', 'user.name', signer.name])
241
+ run('git', ['config', 'user.email', signer.email])
242
+ run('git', ['config', 'user.signingkey', fingerprint])
243
+ run('git', ['config', 'commit.gpgsign', 'true'])
244
+ run('git', ['config', 'tag.gpgsign', 'true'])
185
245
  }
186
246
 
187
247
  function remoteUrl() {
@@ -244,7 +304,7 @@ async function update(config) {
244
304
  return
245
305
  }
246
306
 
247
- configureGit()
307
+ configureSigning()
248
308
  const before = JSON.parse(readFileSync('package.json', 'utf8'))
249
309
  const branch = `${BRANCH_PREFIX}-${new Date().toISOString().slice(0, 10).replaceAll('-', '')}`
250
310
  run('git', ['switch', '-c', branch])
@@ -333,16 +393,11 @@ async function tag(config) {
333
393
  return
334
394
  }
335
395
 
336
- const result = await api('POST', `/projects/${projectId()}/repository/tags`, {
337
- tag_name: plan.version,
338
- ref: process.env.CI_COMMIT_SHA,
339
- message: plan.version,
340
- })
341
-
342
- if (!result.ok)
343
- throw new Error(`The tag ${plan.version} could not be created: ${secret(JSON.stringify(result.data))}`)
396
+ configureSigning()
397
+ run('git', ['tag', '-s', plan.version, '-m', plan.version, process.env.CI_COMMIT_SHA])
398
+ run('git', ['push', remoteUrl(), `refs/tags/${plan.version}`])
344
399
 
345
- log(`Tag ${plan.version} created (last tag ${last}): ${plan.reason}. Its pipeline publishes the package.`)
400
+ log(`Tag ${plan.version} signed and pushed (last tag ${last}): ${plan.reason}. Its pipeline publishes the package.`)
346
401
  }
347
402
 
348
403
  async function cascade(config) {