create-qa-architect 5.13.2 → 5.13.3
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/LICENSE +3 -3
- package/README.md +5 -3
- package/config/defaults.js +2 -3
- package/lib/license-validator.js +1 -1
- package/lib/licensing.js +4 -6
- package/lib/workflow-config.js +70 -11
- package/package.json +3 -3
- package/setup.js +66 -48
package/LICENSE
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
VIBE BUILD LAB COMMERCIAL LICENSE
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2025
|
|
3
|
+
Copyright (c) 2025 BuildProven. All rights reserved.
|
|
4
4
|
|
|
5
5
|
COMMERCIAL SOFTWARE - FREEMIUM MODEL
|
|
6
6
|
|
|
7
7
|
This software and associated documentation files (the "Software") are
|
|
8
|
-
proprietary commercial products of
|
|
8
|
+
proprietary commercial products of BuildProven.
|
|
9
9
|
|
|
10
10
|
TERMS OF USE:
|
|
11
11
|
|
|
@@ -62,5 +62,5 @@ For licensing inquiries: support@buildproven.ai
|
|
|
62
62
|
|
|
63
63
|
---
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
BuildProven
|
|
66
66
|
https://buildproven.ai
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Quality automation CLI for JavaScript/TypeScript, Python, and shell script proje
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
> **Maintainer & Ownership**
|
|
10
|
-
> This project is maintained by **
|
|
10
|
+
> This project is maintained by **BuildProven**, a studio focused on AI-assisted product development, micro-SaaS, and "vibe coding" workflows for solo founders and small teams.
|
|
11
11
|
> Learn more at **https://buildproven.ai**.
|
|
12
12
|
|
|
13
13
|
---
|
|
@@ -158,7 +158,7 @@ npx create-qa-architect@latest --workflow-minimal
|
|
|
158
158
|
|
|
159
159
|
**Best for:** Small teams, client projects, production apps
|
|
160
160
|
|
|
161
|
-
-
|
|
161
|
+
- Single Node 22 testing **only on main branch**
|
|
162
162
|
- Security scans run monthly
|
|
163
163
|
- Path filters enabled
|
|
164
164
|
- **Runtime:** ~15-20 min/commit
|
|
@@ -276,6 +276,8 @@ npm install
|
|
|
276
276
|
npm run lint
|
|
277
277
|
```
|
|
278
278
|
|
|
279
|
+
`--update` refreshes the existing `quality.yml` from the latest template while preserving the detected workflow tier and existing matrix setting unless you explicitly override the tier with `--workflow-minimal`, `--workflow-standard`, or `--workflow-comprehensive`.
|
|
280
|
+
|
|
279
281
|
### Dependency Monitoring (Free)
|
|
280
282
|
|
|
281
283
|
```bash
|
|
@@ -453,4 +455,4 @@ Commercial freemium license — the base CLI is free to use; Pro features requir
|
|
|
453
455
|
|
|
454
456
|
---
|
|
455
457
|
|
|
456
|
-
> **
|
|
458
|
+
> **BuildProven** · [buildproven.ai](https://buildproven.ai)
|
package/config/defaults.js
CHANGED
|
@@ -19,7 +19,7 @@ const baseScripts = {
|
|
|
19
19
|
'test:coverage': 'vitest run --coverage',
|
|
20
20
|
'test:changed': 'vitest run --changed HEAD~1 --passWithNoTests',
|
|
21
21
|
'security:audit':
|
|
22
|
-
'[ -f pnpm-lock.yaml ]
|
|
22
|
+
'if [ -f pnpm-lock.yaml ]; then pnpm audit --audit-level high; elif [ -f yarn.lock ]; then yarn audit; else npm audit --audit-level high; fi',
|
|
23
23
|
'security:secrets':
|
|
24
24
|
"node -e \"const fs=require('fs');const content=fs.readFileSync('package.json','utf8');if(/[\\\"\\'][a-zA-Z0-9+/]{20,}[\\\"\\']/.test(content)){console.error('❌ Potential hardcoded secrets in package.json');process.exit(1)}else{console.log('✅ No secrets detected in package.json')}\"",
|
|
25
25
|
'security:config': 'npx create-qa-architect@latest --security-config',
|
|
@@ -28,8 +28,7 @@ const baseScripts = {
|
|
|
28
28
|
'validate:docs': 'npx create-qa-architect@latest --validate-docs',
|
|
29
29
|
'validate:comprehensive': 'npx create-qa-architect@latest --comprehensive',
|
|
30
30
|
'validate:all': 'npm run validate:comprehensive && npm run security:audit',
|
|
31
|
-
'validate:pre-push':
|
|
32
|
-
'npm run test:patterns --if-present && npm run test:commands --if-present && npm run test:changed --if-present || npm test --if-present',
|
|
31
|
+
'validate:pre-push': `npm run test:patterns --if-present && npm run test:commands --if-present && if node -e "const pkg=require('./package.json');process.exit(pkg.scripts&&pkg.scripts['test:changed']?0:1)" 2>/dev/null; then npm run test:changed; else npm test --if-present; fi`,
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
const normalizeStylelintTargets = stylelintTargets => {
|
package/lib/license-validator.js
CHANGED
package/lib/licensing.js
CHANGED
|
@@ -16,15 +16,16 @@ const {
|
|
|
16
16
|
verifyPayload,
|
|
17
17
|
loadKeyFromEnv,
|
|
18
18
|
} = require('./license-signing')
|
|
19
|
+
const { validateLicenseDir } = require('./license-validator')
|
|
19
20
|
|
|
20
21
|
// License storage locations
|
|
21
22
|
// Support environment variable override for testing (like telemetry/error-reporter)
|
|
22
23
|
// Use getter functions to allow env override before module load
|
|
23
24
|
function getLicenseDir() {
|
|
24
|
-
|
|
25
|
+
const requestedDir =
|
|
25
26
|
process.env.QAA_LICENSE_DIR ||
|
|
26
27
|
path.join(os.homedir(), '.create-qa-architect')
|
|
27
|
-
)
|
|
28
|
+
return validateLicenseDir(requestedDir)
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
function getLicenseFile() {
|
|
@@ -586,10 +587,7 @@ async function addLegitimateKey(
|
|
|
586
587
|
}
|
|
587
588
|
|
|
588
589
|
const normalizedKey = normalizeLicenseKey(licenseKey)
|
|
589
|
-
|
|
590
|
-
const licenseDir =
|
|
591
|
-
process.env.QAA_LICENSE_DIR ||
|
|
592
|
-
path.join(os.homedir(), '.create-qa-architect')
|
|
590
|
+
const licenseDir = getLicenseDir()
|
|
593
591
|
const legitimateDBFile = path.join(licenseDir, 'legitimate-licenses.json')
|
|
594
592
|
const privateKey = loadKeyFromEnv(
|
|
595
593
|
process.env.LICENSE_REGISTRY_PRIVATE_KEY,
|
package/lib/workflow-config.js
CHANGED
|
@@ -65,6 +65,37 @@ function detectExistingWorkflowMode(projectPath) {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Detect whether matrix testing is enabled in an existing workflow.
|
|
70
|
+
* @param {string} projectPath - Path to project
|
|
71
|
+
* @returns {boolean} True when matrix testing is enabled
|
|
72
|
+
*/
|
|
73
|
+
function detectExistingMatrix(projectPath) {
|
|
74
|
+
const workflowPath = path.join(
|
|
75
|
+
projectPath,
|
|
76
|
+
'.github',
|
|
77
|
+
'workflows',
|
|
78
|
+
'quality.yml'
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
if (!fs.existsSync(workflowPath)) {
|
|
82
|
+
return false
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const content = fs.readFileSync(workflowPath, 'utf8')
|
|
87
|
+
return (
|
|
88
|
+
content.includes('# MATRIX_ENABLED: true') ||
|
|
89
|
+
content.includes('node-version: [20, 22]')
|
|
90
|
+
)
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.warn(
|
|
93
|
+
`⚠️ Could not detect existing matrix configuration: ${error.message}`
|
|
94
|
+
)
|
|
95
|
+
return false
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
68
99
|
/**
|
|
69
100
|
* Strip a named section from workflow content.
|
|
70
101
|
* Removes everything between # {{NAME_BEGIN}} and # {{NAME_END}} markers (inclusive).
|
|
@@ -135,6 +166,39 @@ function removeTriggerPathsIgnore(content, triggerName) {
|
|
|
135
166
|
return output.join('\n')
|
|
136
167
|
}
|
|
137
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Restrict the tests job to main branch pushes in standard mode.
|
|
171
|
+
* @param {string} content - Workflow content
|
|
172
|
+
* @returns {string} Updated content
|
|
173
|
+
*/
|
|
174
|
+
function addStandardTestsBranchGate(content) {
|
|
175
|
+
const lines = content.split('\n')
|
|
176
|
+
const output = []
|
|
177
|
+
let inTestsJob = false
|
|
178
|
+
let branchGateInserted = false
|
|
179
|
+
|
|
180
|
+
for (const line of lines) {
|
|
181
|
+
if (line === ' tests:') {
|
|
182
|
+
inTestsJob = true
|
|
183
|
+
} else if (
|
|
184
|
+
inTestsJob &&
|
|
185
|
+
line.startsWith(' ') &&
|
|
186
|
+
!line.startsWith(' ')
|
|
187
|
+
) {
|
|
188
|
+
inTestsJob = false
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
output.push(line)
|
|
192
|
+
|
|
193
|
+
if (inTestsJob && line === ' if: |' && !branchGateInserted) {
|
|
194
|
+
output.push(" github.ref == 'refs/heads/main' &&")
|
|
195
|
+
branchGateInserted = true
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return output.join('\n')
|
|
200
|
+
}
|
|
201
|
+
|
|
138
202
|
/**
|
|
139
203
|
* Inject workflow mode-specific configuration into quality.yml
|
|
140
204
|
* Uses section markers (# {{SECTION_BEGIN/END}}) for reliable content removal
|
|
@@ -162,19 +226,13 @@ function injectWorkflowMode(workflowContent, mode) {
|
|
|
162
226
|
|
|
163
227
|
// Mode-specific transformations
|
|
164
228
|
if (mode === 'standard') {
|
|
165
|
-
// Standard:
|
|
229
|
+
// Standard: run tests on main only to keep CI costs bounded.
|
|
166
230
|
if (
|
|
167
|
-
updated.includes('tests:') &&
|
|
168
|
-
updated.includes('
|
|
231
|
+
updated.includes(' tests:') &&
|
|
232
|
+
updated.includes(' if: |') &&
|
|
233
|
+
!updated.includes("github.ref == 'refs/heads/main'")
|
|
169
234
|
) {
|
|
170
|
-
updated = updated
|
|
171
|
-
/(tests:\s+runs-on:[^\n]+\s+needs:[^\n]+\s+if: \|\s*\n\s+)fromJSON\(needs\.detect-maturity\.outputs\.test-count\)/,
|
|
172
|
-
"$1github.ref == 'refs/heads/main' &&\n fromJSON(needs.detect-maturity.outputs.test-count)"
|
|
173
|
-
)
|
|
174
|
-
updated = updated.replace(
|
|
175
|
-
/(\s+tests:\s+runs-on:[^\n]+\s+needs:[^\n]+\s+)if: fromJSON\(needs\.detect-maturity\.outputs\.test-count\) > 0/,
|
|
176
|
-
"$1if: github.ref == 'refs/heads/main' && fromJSON(needs.detect-maturity.outputs.test-count) > 0"
|
|
177
|
-
)
|
|
235
|
+
updated = addStandardTestsBranchGate(updated)
|
|
178
236
|
}
|
|
179
237
|
} else if (mode === 'comprehensive') {
|
|
180
238
|
// Comprehensive: Remove paths-ignore blocks
|
|
@@ -284,6 +342,7 @@ function injectMatrix(workflowContent, enableMatrix) {
|
|
|
284
342
|
|
|
285
343
|
module.exports = {
|
|
286
344
|
detectExistingWorkflowMode,
|
|
345
|
+
detectExistingMatrix,
|
|
287
346
|
injectWorkflowMode,
|
|
288
347
|
injectMatrix,
|
|
289
348
|
stripSection,
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-qa-architect",
|
|
3
|
-
"version": "5.13.
|
|
3
|
+
"version": "5.13.3",
|
|
4
4
|
"description": "QA Architect - Bootstrap quality automation for JavaScript/TypeScript and Python projects with GitHub Actions, pre-commit hooks, linting, formatting, and smart test strategy",
|
|
5
5
|
"main": "setup.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"create-qa-architect": "
|
|
7
|
+
"create-qa-architect": "setup.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"prepare": "[ \"$CI\" = \"true\" ] && echo 'Skipping Husky in CI' || husky",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"dependency-monitoring",
|
|
81
81
|
"security-audit"
|
|
82
82
|
],
|
|
83
|
-
"author": "
|
|
83
|
+
"author": "BuildProven",
|
|
84
84
|
"license": "SEE LICENSE IN LICENSE",
|
|
85
85
|
"files": [
|
|
86
86
|
"setup.js",
|
package/setup.js
CHANGED
|
@@ -125,6 +125,7 @@ const { runInteractiveFlow } = require('./lib/interactive/questions')
|
|
|
125
125
|
const { TemplateLoader } = require('./lib/template-loader')
|
|
126
126
|
const {
|
|
127
127
|
detectExistingWorkflowMode,
|
|
128
|
+
detectExistingMatrix,
|
|
128
129
|
injectWorkflowMode,
|
|
129
130
|
injectMatrix,
|
|
130
131
|
} = require('./lib/workflow-config')
|
|
@@ -693,7 +694,7 @@ Usage: npx create-qa-architect@latest [options]
|
|
|
693
694
|
SETUP OPTIONS:
|
|
694
695
|
(no args) Run complete quality automation setup
|
|
695
696
|
--interactive Interactive mode with guided configuration prompts
|
|
696
|
-
--update Update existing configuration
|
|
697
|
+
--update Update existing configuration (refreshes quality.yml, preserves detected workflow tier)
|
|
697
698
|
--deps Add basic dependency monitoring (Free Tier)
|
|
698
699
|
--dependency-monitoring Same as --deps
|
|
699
700
|
--ci <provider> Select CI provider: github (default) | gitlab | circleci
|
|
@@ -703,7 +704,7 @@ SETUP OPTIONS:
|
|
|
703
704
|
WORKFLOW TIERS (GitHub Actions optimization):
|
|
704
705
|
--workflow-minimal Minimal CI (default) - Single Node version, monthly security
|
|
705
706
|
Budget-first mode: detection-only CI (target <1000 min/month)
|
|
706
|
-
--workflow-standard Standard CI -
|
|
707
|
+
--workflow-standard Standard CI - Main-branch tests, monthly security
|
|
707
708
|
~15-20 min/commit, ~$5-20/mo for typical projects
|
|
708
709
|
--workflow-comprehensive Comprehensive CI - Matrix on every push, security inline
|
|
709
710
|
~50-100 min/commit, ~$100-350/mo for typical projects
|
|
@@ -1651,6 +1652,16 @@ HELP:
|
|
|
1651
1652
|
}
|
|
1652
1653
|
}
|
|
1653
1654
|
|
|
1655
|
+
const hasExplicitWorkflowMode =
|
|
1656
|
+
isWorkflowMinimal || isWorkflowStandard || isWorkflowComprehensive
|
|
1657
|
+
const existingMatrixEnabled =
|
|
1658
|
+
fs.existsSync(workflowFile) &&
|
|
1659
|
+
!isMatrixEnabled &&
|
|
1660
|
+
!hasExplicitWorkflowMode
|
|
1661
|
+
? detectExistingMatrix(process.cwd())
|
|
1662
|
+
: false
|
|
1663
|
+
const matrixEnabled = isMatrixEnabled || existingMatrixEnabled
|
|
1664
|
+
|
|
1654
1665
|
if (!fs.existsSync(workflowFile)) {
|
|
1655
1666
|
let templateWorkflow =
|
|
1656
1667
|
templateLoader.getTemplate(
|
|
@@ -1666,7 +1677,7 @@ HELP:
|
|
|
1666
1677
|
templateWorkflow = injectWorkflowMode(templateWorkflow, workflowMode)
|
|
1667
1678
|
|
|
1668
1679
|
// Inject matrix testing if enabled (for library authors)
|
|
1669
|
-
templateWorkflow = injectMatrix(templateWorkflow,
|
|
1680
|
+
templateWorkflow = injectMatrix(templateWorkflow, matrixEnabled)
|
|
1670
1681
|
|
|
1671
1682
|
// Inject collaboration steps
|
|
1672
1683
|
templateWorkflow = injectCollaborationSteps(templateWorkflow, {
|
|
@@ -1677,58 +1688,47 @@ HELP:
|
|
|
1677
1688
|
fs.writeFileSync(workflowFile, templateWorkflow)
|
|
1678
1689
|
console.log(`✅ Added GitHub Actions workflow (${workflowMode} mode)`)
|
|
1679
1690
|
} else if (isUpdateMode) {
|
|
1680
|
-
//
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
templates,
|
|
1690
|
-
path.join('.github', 'workflows', 'quality.yml')
|
|
1691
|
-
) ||
|
|
1692
|
-
fs.readFileSync(
|
|
1693
|
-
path.join(__dirname, '.github/workflows/quality.yml'),
|
|
1694
|
-
'utf8'
|
|
1695
|
-
)
|
|
1696
|
-
|
|
1697
|
-
// Inject workflow mode configuration
|
|
1698
|
-
templateWorkflow = injectWorkflowMode(
|
|
1699
|
-
templateWorkflow,
|
|
1700
|
-
workflowMode
|
|
1691
|
+
// Refresh existing workflow from the current template.
|
|
1692
|
+
let templateWorkflow =
|
|
1693
|
+
templateLoader.getTemplate(
|
|
1694
|
+
templates,
|
|
1695
|
+
path.join('.github', 'workflows', 'quality.yml')
|
|
1696
|
+
) ||
|
|
1697
|
+
fs.readFileSync(
|
|
1698
|
+
path.join(__dirname, '.github/workflows/quality.yml'),
|
|
1699
|
+
'utf8'
|
|
1701
1700
|
)
|
|
1702
1701
|
|
|
1703
|
-
|
|
1704
|
-
|
|
1702
|
+
// Inject workflow mode configuration
|
|
1703
|
+
templateWorkflow = injectWorkflowMode(templateWorkflow, workflowMode)
|
|
1704
|
+
|
|
1705
|
+
// Inject matrix testing if enabled (for library authors)
|
|
1706
|
+
templateWorkflow = injectMatrix(templateWorkflow, matrixEnabled)
|
|
1705
1707
|
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
)
|
|
1708
|
+
// Inject collaboration steps (preserve from existing if present)
|
|
1709
|
+
const existingWorkflow = fs.readFileSync(workflowFile, 'utf8')
|
|
1710
|
+
const hasSlackAlerts = existingWorkflow.includes('SLACK_WEBHOOK_URL')
|
|
1711
|
+
const hasPrComments = existingWorkflow.includes(
|
|
1712
|
+
'PR_COMMENT_PLACEHOLDER'
|
|
1713
|
+
)
|
|
1713
1714
|
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1715
|
+
templateWorkflow = injectCollaborationSteps(templateWorkflow, {
|
|
1716
|
+
enableSlackAlerts: hasSlackAlerts,
|
|
1717
|
+
enablePrComments: hasPrComments,
|
|
1718
|
+
})
|
|
1718
1719
|
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
console.log(
|
|
1722
|
-
'⚠️ Minimal mode disables tests and security scans in CI (detection-only).'
|
|
1723
|
-
)
|
|
1724
|
-
console.log(
|
|
1725
|
-
' Use --workflow-standard to re-enable full CI checks.'
|
|
1726
|
-
)
|
|
1727
|
-
}
|
|
1720
|
+
fs.writeFileSync(workflowFile, templateWorkflow)
|
|
1721
|
+
if (workflowMode === 'minimal') {
|
|
1728
1722
|
console.log(
|
|
1729
|
-
|
|
1723
|
+
'⚠️ Minimal mode disables tests and security scans in CI (detection-only).'
|
|
1724
|
+
)
|
|
1725
|
+
console.log(
|
|
1726
|
+
' Use --workflow-standard to re-enable full CI checks.'
|
|
1730
1727
|
)
|
|
1731
1728
|
}
|
|
1729
|
+
console.log(
|
|
1730
|
+
`♻️ Updated GitHub Actions workflow to ${workflowMode} mode`
|
|
1731
|
+
)
|
|
1732
1732
|
}
|
|
1733
1733
|
}
|
|
1734
1734
|
|
|
@@ -1918,8 +1918,26 @@ const fs = require('fs')
|
|
|
1918
1918
|
const path = require('path')
|
|
1919
1919
|
const os = require('os')
|
|
1920
1920
|
|
|
1921
|
-
|
|
1921
|
+
function validateLicenseDir(dirPath) {
|
|
1922
|
+
const resolved = path.resolve(dirPath)
|
|
1923
|
+
const home = os.homedir()
|
|
1924
|
+
const tmp = os.tmpdir()
|
|
1925
|
+
const isInHome = resolved.startsWith(home + path.sep) || resolved === home
|
|
1926
|
+
const isInTmp = resolved.startsWith(tmp + path.sep) || resolved === tmp
|
|
1927
|
+
|
|
1928
|
+
if (!isInHome && !isInTmp) {
|
|
1929
|
+
console.warn(
|
|
1930
|
+
'⚠️ QAA_LICENSE_DIR must be within home or temp directory, ignoring: ' + dirPath
|
|
1931
|
+
)
|
|
1932
|
+
return path.join(home, '.create-qa-architect')
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
return resolved
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
const requestedLicenseDir =
|
|
1922
1939
|
process.env.QAA_LICENSE_DIR || path.join(os.homedir(), '.create-qa-architect')
|
|
1940
|
+
const licenseDir = validateLicenseDir(requestedLicenseDir)
|
|
1923
1941
|
const licenseFile = path.join(licenseDir, 'license.json')
|
|
1924
1942
|
const usageFile = path.join(licenseDir, 'usage.json')
|
|
1925
1943
|
const now = new Date()
|