create-qa-architect 5.0.1 → 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/release.yml +12 -10
- package/.github/workflows/weekly-audit.yml +173 -0
- package/README.md +4 -4
- package/config/defaults.js +22 -1
- package/config/quality-config.schema.json +1 -1
- package/create-saas-monetization.js +65 -27
- package/docs/ARCHITECTURE.md +0 -1
- package/docs/DEPLOYMENT.md +1 -2
- package/docs/PREFLIGHT_REPORT.md +108 -0
- package/docs/TESTING.md +1 -2
- package/lib/config-validator.js +8 -2
- 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 +9 -9
- package/lib/package-utils.js +9 -8
- package/lib/project-maturity.js +1 -1
- 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 +12 -9
- 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 +33 -9
- package/templates/scripts/smart-test-strategy.sh +1 -1
|
@@ -27,14 +27,12 @@ Use this checklist before any version bump or npm publication.
|
|
|
27
27
|
|
|
28
28
|
### Security Audit Compliance
|
|
29
29
|
|
|
30
|
-
- [ ] `
|
|
30
|
+
- [ ] `npm audit` shows no high/critical vulnerabilities
|
|
31
|
+
- [ ] `gitleaks detect --source . --redact` passes (no secrets)
|
|
31
32
|
- [ ] **CRITICAL**: Gitleaks checksums are real SHA256 values, not placeholders
|
|
32
33
|
- [ ] `lib/validation/config-security.js` GITLEAKS_CHECKSUMS contains verified hashes
|
|
33
34
|
- [ ] No "PLACEHOLDER_CHECKSUM" strings exist in security validation code
|
|
34
35
|
- [ ] Gitleaks pinned version in code matches documented security version
|
|
35
|
-
- [ ] No new security vulnerabilities introduced since audit
|
|
36
|
-
- [ ] All security fixes from audit still in place
|
|
37
|
-
- [ ] Security audit document references current version (or base version for pre-releases like `4.0.1-rc.1`)
|
|
38
36
|
|
|
39
37
|
### Real Binary Verification
|
|
40
38
|
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Daily Deploy Check Workflow
|
|
2
|
+
# Copy this to .github/workflows/daily-deploy-check.yml in each project
|
|
3
|
+
#
|
|
4
|
+
# Required secrets:
|
|
5
|
+
# AUDIT_WEBHOOK_SECRET - shared secret for authenticating with vibebuildlab API
|
|
6
|
+
#
|
|
7
|
+
# Required variables (set in repo settings):
|
|
8
|
+
# PRODUCTION_URL - the production URL to check (e.g., https://saas.vibebuildlab.com)
|
|
9
|
+
#
|
|
10
|
+
# This workflow:
|
|
11
|
+
# 1. Runs daily at 6am UTC
|
|
12
|
+
# 2. Checks if production URL responds and SSL is valid
|
|
13
|
+
# 3. Reports deploy stage status to vibebuildlab dashboard
|
|
14
|
+
|
|
15
|
+
name: Daily Deploy Check
|
|
16
|
+
|
|
17
|
+
on:
|
|
18
|
+
schedule:
|
|
19
|
+
# Every day at 6am UTC
|
|
20
|
+
- cron: '0 6 * * *'
|
|
21
|
+
workflow_dispatch: # Manual trigger
|
|
22
|
+
|
|
23
|
+
env:
|
|
24
|
+
VIBEBUILDLAB_API: https://dash.vibebuildlab.com/api/audit-results
|
|
25
|
+
|
|
26
|
+
jobs:
|
|
27
|
+
deploy-check:
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
timeout-minutes: 5
|
|
30
|
+
|
|
31
|
+
steps:
|
|
32
|
+
- name: Check URL responds
|
|
33
|
+
id: url_check
|
|
34
|
+
run: |
|
|
35
|
+
URL="${{ vars.PRODUCTION_URL }}"
|
|
36
|
+
|
|
37
|
+
if [ -z "$URL" ]; then
|
|
38
|
+
echo "::error::PRODUCTION_URL variable not set"
|
|
39
|
+
echo "responds=false" >> $GITHUB_OUTPUT
|
|
40
|
+
exit 0
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$URL" --max-time 30 || echo "000")
|
|
44
|
+
|
|
45
|
+
echo "status_code=$STATUS" >> $GITHUB_OUTPUT
|
|
46
|
+
|
|
47
|
+
if [ "$STATUS" = "200" ] || [ "$STATUS" = "301" ] || [ "$STATUS" = "302" ]; then
|
|
48
|
+
echo "responds=true" >> $GITHUB_OUTPUT
|
|
49
|
+
else
|
|
50
|
+
echo "responds=false" >> $GITHUB_OUTPUT
|
|
51
|
+
fi
|
|
52
|
+
continue-on-error: true
|
|
53
|
+
|
|
54
|
+
- name: Check SSL certificate
|
|
55
|
+
id: ssl_check
|
|
56
|
+
run: |
|
|
57
|
+
URL="${{ vars.PRODUCTION_URL }}"
|
|
58
|
+
|
|
59
|
+
if [ -z "$URL" ]; then
|
|
60
|
+
echo "valid=false" >> $GITHUB_OUTPUT
|
|
61
|
+
exit 0
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# Extract domain from URL
|
|
65
|
+
DOMAIN=$(echo "$URL" | sed -E 's|https?://([^/]+).*|\1|')
|
|
66
|
+
|
|
67
|
+
# Check SSL expiry
|
|
68
|
+
EXPIRY=$(echo | openssl s_client -servername "$DOMAIN" -connect "$DOMAIN:443" 2>/dev/null | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2 || echo "")
|
|
69
|
+
|
|
70
|
+
if [ -n "$EXPIRY" ]; then
|
|
71
|
+
echo "valid=true" >> $GITHUB_OUTPUT
|
|
72
|
+
echo "expiry=$EXPIRY" >> $GITHUB_OUTPUT
|
|
73
|
+
|
|
74
|
+
# Check if expiring within 7 days
|
|
75
|
+
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s 2>/dev/null || date -j -f "%b %d %H:%M:%S %Y %Z" "$EXPIRY" +%s 2>/dev/null || echo "0")
|
|
76
|
+
NOW_EPOCH=$(date +%s)
|
|
77
|
+
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
|
|
78
|
+
|
|
79
|
+
echo "days_left=$DAYS_LEFT" >> $GITHUB_OUTPUT
|
|
80
|
+
|
|
81
|
+
if [ "$DAYS_LEFT" -lt 7 ]; then
|
|
82
|
+
echo "::warning::SSL certificate expires in $DAYS_LEFT days!"
|
|
83
|
+
fi
|
|
84
|
+
else
|
|
85
|
+
echo "valid=false" >> $GITHUB_OUTPUT
|
|
86
|
+
fi
|
|
87
|
+
continue-on-error: true
|
|
88
|
+
|
|
89
|
+
- name: Determine deploy status
|
|
90
|
+
id: result
|
|
91
|
+
run: |
|
|
92
|
+
if [ "${{ steps.url_check.outputs.responds }}" = "true" ]; then
|
|
93
|
+
echo "deploy=pass" >> $GITHUB_OUTPUT
|
|
94
|
+
else
|
|
95
|
+
echo "deploy=fail" >> $GITHUB_OUTPUT
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
- name: Report to vibebuildlab
|
|
99
|
+
if: always()
|
|
100
|
+
run: |
|
|
101
|
+
# Get project slug from repo name
|
|
102
|
+
PROJECT_SLUG="${{ github.event.repository.name }}"
|
|
103
|
+
|
|
104
|
+
curl -X POST "$VIBEBUILDLAB_API" \
|
|
105
|
+
-H "Content-Type: application/json" \
|
|
106
|
+
-H "Authorization: Bearer ${{ secrets.AUDIT_WEBHOOK_SECRET }}" \
|
|
107
|
+
-d "{
|
|
108
|
+
\"project\": \"${PROJECT_SLUG}\",
|
|
109
|
+
\"stages\": {
|
|
110
|
+
\"deploy\": \"${{ steps.result.outputs.deploy }}\"
|
|
111
|
+
},
|
|
112
|
+
\"details\": {
|
|
113
|
+
\"url_responds\": ${{ steps.url_check.outputs.responds }},
|
|
114
|
+
\"ssl_valid\": ${{ steps.ssl_check.outputs.valid || false }},
|
|
115
|
+
\"status_code\": ${{ steps.url_check.outputs.status_code || 0 }}
|
|
116
|
+
},
|
|
117
|
+
\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"
|
|
118
|
+
}"
|
|
119
|
+
continue-on-error: true
|
|
120
|
+
|
|
121
|
+
- name: Summary
|
|
122
|
+
if: always()
|
|
123
|
+
run: |
|
|
124
|
+
echo "## Deploy Check Results" >> $GITHUB_STEP_SUMMARY
|
|
125
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
126
|
+
echo "**URL:** ${{ vars.PRODUCTION_URL }}" >> $GITHUB_STEP_SUMMARY
|
|
127
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
128
|
+
echo "| Check | Status |" >> $GITHUB_STEP_SUMMARY
|
|
129
|
+
echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
|
|
130
|
+
echo "| URL Responds | ${{ steps.url_check.outputs.responds == 'true' && '✅' || '❌' }} (HTTP ${{ steps.url_check.outputs.status_code }}) |" >> $GITHUB_STEP_SUMMARY
|
|
131
|
+
echo "| SSL Valid | ${{ steps.ssl_check.outputs.valid == 'true' && '✅' || '❌' }} |" >> $GITHUB_STEP_SUMMARY
|
|
132
|
+
if [ -n "${{ steps.ssl_check.outputs.expiry }}" ]; then
|
|
133
|
+
echo "| SSL Expiry | ${{ steps.ssl_check.outputs.expiry }} (${{ steps.ssl_check.outputs.days_left }} days) |" >> $GITHUB_STEP_SUMMARY
|
|
134
|
+
fi
|
|
135
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
136
|
+
echo "**Deploy Stage:** ${{ steps.result.outputs.deploy }}" >> $GITHUB_STEP_SUMMARY
|
|
@@ -42,7 +42,7 @@ jobs:
|
|
|
42
42
|
run: |
|
|
43
43
|
echo "🔐 Running REAL gitleaks download and verification test..."
|
|
44
44
|
echo "Platform: $(uname -s)-$(uname -m)"
|
|
45
|
-
echo "Expected checksum:
|
|
45
|
+
echo "Expected checksum: 5fd1b3b0073269484d40078662e921d07427340ab9e6ed526ccd215a565b3298"
|
|
46
46
|
|
|
47
47
|
# Create a test script that downloads and verifies gitleaks
|
|
48
48
|
cat > test-real-download.js << 'EOF'
|
|
@@ -4,6 +4,10 @@ on:
|
|
|
4
4
|
push:
|
|
5
5
|
tags: ['v*']
|
|
6
6
|
|
|
7
|
+
permissions:
|
|
8
|
+
id-token: write # Required for OIDC trusted publishing to npm
|
|
9
|
+
contents: write # Required for creating GitHub releases
|
|
10
|
+
|
|
7
11
|
jobs:
|
|
8
12
|
release:
|
|
9
13
|
runs-on: ubuntu-latest
|
|
@@ -14,27 +18,25 @@ jobs:
|
|
|
14
18
|
- name: Setup Node.js
|
|
15
19
|
uses: actions/setup-node@v4
|
|
16
20
|
with:
|
|
17
|
-
node-version: '
|
|
21
|
+
node-version: '22'
|
|
18
22
|
registry-url: 'https://registry.npmjs.org'
|
|
19
23
|
|
|
24
|
+
- name: Upgrade npm for OIDC trusted publishing
|
|
25
|
+
run: npm install -g npm@latest
|
|
26
|
+
|
|
20
27
|
- name: Install dependencies
|
|
21
28
|
run: npm ci
|
|
22
29
|
|
|
23
30
|
- name: Run pre-release checks
|
|
24
31
|
run: npm run prerelease
|
|
25
32
|
|
|
26
|
-
- name: Publish to npm
|
|
27
|
-
run: npm publish
|
|
28
|
-
env:
|
|
29
|
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
33
|
+
- name: Publish to npm with provenance
|
|
34
|
+
run: npm publish --provenance
|
|
30
35
|
|
|
31
36
|
- name: Create GitHub Release
|
|
32
|
-
uses:
|
|
33
|
-
env:
|
|
34
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
37
|
+
uses: softprops/action-gh-release@v2
|
|
35
38
|
with:
|
|
36
|
-
|
|
37
|
-
release_name: Release ${{ github.ref_name }}
|
|
39
|
+
name: Release ${{ github.ref_name }}
|
|
38
40
|
body: |
|
|
39
41
|
## Changes in ${{ github.ref_name }}
|
|
40
42
|
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Weekly Audit Workflow
|
|
2
|
+
# Copy this to .github/workflows/weekly-audit.yml in each project
|
|
3
|
+
#
|
|
4
|
+
# Required secrets:
|
|
5
|
+
# AUDIT_WEBHOOK_SECRET - shared secret for authenticating with vibebuildlab API
|
|
6
|
+
#
|
|
7
|
+
# This workflow:
|
|
8
|
+
# 1. Runs weekly (Sunday 2am UTC) and on manual trigger
|
|
9
|
+
# 2. Checks build, tests, lint, typecheck, security
|
|
10
|
+
# 3. Reports results to vibebuildlab dashboard
|
|
11
|
+
|
|
12
|
+
name: Weekly Audit
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
schedule:
|
|
16
|
+
# Every Sunday at 2am UTC
|
|
17
|
+
- cron: '0 2 * * 0'
|
|
18
|
+
workflow_dispatch: # Manual trigger
|
|
19
|
+
|
|
20
|
+
env:
|
|
21
|
+
VIBEBUILDLAB_API: https://dash.vibebuildlab.com/api/audit-results
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
audit:
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
timeout-minutes: 15
|
|
27
|
+
|
|
28
|
+
steps:
|
|
29
|
+
- name: Checkout
|
|
30
|
+
uses: actions/checkout@v4
|
|
31
|
+
|
|
32
|
+
- name: Setup Node.js
|
|
33
|
+
uses: actions/setup-node@v4
|
|
34
|
+
with:
|
|
35
|
+
node-version: '20'
|
|
36
|
+
cache: 'npm'
|
|
37
|
+
|
|
38
|
+
- name: Install dependencies
|
|
39
|
+
run: npm ci
|
|
40
|
+
|
|
41
|
+
- name: Run build
|
|
42
|
+
id: build
|
|
43
|
+
run: |
|
|
44
|
+
if npm run build; then
|
|
45
|
+
echo "success=true" >> $GITHUB_OUTPUT
|
|
46
|
+
else
|
|
47
|
+
echo "success=false" >> $GITHUB_OUTPUT
|
|
48
|
+
fi
|
|
49
|
+
continue-on-error: true
|
|
50
|
+
|
|
51
|
+
- name: Run tests
|
|
52
|
+
id: tests
|
|
53
|
+
run: |
|
|
54
|
+
# Run tests and capture output
|
|
55
|
+
TEST_OUTPUT=$(npm test 2>&1) || true
|
|
56
|
+
echo "$TEST_OUTPUT"
|
|
57
|
+
|
|
58
|
+
# Parse results
|
|
59
|
+
PASSED=$(echo "$TEST_OUTPUT" | grep -oE '[0-9]+ passed' | tail -1 | awk '{print $1}' || echo "0")
|
|
60
|
+
FAILED=$(echo "$TEST_OUTPUT" | grep -oE '[0-9]+ failed' | tail -1 | awk '{print $1}' || echo "0")
|
|
61
|
+
|
|
62
|
+
echo "passed=${PASSED:-0}" >> $GITHUB_OUTPUT
|
|
63
|
+
echo "failed=${FAILED:-0}" >> $GITHUB_OUTPUT
|
|
64
|
+
|
|
65
|
+
if [ "${FAILED:-0}" = "0" ] && [ "${PASSED:-0}" != "0" ]; then
|
|
66
|
+
echo "success=true" >> $GITHUB_OUTPUT
|
|
67
|
+
else
|
|
68
|
+
echo "success=false" >> $GITHUB_OUTPUT
|
|
69
|
+
fi
|
|
70
|
+
continue-on-error: true
|
|
71
|
+
|
|
72
|
+
- name: Run lint
|
|
73
|
+
id: lint
|
|
74
|
+
run: |
|
|
75
|
+
LINT_OUTPUT=$(npm run lint 2>&1) || true
|
|
76
|
+
ERRORS=$(echo "$LINT_OUTPUT" | grep -cE '^\s+[0-9]+:[0-9]+\s+error' || echo "0")
|
|
77
|
+
echo "errors=${ERRORS:-0}" >> $GITHUB_OUTPUT
|
|
78
|
+
|
|
79
|
+
if [ "${ERRORS:-0}" = "0" ]; then
|
|
80
|
+
echo "success=true" >> $GITHUB_OUTPUT
|
|
81
|
+
else
|
|
82
|
+
echo "success=false" >> $GITHUB_OUTPUT
|
|
83
|
+
fi
|
|
84
|
+
continue-on-error: true
|
|
85
|
+
|
|
86
|
+
- name: Run typecheck
|
|
87
|
+
id: typecheck
|
|
88
|
+
run: |
|
|
89
|
+
if npm run typecheck 2>/dev/null || npx tsc --noEmit 2>/dev/null; then
|
|
90
|
+
echo "success=true" >> $GITHUB_OUTPUT
|
|
91
|
+
echo "errors=0" >> $GITHUB_OUTPUT
|
|
92
|
+
else
|
|
93
|
+
echo "success=false" >> $GITHUB_OUTPUT
|
|
94
|
+
echo "errors=1" >> $GITHUB_OUTPUT
|
|
95
|
+
fi
|
|
96
|
+
continue-on-error: true
|
|
97
|
+
|
|
98
|
+
- name: Security audit
|
|
99
|
+
id: security
|
|
100
|
+
run: |
|
|
101
|
+
AUDIT_OUTPUT=$(npm audit 2>&1) || true
|
|
102
|
+
|
|
103
|
+
if echo "$AUDIT_OUTPUT" | grep -qiE '[0-9]+ (high|critical)'; then
|
|
104
|
+
echo "vulnerabilities=1" >> $GITHUB_OUTPUT
|
|
105
|
+
echo "success=false" >> $GITHUB_OUTPUT
|
|
106
|
+
else
|
|
107
|
+
echo "vulnerabilities=0" >> $GITHUB_OUTPUT
|
|
108
|
+
echo "success=true" >> $GITHUB_OUTPUT
|
|
109
|
+
fi
|
|
110
|
+
continue-on-error: true
|
|
111
|
+
|
|
112
|
+
- name: Determine audit result
|
|
113
|
+
id: result
|
|
114
|
+
run: |
|
|
115
|
+
# execute stage: build must pass
|
|
116
|
+
if [ "${{ steps.build.outputs.success }}" = "true" ]; then
|
|
117
|
+
echo "execute=pass" >> $GITHUB_OUTPUT
|
|
118
|
+
else
|
|
119
|
+
echo "execute=fail" >> $GITHUB_OUTPUT
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
# audit stage: tests + lint + typecheck + security
|
|
123
|
+
if [ "${{ steps.tests.outputs.success }}" = "true" ] && \
|
|
124
|
+
[ "${{ steps.lint.outputs.success }}" = "true" ] && \
|
|
125
|
+
[ "${{ steps.typecheck.outputs.success }}" = "true" ] && \
|
|
126
|
+
[ "${{ steps.security.outputs.success }}" = "true" ]; then
|
|
127
|
+
echo "audit=pass" >> $GITHUB_OUTPUT
|
|
128
|
+
else
|
|
129
|
+
echo "audit=fail" >> $GITHUB_OUTPUT
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
- name: Report to vibebuildlab
|
|
133
|
+
if: always()
|
|
134
|
+
run: |
|
|
135
|
+
# Get project slug from package.json name or repo name
|
|
136
|
+
PROJECT_SLUG=$(node -e "console.log(require('./package.json').name)" 2>/dev/null || echo "${{ github.event.repository.name }}")
|
|
137
|
+
|
|
138
|
+
curl -X POST "$VIBEBUILDLAB_API" \
|
|
139
|
+
-H "Content-Type: application/json" \
|
|
140
|
+
-H "Authorization: Bearer ${{ secrets.AUDIT_WEBHOOK_SECRET }}" \
|
|
141
|
+
-d "{
|
|
142
|
+
\"project\": \"${PROJECT_SLUG}\",
|
|
143
|
+
\"stages\": {
|
|
144
|
+
\"execute\": \"${{ steps.result.outputs.execute }}\",
|
|
145
|
+
\"audit\": \"${{ steps.result.outputs.audit }}\"
|
|
146
|
+
},
|
|
147
|
+
\"details\": {
|
|
148
|
+
\"tests_passed\": ${{ steps.tests.outputs.passed || 0 }},
|
|
149
|
+
\"tests_failed\": ${{ steps.tests.outputs.failed || 0 }},
|
|
150
|
+
\"lint_errors\": ${{ steps.lint.outputs.errors || 0 }},
|
|
151
|
+
\"type_errors\": ${{ steps.typecheck.outputs.errors || 0 }},
|
|
152
|
+
\"vulnerabilities\": ${{ steps.security.outputs.vulnerabilities || 0 }},
|
|
153
|
+
\"build_success\": ${{ steps.build.outputs.success }}
|
|
154
|
+
},
|
|
155
|
+
\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"
|
|
156
|
+
}"
|
|
157
|
+
continue-on-error: true
|
|
158
|
+
|
|
159
|
+
- name: Summary
|
|
160
|
+
if: always()
|
|
161
|
+
run: |
|
|
162
|
+
echo "## Audit Results" >> $GITHUB_STEP_SUMMARY
|
|
163
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
164
|
+
echo "| Check | Status |" >> $GITHUB_STEP_SUMMARY
|
|
165
|
+
echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
|
|
166
|
+
echo "| Build | ${{ steps.build.outputs.success == 'true' && '✅' || '❌' }} |" >> $GITHUB_STEP_SUMMARY
|
|
167
|
+
echo "| Tests | ${{ steps.tests.outputs.success == 'true' && '✅' || '❌' }} (${{ steps.tests.outputs.passed }} passed, ${{ steps.tests.outputs.failed }} failed) |" >> $GITHUB_STEP_SUMMARY
|
|
168
|
+
echo "| Lint | ${{ steps.lint.outputs.success == 'true' && '✅' || '❌' }} (${{ steps.lint.outputs.errors }} errors) |" >> $GITHUB_STEP_SUMMARY
|
|
169
|
+
echo "| TypeScript | ${{ steps.typecheck.outputs.success == 'true' && '✅' || '❌' }} |" >> $GITHUB_STEP_SUMMARY
|
|
170
|
+
echo "| Security | ${{ steps.security.outputs.success == 'true' && '✅' || '❌' }} |" >> $GITHUB_STEP_SUMMARY
|
|
171
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
172
|
+
echo "**Execute Stage:** ${{ steps.result.outputs.execute }}" >> $GITHUB_STEP_SUMMARY
|
|
173
|
+
echo "**Audit Stage:** ${{ steps.result.outputs.audit }}" >> $GITHUB_STEP_SUMMARY
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Quality automation CLI for JavaScript/TypeScript and Python projects. One command adds ESLint, Prettier, Husky, lint-staged, and GitHub Actions. Pro tiers add security scanning (Gitleaks), Smart Test Strategy, and multi-language support.
|
|
4
4
|
|
|
5
|
-
**This repo = the free CLI.** For the Pro dashboard with repo analytics, CI integration, and automation workflows, see [QA Architect Pro](https://vibebuildlab.com/qa-architect
|
|
5
|
+
**This repo = the free CLI.** For the Pro dashboard with repo analytics, CI integration, and automation workflows, see [QA Architect Pro](https://vibebuildlab.com/tools/qa-architect) (included in VBL Starter Kit).
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -48,7 +48,7 @@ npx create-qa-architect@latest
|
|
|
48
48
|
| **Team** | $15/user/mo (5-seat min) | + RBAC, Slack alerts, multi-repo dashboard, team audit log |
|
|
49
49
|
| **Enterprise** | $249/mo + $499 onboarding | + SSO/SAML, custom policies, compliance pack, dedicated TAM |
|
|
50
50
|
|
|
51
|
-
> **Pro included in [
|
|
51
|
+
> **Pro included in [VBL Starter Kit](https://vibebuildlab.com/starter-kit)** — Team/Enterprise are standalone purchases.
|
|
52
52
|
|
|
53
53
|
### Security Features by Tier
|
|
54
54
|
|
|
@@ -60,7 +60,7 @@ npx create-qa-architect@latest
|
|
|
60
60
|
|
|
61
61
|
### License
|
|
62
62
|
|
|
63
|
-
**
|
|
63
|
+
**Commercial License (freemium)** — free tier covers the basic CLI; Pro/Team/Enterprise features require a paid subscription. See [LICENSE](LICENSE).
|
|
64
64
|
|
|
65
65
|
## Tech Stack
|
|
66
66
|
|
|
@@ -214,7 +214,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
|
214
214
|
|
|
215
215
|
## License
|
|
216
216
|
|
|
217
|
-
|
|
217
|
+
Commercial freemium license — the base CLI is free to use; Pro/Team/Enterprise features require a paid subscription. See [LICENSE](LICENSE) for details.
|
|
218
218
|
|
|
219
219
|
## Legal
|
|
220
220
|
|
package/config/defaults.js
CHANGED
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
const STYLELINT_EXTENSIONS = ['css', 'scss', 'sass', 'less', 'pcss']
|
|
5
5
|
const DEFAULT_STYLELINT_TARGET = `**/*.{${STYLELINT_EXTENSIONS.join(',')}}`
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {Object} DefaultsOptions
|
|
9
|
+
* @property {string[]=} stylelintTargets
|
|
10
|
+
* @property {boolean=} typescript
|
|
11
|
+
* @property {boolean=} python
|
|
12
|
+
*/
|
|
13
|
+
|
|
7
14
|
const baseScripts = {
|
|
8
15
|
format: 'prettier --write .',
|
|
9
16
|
'format:check': 'prettier --check .',
|
|
@@ -41,7 +48,12 @@ const stylelintBraceGroup = stylelintTargets => {
|
|
|
41
48
|
return `{${targets.join(',')}}`
|
|
42
49
|
}
|
|
43
50
|
|
|
44
|
-
|
|
51
|
+
/**
|
|
52
|
+
* @param {DefaultsOptions} [options]
|
|
53
|
+
* @returns {Record<string, string>}
|
|
54
|
+
*/
|
|
55
|
+
const baseLintScripts = (options = {}) => {
|
|
56
|
+
const { stylelintTargets } = options
|
|
45
57
|
const stylelintTarget = stylelintBraceGroup(stylelintTargets)
|
|
46
58
|
return {
|
|
47
59
|
lint: `eslint . && stylelint "${stylelintTarget}" --allow-empty-input`,
|
|
@@ -96,6 +108,9 @@ const TS_LINT_STAGED_PATTERN = '**/*.{js,jsx,ts,tsx,mjs,cjs,html}'
|
|
|
96
108
|
|
|
97
109
|
const clone = value => JSON.parse(JSON.stringify(value))
|
|
98
110
|
|
|
111
|
+
/**
|
|
112
|
+
* @param {DefaultsOptions} [options]
|
|
113
|
+
*/
|
|
99
114
|
function getDefaultScripts({ stylelintTargets } = {}) {
|
|
100
115
|
return {
|
|
101
116
|
...clone(baseScripts),
|
|
@@ -103,6 +118,9 @@ function getDefaultScripts({ stylelintTargets } = {}) {
|
|
|
103
118
|
}
|
|
104
119
|
}
|
|
105
120
|
|
|
121
|
+
/**
|
|
122
|
+
* @param {DefaultsOptions} [options]
|
|
123
|
+
*/
|
|
106
124
|
function getDefaultDevDependencies({ typescript } = {}) {
|
|
107
125
|
const devDeps = { ...clone(baseDevDependencies) }
|
|
108
126
|
if (typescript) {
|
|
@@ -111,6 +129,9 @@ function getDefaultDevDependencies({ typescript } = {}) {
|
|
|
111
129
|
return devDeps
|
|
112
130
|
}
|
|
113
131
|
|
|
132
|
+
/**
|
|
133
|
+
* @param {DefaultsOptions} [options]
|
|
134
|
+
*/
|
|
114
135
|
function getDefaultLintStaged({ typescript, stylelintTargets, python } = {}) {
|
|
115
136
|
const pattern = typescript ? TS_LINT_STAGED_PATTERN : JS_LINT_STAGED_PATTERN
|
|
116
137
|
return clone(baseLintStaged(pattern, stylelintTargets, python))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"$id": "https://github.com/
|
|
3
|
+
"$id": "https://github.com/vibebuildlab/qa-architect/blob/main/config/quality-config.schema.json",
|
|
4
4
|
"title": "Quality Automation Configuration",
|
|
5
5
|
"description": "Configuration for create-qa-architect progressive quality checks",
|
|
6
6
|
"type": "object",
|
|
@@ -39,7 +39,7 @@ const FOUNDER_ENTERPRISE_PRICE = '74.50'
|
|
|
39
39
|
|
|
40
40
|
class SaaSMonetizationBootstrap {
|
|
41
41
|
constructor() {
|
|
42
|
-
this.projectRoot = process.cwd()
|
|
42
|
+
this.projectRoot = path.resolve(process.cwd())
|
|
43
43
|
this.config = {}
|
|
44
44
|
this.templates = {
|
|
45
45
|
stripe: this.getStripeTemplate(),
|
|
@@ -50,6 +50,56 @@ class SaaSMonetizationBootstrap {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
resolveProjectPath(relativePath) {
|
|
54
|
+
const normalizedRoot = this.projectRoot.endsWith(path.sep)
|
|
55
|
+
? this.projectRoot
|
|
56
|
+
: `${this.projectRoot}${path.sep}`
|
|
57
|
+
const resolvedPath = path.resolve(this.projectRoot, relativePath)
|
|
58
|
+
|
|
59
|
+
if (
|
|
60
|
+
resolvedPath !== this.projectRoot &&
|
|
61
|
+
!resolvedPath.startsWith(normalizedRoot)
|
|
62
|
+
) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`Refusing to access path outside project root: ${relativePath}`
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return resolvedPath
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
ensureDir(relativePath) {
|
|
72
|
+
const target = this.resolveProjectPath(relativePath)
|
|
73
|
+
// Path is constrained to the project root before touching the filesystem
|
|
74
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
75
|
+
if (!fs.existsSync(target)) {
|
|
76
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
77
|
+
fs.mkdirSync(target, { recursive: true })
|
|
78
|
+
}
|
|
79
|
+
return target
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
writeProjectFile(relativePath, content) {
|
|
83
|
+
const target = this.resolveProjectPath(relativePath)
|
|
84
|
+
// Path is constrained to the project root before touching the filesystem
|
|
85
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
86
|
+
fs.writeFileSync(target, content)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
readProjectFile(relativePath) {
|
|
90
|
+
const target = this.resolveProjectPath(relativePath)
|
|
91
|
+
// Path is constrained to the project root before touching the filesystem
|
|
92
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
93
|
+
return fs.readFileSync(target, 'utf8')
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
projectFileExists(relativePath) {
|
|
97
|
+
const target = this.resolveProjectPath(relativePath)
|
|
98
|
+
// Path is constrained to the project root before touching the filesystem
|
|
99
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
100
|
+
return fs.existsSync(target)
|
|
101
|
+
}
|
|
102
|
+
|
|
53
103
|
async run() {
|
|
54
104
|
console.log('🚀 Create SaaS Monetization')
|
|
55
105
|
console.log('═══════════════════════════════════')
|
|
@@ -159,10 +209,7 @@ class SaaSMonetizationBootstrap {
|
|
|
159
209
|
const dirs = ['lib/monetization', 'legal', 'marketing', 'billing']
|
|
160
210
|
|
|
161
211
|
dirs.forEach(dir => {
|
|
162
|
-
|
|
163
|
-
if (!fs.existsSync(fullPath)) {
|
|
164
|
-
fs.mkdirSync(fullPath, { recursive: true })
|
|
165
|
-
}
|
|
212
|
+
this.ensureDir(dir)
|
|
166
213
|
})
|
|
167
214
|
}
|
|
168
215
|
|
|
@@ -172,8 +219,8 @@ class SaaSMonetizationBootstrap {
|
|
|
172
219
|
.replace(/{{PRO_PRICE}}/g, this.config.proPrice)
|
|
173
220
|
.replace(/{{ENTERPRISE_PRICE}}/g, this.config.enterprisePrice)
|
|
174
221
|
|
|
175
|
-
|
|
176
|
-
path.join(
|
|
222
|
+
this.writeProjectFile(
|
|
223
|
+
path.join('lib/monetization', 'stripe-integration.js'),
|
|
177
224
|
stripeCode
|
|
178
225
|
)
|
|
179
226
|
}
|
|
@@ -188,8 +235,8 @@ class SaaSMonetizationBootstrap {
|
|
|
188
235
|
.replace(/{{FOUNDER_PRO_PRICE}}/g, this.config.founderProPrice)
|
|
189
236
|
.replace(/{{DOMAIN}}/g, this.config.domain)
|
|
190
237
|
|
|
191
|
-
|
|
192
|
-
path.join(
|
|
238
|
+
this.writeProjectFile(
|
|
239
|
+
path.join('lib/monetization', 'licensing.js'),
|
|
193
240
|
licensingCode
|
|
194
241
|
)
|
|
195
242
|
}
|
|
@@ -204,7 +251,7 @@ class SaaSMonetizationBootstrap {
|
|
|
204
251
|
.replace(/{{DESCRIPTION}}/g, this.config.description)
|
|
205
252
|
.replace(/{{DATE}}/g, new Date().toISOString().split('T')[0])
|
|
206
253
|
|
|
207
|
-
|
|
254
|
+
this.writeProjectFile(path.join('legal', filename), content)
|
|
208
255
|
}
|
|
209
256
|
}
|
|
210
257
|
|
|
@@ -233,10 +280,7 @@ class SaaSMonetizationBootstrap {
|
|
|
233
280
|
)
|
|
234
281
|
.replace(/{{SUPPORT_EMAIL}}/g, this.config.supportEmail)
|
|
235
282
|
|
|
236
|
-
|
|
237
|
-
path.join(this.projectRoot, 'marketing', filename),
|
|
238
|
-
content
|
|
239
|
-
)
|
|
283
|
+
this.writeProjectFile(path.join('marketing', filename), content)
|
|
240
284
|
}
|
|
241
285
|
}
|
|
242
286
|
|
|
@@ -252,17 +296,14 @@ class SaaSMonetizationBootstrap {
|
|
|
252
296
|
)
|
|
253
297
|
.replace(/{{PREMIUM_FEATURES}}/g, this.config.premiumFeatures)
|
|
254
298
|
|
|
255
|
-
|
|
256
|
-
path.join(this.projectRoot, 'billing/dashboard.html'),
|
|
257
|
-
billingCode
|
|
258
|
-
)
|
|
299
|
+
this.writeProjectFile(path.join('billing', 'dashboard.html'), billingCode)
|
|
259
300
|
}
|
|
260
301
|
|
|
261
302
|
async updatePackageJson() {
|
|
262
|
-
const packagePath =
|
|
303
|
+
const packagePath = 'package.json'
|
|
263
304
|
|
|
264
|
-
if (
|
|
265
|
-
const pkg = JSON.parse(
|
|
305
|
+
if (this.projectFileExists(packagePath)) {
|
|
306
|
+
const pkg = JSON.parse(this.readProjectFile(packagePath))
|
|
266
307
|
|
|
267
308
|
// Add monetization scripts
|
|
268
309
|
pkg.scripts = pkg.scripts || {}
|
|
@@ -276,7 +317,7 @@ class SaaSMonetizationBootstrap {
|
|
|
276
317
|
pkg.dependencies.stripe = '^14.15.0'
|
|
277
318
|
pkg.dependencies.crypto = '^1.0.1'
|
|
278
319
|
|
|
279
|
-
|
|
320
|
+
this.writeProjectFile(packagePath, JSON.stringify(pkg, null, 2))
|
|
280
321
|
}
|
|
281
322
|
}
|
|
282
323
|
|
|
@@ -307,7 +348,7 @@ COMPANY_NAME=${this.config.companyName}
|
|
|
307
348
|
# STRIPE_PUBLISHABLE_KEY=pk_live_your_live_key_here
|
|
308
349
|
`
|
|
309
350
|
|
|
310
|
-
|
|
351
|
+
this.writeProjectFile('.env.template', envTemplate)
|
|
311
352
|
}
|
|
312
353
|
|
|
313
354
|
async generateDeploymentGuide() {
|
|
@@ -483,10 +524,7 @@ For implementation questions:
|
|
|
483
524
|
**Revenue Potential**: $1,500-5,000/month recurring
|
|
484
525
|
`
|
|
485
526
|
|
|
486
|
-
|
|
487
|
-
path.join(this.projectRoot, 'MONETIZATION_GUIDE.md'),
|
|
488
|
-
guide
|
|
489
|
-
)
|
|
527
|
+
this.writeProjectFile('MONETIZATION_GUIDE.md', guide)
|
|
490
528
|
}
|
|
491
529
|
|
|
492
530
|
// Template methods (condensed versions of our implementations)
|
package/docs/ARCHITECTURE.md
CHANGED
package/docs/DEPLOYMENT.md
CHANGED
|
@@ -59,5 +59,4 @@ npm deprecate create-qa-architect@VERSION "Critical bug, use VERSION instead"
|
|
|
59
59
|
## npm Registry
|
|
60
60
|
|
|
61
61
|
- Package: https://www.npmjs.com/package/create-qa-architect
|
|
62
|
-
- Documentation: https://github.com/vibebuildlab/
|
|
63
|
-
|
|
62
|
+
- Documentation: https://github.com/vibebuildlab/qa-architect
|