@paths.design/caws-cli 6.0.0 → 7.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/dist/commands/quality-gates.js +4 -2
- package/dist/index.js +3 -1
- package/dist/scaffold/git-hooks.js +65 -7
- package/dist/scaffold/index.js +179 -41
- package/dist/utils/project-analysis.js +103 -0
- package/package.json +1 -1
|
@@ -51,9 +51,9 @@ async function qualityGatesCommand(options = {}) {
|
|
|
51
51
|
? path.join(vscodeExtensionPath, 'bundled', 'quality-gates', 'run-quality-gates.mjs')
|
|
52
52
|
: null;
|
|
53
53
|
|
|
54
|
-
// Option 3: Check node_modules for quality-gates package
|
|
54
|
+
// Option 3: Check node_modules for quality-gates package (prioritize published package)
|
|
55
55
|
const nodeModulesPaths = [
|
|
56
|
-
|
|
56
|
+
// Published npm package (priority)
|
|
57
57
|
path.join(
|
|
58
58
|
projectRoot,
|
|
59
59
|
'node_modules',
|
|
@@ -61,6 +61,8 @@ async function qualityGatesCommand(options = {}) {
|
|
|
61
61
|
'quality-gates',
|
|
62
62
|
'run-quality-gates.mjs'
|
|
63
63
|
),
|
|
64
|
+
// Legacy monorepo local copy (fallback)
|
|
65
|
+
path.join(projectRoot, 'node_modules', '@caws', 'quality-gates', 'run-quality-gates.mjs'),
|
|
64
66
|
path.join(projectRoot, 'node_modules', 'quality-gates', 'run-quality-gates.mjs'),
|
|
65
67
|
];
|
|
66
68
|
|
package/dist/index.js
CHANGED
|
@@ -138,7 +138,7 @@ program
|
|
|
138
138
|
.option('--json', 'Output machine-readable JSON to stdout', false)
|
|
139
139
|
.option(
|
|
140
140
|
'--gates <gates>',
|
|
141
|
-
'Run only specific gates (comma-separated: naming,code_freeze,duplication,god_objects,documentation)',
|
|
141
|
+
'Run only specific gates (comma-separated: naming,code_freeze,duplication,god_objects,hidden-todo,documentation,placeholders)',
|
|
142
142
|
''
|
|
143
143
|
)
|
|
144
144
|
.option('--fix', 'Attempt automatic fixes (experimental)', false)
|
|
@@ -168,7 +168,9 @@ VALID GATES:
|
|
|
168
168
|
code_freeze Enforce code freeze compliance
|
|
169
169
|
duplication Detect functional duplication
|
|
170
170
|
god_objects Prevent oversized files
|
|
171
|
+
hidden-todo Detect hidden incomplete implementations
|
|
171
172
|
documentation Check documentation quality
|
|
173
|
+
placeholders Placeholder governance (explicit degradations)
|
|
172
174
|
|
|
173
175
|
EXAMPLES:
|
|
174
176
|
# Run all gates in development mode
|
|
@@ -147,10 +147,36 @@ fi
|
|
|
147
147
|
|
|
148
148
|
QUALITY_GATES_RAN=false
|
|
149
149
|
|
|
150
|
-
# Option 1:
|
|
151
|
-
if [ -f "
|
|
150
|
+
# Option 1: Quality gates package (installed via npm)
|
|
151
|
+
if [ -f "node_modules/@paths.design/quality-gates/run-quality-gates.mjs" ]; then
|
|
152
152
|
if command -v node >/dev/null 2>&1; then
|
|
153
|
-
echo "📁 Running
|
|
153
|
+
echo "📁 Running quality gates package..."
|
|
154
|
+
if node node_modules/@paths.design/quality-gates/run-quality-gates.mjs --ci; then
|
|
155
|
+
echo "✅ Quality gates passed"
|
|
156
|
+
QUALITY_GATES_RAN=true
|
|
157
|
+
else
|
|
158
|
+
echo "❌ Quality gates failed - commit blocked"
|
|
159
|
+
echo "💡 Fix the violations above before committing"
|
|
160
|
+
exit 1
|
|
161
|
+
fi
|
|
162
|
+
fi
|
|
163
|
+
# Option 1b: Quality gates package (monorepo/local copy)
|
|
164
|
+
elif [ -f "node_modules/@caws/quality-gates/run-quality-gates.mjs" ]; then
|
|
165
|
+
if command -v node >/dev/null 2>&1; then
|
|
166
|
+
echo "📁 Running quality gates package (local)..."
|
|
167
|
+
if node node_modules/@caws/quality-gates/run-quality-gates.mjs --ci; then
|
|
168
|
+
echo "✅ Quality gates passed"
|
|
169
|
+
QUALITY_GATES_RAN=true
|
|
170
|
+
else
|
|
171
|
+
echo "❌ Quality gates failed - commit blocked"
|
|
172
|
+
echo "💡 Fix the violations above before committing"
|
|
173
|
+
exit 1
|
|
174
|
+
fi
|
|
175
|
+
fi
|
|
176
|
+
# Option 2: Legacy Node.js quality gates script (deprecated)
|
|
177
|
+
elif [ -f "scripts/quality-gates/run-quality-gates.js" ]; then
|
|
178
|
+
if command -v node >/dev/null 2>&1; then
|
|
179
|
+
echo "📁 Running legacy Node.js quality gates script..."
|
|
154
180
|
if node scripts/quality-gates/run-quality-gates.js; then
|
|
155
181
|
echo "✅ Quality gates passed"
|
|
156
182
|
QUALITY_GATES_RAN=true
|
|
@@ -160,7 +186,7 @@ if [ -f "scripts/quality-gates/run-quality-gates.js" ]; then
|
|
|
160
186
|
exit 1
|
|
161
187
|
fi
|
|
162
188
|
fi
|
|
163
|
-
# Option
|
|
189
|
+
# Option 3: CAWS CLI validation
|
|
164
190
|
elif command -v caws >/dev/null 2>&1; then
|
|
165
191
|
echo "📋 Running CAWS CLI validation..."
|
|
166
192
|
if caws validate --quiet 2>/dev/null; then
|
|
@@ -195,16 +221,48 @@ elif [ -f "scripts/simple_gates.py" ] && command -v python3 >/dev/null 2>&1; the
|
|
|
195
221
|
else
|
|
196
222
|
echo "⚠️ Quality gates not available - skipping"
|
|
197
223
|
echo "💡 Available options:"
|
|
198
|
-
echo " • Install: npm install -
|
|
224
|
+
echo " • Install quality gates: npm install --save-dev @paths.design/quality-gates"
|
|
225
|
+
echo " • Install CAWS CLI: npm install -g @paths.design/caws-cli"
|
|
199
226
|
echo " • Use Python: python3 scripts/simple_gates.py"
|
|
200
227
|
echo " • Use Makefile: make caws-gates"
|
|
201
228
|
QUALITY_GATES_RAN=true
|
|
202
229
|
fi
|
|
203
230
|
|
|
204
|
-
# Run hidden TODO analysis on staged files only (if
|
|
231
|
+
# Run hidden TODO analysis on staged files only (if available)
|
|
205
232
|
if [ "$QUALITY_GATES_RAN" = true ]; then
|
|
206
233
|
echo "🔍 Checking for hidden TODOs in staged files..."
|
|
207
|
-
|
|
234
|
+
# Try quality gates package TODO analyzer first (published package)
|
|
235
|
+
if [ -f "node_modules/@paths.design/quality-gates/todo-analyzer.mjs" ]; then
|
|
236
|
+
if command -v node >/dev/null 2>&1; then
|
|
237
|
+
if node node_modules/@paths.design/quality-gates/todo-analyzer.mjs --staged-only --ci-mode --min-confidence 0.8 >/dev/null 2>&1; then
|
|
238
|
+
echo "✅ No critical hidden TODOs found in staged files"
|
|
239
|
+
else
|
|
240
|
+
echo "❌ Critical hidden TODOs detected in staged files - commit blocked"
|
|
241
|
+
echo "💡 Fix stub implementations and placeholder code before committing"
|
|
242
|
+
echo "📖 See docs/PLACEHOLDER-DETECTION-GUIDE.md for classification"
|
|
243
|
+
echo ""
|
|
244
|
+
echo "🔍 Running detailed analysis on staged files..."
|
|
245
|
+
node node_modules/@paths.design/quality-gates/todo-analyzer.mjs --staged-only --min-confidence 0.8
|
|
246
|
+
exit 1
|
|
247
|
+
fi
|
|
248
|
+
fi
|
|
249
|
+
# Try quality gates package TODO analyzer (monorepo/local copy)
|
|
250
|
+
elif [ -f "node_modules/@caws/quality-gates/todo-analyzer.mjs" ]; then
|
|
251
|
+
if command -v node >/dev/null 2>&1; then
|
|
252
|
+
if node node_modules/@caws/quality-gates/todo-analyzer.mjs --staged-only --ci-mode --min-confidence 0.8 >/dev/null 2>&1; then
|
|
253
|
+
echo "✅ No critical hidden TODOs found in staged files"
|
|
254
|
+
else
|
|
255
|
+
echo "❌ Critical hidden TODOs detected in staged files - commit blocked"
|
|
256
|
+
echo "💡 Fix stub implementations and placeholder code before committing"
|
|
257
|
+
echo "📖 See docs/PLACEHOLDER-DETECTION-GUIDE.md for classification"
|
|
258
|
+
echo ""
|
|
259
|
+
echo "🔍 Running detailed analysis on staged files..."
|
|
260
|
+
node node_modules/@caws/quality-gates/todo-analyzer.mjs --staged-only --min-confidence 0.8
|
|
261
|
+
exit 1
|
|
262
|
+
fi
|
|
263
|
+
fi
|
|
264
|
+
# Fallback to legacy Python analyzer
|
|
265
|
+
elif command -v python3 >/dev/null 2>&1 && [ -f "scripts/v3/analysis/todo_analyzer.py" ]; then
|
|
208
266
|
if python3 scripts/v3/analysis/todo_analyzer.py --staged-only --ci-mode --min-confidence 0.8 >/dev/null 2>&1; then
|
|
209
267
|
echo "✅ No critical hidden TODOs found in staged files"
|
|
210
268
|
else
|
package/dist/scaffold/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const chalk = require('chalk');
|
|
|
10
10
|
|
|
11
11
|
// Import detection utilities
|
|
12
12
|
const { detectCAWSSetup } = require('../utils/detection');
|
|
13
|
+
const { detectsPublishing } = require('../utils/project-analysis');
|
|
13
14
|
|
|
14
15
|
// Import git hooks scaffolding
|
|
15
16
|
const { scaffoldGitHooks } = require('./git-hooks');
|
|
@@ -311,47 +312,152 @@ async function scaffoldProject(options) {
|
|
|
311
312
|
},
|
|
312
313
|
});
|
|
313
314
|
|
|
314
|
-
// Add quality gates
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
315
|
+
// Add quality gates package and configuration if requested
|
|
316
|
+
// Note: These are optional - git hooks fall back to CAWS CLI if package isn't installed
|
|
317
|
+
if (options.withQualityGates) {
|
|
318
|
+
// Copy quality gates configuration files from templates
|
|
319
|
+
enhancements.push({
|
|
320
|
+
name: 'duplication.qualitygatesrc.yaml',
|
|
321
|
+
description: 'Duplication gate configuration',
|
|
322
|
+
required: false,
|
|
323
|
+
sourcePath: path.join(
|
|
324
|
+
__dirname,
|
|
325
|
+
'../../quality-gates/templates/duplication.qualitygatesrc.yaml'
|
|
326
|
+
),
|
|
327
|
+
});
|
|
320
328
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
329
|
+
enhancements.push({
|
|
330
|
+
name: 'godObject.qualitygatesrc.yaml',
|
|
331
|
+
description: 'God objects gate configuration',
|
|
332
|
+
required: false,
|
|
333
|
+
sourcePath: path.join(
|
|
334
|
+
__dirname,
|
|
335
|
+
'../../quality-gates/templates/godObject.qualitygatesrc.yaml'
|
|
336
|
+
),
|
|
337
|
+
});
|
|
326
338
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
339
|
+
// Create docs-status directory structure
|
|
340
|
+
enhancements.push({
|
|
341
|
+
name: 'docs-status',
|
|
342
|
+
description: 'Quality gates status directory',
|
|
343
|
+
required: false,
|
|
344
|
+
customHandler: async (targetDir) => {
|
|
345
|
+
const docsStatusDir = path.join(targetDir, 'docs-status');
|
|
346
|
+
await fs.ensureDir(docsStatusDir);
|
|
347
|
+
|
|
348
|
+
// Copy template files from quality-gates package
|
|
349
|
+
const qualityGatesTemplates = path.join(
|
|
350
|
+
__dirname,
|
|
351
|
+
'../../quality-gates/templates/docs-status'
|
|
352
|
+
);
|
|
353
|
+
if (fs.existsSync(qualityGatesTemplates)) {
|
|
354
|
+
await fs.copy(qualityGatesTemplates, docsStatusDir);
|
|
355
|
+
return { added: 1, skipped: 0 };
|
|
356
|
+
}
|
|
357
|
+
return { added: 1, skipped: 0 };
|
|
358
|
+
},
|
|
359
|
+
});
|
|
332
360
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
console.log(chalk.blue('\n📦 Installing quality gates package...'));
|
|
361
|
+
// Install quality gates package
|
|
362
|
+
console.log(chalk.blue('\n📦 Setting up quality gates package...'));
|
|
336
363
|
try {
|
|
337
364
|
const { execSync } = require('child_process');
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
365
|
+
|
|
366
|
+
// Check if we're in monorepo (can copy files directly) or need npm install
|
|
367
|
+
const qualityGatesPath = path.resolve(__dirname, '../../../quality-gates');
|
|
368
|
+
const isMonorepo = fs.existsSync(qualityGatesPath);
|
|
369
|
+
|
|
370
|
+
if (isMonorepo && fs.existsSync(path.join(currentDir, 'package.json'))) {
|
|
371
|
+
// In monorepo - copy files directly instead of installing package
|
|
372
|
+
console.log(
|
|
373
|
+
chalk.gray(' Detected monorepo structure - copying quality gates files...')
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
const qualityGatesDest = path.join(currentDir, 'node_modules', '@caws', 'quality-gates');
|
|
377
|
+
await fs.ensureDir(qualityGatesDest);
|
|
378
|
+
|
|
379
|
+
// Copy all .mjs files
|
|
380
|
+
const mjsFiles = fs.readdirSync(qualityGatesPath).filter((f) => f.endsWith('.mjs'));
|
|
381
|
+
for (const file of mjsFiles) {
|
|
382
|
+
await fs.copy(path.join(qualityGatesPath, file), path.join(qualityGatesDest, file));
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Copy templates directory
|
|
386
|
+
if (fs.existsSync(path.join(qualityGatesPath, 'templates'))) {
|
|
387
|
+
await fs.copy(
|
|
388
|
+
path.join(qualityGatesPath, 'templates'),
|
|
389
|
+
path.join(qualityGatesDest, 'templates')
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Copy package.json for dependencies
|
|
394
|
+
await fs.copy(
|
|
395
|
+
path.join(qualityGatesPath, 'package.json'),
|
|
396
|
+
path.join(qualityGatesDest, 'package.json')
|
|
397
|
+
);
|
|
398
|
+
|
|
399
|
+
// Install dependencies
|
|
400
|
+
console.log(chalk.gray(' Installing quality gates dependencies...'));
|
|
401
|
+
execSync('npm install --production --no-audit --no-fund', {
|
|
402
|
+
cwd: qualityGatesDest,
|
|
403
|
+
stdio: 'inherit',
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
console.log(chalk.green('✅ Quality gates files copied and dependencies installed'));
|
|
407
|
+
} else if (fs.existsSync(path.join(currentDir, 'package.json'))) {
|
|
408
|
+
// Regular project - try to install from npm (when published)
|
|
409
|
+
console.log(chalk.gray(' Installing @paths.design/quality-gates package...'));
|
|
410
|
+
|
|
411
|
+
try {
|
|
412
|
+
const npmCommand = 'npm install --save-dev @paths.design/quality-gates';
|
|
413
|
+
execSync(npmCommand, {
|
|
414
|
+
cwd: currentDir,
|
|
415
|
+
stdio: 'inherit',
|
|
416
|
+
});
|
|
417
|
+
console.log(chalk.green('✅ Quality gates package installed from npm'));
|
|
418
|
+
} catch (npmError) {
|
|
419
|
+
console.log(
|
|
420
|
+
chalk.yellow('⚠️ Package not found on npm - quality gates will use local files')
|
|
421
|
+
);
|
|
422
|
+
console.log(
|
|
423
|
+
chalk.gray(
|
|
424
|
+
' Package will be available once published as @paths.design/quality-gates'
|
|
425
|
+
)
|
|
426
|
+
);
|
|
427
|
+
console.log(
|
|
428
|
+
chalk.gray(' For now, quality gates will work via CAWS CLI or local scripts')
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
} else {
|
|
432
|
+
// No package.json - suggest global install or manual setup
|
|
433
|
+
console.log(chalk.yellow('⚠️ No package.json found - skipping package installation'));
|
|
434
|
+
console.log(chalk.gray(' Options:'));
|
|
435
|
+
console.log(
|
|
436
|
+
chalk.gray(' • Install globally: npm install -g @paths.design/quality-gates')
|
|
437
|
+
);
|
|
438
|
+
console.log(
|
|
439
|
+
chalk.gray(
|
|
440
|
+
' • Create package.json and run: npm install --save-dev @paths.design/quality-gates'
|
|
441
|
+
)
|
|
442
|
+
);
|
|
443
|
+
console.log(chalk.gray(' • Use CAWS CLI: caws quality-gates'));
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
console.log(
|
|
447
|
+
chalk.blue(
|
|
448
|
+
'💡 You can now use: node node_modules/@paths.design/quality-gates/run-quality-gates.mjs'
|
|
449
|
+
)
|
|
450
|
+
);
|
|
451
|
+
console.log(chalk.blue(' Or: caws quality-gates'));
|
|
349
452
|
} catch (error) {
|
|
350
|
-
console.log(chalk.yellow(`⚠️ Failed to
|
|
453
|
+
console.log(chalk.yellow(`⚠️ Failed to set up quality gates package: ${error.message}`));
|
|
351
454
|
console.log(
|
|
352
|
-
chalk.gray(
|
|
455
|
+
chalk.gray(
|
|
456
|
+
' You can install manually: npm install --save-dev @paths.design/quality-gates'
|
|
457
|
+
)
|
|
353
458
|
);
|
|
354
|
-
console.log(chalk.gray(' Or
|
|
459
|
+
console.log(chalk.gray(' Or globally: npm install -g @paths.design/quality-gates'));
|
|
460
|
+
console.log(chalk.gray(' Or use CAWS CLI: caws quality-gates'));
|
|
355
461
|
}
|
|
356
462
|
}
|
|
357
463
|
|
|
@@ -377,16 +483,26 @@ async function scaffoldProject(options) {
|
|
|
377
483
|
});
|
|
378
484
|
}
|
|
379
485
|
|
|
380
|
-
// Add OIDC setup guide if
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
)
|
|
486
|
+
// Add OIDC setup guide only if:
|
|
487
|
+
// 1. Explicitly requested with --with-oidc flag, OR
|
|
488
|
+
// 2. Project detects publishing configuration (package.json with publishConfig, pyproject.toml, etc.)
|
|
489
|
+
const needsOidc = options.withOidc || detectsPublishing(currentDir);
|
|
490
|
+
const oidcExists = fs.existsSync(path.join(currentDir, 'OIDC_SETUP.md'));
|
|
491
|
+
|
|
492
|
+
if (needsOidc && !oidcExists) {
|
|
385
493
|
enhancements.push({
|
|
386
494
|
name: 'OIDC_SETUP.md',
|
|
387
495
|
description: 'OIDC trusted publisher setup guide',
|
|
388
496
|
required: false,
|
|
389
497
|
});
|
|
498
|
+
} else if (needsOidc && oidcExists) {
|
|
499
|
+
console.log(chalk.gray('⏭️ Skipped OIDC_SETUP.md (already exists)'));
|
|
500
|
+
} else if (!needsOidc && !options.minimal) {
|
|
501
|
+
// Inform user that OIDC is available but not needed
|
|
502
|
+
console.log(
|
|
503
|
+
chalk.blue('ℹ️ OIDC setup skipped (project does not appear to publish packages)')
|
|
504
|
+
);
|
|
505
|
+
console.log(chalk.gray(' Add --with-oidc flag if you plan to publish packages later'));
|
|
390
506
|
}
|
|
391
507
|
|
|
392
508
|
// For enhanced setups, preserve existing tools
|
|
@@ -424,13 +540,20 @@ async function scaffoldProject(options) {
|
|
|
424
540
|
continue;
|
|
425
541
|
}
|
|
426
542
|
|
|
427
|
-
|
|
543
|
+
// Handle custom sourcePath (for quality gates configs from monorepo)
|
|
544
|
+
const sourcePath = enhancement.sourcePath
|
|
545
|
+
? enhancement.sourcePath
|
|
546
|
+
: setup?.templateDir
|
|
547
|
+
? path.join(setup.templateDir, enhancement.name)
|
|
548
|
+
: null;
|
|
549
|
+
|
|
550
|
+
if (!sourcePath && !enhancement.sourcePath) {
|
|
428
551
|
console.warn(
|
|
429
552
|
chalk.yellow(`⚠️ Template directory not available for enhancement: ${enhancement.name}`)
|
|
430
553
|
);
|
|
431
554
|
continue;
|
|
432
555
|
}
|
|
433
|
-
|
|
556
|
+
|
|
434
557
|
const destPath = path.join(currentDir, enhancement.name);
|
|
435
558
|
|
|
436
559
|
if (!fs.existsSync(destPath)) {
|
|
@@ -497,6 +620,10 @@ async function scaffoldProject(options) {
|
|
|
497
620
|
|
|
498
621
|
// Check if OIDC was added
|
|
499
622
|
const oidcAdded = addedFiles.some((file) => file.includes('OIDC_SETUP'));
|
|
623
|
+
const qualityGatesAdded = addedFiles.some(
|
|
624
|
+
(file) => file.includes('quality-gates') || file.includes('todo_analyzer')
|
|
625
|
+
);
|
|
626
|
+
|
|
500
627
|
if (oidcAdded) {
|
|
501
628
|
console.log('2. Set up OIDC trusted publisher (see OIDC_SETUP.md)');
|
|
502
629
|
console.log('3. Push to trigger automated publishing');
|
|
@@ -504,7 +631,18 @@ async function scaffoldProject(options) {
|
|
|
504
631
|
} else {
|
|
505
632
|
console.log('2. Customize your working spec in .caws/working-spec.yaml');
|
|
506
633
|
console.log('3. Run validation: caws validate --suggestions');
|
|
507
|
-
|
|
634
|
+
if (!qualityGatesAdded && !options.minimal) {
|
|
635
|
+
console.log(
|
|
636
|
+
chalk.gray('4. Note: Quality gates scripts skipped (git hooks use CAWS CLI by default)')
|
|
637
|
+
);
|
|
638
|
+
console.log(
|
|
639
|
+
chalk.gray(
|
|
640
|
+
' Add --with-quality-gates flag if you want local scripts without global CLI'
|
|
641
|
+
)
|
|
642
|
+
);
|
|
643
|
+
} else {
|
|
644
|
+
console.log('4. Your existing CAWS tools remain unchanged');
|
|
645
|
+
}
|
|
508
646
|
}
|
|
509
647
|
}
|
|
510
648
|
|
|
@@ -99,7 +99,110 @@ function shouldInitInCurrentDirectory(projectName, currentDir) {
|
|
|
99
99
|
return hasProjectIndicators;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Detect if project publishes packages to registries
|
|
104
|
+
* Checks for publishing configuration in package.json, pyproject.toml, etc.
|
|
105
|
+
* @param {string} cwd - Current working directory
|
|
106
|
+
* @returns {boolean} Whether project appears to publish packages
|
|
107
|
+
*/
|
|
108
|
+
function detectsPublishing(cwd = process.cwd()) {
|
|
109
|
+
const files = fs.readdirSync(cwd);
|
|
110
|
+
|
|
111
|
+
// Check package.json for npm publishing
|
|
112
|
+
if (files.includes('package.json')) {
|
|
113
|
+
try {
|
|
114
|
+
const packageJson = JSON.parse(
|
|
115
|
+
fs.readFileSync(path.join(cwd, 'package.json'), 'utf8')
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// Indicators of publishing:
|
|
119
|
+
// - Has publishConfig
|
|
120
|
+
// - Has scripts that include "publish"
|
|
121
|
+
// - Has name that suggests it's a published package
|
|
122
|
+
// - Has repository field (often indicates published package)
|
|
123
|
+
const hasPublishConfig = packageJson.publishConfig;
|
|
124
|
+
const hasPublishScript =
|
|
125
|
+
packageJson.scripts &&
|
|
126
|
+
Object.keys(packageJson.scripts).some((key) =>
|
|
127
|
+
key.toLowerCase().includes('publish')
|
|
128
|
+
);
|
|
129
|
+
const hasScopedName = packageJson.name && packageJson.name.startsWith('@');
|
|
130
|
+
const hasRepository = packageJson.repository;
|
|
131
|
+
|
|
132
|
+
if (hasPublishConfig || hasPublishScript || (hasScopedName && hasRepository)) {
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
} catch (e) {
|
|
136
|
+
// Ignore parse errors
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Check pyproject.toml for PyPI publishing
|
|
141
|
+
if (files.includes('pyproject.toml')) {
|
|
142
|
+
try {
|
|
143
|
+
const pyprojectContent = fs.readFileSync(
|
|
144
|
+
path.join(cwd, 'pyproject.toml'),
|
|
145
|
+
'utf8'
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// Check for build system and project metadata (indicates publishable package)
|
|
149
|
+
const hasBuildSystem = pyprojectContent.includes('[build-system]');
|
|
150
|
+
const hasProjectMetadata = pyprojectContent.includes('[project]');
|
|
151
|
+
const hasToolPublish = pyprojectContent.includes('[tool.publish]') ||
|
|
152
|
+
pyprojectContent.includes('[tool.twine]');
|
|
153
|
+
|
|
154
|
+
if ((hasBuildSystem && hasProjectMetadata) || hasToolPublish) {
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
} catch (e) {
|
|
158
|
+
// Ignore read errors
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Check for Maven publishing (pom.xml)
|
|
163
|
+
if (files.includes('pom.xml')) {
|
|
164
|
+
return true; // Maven projects typically publish
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Check for .csproj (NuGet publishing)
|
|
168
|
+
const csprojFiles = files.filter((f) => f.endsWith('.csproj'));
|
|
169
|
+
if (csprojFiles.length > 0) {
|
|
170
|
+
return true; // .NET projects typically publish
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Check for GitHub Actions workflows that publish
|
|
174
|
+
const workflowsPath = path.join(cwd, '.github', 'workflows');
|
|
175
|
+
if (fs.existsSync(workflowsPath)) {
|
|
176
|
+
try {
|
|
177
|
+
const workflowFiles = fs.readdirSync(workflowsPath);
|
|
178
|
+
for (const workflowFile of workflowFiles) {
|
|
179
|
+
if (workflowFile.endsWith('.yml') || workflowFile.endsWith('.yaml')) {
|
|
180
|
+
const workflowContent = fs.readFileSync(
|
|
181
|
+
path.join(workflowsPath, workflowFile),
|
|
182
|
+
'utf8'
|
|
183
|
+
);
|
|
184
|
+
// Check for common publishing actions/commands
|
|
185
|
+
if (
|
|
186
|
+
workflowContent.includes('npm publish') ||
|
|
187
|
+
workflowContent.includes('pypa/gh-action-pypi-publish') ||
|
|
188
|
+
workflowContent.includes('publish-to-npm') ||
|
|
189
|
+
workflowContent.includes('semantic-release') ||
|
|
190
|
+
workflowContent.includes('publish')
|
|
191
|
+
) {
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
} catch (e) {
|
|
197
|
+
// Ignore read errors
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
102
204
|
module.exports = {
|
|
103
205
|
detectProjectType,
|
|
104
206
|
shouldInitInCurrentDirectory,
|
|
207
|
+
detectsPublishing,
|
|
105
208
|
};
|