create-qa-architect 5.0.0 → 5.0.6
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/.github/RELEASE_CHECKLIST.md +2 -4
- package/.github/workflows/daily-deploy-check.yml +136 -0
- package/.github/workflows/nightly-gitleaks-verification.yml +1 -1
- package/.github/workflows/quality.yml +3 -0
- package/.github/workflows/release.yml +12 -10
- package/.github/workflows/weekly-audit.yml +173 -0
- package/LICENSE +66 -0
- package/README.md +44 -30
- package/config/defaults.js +22 -1
- package/config/quality-config.schema.json +1 -1
- package/create-saas-monetization.js +75 -27
- package/docs/ARCHITECTURE.md +53 -0
- package/docs/DEPLOYMENT.md +62 -0
- package/docs/PREFLIGHT_REPORT.md +108 -0
- package/docs/SLA_GATES.md +28 -0
- package/docs/TESTING.md +61 -0
- package/docs/security/SOC2_STARTER.md +29 -0
- package/lib/config-validator.js +8 -2
- package/lib/dependency-monitoring-basic.js +73 -26
- package/lib/dependency-monitoring-premium.js +21 -19
- package/lib/github-api.js +249 -0
- package/lib/interactive/questions.js +4 -0
- package/lib/license-validator.js +1 -1
- package/lib/licensing.js +11 -10
- package/lib/package-utils.js +224 -8
- package/lib/project-maturity.js +1 -1
- package/lib/setup-enhancements.js +33 -0
- package/lib/template-loader.js +2 -0
- package/lib/ui-helpers.js +2 -1
- package/lib/validation/base-validator.js +5 -1
- package/lib/validation/cache-manager.js +1 -0
- package/lib/validation/config-security.js +5 -4
- package/lib/validation/validation-factory.js +1 -1
- package/lib/yaml-utils.js +15 -10
- package/package.json +18 -13
- package/scripts/check-docs.sh +63 -0
- package/scripts/smart-test-strategy.sh +98 -0
- package/scripts/test-e2e-package.sh +283 -0
- package/scripts/validate-command-patterns.js +112 -0
- package/setup.js +161 -44
- package/templates/QUALITY_TROUBLESHOOTING.md +403 -0
- package/templates/ci/circleci-config.yml +35 -0
- package/templates/ci/gitlab-ci.yml +47 -0
- package/templates/integration-tests/api-service.test.js +244 -0
- package/templates/integration-tests/frontend-app.test.js +267 -0
- package/templates/scripts/smart-test-strategy.sh +109 -0
- package/templates/test-stubs/e2e.smoke.test.js +12 -0
- package/templates/test-stubs/unit.test.js +7 -0
- package/legal/README.md +0 -106
- package/legal/copyright.md +0 -76
- package/legal/disclaimer.md +0 -146
- package/legal/privacy-policy.html +0 -324
- package/legal/privacy-policy.md +0 -196
- package/legal/terms-of-service.md +0 -224
- package/marketing/beta-user-email-campaign.md +0 -372
- package/marketing/landing-page.html +0 -721
package/setup.js
CHANGED
|
@@ -113,6 +113,27 @@ const STYLELINT_EXTENSION_GLOB = `*.{${STYLELINT_EXTENSIONS.join(',')}}`
|
|
|
113
113
|
const STYLELINT_SCAN_EXCLUDES = new Set(EXCLUDE_DIRECTORIES.STYLELINT)
|
|
114
114
|
const MAX_STYLELINT_SCAN_DEPTH = SCAN_LIMITS.STYLELINT_MAX_DEPTH
|
|
115
115
|
|
|
116
|
+
function injectCollaborationSteps(workflowContent, options = {}) {
|
|
117
|
+
const { enableSlackAlerts = false, enablePrComments = false } = options
|
|
118
|
+
let updated = workflowContent
|
|
119
|
+
|
|
120
|
+
if (workflowContent.includes('# ALERTS_PLACEHOLDER')) {
|
|
121
|
+
const alertsJob = enableSlackAlerts
|
|
122
|
+
? ` alerts:\n runs-on: ubuntu-latest\n needs: [summary]\n if: failure() || cancelled()\n steps:\n - name: Notify Slack on failures\n env:\n SLACK_WEBHOOK_URL: \${{ secrets.SLACK_WEBHOOK_URL }}\n run: |\n if [ -z "$SLACK_WEBHOOK_URL" ]; then\n echo "::warning::SLACK_WEBHOOK_URL not set; skipping Slack notification"\n exit 0\n fi\n payload='{"text":"❌ Quality checks failed for $GITHUB_REPOSITORY ($GITHUB_REF)"}'\n curl -X POST -H 'Content-type: application/json' --data "$payload" "$SLACK_WEBHOOK_URL"\n`
|
|
123
|
+
: ' # Slack alerts not enabled (use --alerts-slack to add)'
|
|
124
|
+
updated = updated.replace('# ALERTS_PLACEHOLDER', alertsJob)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (workflowContent.includes('# PR_COMMENTS_PLACEHOLDER')) {
|
|
128
|
+
const prSteps = enablePrComments
|
|
129
|
+
? ` - name: Post PR summary comment\n if: github.event_name == 'pull_request'\n uses: actions/github-script@v7\n with:\n script: |\n const summaryPath = process.env.GITHUB_STEP_SUMMARY\n const fs = require('fs')\n const body = summaryPath && fs.existsSync(summaryPath)\n ? fs.readFileSync(summaryPath, 'utf8')\n : 'Quality checks completed.'\n const { context, github } = require('@actions/github')\n await github.rest.issues.createComment({\n owner: context.repo.owner,\n repo: context.repo.repo,\n issue_number: context.payload.pull_request.number,\n body,\n })\n`
|
|
130
|
+
: ' # PR comment step not enabled (use --pr-comments to add)'
|
|
131
|
+
updated = updated.replace('# PR_COMMENTS_PLACEHOLDER', prSteps)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return updated
|
|
135
|
+
}
|
|
136
|
+
|
|
116
137
|
/**
|
|
117
138
|
* Safely reads directory contents without throwing on permission errors
|
|
118
139
|
*
|
|
@@ -312,6 +333,13 @@ function parseArguments(rawArgs) {
|
|
|
312
333
|
const isValidateConfigMode = sanitizedArgs.includes('--validate-config')
|
|
313
334
|
const isActivateLicenseMode = sanitizedArgs.includes('--activate-license')
|
|
314
335
|
const isDryRun = sanitizedArgs.includes('--dry-run')
|
|
336
|
+
const ciProviderIndex = sanitizedArgs.findIndex(arg => arg === '--ci')
|
|
337
|
+
const ciProvider =
|
|
338
|
+
ciProviderIndex !== -1 && sanitizedArgs[ciProviderIndex + 1]
|
|
339
|
+
? sanitizedArgs[ciProviderIndex + 1].toLowerCase()
|
|
340
|
+
: 'github'
|
|
341
|
+
const enableSlackAlerts = sanitizedArgs.includes('--alerts-slack')
|
|
342
|
+
const enablePrComments = sanitizedArgs.includes('--pr-comments')
|
|
315
343
|
|
|
316
344
|
// Custom template directory - use raw args to preserve valid path characters (&, <, >, etc.)
|
|
317
345
|
// Normalize path to prevent traversal attacks and make absolute
|
|
@@ -344,6 +372,9 @@ function parseArguments(rawArgs) {
|
|
|
344
372
|
isValidateConfigMode,
|
|
345
373
|
isActivateLicenseMode,
|
|
346
374
|
isDryRun,
|
|
375
|
+
ciProvider,
|
|
376
|
+
enableSlackAlerts,
|
|
377
|
+
enablePrComments,
|
|
347
378
|
customTemplatePath,
|
|
348
379
|
disableNpmAudit,
|
|
349
380
|
disableGitleaks,
|
|
@@ -378,6 +409,9 @@ function parseArguments(rawArgs) {
|
|
|
378
409
|
isValidateConfigMode,
|
|
379
410
|
isActivateLicenseMode,
|
|
380
411
|
isDryRun,
|
|
412
|
+
ciProvider,
|
|
413
|
+
enableSlackAlerts,
|
|
414
|
+
enablePrComments,
|
|
381
415
|
customTemplatePath,
|
|
382
416
|
disableNpmAudit,
|
|
383
417
|
disableGitleaks,
|
|
@@ -444,7 +478,12 @@ function parseArguments(rawArgs) {
|
|
|
444
478
|
isTelemetryStatusMode,
|
|
445
479
|
isErrorReportingStatusMode,
|
|
446
480
|
isCheckMaturityMode,
|
|
481
|
+
isValidateConfigMode,
|
|
482
|
+
isActivateLicenseMode,
|
|
447
483
|
isDryRun,
|
|
484
|
+
ciProvider,
|
|
485
|
+
enableSlackAlerts,
|
|
486
|
+
enablePrComments,
|
|
448
487
|
customTemplatePath,
|
|
449
488
|
disableNpmAudit,
|
|
450
489
|
disableGitleaks,
|
|
@@ -481,6 +520,7 @@ SETUP OPTIONS:
|
|
|
481
520
|
--update Update existing configuration
|
|
482
521
|
--deps Add basic dependency monitoring (Free Tier)
|
|
483
522
|
--dependency-monitoring Same as --deps
|
|
523
|
+
--ci <provider> Select CI provider: github (default) | gitlab | circleci
|
|
484
524
|
--template <path> Use custom templates from specified directory
|
|
485
525
|
--dry-run Preview changes without modifying files
|
|
486
526
|
|
|
@@ -506,6 +546,10 @@ GRANULAR TOOL CONTROL:
|
|
|
506
546
|
--no-markdownlint Disable markdownlint markdown formatting checks
|
|
507
547
|
--no-eslint-security Disable ESLint security rule checking
|
|
508
548
|
|
|
549
|
+
ALERTING & COLLABORATION (GitHub CI):
|
|
550
|
+
--alerts-slack Add Slack webhook notification step (expects secret SLACK_WEBHOOK_URL)
|
|
551
|
+
--pr-comments Add PR summary comment step (uses GitHub token)
|
|
552
|
+
|
|
509
553
|
EXAMPLES:
|
|
510
554
|
npx create-qa-architect@latest
|
|
511
555
|
→ Set up quality automation with all tools
|
|
@@ -697,7 +741,7 @@ HELP:
|
|
|
697
741
|
if (!capCheck.allowed) {
|
|
698
742
|
console.error(`❌ ${capCheck.reason}`)
|
|
699
743
|
console.error(
|
|
700
|
-
' Upgrade to Pro, Team, or Enterprise for unlimited runs: https://vibebuildlab.com/
|
|
744
|
+
' Upgrade to Pro, Team, or Enterprise for unlimited runs: https://vibebuildlab.com/tools/qa-architect'
|
|
701
745
|
)
|
|
702
746
|
process.exit(1)
|
|
703
747
|
}
|
|
@@ -822,9 +866,31 @@ HELP:
|
|
|
822
866
|
showUpgradeMessage('Framework-Aware Dependency Grouping')
|
|
823
867
|
}
|
|
824
868
|
|
|
869
|
+
// Auto-enable Dependabot on GitHub if token available
|
|
870
|
+
console.log('\n🔧 Attempting to enable Dependabot on GitHub...')
|
|
871
|
+
try {
|
|
872
|
+
const { setupDependabot } = require('./lib/github-api')
|
|
873
|
+
const result = await setupDependabot(projectPath, { verbose: true })
|
|
874
|
+
|
|
875
|
+
if (result.success) {
|
|
876
|
+
console.log('✅ Dependabot alerts and security updates enabled!')
|
|
877
|
+
} else if (result.errors.length > 0) {
|
|
878
|
+
console.log('⚠️ Could not auto-enable Dependabot:')
|
|
879
|
+
result.errors.forEach(err => console.log(` • ${err}`))
|
|
880
|
+
console.log('\n💡 Manual steps needed:')
|
|
881
|
+
console.log(' • Go to GitHub repo → Settings → Code security')
|
|
882
|
+
console.log(
|
|
883
|
+
' • Enable "Dependabot alerts" and "Dependabot security updates"'
|
|
884
|
+
)
|
|
885
|
+
}
|
|
886
|
+
} catch (error) {
|
|
887
|
+
console.log('⚠️ Could not auto-enable Dependabot:', error.message)
|
|
888
|
+
console.log('\n💡 Manual steps:')
|
|
889
|
+
console.log(' • Enable Dependabot in GitHub repo settings')
|
|
890
|
+
}
|
|
891
|
+
|
|
825
892
|
console.log('\n💡 Next steps:')
|
|
826
893
|
console.log(' • Review and commit .github/dependabot.yml')
|
|
827
|
-
console.log(' • Enable Dependabot alerts in GitHub repository settings')
|
|
828
894
|
console.log(
|
|
829
895
|
' • Dependabot will start monitoring weekly for dependency updates'
|
|
830
896
|
)
|
|
@@ -856,11 +922,11 @@ HELP:
|
|
|
856
922
|
console.log('\n❌ License activation failed.')
|
|
857
923
|
console.log('• Check your license key format (QAA-XXXX-XXXX-XXXX-XXXX)')
|
|
858
924
|
console.log('• Verify your email address')
|
|
859
|
-
console.log('• Contact support:
|
|
925
|
+
console.log('• Contact support: support@vibebuildlab.com')
|
|
860
926
|
}
|
|
861
927
|
} catch (error) {
|
|
862
928
|
console.error('\n❌ License activation error:', error.message)
|
|
863
|
-
console.log('Contact support for assistance:
|
|
929
|
+
console.log('Contact support for assistance: support@vibebuildlab.com')
|
|
864
930
|
}
|
|
865
931
|
|
|
866
932
|
process.exit(0)
|
|
@@ -961,7 +1027,7 @@ HELP:
|
|
|
961
1027
|
if (!repoCheck.allowed) {
|
|
962
1028
|
console.error(`\n❌ ${repoCheck.reason}`)
|
|
963
1029
|
console.error(
|
|
964
|
-
' Upgrade to Pro for unlimited repos: https://vibebuildlab.com/
|
|
1030
|
+
' Upgrade to Pro for unlimited repos: https://vibebuildlab.com/tools/qa-architect'
|
|
965
1031
|
)
|
|
966
1032
|
process.exit(1)
|
|
967
1033
|
}
|
|
@@ -969,7 +1035,7 @@ HELP:
|
|
|
969
1035
|
// Register this repo
|
|
970
1036
|
incrementUsage('repo', 1, repoId)
|
|
971
1037
|
console.log(
|
|
972
|
-
`✅ Registered repo (FREE tier: ${
|
|
1038
|
+
`✅ Registered repo (FREE tier: ${(repoCheck.usage?.repoCount || 0) + 1}/1 repos used)`
|
|
973
1039
|
)
|
|
974
1040
|
}
|
|
975
1041
|
}
|
|
@@ -1054,6 +1120,8 @@ HELP:
|
|
|
1054
1120
|
description: '',
|
|
1055
1121
|
main: 'index.js',
|
|
1056
1122
|
scripts: {},
|
|
1123
|
+
devDependencies: {},
|
|
1124
|
+
'lint-staged': {},
|
|
1057
1125
|
}
|
|
1058
1126
|
}
|
|
1059
1127
|
|
|
@@ -1215,8 +1283,8 @@ HELP:
|
|
|
1215
1283
|
}
|
|
1216
1284
|
|
|
1217
1285
|
packageJson['lint-staged'] = mergeLintStaged(
|
|
1218
|
-
packageJson['lint-staged'] || {},
|
|
1219
1286
|
finalLintStaged,
|
|
1287
|
+
packageJson['lint-staged'] || {},
|
|
1220
1288
|
{ stylelintTargets },
|
|
1221
1289
|
patternIncludesStylelintExtension
|
|
1222
1290
|
)
|
|
@@ -1351,28 +1419,72 @@ HELP:
|
|
|
1351
1419
|
process.exit(1)
|
|
1352
1420
|
}
|
|
1353
1421
|
|
|
1354
|
-
// Create
|
|
1422
|
+
// Create CI configuration based on provider
|
|
1355
1423
|
const configSpinner = showProgress('Copying configuration files...')
|
|
1356
|
-
const
|
|
1357
|
-
if (!fs.existsSync(workflowDir)) {
|
|
1358
|
-
fs.mkdirSync(workflowDir, { recursive: true })
|
|
1359
|
-
console.log('📁 Created .github/workflows directory')
|
|
1360
|
-
}
|
|
1424
|
+
const githubWorkflowDir = path.join(process.cwd(), '.github', 'workflows')
|
|
1361
1425
|
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1426
|
+
if (ciProvider === 'gitlab') {
|
|
1427
|
+
const gitlabConfigPath = path.join(process.cwd(), '.gitlab-ci.yml')
|
|
1428
|
+
if (!fs.existsSync(gitlabConfigPath)) {
|
|
1429
|
+
const templateGitlab =
|
|
1430
|
+
templateLoader.getTemplate(
|
|
1431
|
+
templates,
|
|
1432
|
+
path.join('ci', 'gitlab-ci.yml')
|
|
1433
|
+
) ||
|
|
1434
|
+
fs.readFileSync(
|
|
1435
|
+
path.join(__dirname, 'templates/ci/gitlab-ci.yml'),
|
|
1436
|
+
'utf8'
|
|
1437
|
+
)
|
|
1438
|
+
fs.writeFileSync(gitlabConfigPath, templateGitlab)
|
|
1439
|
+
console.log('✅ Added GitLab CI workflow')
|
|
1440
|
+
}
|
|
1441
|
+
} else if (ciProvider === 'circleci') {
|
|
1442
|
+
const circleDir = path.join(process.cwd(), '.circleci')
|
|
1443
|
+
if (!fs.existsSync(circleDir)) {
|
|
1444
|
+
fs.mkdirSync(circleDir, { recursive: true })
|
|
1445
|
+
console.log('📁 Created .circleci directory')
|
|
1446
|
+
}
|
|
1447
|
+
const circleConfigPath = path.join(circleDir, 'config.yml')
|
|
1448
|
+
if (!fs.existsSync(circleConfigPath)) {
|
|
1449
|
+
const templateCircle =
|
|
1450
|
+
templateLoader.getTemplate(
|
|
1451
|
+
templates,
|
|
1452
|
+
path.join('ci', 'circleci-config.yml')
|
|
1453
|
+
) ||
|
|
1454
|
+
fs.readFileSync(
|
|
1455
|
+
path.join(__dirname, 'templates/ci/circleci-config.yml'),
|
|
1456
|
+
'utf8'
|
|
1457
|
+
)
|
|
1458
|
+
fs.writeFileSync(circleConfigPath, templateCircle)
|
|
1459
|
+
console.log('✅ Added CircleCI workflow')
|
|
1460
|
+
}
|
|
1461
|
+
} else {
|
|
1462
|
+
// Default: GitHub Actions
|
|
1463
|
+
if (!fs.existsSync(githubWorkflowDir)) {
|
|
1464
|
+
fs.mkdirSync(githubWorkflowDir, { recursive: true })
|
|
1465
|
+
console.log('📁 Created .github/workflows directory')
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
const workflowFile = path.join(githubWorkflowDir, 'quality.yml')
|
|
1469
|
+
if (!fs.existsSync(workflowFile)) {
|
|
1470
|
+
let templateWorkflow =
|
|
1471
|
+
templateLoader.getTemplate(
|
|
1472
|
+
templates,
|
|
1473
|
+
path.join('.github', 'workflows', 'quality.yml')
|
|
1474
|
+
) ||
|
|
1475
|
+
fs.readFileSync(
|
|
1476
|
+
path.join(__dirname, '.github/workflows/quality.yml'),
|
|
1477
|
+
'utf8'
|
|
1478
|
+
)
|
|
1479
|
+
|
|
1480
|
+
templateWorkflow = injectCollaborationSteps(templateWorkflow, {
|
|
1481
|
+
enableSlackAlerts,
|
|
1482
|
+
enablePrComments,
|
|
1483
|
+
})
|
|
1484
|
+
|
|
1485
|
+
fs.writeFileSync(workflowFile, templateWorkflow)
|
|
1486
|
+
console.log('✅ Added GitHub Actions workflow')
|
|
1487
|
+
}
|
|
1376
1488
|
}
|
|
1377
1489
|
|
|
1378
1490
|
// Copy Prettier config if it doesn't exist
|
|
@@ -1582,7 +1694,7 @@ let tier = 'FREE'
|
|
|
1582
1694
|
try {
|
|
1583
1695
|
const data = JSON.parse(fs.readFileSync(licenseFile, 'utf8'))
|
|
1584
1696
|
tier = (data && data.tier) || 'FREE'
|
|
1585
|
-
} catch
|
|
1697
|
+
} catch {
|
|
1586
1698
|
tier = 'FREE'
|
|
1587
1699
|
}
|
|
1588
1700
|
|
|
@@ -1595,14 +1707,14 @@ try {
|
|
|
1595
1707
|
if (data.month === currentMonth) {
|
|
1596
1708
|
usage = { ...usage, ...data }
|
|
1597
1709
|
}
|
|
1598
|
-
} catch
|
|
1710
|
+
} catch {
|
|
1599
1711
|
// First run or corrupt file – start fresh
|
|
1600
1712
|
}
|
|
1601
1713
|
|
|
1602
1714
|
const CAP = 50
|
|
1603
1715
|
if (usage.prePushRuns >= CAP) {
|
|
1604
1716
|
console.error('❌ Free tier limit reached: ' + usage.prePushRuns + '/' + CAP + ' pre-push runs this month')
|
|
1605
|
-
console.error(' Upgrade to Pro, Team, or Enterprise: https://vibebuildlab.com/
|
|
1717
|
+
console.error(' Upgrade to Pro, Team, or Enterprise: https://vibebuildlab.com/tools/qa-architect')
|
|
1606
1718
|
process.exit(1)
|
|
1607
1719
|
}
|
|
1608
1720
|
|
|
@@ -1762,20 +1874,25 @@ echo "✅ Pre-push validation passed!"
|
|
|
1762
1874
|
console.log('✅ Added requirements-dev.txt')
|
|
1763
1875
|
}
|
|
1764
1876
|
|
|
1765
|
-
// Copy Python workflow
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1877
|
+
// Copy Python workflow (GitHub Actions only)
|
|
1878
|
+
if (ciProvider === 'github') {
|
|
1879
|
+
const pythonWorkflowFile = path.join(
|
|
1880
|
+
githubWorkflowDir,
|
|
1881
|
+
'quality-python.yml'
|
|
1882
|
+
)
|
|
1883
|
+
if (!fs.existsSync(pythonWorkflowFile)) {
|
|
1884
|
+
const templatePythonWorkflow =
|
|
1885
|
+
templateLoader.getTemplate(
|
|
1886
|
+
templates,
|
|
1887
|
+
path.join('config', 'quality-python.yml')
|
|
1888
|
+
) ||
|
|
1889
|
+
fs.readFileSync(
|
|
1890
|
+
path.join(__dirname, 'config/quality-python.yml'),
|
|
1891
|
+
'utf8'
|
|
1892
|
+
)
|
|
1893
|
+
fs.writeFileSync(pythonWorkflowFile, templatePythonWorkflow)
|
|
1894
|
+
console.log('✅ Added Python GitHub Actions workflow')
|
|
1895
|
+
}
|
|
1779
1896
|
}
|
|
1780
1897
|
|
|
1781
1898
|
// Create tests directory if it doesn't exist
|