pumuki-ast-hooks 5.5.53 → 5.5.54

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.
@@ -9,9 +9,23 @@
9
9
  */
10
10
 
11
11
  const { pushFinding } = require('../../ast-core');
12
- const fs = require('fs');
13
- const path = require('path');
14
- const glob = require('glob');
12
+ const {
13
+ checkFastfileExists,
14
+ checkGitHubActionsWorkflow,
15
+ checkTestFlightConfiguration,
16
+ checkBuildConfiguration,
17
+ checkCertificateManagement,
18
+ checkCodeSigningConfiguration,
19
+ checkAutomatedTesting,
20
+ checkVersionBumping,
21
+ checkReleaseNotes,
22
+ checkSlackNotifications,
23
+ checkMatchConfiguration,
24
+ checkGymConfiguration,
25
+ checkScanConfiguration,
26
+ checkPilotConfiguration,
27
+ checkAppStoreMetadata,
28
+ } = require('./iOSCICDChecks');
15
29
 
16
30
  class iOSCICDRules {
17
31
  constructor(findings, projectRoot) {
@@ -20,411 +34,27 @@ class iOSCICDRules {
20
34
  }
21
35
 
22
36
  analyze() {
23
- this.checkFastfileExists();
24
- this.checkGitHubActionsWorkflow();
25
- this.checkTestFlightConfiguration();
26
- this.checkBuildConfiguration();
27
- this.checkCertificateManagement();
28
- this.checkCodeSigningConfiguration();
29
- this.checkAutomatedTesting();
30
- this.checkVersionBumping();
31
- this.checkReleaseNotes();
32
- this.checkSlackNotifications();
33
- this.checkMatchConfiguration();
34
- this.checkGymConfiguration();
35
- this.checkScanConfiguration();
36
- this.checkPilotConfiguration();
37
- this.checkAppStoreMetadata();
38
- }
39
-
40
- checkFastfileExists() {
41
- const fastfilePath = path.join(this.projectRoot, 'fastlane/Fastfile');
42
-
43
- if (!fs.existsSync(fastfilePath)) {
44
- pushFinding(this.findings, {
45
- ruleId: 'ios.cicd.missing_fastfile',
46
- severity: 'medium',
47
- message: 'Proyecto iOS sin Fastfile. Fastlane automatiza builds, tests y deployments.',
48
- filePath: 'PROJECT_ROOT',
49
- line: 1,
50
- suggestion: `Inicializar Fastlane:
51
-
52
- fastlane init
53
-
54
- Esto crea:
55
- - fastlane/Fastfile
56
- - fastlane/Appfile
57
- - fastlane/Matchfile`
58
- });
59
- } else {
60
- this.checkFastfileLanes(fastfilePath);
61
- }
62
- }
63
-
64
- checkFastfileLanes(fastfilePath) {
65
- const content = fs.readFileSync(fastfilePath, 'utf-8');
66
-
67
- const requiredLanes = ['test', 'beta', 'release'];
68
- const existingLanes = content.match(/lane\s+:(\w+)/g)?.map(l => l.match(/:(\w+)/)?.[1]) || [];
69
-
70
- const missingLanes = requiredLanes.filter(lane => !existingLanes.includes(lane));
71
-
72
- if (missingLanes.length > 0) {
73
- pushFinding(this.findings, {
74
- ruleId: 'ios.cicd.fastfile_missing_lanes',
75
- severity: 'medium',
76
- message: `Fastfile sin lanes esenciales: ${missingLanes.join(', ')}`,
77
- filePath: fastfilePath,
78
- line: 1,
79
- suggestion: `Añadir lanes:
80
-
81
- lane :test do
82
- scan
83
- end
84
-
85
- lane :beta do
86
- build_app
87
- upload_to_testflight
88
- end
89
-
90
- lane :release do
91
- build_app
92
- upload_to_app_store
93
- end`
94
- });
95
- }
96
-
97
- if (!content.includes('increment_build_number') && !content.includes('increment_version_number')) {
98
- pushFinding(this.findings, {
99
- ruleId: 'ios.cicd.missing_version_increment',
100
- severity: 'medium',
101
- message: 'Fastfile sin incremento automático de versión/build.',
102
- filePath: fastfilePath,
103
- line: 1
104
- });
105
- }
106
- }
107
-
108
- checkGitHubActionsWorkflow() {
109
- const workflowPath = path.join(this.projectRoot, '.github/workflows');
110
-
111
- if (!fs.existsSync(workflowPath)) {
112
- pushFinding(this.findings, {
113
- ruleId: 'ios.cicd.missing_github_actions',
114
- severity: 'low',
115
- message: 'Proyecto sin GitHub Actions workflows. Automatizar CI/CD.',
116
- filePath: 'PROJECT_ROOT',
117
- line: 1,
118
- suggestion: 'Crear .github/workflows/ios-ci.yml para tests automáticos'
119
- });
120
- return;
121
- }
122
-
123
- const workflows = glob.sync('*.yml', { cwd: workflowPath, absolute: true });
124
- const iosWorkflows = workflows.filter(w => {
125
- const content = fs.readFileSync(w, 'utf-8');
126
- return content.includes('macos') || content.includes('ios') || content.includes('xcodebuild');
127
- });
128
-
129
- if (iosWorkflows.length === 0) {
130
- pushFinding(this.findings, {
131
- ruleId: 'ios.cicd.no_ios_workflow',
132
- severity: 'medium',
133
- message: 'GitHub Actions sin workflow de iOS.',
134
- filePath: '.github/workflows/',
135
- line: 1
136
- });
137
- } else {
138
- iosWorkflows.forEach(workflow => {
139
- const content = fs.readFileSync(workflow, 'utf-8');
140
-
141
- if (!content.includes('xcodebuild test')) {
142
- pushFinding(this.findings, {
143
- ruleId: 'ios.cicd.workflow_missing_tests',
144
- severity: 'high',
145
- message: 'Workflow de iOS sin tests automáticos.',
146
- filePath: workflow,
147
- line: 1
148
- });
149
- }
150
-
151
- if (!content.includes('fastlane')) {
152
- pushFinding(this.findings, {
153
- ruleId: 'ios.cicd.workflow_without_fastlane',
154
- severity: 'low',
155
- message: 'Workflow sin Fastlane. Considerar para simplificar pipeline.',
156
- filePath: workflow,
157
- line: 1
158
- });
159
- }
160
- });
161
- }
162
- }
163
-
164
- checkTestFlightConfiguration() {
165
- const fastfilePath = path.join(this.projectRoot, 'fastlane/Fastfile');
166
-
167
- if (fs.existsSync(fastfilePath)) {
168
- const content = fs.readFileSync(fastfilePath, 'utf-8');
169
-
170
- if (content.includes('beta') && !content.includes('upload_to_testflight')) {
171
- pushFinding(this.findings, {
172
- ruleId: 'ios.cicd.beta_without_testflight',
173
- severity: 'medium',
174
- message: 'Lane beta sin upload_to_testflight. Automatizar distribución.',
175
- filePath: fastfilePath,
176
- line: 1
177
- });
178
- }
179
-
180
- if (content.includes('upload_to_testflight') && !content.includes('changelog')) {
181
- pushFinding(this.findings, {
182
- ruleId: 'ios.cicd.testflight_without_changelog',
183
- severity: 'low',
184
- message: 'TestFlight sin changelog. Añadir notas de release.',
185
- filePath: fastfilePath,
186
- line: 1
187
- });
188
- }
189
- }
190
- }
191
-
192
- checkBuildConfiguration() {
193
- const projectFiles = glob.sync('**/*.xcodeproj/project.pbxproj', {
194
- cwd: this.projectRoot,
195
- absolute: true
196
- });
197
-
198
- if (projectFiles.length === 0) return;
199
-
200
- projectFiles.forEach(projectFile => {
201
- const content = fs.readFileSync(projectFile, 'utf-8');
202
-
203
- const configurations = content.match(/buildConfiguration\s*=\s*(\w+)/g) || [];
204
- const hasDebug = configurations.some(c => c.includes('Debug'));
205
- const hasRelease = configurations.some(c => c.includes('Release'));
206
- const hasStaging = configurations.some(c => c.includes('Staging'));
207
-
208
- if (!hasStaging && (hasDebug && hasRelease)) {
209
- pushFinding(this.findings, {
210
- ruleId: 'ios.cicd.missing_staging_config',
211
- severity: 'low',
212
- message: 'Sin configuración Staging. Útil para testing pre-production.',
213
- filePath: projectFile,
214
- line: 1
215
- });
216
- }
217
- });
218
- }
219
-
220
- checkCertificateManagement() {
221
- const matchfilePath = path.join(this.projectRoot, 'fastlane/Matchfile');
222
-
223
- if (!fs.existsSync(matchfilePath)) {
224
- const fastfilePath = path.join(this.projectRoot, 'fastlane/Fastfile');
225
- if (fs.existsSync(fastfilePath)) {
226
- pushFinding(this.findings, {
227
- ruleId: 'ios.cicd.missing_match_config',
228
- severity: 'medium',
229
- message: 'Fastlane sin Match para gestión de certificados.',
230
- filePath: 'fastlane/',
231
- line: 1,
232
- suggestion: 'fastlane match init para gestionar certificados en equipo'
233
- });
234
- }
235
- }
236
- }
237
-
238
- checkCodeSigningConfiguration() {
239
- const projectFiles = glob.sync('**/*.xcodeproj/project.pbxproj', {
240
- cwd: this.projectRoot,
241
- absolute: true
242
- });
243
-
244
- projectFiles.forEach(projectFile => {
245
- const content = fs.readFileSync(projectFile, 'utf-8');
246
-
247
- if (content.includes('CODE_SIGN_STYLE = Manual')) {
248
- pushFinding(this.findings, {
249
- ruleId: 'ios.cicd.manual_code_signing',
250
- severity: 'low',
251
- message: 'Code signing manual. Considerar Automatic o Match para CI/CD.',
252
- filePath: projectFile,
253
- line: 1
254
- });
255
- }
256
- });
257
- }
258
-
259
- checkAutomatedTesting() {
260
- const fastfilePath = path.join(this.projectRoot, 'fastlane/Fastfile');
261
-
262
- if (fs.existsSync(fastfilePath)) {
263
- const content = fs.readFileSync(fastfilePath, 'utf-8');
264
-
265
- if (!content.includes('scan') && !content.includes('run_tests')) {
266
- pushFinding(this.findings, {
267
- ruleId: 'ios.cicd.no_automated_tests',
268
- severity: 'high',
269
- message: 'Fastlane sin tests automatizados. Añadir scan action.',
270
- filePath: fastfilePath,
271
- line: 1,
272
- suggestion: `lane :test do
273
- scan(scheme: "MyApp")
274
- end`
275
- });
276
- }
277
- }
278
- }
279
-
280
- checkVersionBumping() {
281
- const fastfilePath = path.join(this.projectRoot, 'fastlane/Fastfile');
282
-
283
- if (fs.existsSync(fastfilePath)) {
284
- const content = fs.readFileSync(fastfilePath, 'utf-8');
285
-
286
- if (content.includes('upload_to') && !content.includes('increment_build_number')) {
287
- pushFinding(this.findings, {
288
- ruleId: 'ios.cicd.missing_build_increment',
289
- severity: 'medium',
290
- message: 'Deployment sin incremento automático de build number.',
291
- filePath: fastfilePath,
292
- line: 1
293
- });
294
- }
295
- }
296
- }
297
-
298
- checkReleaseNotes() {
299
- const changelogPath = path.join(this.projectRoot, 'CHANGELOG.md');
300
- const fastfilePath = path.join(this.projectRoot, 'fastlane/Fastfile');
301
-
302
- if (!fs.existsSync(changelogPath) && fs.existsSync(fastfilePath)) {
303
- pushFinding(this.findings, {
304
- ruleId: 'ios.cicd.missing_changelog',
305
- severity: 'low',
306
- message: 'Proyecto sin CHANGELOG.md. Documentar cambios para releases.',
307
- filePath: 'PROJECT_ROOT',
308
- line: 1
309
- });
310
- }
311
- }
312
-
313
- checkSlackNotifications() {
314
- const fastfilePath = path.join(this.projectRoot, 'fastlane/Fastfile');
315
-
316
- if (fs.existsSync(fastfilePath)) {
317
- const content = fs.readFileSync(fastfilePath, 'utf-8');
318
-
319
- if (content.includes('upload_to') && !content.includes('slack')) {
320
- pushFinding(this.findings, {
321
- ruleId: 'ios.cicd.missing_notifications',
322
- severity: 'low',
323
- message: 'Deployment sin notificaciones. Añadir Slack para avisar al equipo.',
324
- filePath: fastfilePath,
325
- line: 1
326
- });
327
- }
328
- }
329
- }
330
-
331
- checkMatchConfiguration() {
332
- const matchfilePath = path.join(this.projectRoot, 'fastlane/Matchfile');
333
-
334
- if (fs.existsSync(matchfilePath)) {
335
- const content = fs.readFileSync(matchfilePath, 'utf-8');
336
-
337
- if (!content.includes('git_url')) {
338
- pushFinding(this.findings, {
339
- ruleId: 'ios.cicd.match_missing_git_url',
340
- severity: 'high',
341
- message: 'Matchfile sin git_url. Match requiere repositorio para certificados.',
342
- filePath: matchfilePath,
343
- line: 1
344
- });
345
- }
346
- }
347
- }
348
-
349
- checkGymConfiguration() {
350
- const gymfilePath = path.join(this.projectRoot, 'fastlane/Gymfile');
351
- const fastfilePath = path.join(this.projectRoot, 'fastlane/Fastfile');
352
-
353
- if (fs.existsSync(fastfilePath) && !fs.existsSync(gymfilePath)) {
354
- const content = fs.readFileSync(fastfilePath, 'utf-8');
355
-
356
- if (content.includes('build_app') || content.includes('gym')) {
357
- pushFinding(this.findings, {
358
- ruleId: 'ios.cicd.missing_gymfile',
359
- severity: 'low',
360
- message: 'build_app sin Gymfile. Considerar para centralizar configuración de build.',
361
- filePath: 'fastlane/',
362
- line: 1
363
- });
364
- }
365
- }
366
- }
367
-
368
- checkScanConfiguration() {
369
- const fastfilePath = path.join(this.projectRoot, 'fastlane/Fastfile');
370
-
371
- if (fs.existsSync(fastfilePath)) {
372
- const content = fs.readFileSync(fastfilePath, 'utf-8');
373
-
374
- if (content.includes('scan') && !content.includes('code_coverage')) {
375
- pushFinding(this.findings, {
376
- ruleId: 'ios.cicd.scan_without_coverage',
377
- severity: 'low',
378
- message: 'scan sin code_coverage: true. Activar para métricas.',
379
- filePath: fastfilePath,
380
- line: 1,
381
- suggestion: 'scan(code_coverage: true, scheme: "MyApp")'
382
- });
383
- }
384
- }
385
- }
386
-
387
- checkPilotConfiguration() {
388
- const fastfilePath = path.join(this.projectRoot, 'fastlane/Fastfile');
389
-
390
- if (fs.existsSync(fastfilePath)) {
391
- const content = fs.readFileSync(fastfilePath, 'utf-8');
392
-
393
- if (content.includes('pilot') || content.includes('upload_to_testflight')) {
394
- if (!content.includes('changelog') && !content.includes('whats_new')) {
395
- pushFinding(this.findings, {
396
- ruleId: 'ios.cicd.pilot_missing_changelog',
397
- severity: 'low',
398
- message: 'TestFlight upload sin changelog/whats_new.',
399
- filePath: fastfilePath,
400
- line: 1
401
- });
402
- }
403
- }
404
- }
405
- }
406
-
407
- checkAppStoreMetadata() {
408
- const metadataPath = path.join(this.projectRoot, 'fastlane/metadata');
409
-
410
- if (!fs.existsSync(metadataPath)) {
411
- const fastfilePath = path.join(this.projectRoot, 'fastlane/Fastfile');
412
-
413
- if (fs.existsSync(fastfilePath)) {
414
- const content = fs.readFileSync(fastfilePath, 'utf-8');
415
-
416
- if (content.includes('upload_to_app_store') || content.includes('deliver')) {
417
- pushFinding(this.findings, {
418
- ruleId: 'ios.cicd.missing_metadata',
419
- severity: 'low',
420
- message: 'Upload a App Store sin metadata/ folder. Versionar descripciones y screenshots.',
421
- filePath: 'fastlane/',
422
- line: 1,
423
- suggestion: 'fastlane deliver init para crear estructura metadata/'
424
- });
425
- }
426
- }
427
- }
37
+ const ctx = {
38
+ findings: this.findings,
39
+ projectRoot: this.projectRoot,
40
+ pushFinding,
41
+ };
42
+
43
+ checkFastfileExists(ctx);
44
+ checkGitHubActionsWorkflow(ctx);
45
+ checkTestFlightConfiguration(ctx);
46
+ checkBuildConfiguration(ctx);
47
+ checkCertificateManagement(ctx);
48
+ checkCodeSigningConfiguration(ctx);
49
+ checkAutomatedTesting(ctx);
50
+ checkVersionBumping(ctx);
51
+ checkReleaseNotes(ctx);
52
+ checkSlackNotifications(ctx);
53
+ checkMatchConfiguration(ctx);
54
+ checkGymConfiguration(ctx);
55
+ checkScanConfiguration(ctx);
56
+ checkPilotConfiguration(ctx);
57
+ checkAppStoreMetadata(ctx);
428
58
  }
429
59
  }
430
60