create-qa-architect 5.11.1 → 5.12.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/.github/workflows/auto-release.yml +39 -0
- package/.github/workflows/quality.yml +4 -1
- package/config/defaults.js +3 -0
- package/config/quality-config.schema.json +75 -0
- package/docs/CI-COST-ANALYSIS.md +79 -279
- package/lib/project-maturity.js +5 -0
- package/lib/quality-tools-generator.js +36 -17
- package/lib/security-enhancements.js +5 -34
- package/lib/workflow-config.js +11 -1
- package/package.json +3 -2
- package/scripts/smart-test-strategy.sh +10 -4
- package/setup.js +38 -2
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: Auto Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
release:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
with:
|
|
17
|
+
fetch-depth: 0
|
|
18
|
+
|
|
19
|
+
- name: Get previous tag
|
|
20
|
+
id: prev_tag
|
|
21
|
+
run: |
|
|
22
|
+
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
|
|
23
|
+
echo "tag=$PREV_TAG" >> $GITHUB_OUTPUT
|
|
24
|
+
|
|
25
|
+
- name: Generate release notes
|
|
26
|
+
run: |
|
|
27
|
+
TAG=${GITHUB_REF#refs/tags/}
|
|
28
|
+
PREV_TAG=${{ steps.prev_tag.outputs.tag }}
|
|
29
|
+
if [ -n "$PREV_TAG" ]; then
|
|
30
|
+
echo "## Changes since $PREV_TAG" > notes.md
|
|
31
|
+
git log ${PREV_TAG}..${TAG} --pretty=format:"- %s" >> notes.md
|
|
32
|
+
echo -e "\n\n**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREV_TAG}...${TAG}" >> notes.md
|
|
33
|
+
else
|
|
34
|
+
echo "Initial release" > notes.md
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
- uses: softprops/action-gh-release@v2
|
|
38
|
+
with:
|
|
39
|
+
body_path: notes.md
|
|
@@ -32,8 +32,11 @@ concurrency:
|
|
|
32
32
|
|
|
33
33
|
jobs:
|
|
34
34
|
# Step 1: Detect project maturity level and package manager
|
|
35
|
+
# Skip for Dependabot PRs - they auto-merge based on their own checks
|
|
36
|
+
# This reduces GitHub Actions minutes by ~50% on active repos
|
|
35
37
|
detect-maturity:
|
|
36
38
|
runs-on: ubuntu-latest
|
|
39
|
+
if: github.actor != 'dependabot[bot]' || github.event_name == 'schedule'
|
|
37
40
|
outputs:
|
|
38
41
|
maturity: ${{ steps.detect.outputs.maturity }}
|
|
39
42
|
source-count: ${{ steps.detect.outputs.source-count }}
|
|
@@ -338,7 +341,7 @@ jobs:
|
|
|
338
341
|
gitleaks-8.28.0-linux-x64-
|
|
339
342
|
|
|
340
343
|
- name: Run real gitleaks binary verification test
|
|
341
|
-
if: runner.os == 'Linux'
|
|
344
|
+
if: runner.os == 'Linux' && hashFiles('tests/gitleaks-real-binary-test.js') != ''
|
|
342
345
|
run: |
|
|
343
346
|
echo "🔐 Running real gitleaks binary verification test..."
|
|
344
347
|
QAA_DEVELOPER=true RUN_REAL_BINARY_TEST=1 node tests/gitleaks-real-binary-test.js
|
package/config/defaults.js
CHANGED
|
@@ -75,6 +75,9 @@ const baseDevDependencies = {
|
|
|
75
75
|
'@lhci/cli': '^0.14.0',
|
|
76
76
|
vitest: '^2.1.8',
|
|
77
77
|
'@vitest/coverage-v8': '^2.1.8',
|
|
78
|
+
commitlint: '^20.4.1',
|
|
79
|
+
'@commitlint/cli': '^20.4.1',
|
|
80
|
+
'@commitlint/config-conventional': '^20.4.1',
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
const typeScriptDevDependencies = {
|
|
@@ -91,6 +91,81 @@
|
|
|
91
91
|
},
|
|
92
92
|
"additionalProperties": false
|
|
93
93
|
}
|
|
94
|
+
},
|
|
95
|
+
"performance": {
|
|
96
|
+
"type": "object",
|
|
97
|
+
"description": "Performance budget configuration",
|
|
98
|
+
"properties": {
|
|
99
|
+
"lighthouse": {
|
|
100
|
+
"type": "object",
|
|
101
|
+
"description": "Lighthouse CI performance thresholds",
|
|
102
|
+
"properties": {
|
|
103
|
+
"performance": {
|
|
104
|
+
"type": "number",
|
|
105
|
+
"minimum": 0,
|
|
106
|
+
"maximum": 1,
|
|
107
|
+
"description": "Minimum performance score (0-1)"
|
|
108
|
+
},
|
|
109
|
+
"accessibility": {
|
|
110
|
+
"type": "number",
|
|
111
|
+
"minimum": 0,
|
|
112
|
+
"maximum": 1,
|
|
113
|
+
"description": "Minimum accessibility score (0-1)"
|
|
114
|
+
},
|
|
115
|
+
"bestPractices": {
|
|
116
|
+
"type": "number",
|
|
117
|
+
"minimum": 0,
|
|
118
|
+
"maximum": 1,
|
|
119
|
+
"description": "Minimum best practices score (0-1)"
|
|
120
|
+
},
|
|
121
|
+
"seo": {
|
|
122
|
+
"type": "number",
|
|
123
|
+
"minimum": 0,
|
|
124
|
+
"maximum": 1,
|
|
125
|
+
"description": "Minimum SEO score (0-1)"
|
|
126
|
+
},
|
|
127
|
+
"maxFCP": {
|
|
128
|
+
"type": "number",
|
|
129
|
+
"minimum": 0,
|
|
130
|
+
"description": "Max First Contentful Paint in ms"
|
|
131
|
+
},
|
|
132
|
+
"maxLCP": {
|
|
133
|
+
"type": "number",
|
|
134
|
+
"minimum": 0,
|
|
135
|
+
"description": "Max Largest Contentful Paint in ms"
|
|
136
|
+
},
|
|
137
|
+
"maxCLS": {
|
|
138
|
+
"type": "number",
|
|
139
|
+
"minimum": 0,
|
|
140
|
+
"description": "Max Cumulative Layout Shift"
|
|
141
|
+
},
|
|
142
|
+
"maxTBT": {
|
|
143
|
+
"type": "number",
|
|
144
|
+
"minimum": 0,
|
|
145
|
+
"description": "Max Total Blocking Time in ms"
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"additionalProperties": false
|
|
149
|
+
},
|
|
150
|
+
"bundleSize": {
|
|
151
|
+
"type": "object",
|
|
152
|
+
"description": "Bundle size limits",
|
|
153
|
+
"properties": {
|
|
154
|
+
"maxJs": {
|
|
155
|
+
"type": "string",
|
|
156
|
+
"pattern": "^[0-9]+ [kKmM][bB]$",
|
|
157
|
+
"description": "Max JS bundle size (e.g., '250 kB')"
|
|
158
|
+
},
|
|
159
|
+
"maxCss": {
|
|
160
|
+
"type": "string",
|
|
161
|
+
"pattern": "^[0-9]+ [kKmM][bB]$",
|
|
162
|
+
"description": "Max CSS bundle size (e.g., '50 kB')"
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
"additionalProperties": false
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
"additionalProperties": false
|
|
94
169
|
}
|
|
95
170
|
},
|
|
96
171
|
"additionalProperties": false
|
package/docs/CI-COST-ANALYSIS.md
CHANGED
|
@@ -1,323 +1,123 @@
|
|
|
1
|
-
# GitHub Actions Cost Analysis
|
|
1
|
+
# GitHub Actions Cost Analysis
|
|
2
2
|
|
|
3
|
-
**
|
|
4
|
-
**
|
|
3
|
+
**Updated**: 2026-02-03
|
|
4
|
+
**Budget**: 2,000 mins/month (GitHub Free tier)
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## Current Status: Within Budget (with fixes)
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
### Actual January 2026 Usage (vibebuildlab org)
|
|
11
11
|
|
|
12
|
-
|
|
|
13
|
-
|
|
|
14
|
-
|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
12
|
+
| Repo | Minutes | Runs | Avg/Run |
|
|
13
|
+
| ----------------------- | ---------- | ----- | ------- |
|
|
14
|
+
| qa-architect | 340 | 349 | 1.0 min |
|
|
15
|
+
| postrail | 1,769 | 295 | 6.0 min |
|
|
16
|
+
| vibebuildlab | 89 | 282 | 0.3 min |
|
|
17
|
+
| keyflash | 74 | 187 | 0.4 min |
|
|
18
|
+
| wfhroulette | 56 | 138 | 0.4 min |
|
|
19
|
+
| jobrecon | 56 | 44 | 1.3 min |
|
|
20
|
+
| ai-second-act | 4 | 33 | 0.1 min |
|
|
21
|
+
| vibebuildlab-newsletter | 1 | 22 | 0.0 min |
|
|
22
|
+
| **TOTAL** | **~2,400** | 1,350 | 1.8 min |
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
## Root Cause Analysis
|
|
22
|
-
|
|
23
|
-
### What qa-architect Is Doing (vibebuildlab example)
|
|
24
|
-
|
|
25
|
-
**Current quality.yml**: 161 minutes per commit, runs 221 times/month
|
|
26
|
-
|
|
27
|
-
```yaml
|
|
28
|
-
Jobs running on EVERY push:
|
|
29
|
-
1. detect-maturity (1 job) ~ 2 min
|
|
30
|
-
2. core-checks (2 jobs) ~ 10 min # Node 20 + 22 matrix
|
|
31
|
-
3. linting (1 job) ~ 8 min
|
|
32
|
-
4. security (1 job) ~ 25 min # Gitleaks + Semgrep + 3× npm audit
|
|
33
|
-
5. tests (2 jobs) ~ 30 min # Node 20 + 22 matrix
|
|
34
|
-
6. documentation (1 job) ~ 15 min # Only if production-ready
|
|
35
|
-
7. summary (1 job) ~ 1 min
|
|
36
|
-
|
|
37
|
-
TOTAL: ~90-100 minutes per push (when all jobs run)
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
**Problems identified**:
|
|
41
|
-
|
|
42
|
-
1. ❌ **No path filters** - Runs full CI on docs/README commits
|
|
43
|
-
2. ❌ **Duplicate matrix testing** - Both core-checks AND tests run Node 20/22
|
|
44
|
-
3. ❌ **Security overkill** - Gitleaks + Semgrep + npm audit (3 variants) on EVERY push
|
|
45
|
-
4. ❌ **No job concurrency limits** - Rapid commits queue up expensive builds
|
|
46
|
-
5. ❌ **Production checks on every commit** - Documentation validation should be release-only
|
|
47
|
-
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
## Industry Standards (Successful Projects)
|
|
51
|
-
|
|
52
|
-
### Vite (Major Framework, 1000+ contributors)
|
|
53
|
-
|
|
54
|
-
- **Runtime**: 50-60 min/commit
|
|
55
|
-
- **Path filters**: ✅ Skips tests on docs-only changes
|
|
56
|
-
- **Matrix**: Node 20, 22, 24 (3 versions)
|
|
57
|
-
- **Cross-platform**: Only on latest Node, not all versions
|
|
58
|
-
- **Security**: Runs on schedule, not every commit
|
|
24
|
+
### February Projection (Pre-Fix)
|
|
59
25
|
|
|
60
|
-
|
|
26
|
+
Based on Feb 1-3 data extrapolated:
|
|
61
27
|
|
|
62
|
-
- **
|
|
63
|
-
- **
|
|
64
|
-
- **
|
|
65
|
-
- **Security**: Separate workflow
|
|
28
|
+
- **Projected**: 3,425 mins/month
|
|
29
|
+
- **Budget**: 2,000 mins/month
|
|
30
|
+
- **Overage**: 71%
|
|
66
31
|
|
|
67
|
-
###
|
|
32
|
+
### Root Cause: Dependabot PR Spam
|
|
68
33
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
- [GitHub Actions alternatives for modern CI/CD](https://northflank.com/blog/github-actions-alternatives)
|
|
78
|
-
- [Ultimate free CI/CD for open-source projects](https://dev.to/itnext/the-ultimate-free-ci-cd-for-your-open-source-projects-3bkd)
|
|
34
|
+
| Repo | Dependabot % of Runs |
|
|
35
|
+
| ------------ | -------------------- |
|
|
36
|
+
| jobrecon | 91% |
|
|
37
|
+
| postrail | 63% |
|
|
38
|
+
| keyflash | 53% |
|
|
39
|
+
| qa-architect | 8% |
|
|
40
|
+
| vibebuildlab | 0% |
|
|
79
41
|
|
|
80
42
|
---
|
|
81
43
|
|
|
82
|
-
##
|
|
83
|
-
|
|
84
|
-
### Phase 1: Quick Wins (Reduce by 60-70%)
|
|
85
|
-
|
|
86
|
-
#### 1. Add Path Filters
|
|
87
|
-
|
|
88
|
-
```yaml
|
|
89
|
-
on:
|
|
90
|
-
push:
|
|
91
|
-
paths-ignore:
|
|
92
|
-
- '**.md'
|
|
93
|
-
- 'docs/**'
|
|
94
|
-
- 'LICENSE'
|
|
95
|
-
- '.gitignore'
|
|
96
|
-
- '.editorconfig'
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
**Savings**: ~20% of commits are docs-only
|
|
100
|
-
**vibebuildlab**: 7,117 min/month saved ($57/mo)
|
|
44
|
+
## Fixes Applied (v5.11.2)
|
|
101
45
|
|
|
102
|
-
|
|
46
|
+
### 1. Skip Quality Checks for Dependabot PRs
|
|
103
47
|
|
|
104
48
|
```yaml
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
matrix:
|
|
108
|
-
node-version: [20, 22] # Runs twice
|
|
109
|
-
|
|
110
|
-
tests:
|
|
111
|
-
matrix:
|
|
112
|
-
node-version: [20, 22] # Runs twice again!
|
|
113
|
-
|
|
114
|
-
# AFTER: 1 matrix job only
|
|
115
|
-
tests:
|
|
116
|
-
matrix:
|
|
117
|
-
node-version: [20, 22] # Runs once
|
|
49
|
+
detect-maturity:
|
|
50
|
+
if: github.actor != 'dependabot[bot]' || github.event_name == 'schedule'
|
|
118
51
|
```
|
|
119
52
|
|
|
120
|
-
**Savings**: 50% reduction in
|
|
121
|
-
**vibebuildlab**: ~18,000 min/month saved ($144/mo)
|
|
122
|
-
|
|
123
|
-
#### 3. Move Security to Scheduled Workflow
|
|
53
|
+
**Savings**: ~50% reduction in runs (~1,400 mins/month)
|
|
124
54
|
|
|
125
|
-
|
|
126
|
-
# New file: .github/workflows/security-weekly.yml
|
|
127
|
-
on:
|
|
128
|
-
schedule:
|
|
129
|
-
- cron: '0 0 * * 0' # Weekly on Sunday
|
|
130
|
-
workflow_dispatch: # Manual trigger
|
|
131
|
-
|
|
132
|
-
jobs:
|
|
133
|
-
security:
|
|
134
|
-
runs-on: ubuntu-latest
|
|
135
|
-
steps:
|
|
136
|
-
- name: Gitleaks
|
|
137
|
-
- name: Semgrep
|
|
138
|
-
- name: npm audit
|
|
139
|
-
```
|
|
55
|
+
### 2. Minimal Workflow Mode (v5.11.1)
|
|
140
56
|
|
|
141
|
-
|
|
142
|
-
|
|
57
|
+
- Security scans: Weekly only (not every push)
|
|
58
|
+
- Test matrix: Node 22 only (not [20, 22])
|
|
59
|
+
- Path filters: Skip docs-only changes
|
|
60
|
+
- Concurrency: Cancel in-progress runs
|
|
143
61
|
|
|
144
|
-
###
|
|
145
|
-
|
|
146
|
-
```yaml
|
|
147
|
-
# .github/workflows/ci.yml
|
|
148
|
-
name: CI
|
|
149
|
-
|
|
150
|
-
on:
|
|
151
|
-
push:
|
|
152
|
-
branches: [main, develop]
|
|
153
|
-
paths-ignore:
|
|
154
|
-
- '**.md'
|
|
155
|
-
- 'docs/**'
|
|
156
|
-
- 'LICENSE'
|
|
157
|
-
pull_request:
|
|
158
|
-
|
|
159
|
-
concurrency:
|
|
160
|
-
group: ${{ github.workflow }}-${{ github.ref }}
|
|
161
|
-
cancel-in-progress: true # Cancel old runs
|
|
162
|
-
|
|
163
|
-
jobs:
|
|
164
|
-
# Quick checks on every commit (current Node only)
|
|
165
|
-
quick-check:
|
|
166
|
-
runs-on: ubuntu-latest
|
|
167
|
-
steps:
|
|
168
|
-
- uses: actions/checkout@v5
|
|
169
|
-
- uses: actions/setup-node@v6
|
|
170
|
-
with:
|
|
171
|
-
node-version: 22
|
|
172
|
-
cache: npm
|
|
173
|
-
- run: npm ci
|
|
174
|
-
- run: npm run lint
|
|
175
|
-
- run: npm run format:check
|
|
176
|
-
- run: npm test
|
|
177
|
-
|
|
178
|
-
# Matrix testing only on main branch
|
|
179
|
-
cross-version:
|
|
180
|
-
if: github.ref == 'refs/heads/main'
|
|
181
|
-
runs-on: ubuntu-latest
|
|
182
|
-
strategy:
|
|
183
|
-
matrix:
|
|
184
|
-
node-version: [20, 22]
|
|
185
|
-
steps:
|
|
186
|
-
- uses: actions/checkout@v5
|
|
187
|
-
- uses: actions/setup-node@v6
|
|
188
|
-
with:
|
|
189
|
-
node-version: ${{ matrix.node-version }}
|
|
190
|
-
cache: npm
|
|
191
|
-
- run: npm ci
|
|
192
|
-
- run: npm test
|
|
193
|
-
```
|
|
62
|
+
### Expected February Usage (Post-Fix)
|
|
194
63
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
**Estimated cost for vibebuildlab**:
|
|
201
|
-
|
|
202
|
-
- Current: 46,852 min/month ($358/mo)
|
|
203
|
-
- After changes: ~3,500 min/month ($12/mo)
|
|
204
|
-
- **Savings: $346/month (97% reduction)**
|
|
64
|
+
| Metric | Before | After | Savings |
|
|
65
|
+
| ------------------ | ------ | ------------ | ------- |
|
|
66
|
+
| Minutes/Month | 3,425 | ~1,200-1,500 | 55-65% |
|
|
67
|
+
| Budget Utilization | 171% | 60-75% | ✅ |
|
|
205
68
|
|
|
206
69
|
---
|
|
207
70
|
|
|
208
|
-
##
|
|
209
|
-
|
|
210
|
-
### For Solo Developers / Small Teams
|
|
211
|
-
|
|
212
|
-
**Make all repos public** → GitHub Actions is FREE
|
|
213
|
-
|
|
214
|
-
- If code can be public, this is the best option
|
|
215
|
-
- vibebuildlab, qa-architect could potentially be public
|
|
216
|
-
|
|
217
|
-
### For Private Repos
|
|
71
|
+
## Workflow Tiers
|
|
218
72
|
|
|
219
|
-
|
|
73
|
+
qa-architect supports three workflow modes:
|
|
220
74
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
✅ Documentation checks on releases only
|
|
227
|
-
|
|
228
|
-
Total: ~500-1,000 min/month ($0-8/mo)
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
**Option B: Self-Hosted Runner**
|
|
75
|
+
| Mode | When to Use | Estimated Cost |
|
|
76
|
+
| --------------------- | ----------------------- | -------------- |
|
|
77
|
+
| **Minimal** (default) | Solo dev, private repos | ~$0-10/mo |
|
|
78
|
+
| **Standard** | Team projects, PRs | ~$10-30/mo |
|
|
79
|
+
| **Comprehensive** | Enterprise, compliance | ~$50-100/mo |
|
|
232
80
|
|
|
233
|
-
|
|
234
|
-
- Install GitHub self-hosted runner
|
|
235
|
-
- Total cost: $20/mo for UNLIMITED minutes
|
|
236
|
-
- **Best if you have 5+ active private repos**
|
|
81
|
+
### Minimal Mode (Default)
|
|
237
82
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
pull_request: # Test on PRs
|
|
244
|
-
push:
|
|
245
|
-
branches: [main] # Test on main
|
|
246
|
-
paths-ignore:
|
|
247
|
-
- '**.md'
|
|
248
|
-
- 'docs/**'
|
|
249
|
-
|
|
250
|
-
# Skip matrix on draft PRs
|
|
251
|
-
if: github.event.pull_request.draft == false
|
|
252
|
-
```
|
|
83
|
+
- Single Node.js version (22)
|
|
84
|
+
- Security scans weekly only
|
|
85
|
+
- Path filters enabled
|
|
86
|
+
- Skip Dependabot PRs
|
|
87
|
+
- Concurrency limits
|
|
253
88
|
|
|
254
|
-
###
|
|
89
|
+
### Standard Mode (`--workflow-standard`)
|
|
255
90
|
|
|
256
|
-
|
|
91
|
+
- Matrix on main only (20, 22)
|
|
92
|
+
- Security on PR + weekly
|
|
93
|
+
- Full test coverage
|
|
257
94
|
|
|
258
|
-
|
|
259
|
-
- ❌ Costs $100-350/mo for typical projects
|
|
260
|
-
- ❌ Over-engineering: Gitleaks + Semgrep on every commit
|
|
95
|
+
### Comprehensive Mode (`--workflow-comprehensive`)
|
|
261
96
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
Basic (Free tier friendly):
|
|
266
|
-
✅ Lint + format + test (current Node only)
|
|
267
|
-
✅ Security scans weekly
|
|
268
|
-
✅ Matrix testing opt-in only
|
|
269
|
-
✅ Path filters enabled by default
|
|
270
|
-
|
|
271
|
-
Pro tier enhancements:
|
|
272
|
-
✅ Add matrix testing (if needed)
|
|
273
|
-
✅ Add cross-platform testing (if needed)
|
|
274
|
-
✅ Add comprehensive security (scheduled)
|
|
275
|
-
```
|
|
97
|
+
- Matrix every commit
|
|
98
|
+
- Inline security scans
|
|
99
|
+
- E2E tests on every PR
|
|
276
100
|
|
|
277
101
|
---
|
|
278
102
|
|
|
279
|
-
##
|
|
280
|
-
|
|
281
|
-
### Immediate (This Week)
|
|
282
|
-
|
|
283
|
-
1. Add path filters to all repos → Save 20% instantly
|
|
284
|
-
2. Move security scans to weekly schedule → Save 95% of security costs
|
|
285
|
-
3. Remove duplicate matrix jobs → Save 50% of test costs
|
|
286
|
-
|
|
287
|
-
### Short Term (This Month)
|
|
288
|
-
|
|
289
|
-
1. Redesign qa-architect default template (minimal-first approach)
|
|
290
|
-
2. Create three tiers:
|
|
291
|
-
- `--minimal`: Lint + test (current Node), FREE tier friendly
|
|
292
|
-
- `--standard`: + matrix testing (main branch only)
|
|
293
|
-
- `--comprehensive`: Current setup (for large teams)
|
|
294
|
-
3. Add `--public` flag that optimizes for unlimited minutes
|
|
103
|
+
## Optimization Tips
|
|
295
104
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
3. Document self-hosted runner setup guide
|
|
301
|
-
4. Create cost monitoring dashboard (track actual usage)
|
|
105
|
+
1. **Make repos public** → Unlimited free minutes
|
|
106
|
+
2. **Group Dependabot updates** → Fewer PRs/week
|
|
107
|
+
3. **Use path filters** → Skip docs-only changes
|
|
108
|
+
4. **Disable CI on inactive repos** → Zero cost
|
|
302
109
|
|
|
303
110
|
---
|
|
304
111
|
|
|
305
|
-
##
|
|
306
|
-
|
|
307
|
-
**YES, you're right to question this.**
|
|
308
|
-
|
|
309
|
-
qa-architect is creating **enterprise-grade CI for solo developers**, resulting in:
|
|
112
|
+
## Commands
|
|
310
113
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
-
|
|
114
|
+
```bash
|
|
115
|
+
# Check workflow mode
|
|
116
|
+
npx create-qa-architect --check-maturity
|
|
314
117
|
|
|
315
|
-
|
|
118
|
+
# Analyze CI costs
|
|
119
|
+
npx create-qa-architect --analyze-ci
|
|
316
120
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
- **qa-architect**: $110/mo → $5/mo (same changes)
|
|
321
|
-
- **Total savings**: $451/month ($5,412/year)
|
|
322
|
-
|
|
323
|
-
Or just make repos public → **$0/month**.
|
|
121
|
+
# Switch to minimal mode
|
|
122
|
+
npx create-qa-architect --workflow-minimal --force
|
|
123
|
+
```
|
package/lib/project-maturity.js
CHANGED
|
@@ -119,6 +119,11 @@ class ProjectMaturityDetector {
|
|
|
119
119
|
stats.testFiles === 0
|
|
120
120
|
) {
|
|
121
121
|
maturity = 'bootstrap'
|
|
122
|
+
} else if (
|
|
123
|
+
stats.totalSourceFiles < MATURITY_THRESHOLDS.MIN_BOOTSTRAP_FILES &&
|
|
124
|
+
stats.testFiles > 0
|
|
125
|
+
) {
|
|
126
|
+
maturity = 'bootstrap'
|
|
122
127
|
} else if (
|
|
123
128
|
stats.testFiles > 0 &&
|
|
124
129
|
stats.totalSourceFiles >= MATURITY_THRESHOLDS.MIN_BOOTSTRAP_FILES &&
|
|
@@ -25,6 +25,7 @@ function generateLighthouseConfig(options = {}) {
|
|
|
25
25
|
hasThresholds = false,
|
|
26
26
|
collectUrl = 'http://localhost:3000',
|
|
27
27
|
staticDistDir = null,
|
|
28
|
+
budgets = null,
|
|
28
29
|
} = options
|
|
29
30
|
|
|
30
31
|
const collectConfig = staticDistDir
|
|
@@ -34,29 +35,42 @@ function generateLighthouseConfig(options = {}) {
|
|
|
34
35
|
startServerReadyPattern: 'ready|listening|started',
|
|
35
36
|
startServerReadyTimeout: 30000,`
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
let assertConfig
|
|
39
|
+
if (hasThresholds) {
|
|
40
|
+
// Use custom budgets from .qualityrc.json if provided, otherwise defaults
|
|
41
|
+
const b = budgets || {}
|
|
42
|
+
const fcp = b.maxFCP || 2000
|
|
43
|
+
const lcp = b.maxLCP || 2500
|
|
44
|
+
const cls = b.maxCLS || 0.1
|
|
45
|
+
const tbt = b.maxTBT || 300
|
|
46
|
+
const perf = b.performance || 0.8
|
|
47
|
+
const a11y = b.accessibility || 0.9
|
|
48
|
+
const bp = b.bestPractices || 0.9
|
|
49
|
+
const seo = b.seo || 0.9
|
|
50
|
+
|
|
51
|
+
assertConfig = `
|
|
39
52
|
assert: {
|
|
40
53
|
preset: 'lighthouse:recommended',
|
|
41
54
|
assertions: {
|
|
42
55
|
// Performance
|
|
43
|
-
'first-contentful-paint': ['warn', { maxNumericValue:
|
|
44
|
-
'largest-contentful-paint': ['error', { maxNumericValue:
|
|
45
|
-
'cumulative-layout-shift': ['error', { maxNumericValue:
|
|
46
|
-
'total-blocking-time': ['warn', { maxNumericValue:
|
|
56
|
+
'first-contentful-paint': ['warn', { maxNumericValue: ${fcp} }],
|
|
57
|
+
'largest-contentful-paint': ['error', { maxNumericValue: ${lcp} }],
|
|
58
|
+
'cumulative-layout-shift': ['error', { maxNumericValue: ${cls} }],
|
|
59
|
+
'total-blocking-time': ['warn', { maxNumericValue: ${tbt} }],
|
|
47
60
|
|
|
48
61
|
// Categories (0-1 scale)
|
|
49
|
-
'categories:performance': ['warn', { minScore:
|
|
50
|
-
'categories:accessibility': ['error', { minScore:
|
|
51
|
-
'categories:best-practices': ['warn', { minScore:
|
|
52
|
-
'categories:seo': ['warn', { minScore:
|
|
62
|
+
'categories:performance': ['warn', { minScore: ${perf} }],
|
|
63
|
+
'categories:accessibility': ['error', { minScore: ${a11y} }],
|
|
64
|
+
'categories:best-practices': ['warn', { minScore: ${bp} }],
|
|
65
|
+
'categories:seo': ['warn', { minScore: ${seo} }],
|
|
53
66
|
|
|
54
67
|
// Allow some common warnings
|
|
55
68
|
'unsized-images': 'off',
|
|
56
69
|
'uses-responsive-images': 'off',
|
|
57
70
|
},
|
|
58
71
|
},`
|
|
59
|
-
|
|
72
|
+
} else {
|
|
73
|
+
assertConfig = `
|
|
60
74
|
assert: {
|
|
61
75
|
// Basic assertions for Free tier - just warnings, no failures
|
|
62
76
|
assertions: {
|
|
@@ -64,6 +78,7 @@ function generateLighthouseConfig(options = {}) {
|
|
|
64
78
|
'categories:best-practices': ['warn', { minScore: 0.7 }],
|
|
65
79
|
},
|
|
66
80
|
},`
|
|
81
|
+
}
|
|
67
82
|
|
|
68
83
|
return `module.exports = {
|
|
69
84
|
ci: {
|
|
@@ -86,7 +101,7 @@ function generateLighthouseConfig(options = {}) {
|
|
|
86
101
|
* @returns {Array} size-limit config array
|
|
87
102
|
*/
|
|
88
103
|
function generateSizeLimitConfig(options = {}) {
|
|
89
|
-
const { projectPath = process.cwd() } = options
|
|
104
|
+
const { projectPath = process.cwd(), budgets = null } = options
|
|
90
105
|
|
|
91
106
|
// Detect build output paths
|
|
92
107
|
const possibleDists = ['dist', 'build', '.next', 'out', 'public']
|
|
@@ -99,6 +114,10 @@ function generateSizeLimitConfig(options = {}) {
|
|
|
99
114
|
}
|
|
100
115
|
}
|
|
101
116
|
|
|
117
|
+
// Use custom budgets from .qualityrc.json if provided
|
|
118
|
+
const jsLimit = (budgets && budgets.maxJs) || null
|
|
119
|
+
const cssLimit = (budgets && budgets.maxCss) || null
|
|
120
|
+
|
|
102
121
|
// Detect if it's a Next.js app
|
|
103
122
|
const isNextJS =
|
|
104
123
|
fs.existsSync(path.join(projectPath, 'next.config.js')) ||
|
|
@@ -108,7 +127,7 @@ function generateSizeLimitConfig(options = {}) {
|
|
|
108
127
|
return [
|
|
109
128
|
{
|
|
110
129
|
path: '.next/static/**/*.js',
|
|
111
|
-
limit: '300 kB',
|
|
130
|
+
limit: jsLimit || '300 kB',
|
|
112
131
|
webpack: false,
|
|
113
132
|
},
|
|
114
133
|
]
|
|
@@ -117,11 +136,11 @@ function generateSizeLimitConfig(options = {}) {
|
|
|
117
136
|
return [
|
|
118
137
|
{
|
|
119
138
|
path: `${distDir}/**/*.js`,
|
|
120
|
-
limit: '250 kB',
|
|
139
|
+
limit: jsLimit || '250 kB',
|
|
121
140
|
},
|
|
122
141
|
{
|
|
123
142
|
path: `${distDir}/**/*.css`,
|
|
124
|
-
limit: '50 kB',
|
|
143
|
+
limit: cssLimit || '50 kB',
|
|
125
144
|
},
|
|
126
145
|
]
|
|
127
146
|
}
|
|
@@ -525,8 +544,8 @@ function getQualityToolsDependencies(features = {}) {
|
|
|
525
544
|
}
|
|
526
545
|
|
|
527
546
|
if (features.commitlint) {
|
|
528
|
-
deps['@commitlint/cli'] = '^
|
|
529
|
-
deps['@commitlint/config-conventional'] = '^
|
|
547
|
+
deps['@commitlint/cli'] = '^20.4.1'
|
|
548
|
+
deps['@commitlint/config-conventional'] = '^20.4.1'
|
|
530
549
|
}
|
|
531
550
|
|
|
532
551
|
if (features.axeCore) {
|
|
@@ -109,46 +109,17 @@ id = "jwt-token"
|
|
|
109
109
|
regex = '''eyJ[A-Za-z0-9_/+-]{10,}={0,2}'''
|
|
110
110
|
tags = ["key", "JWT"]
|
|
111
111
|
|
|
112
|
-
[[rules]]
|
|
113
|
-
description = "Base64 encoded secrets (long)"
|
|
114
|
-
id = "base64-secret"
|
|
115
|
-
regex = '''[A-Za-z0-9+/]{40,}={0,2}'''
|
|
116
|
-
tags = ["secret", "base64"]
|
|
117
|
-
keywords = ["secret", "key", "token", "password"]
|
|
118
|
-
|
|
119
112
|
[[rules]]
|
|
120
113
|
description = "Environment variable secrets"
|
|
121
114
|
id = "env-secret"
|
|
122
115
|
regex = '''(?i)(api_key|secret|password|token)\\s*=\\s*['""][^'"\\s]{10,}['""]'''
|
|
123
116
|
tags = ["env", "secret"]
|
|
124
117
|
|
|
125
|
-
# Allowlist for test files
|
|
126
|
-
[
|
|
127
|
-
description = "Test
|
|
128
|
-
regexes = [
|
|
129
|
-
|
|
130
|
-
'''example_.*''',
|
|
131
|
-
'''dummy_.*''',
|
|
132
|
-
'''fake_.*'''
|
|
133
|
-
]
|
|
134
|
-
paths = [
|
|
135
|
-
'''tests/''',
|
|
136
|
-
'''test/''',
|
|
137
|
-
'''__tests__/''',
|
|
138
|
-
'''examples/''',
|
|
139
|
-
'''docs/''',
|
|
140
|
-
'''.md$'''
|
|
141
|
-
]
|
|
142
|
-
|
|
143
|
-
# Global allowlist for common false positives
|
|
144
|
-
[[allowlist]]
|
|
145
|
-
description = "Common false positives"
|
|
146
|
-
regexes = [
|
|
147
|
-
'''EXAMPLE_.*''',
|
|
148
|
-
'''your_.*_here''',
|
|
149
|
-
'''replace_with_.*''',
|
|
150
|
-
'''TODO:.*'''
|
|
151
|
-
]
|
|
118
|
+
# Allowlist for test/example files (broad exclusion)
|
|
119
|
+
[allowlist]
|
|
120
|
+
description = "Test and example files"
|
|
121
|
+
regexes = ['''test_secret_.*|example_.*|dummy_.*|fake_.*|EXAMPLE_.*|your_.*_here|replace_with_.*|TODO:.*''']
|
|
122
|
+
paths = ['''(tests/|test/|__tests__/|examples/|docs/|\\.md$|\\.gitleaksignore$|node_modules/|dist/|build/|\\.next/|coverage/)''']
|
|
152
123
|
`
|
|
153
124
|
}
|
|
154
125
|
|
package/lib/workflow-config.js
CHANGED
|
@@ -179,7 +179,17 @@ function injectWorkflowMode(workflowContent, mode) {
|
|
|
179
179
|
"has-css: 'false'"
|
|
180
180
|
)
|
|
181
181
|
|
|
182
|
-
// 4.
|
|
182
|
+
// 4. Remove dev-only gitleaks binary test steps (only relevant for qa-architect itself)
|
|
183
|
+
updated = updated.replace(
|
|
184
|
+
/\s+- name: Cache gitleaks binary for real download test[\s\S]*?restore-keys: \|[^\n]*\n[^\n]*\n/,
|
|
185
|
+
'\n'
|
|
186
|
+
)
|
|
187
|
+
updated = updated.replace(
|
|
188
|
+
/\s+- name: Run real gitleaks binary verification test[\s\S]*?node tests\/gitleaks-real-binary-test\.js\n/,
|
|
189
|
+
'\n'
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
// 5. Simplify "Display Detection Report" to show only package manager info
|
|
183
193
|
updated = updated.replace(
|
|
184
194
|
/- name: Display Detection Report\s+run: \|[\s\S]*?echo "Has CSS files:[^"]*"/,
|
|
185
195
|
`- name: Display Detection Report
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-qa-architect",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.12.0",
|
|
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": {
|
|
@@ -51,7 +51,8 @@
|
|
|
51
51
|
"size": "size-limit",
|
|
52
52
|
"size:why": "size-limit --why",
|
|
53
53
|
"test:a11y": "vitest run tests/accessibility.test.js",
|
|
54
|
-
"test:coverage:check": "vitest run --coverage --coverage.thresholds.lines=70 --coverage.thresholds.functions=70"
|
|
54
|
+
"test:coverage:check": "vitest run --coverage --coverage.thresholds.lines=70 --coverage.thresholds.functions=70",
|
|
55
|
+
"test:changed": "vitest run --changed HEAD~1 --passWithNoTests"
|
|
55
56
|
},
|
|
56
57
|
"keywords": [
|
|
57
58
|
"qa-architect",
|
|
@@ -82,11 +82,17 @@ echo " ⚡ Speed Bonus: $SPEED_BONUS"
|
|
|
82
82
|
echo ""
|
|
83
83
|
|
|
84
84
|
# Test tier selection based on risk score
|
|
85
|
+
# NOTE: E2E tests and slow command tests are ALWAYS excluded from pre-push
|
|
86
|
+
# - E2E tests: Require dev server, browsers, proper infrastructure (run in CI only)
|
|
87
|
+
# - Command tests: Take 60+ seconds, verify npm scripts work (run in CI only)
|
|
88
|
+
# These run in GitHub Actions on every PR and push to main
|
|
89
|
+
|
|
85
90
|
if [[ $RISK_SCORE -ge 7 ]]; then
|
|
86
|
-
echo "🔴 HIGH RISK - Comprehensive validation"
|
|
87
|
-
echo " •
|
|
88
|
-
|
|
89
|
-
npm run test:
|
|
91
|
+
echo "🔴 HIGH RISK - Comprehensive validation (pre-push)"
|
|
92
|
+
echo " • Unit + integration tests + security audit"
|
|
93
|
+
echo " • (E2E and command tests run in CI only)"
|
|
94
|
+
# Runs: npm run test:medium 2>/dev/null || npm run test:fast 2>/dev/null || npm test
|
|
95
|
+
npm run test:medium 2>/dev/null || npm run test:fast 2>/dev/null || npm test
|
|
90
96
|
elif [[ $RISK_SCORE -ge 4 ]]; then
|
|
91
97
|
echo "🟡 MEDIUM RISK - Standard validation"
|
|
92
98
|
echo " • Fast tests + integration (excludes slow tests)"
|
package/setup.js
CHANGED
|
@@ -927,13 +927,44 @@ HELP:
|
|
|
927
927
|
const hasConventionalCommits = hasFeature('conventionalCommits')
|
|
928
928
|
const hasCoverageThresholds = hasFeature('coverageThresholds')
|
|
929
929
|
|
|
930
|
+
// Load .qualityrc.json for performance budgets and check overrides
|
|
931
|
+
let performanceBudgets = null
|
|
932
|
+
let qualityChecks = {}
|
|
933
|
+
const qualityrcPath = path.join(projectPath, '.qualityrc.json')
|
|
934
|
+
if (fs.existsSync(qualityrcPath)) {
|
|
935
|
+
try {
|
|
936
|
+
const qualityrc = JSON.parse(fs.readFileSync(qualityrcPath, 'utf8'))
|
|
937
|
+
if (qualityrc.performance) {
|
|
938
|
+
performanceBudgets = qualityrc.performance
|
|
939
|
+
}
|
|
940
|
+
if (qualityrc.checks) {
|
|
941
|
+
qualityChecks = qualityrc.checks
|
|
942
|
+
}
|
|
943
|
+
} catch {
|
|
944
|
+
// Ignore parse errors - config validation handles this elsewhere
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// Helper: check if a quality check is enabled (respects .qualityrc.json overrides)
|
|
949
|
+
function isCheckEnabled(checkName, licenseDefault) {
|
|
950
|
+
const override = qualityChecks[checkName]
|
|
951
|
+
if (override && override.enabled === false) return false
|
|
952
|
+
if (override && override.enabled === true) return true
|
|
953
|
+
// "auto" or missing: use license-based default
|
|
954
|
+
return licenseDefault
|
|
955
|
+
}
|
|
956
|
+
|
|
930
957
|
// 1. Lighthouse CI - available to all, thresholds for Pro+
|
|
931
|
-
if (hasLighthouse) {
|
|
958
|
+
if (isCheckEnabled('lighthouse', hasLighthouse)) {
|
|
932
959
|
try {
|
|
933
960
|
const lighthousePath = path.join(projectPath, 'lighthouserc.js')
|
|
934
961
|
if (!fs.existsSync(lighthousePath)) {
|
|
935
962
|
writeLighthouseConfig(projectPath, {
|
|
936
963
|
hasThresholds: hasLighthouseThresholds,
|
|
964
|
+
budgets:
|
|
965
|
+
performanceBudgets && performanceBudgets.lighthouse
|
|
966
|
+
? performanceBudgets.lighthouse
|
|
967
|
+
: null,
|
|
937
968
|
})
|
|
938
969
|
addedTools.push(
|
|
939
970
|
hasLighthouseThresholds
|
|
@@ -953,7 +984,12 @@ HELP:
|
|
|
953
984
|
if (hasBundleSizeLimits) {
|
|
954
985
|
try {
|
|
955
986
|
if (!pkgJson.content['size-limit']) {
|
|
956
|
-
writeSizeLimitConfig(projectPath
|
|
987
|
+
writeSizeLimitConfig(projectPath, {
|
|
988
|
+
budgets:
|
|
989
|
+
performanceBudgets && performanceBudgets.bundleSize
|
|
990
|
+
? performanceBudgets.bundleSize
|
|
991
|
+
: null,
|
|
992
|
+
})
|
|
957
993
|
addedTools.push('Bundle size limits (size-limit)')
|
|
958
994
|
}
|
|
959
995
|
} catch (error) {
|