@lvce-editor/eslint-plugin-github-actions 15.4.0 → 16.1.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 (2) hide show
  1. package/dist/index.js +184 -32
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -14,15 +14,15 @@ const platforms = {
14
14
  windows: 'windows'
15
15
  };
16
16
  const actions = {
17
- 'actions/cache': ['actions/cache@v5'],
18
- 'actions/checkout': ['actions/checkout@v6'],
17
+ 'actions/cache': ['actions/cache@v6'],
18
+ 'actions/checkout': ['actions/checkout@v7'],
19
19
  'actions/create-release': ['actions/create-release@v1'],
20
- 'actions/deploy-pages': ['actions/deploy-pages@v4'],
21
- 'actions/download-artifact': ['actions/download-artifact@v7'],
20
+ 'actions/deploy-pages': ['actions/deploy-pages@v5'],
21
+ 'actions/download-artifact': ['actions/download-artifact@v8'],
22
22
  'actions/setup-node': ['actions/setup-node@v6'],
23
23
  'actions/setup-python': ['actions/setup-python@v6'],
24
- 'actions/upload-artifact': ['actions/upload-artifact@v6'],
25
- 'actions/upload-pages-artifact': ['actions/upload-pages-artifact@v4']
24
+ 'actions/upload-artifact': ['actions/upload-artifact@v7'],
25
+ 'actions/upload-pages-artifact': ['actions/upload-pages-artifact@v5']
26
26
  };
27
27
  const npmRegistries = ['https://registry.npmjs.org', 'https://npm.pkg.github.com'];
28
28
  const onProperties = ['push', 'pull_request'];
@@ -38,7 +38,7 @@ const permissions$1 = {
38
38
  const isSupported = (actions, value) => {
39
39
  return actions.includes(value);
40
40
  };
41
- const meta$g = {
41
+ const meta$i = {
42
42
  docs: {
43
43
  description: 'Disallow unsupported action versions'
44
44
  },
@@ -48,7 +48,7 @@ const meta$g = {
48
48
  },
49
49
  type: 'problem'
50
50
  };
51
- const create$g = context => {
51
+ const create$i = context => {
52
52
  const sourceCode = getSourceCode(context);
53
53
  if (!sourceCode.parserServices?.isYAML) {
54
54
  return {};
@@ -79,8 +79,8 @@ const create$g = context => {
79
79
 
80
80
  const actionVersions = {
81
81
  __proto__: null,
82
- create: create$g,
83
- meta: meta$g
82
+ create: create$i,
83
+ meta: meta$i
84
84
  };
85
85
 
86
86
  const parseVersion = value => {
@@ -104,7 +104,7 @@ const isSupportedMacosversion = version => {
104
104
  const isSupportedWindowsVersion = version => {
105
105
  return config.windows.includes(version);
106
106
  };
107
- const meta$f = {
107
+ const meta$h = {
108
108
  docs: {
109
109
  description: 'Disallow unsupported ci versions'
110
110
  },
@@ -113,7 +113,7 @@ const meta$f = {
113
113
  },
114
114
  type: 'problem'
115
115
  };
116
- const create$f = context => {
116
+ const create$h = context => {
117
117
  const sourceCode = getSourceCode(context);
118
118
  if (!sourceCode.parserServices?.isYAML) {
119
119
  return {};
@@ -157,11 +157,11 @@ const create$f = context => {
157
157
 
158
158
  const ciVersions = {
159
159
  __proto__: null,
160
- create: create$f,
161
- meta: meta$f
160
+ create: create$h,
161
+ meta: meta$h
162
162
  };
163
163
 
164
- const meta$e = {
164
+ const meta$g = {
165
165
  docs: {
166
166
  description: 'Disallow unsupported fail fast values'
167
167
  },
@@ -170,7 +170,7 @@ const meta$e = {
170
170
  },
171
171
  type: 'problem'
172
172
  };
173
- const create$e = context => {
173
+ const create$g = context => {
174
174
  const sourceCode = getSourceCode(context);
175
175
  if (!sourceCode.parserServices?.isYAML) {
176
176
  return {};
@@ -193,11 +193,108 @@ const create$e = context => {
193
193
 
194
194
  const failFast = {
195
195
  __proto__: null,
196
- create: create$e,
197
- meta: meta$e
196
+ create: create$g,
197
+ meta: meta$g
198
198
  };
199
199
 
200
- const meta$d = {
200
+ const meta$f = {
201
+ docs: {
202
+ description: 'Require GitHub Pages deployment best practices'
203
+ },
204
+ messages: {
205
+ missingGithubPagesConcurrency: 'GitHub Pages deployment requires concurrency group pages with cancel-in-progress false',
206
+ missingGithubPagesPermissions: 'GitHub Pages deployment requires contents read, pages write, and id-token write permissions'
207
+ },
208
+ type: 'problem'
209
+ };
210
+ const isCiWorkflow = fileName => {
211
+ return fileName.endsWith('/ci.yml') || fileName.endsWith('/ci.yaml') || fileName.endsWith('\\ci.yml') || fileName.endsWith('\\ci.yaml');
212
+ };
213
+ const isYamlScalarString = (node, value) => {
214
+ return node?.type === 'YAMLScalar' && node.value === value;
215
+ };
216
+ const isYamlScalarBoolean = (node, value) => {
217
+ return node?.type === 'YAMLScalar' && node.value === value;
218
+ };
219
+ const isDeployPagesAction = node => {
220
+ return node?.type === 'YAMLScalar' && typeof node.value === 'string' && node.value.startsWith('actions/deploy-pages@');
221
+ };
222
+ const isTopLevelPair = node => {
223
+ return node.parent?.parent?.type === 'YAMLDocument';
224
+ };
225
+ const getMappingValue = (mapping, key) => {
226
+ for (const pair of mapping.pairs) {
227
+ if (pair.key?.type === 'YAMLScalar' && pair.key.value === key) {
228
+ return pair.value;
229
+ }
230
+ }
231
+ return null;
232
+ };
233
+ const hasGithubPagesPermissions = mapping => {
234
+ if (!mapping) {
235
+ return false;
236
+ }
237
+ return isYamlScalarString(getMappingValue(mapping, 'contents'), 'read') && isYamlScalarString(getMappingValue(mapping, 'pages'), 'write') && isYamlScalarString(getMappingValue(mapping, 'id-token'), 'write');
238
+ };
239
+ const hasGithubPagesConcurrency = mapping => {
240
+ if (!mapping) {
241
+ return false;
242
+ }
243
+ return isYamlScalarString(getMappingValue(mapping, 'group'), 'pages') && isYamlScalarBoolean(getMappingValue(mapping, 'cancel-in-progress'), false);
244
+ };
245
+ const create$f = context => {
246
+ const sourceCode = getSourceCode(context);
247
+ if (!sourceCode.parserServices?.isYAML || !isCiWorkflow(context.filename)) {
248
+ return {};
249
+ }
250
+ let deployPagesNode;
251
+ let permissions;
252
+ let concurrency;
253
+ return {
254
+ 'Program:exit'() {
255
+ if (!deployPagesNode) {
256
+ return;
257
+ }
258
+ if (!hasGithubPagesPermissions(permissions)) {
259
+ context.report({
260
+ messageId: 'missingGithubPagesPermissions',
261
+ node: deployPagesNode
262
+ });
263
+ }
264
+ if (!hasGithubPagesConcurrency(concurrency)) {
265
+ context.report({
266
+ messageId: 'missingGithubPagesConcurrency',
267
+ node: deployPagesNode
268
+ });
269
+ }
270
+ },
271
+ YAMLPair(node) {
272
+ if (!node.key || node.key.type !== 'YAMLScalar' || typeof node.key.value !== 'string') {
273
+ return;
274
+ }
275
+ if (node.key.value === 'uses' && isDeployPagesAction(node.value)) {
276
+ deployPagesNode = node;
277
+ return;
278
+ }
279
+ if (!isTopLevelPair(node) || node.value?.type !== 'YAMLMapping') {
280
+ return;
281
+ }
282
+ if (node.key.value === 'permissions') {
283
+ permissions = node.value;
284
+ } else if (node.key.value === 'concurrency') {
285
+ concurrency = node.value;
286
+ }
287
+ }
288
+ };
289
+ };
290
+
291
+ const githubPages = {
292
+ __proto__: null,
293
+ create: create$f,
294
+ meta: meta$f
295
+ };
296
+
297
+ const meta$e = {
201
298
  docs: {
202
299
  description: 'Disallow unsupported github token values'
203
300
  },
@@ -206,7 +303,7 @@ const meta$d = {
206
303
  },
207
304
  type: 'problem'
208
305
  };
209
- const create$d = context => {
306
+ const create$e = context => {
210
307
  const sourceCode = getSourceCode(context);
211
308
  if (!sourceCode.parserServices?.isYAML) {
212
309
  return {};
@@ -231,11 +328,11 @@ const create$d = context => {
231
328
 
232
329
  const githubToken = {
233
330
  __proto__: null,
234
- create: create$d,
235
- meta: meta$d
331
+ create: create$e,
332
+ meta: meta$e
236
333
  };
237
334
 
238
- const meta$c = {
335
+ const meta$d = {
239
336
  docs: {
240
337
  description: 'Disallow unsupported matrix values'
241
338
  },
@@ -244,7 +341,7 @@ const meta$c = {
244
341
  },
245
342
  type: 'problem'
246
343
  };
247
- const create$c = context => {
344
+ const create$d = context => {
248
345
  const sourceCode = getSourceCode(context);
249
346
  if (!sourceCode.parserServices?.isYAML) {
250
347
  return {};
@@ -267,11 +364,11 @@ const create$c = context => {
267
364
 
268
365
  const matrix = {
269
366
  __proto__: null,
270
- create: create$c,
271
- meta: meta$c
367
+ create: create$d,
368
+ meta: meta$d
272
369
  };
273
370
 
274
- const meta$b = {
371
+ const meta$c = {
275
372
  docs: {
276
373
  description: 'Disallow unsupported max parallel values'
277
374
  },
@@ -280,7 +377,7 @@ const meta$b = {
280
377
  },
281
378
  type: 'problem'
282
379
  };
283
- const create$b = context => {
380
+ const create$c = context => {
284
381
  const sourceCode = getSourceCode(context);
285
382
  if (!sourceCode.parserServices?.isYAML) {
286
383
  return {};
@@ -303,11 +400,11 @@ const create$b = context => {
303
400
 
304
401
  const maxParallel = {
305
402
  __proto__: null,
306
- create: create$b,
307
- meta: meta$b
403
+ create: create$c,
404
+ meta: meta$c
308
405
  };
309
406
 
310
- const meta$a = {
407
+ const meta$b = {
311
408
  docs: {
312
409
  description: 'Disallow unsupported needs values'
313
410
  },
@@ -340,7 +437,7 @@ const getValidNeeds = node => {
340
437
  }
341
438
  return validNeeds;
342
439
  };
343
- const create$a = context => {
440
+ const create$b = context => {
344
441
  const sourceCode = getSourceCode(context);
345
442
  if (!sourceCode.parserServices?.isYAML) {
346
443
  return {};
@@ -398,6 +495,57 @@ const create$a = context => {
398
495
  };
399
496
 
400
497
  const needs = {
498
+ __proto__: null,
499
+ create: create$b,
500
+ meta: meta$b
501
+ };
502
+
503
+ const meta$a = {
504
+ docs: {
505
+ description: 'Disallow e2e tests in release workflows'
506
+ },
507
+ messages: {
508
+ noE2eInRelease: 'Do not run e2e tests in release workflows'
509
+ },
510
+ type: 'problem'
511
+ };
512
+ const isReleaseWorkflow = filename => {
513
+ const normalized = filename.replaceAll('\\', '/');
514
+ return normalized.endsWith('/release.yml') || normalized.endsWith('/release.yaml') || normalized === 'release.yml' || normalized === 'release.yaml';
515
+ };
516
+ const runsE2eTests = value => {
517
+ const commands = value.replaceAll('\r', '\n').replaceAll('&&', ';').replaceAll('||', ';').split(/[\n;]/);
518
+ for (const command of commands) {
519
+ const tokens = command.trim().split(/\s+/);
520
+ const npmIndex = tokens.indexOf('npm');
521
+ if (npmIndex === -1) {
522
+ continue;
523
+ }
524
+ const scriptName = tokens[npmIndex + 2];
525
+ if (tokens[npmIndex + 1] === 'run' && (scriptName === 'e2e' || scriptName?.startsWith('e2e:'))) {
526
+ return true;
527
+ }
528
+ }
529
+ return false;
530
+ };
531
+ const create$a = context => {
532
+ const sourceCode = getSourceCode(context);
533
+ if (!sourceCode.parserServices?.isYAML || !isReleaseWorkflow(context.filename)) {
534
+ return {};
535
+ }
536
+ return {
537
+ YAMLPair(node) {
538
+ if (node && node.type === 'YAMLPair' && node.key && typeof node.key === 'object' && 'type' in node.key && node.key.type === 'YAMLScalar' && node.key.value === 'run' && node.value && typeof node.value === 'object' && 'type' in node.value && node.value.type === 'YAMLScalar' && typeof node.value.value === 'string' && runsE2eTests(node.value.value)) {
539
+ context.report({
540
+ messageId: 'noE2eInRelease',
541
+ node: node.value
542
+ });
543
+ }
544
+ }
545
+ };
546
+ };
547
+
548
+ const noE2eInRelease = {
401
549
  __proto__: null,
402
550
  create: create$a,
403
551
  meta: meta$a
@@ -862,10 +1010,12 @@ const plugin = {
862
1010
  'action-versions': actionVersions,
863
1011
  'ci-versions': ciVersions,
864
1012
  'fail-fast': failFast,
1013
+ 'github-pages': githubPages,
865
1014
  'github-token': githubToken,
866
1015
  matrix: matrix,
867
1016
  'max-parallel': maxParallel,
868
1017
  needs: needs,
1018
+ 'no-e2e-in-release': noE2eInRelease,
869
1019
  'node-version-file': nodeVersionFile,
870
1020
  npm: npm,
871
1021
  'npm-registry': npmRegistry,
@@ -890,10 +1040,12 @@ const recommended = [{
890
1040
  'github-actions/action-versions': 'error',
891
1041
  'github-actions/ci-versions': 'error',
892
1042
  'github-actions/fail-fast': 'error',
1043
+ 'github-actions/github-pages': 'error',
893
1044
  'github-actions/github-token': 'error',
894
1045
  'github-actions/matrix': 'off',
895
1046
  'github-actions/max-parallel': 'error',
896
1047
  'github-actions/needs': 'error',
1048
+ 'github-actions/no-e2e-in-release': 'error',
897
1049
  'github-actions/node-version-file': 'error',
898
1050
  'github-actions/npm': 'error',
899
1051
  'github-actions/npm-registry': 'error',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/eslint-plugin-github-actions",
3
- "version": "15.4.0",
3
+ "version": "16.1.0",
4
4
  "main": "dist/index.js",
5
5
  "type": "module",
6
6
  "license": "MIT",