create-qa-architect 5.12.1 → 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/.github/dependabot.yml +10 -30
- package/.github/workflows/claude-md-validation.yml +5 -7
- package/.github/workflows/dependabot-auto-merge.yml +1 -0
- package/.github/workflows/quality.yml +26 -12
- package/.github/workflows/release.yml +2 -1
- package/.github/workflows/stale-prs.yml +42 -0
- package/.github/workflows/weekly-gitleaks-verification.yml +6 -4
- package/LICENSE +5 -5
- package/README.md +22 -21
- package/config/defaults.js +2 -3
- package/config/quality-config.schema.json +1 -1
- package/docs/CI-COST-ANALYSIS.md +8 -8
- package/docs/DEPLOYMENT.md +1 -1
- package/docs/DEVELOPMENT-WORKFLOW.md +2 -2
- package/docs/TURBOREPO-SUPPORT.md +3 -3
- package/docs/dev_guide/CONVENTIONS.md +132 -0
- package/eslint.config.cjs +25 -0
- package/lib/blob-storage.js +57 -0
- package/lib/commands/analyze-ci.js +267 -27
- package/lib/commands/deps.js +5 -5
- package/lib/commands/license-commands.js +2 -2
- package/lib/commands/maturity-check.js +20 -2
- package/lib/dependency-monitoring-basic.js +4 -4
- package/lib/dependency-monitoring-premium.js +5 -5
- package/lib/license-validator.js +2 -2
- package/lib/licensing.js +7 -9
- package/lib/smart-strategy-generator.js +1 -1
- package/lib/validation/documentation.js +2 -0
- package/lib/workflow-config.js +176 -73
- package/package.json +53 -23
- package/scripts/deploy-consumers.sh +369 -0
- package/scripts/pattern-check.sh +607 -0
- package/scripts/run-semgrep.sh +244 -0
- package/scripts/smart-test-strategy.sh +1 -1
- package/setup.js +119 -71
- package/templates/CLAUDE_WORKFLOW_POLICY.md +3 -3
- package/templates/scripts/smart-test-strategy.sh +1 -1
- package/.github/workflows/auto-release.yml +0 -39
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-qa-architect",
|
|
3
|
-
"version": "5.
|
|
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",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"validate:comprehensive": "node setup.js --comprehensive --no-markdownlint",
|
|
21
21
|
"validate:all": "npm run validate:comprehensive && npm run security:audit",
|
|
22
22
|
"validate:pre-push": "npm run test:patterns --if-present && npm run lint && npm run format:check && npm run test:commands --if-present && npm test --if-present",
|
|
23
|
-
"test": "export QAA_DEVELOPER=true && node tests/result-types.test.js && node tests/setup.test.js && node tests/integration.test.js && node tests/error-paths.test.js && node tests/error-messages.test.js && node tests/cache-manager.test.js && node tests/parallel-validation.test.js && node tests/python-integration.test.js && node tests/interactive.test.js && node tests/monorepo.test.js && node tests/template-loader.test.js && node tests/critical-fixes.test.js && node tests/interactive-routing-fix.test.js && node tests/telemetry.test.js && node tests/error-reporter.test.js && node tests/premium-dependency-monitoring.test.js && node tests/multi-language-dependency-monitoring.test.js && node tests/cli-deps-integration.test.js && node tests/deps-edge-cases.test.js && node tests/real-world-packages.test.js && node tests/validation-factory.test.js && node tests/setup-error-coverage.test.js && node tests/python-detection-sensitivity.test.js && node tests/python-parser-fixes.test.js && node tests/licensing.test.js && node tests/security-licensing.test.js && node tests/real-purchase-flow.test.js && node tests/base-validator.test.js && node tests/dependency-monitoring-basic.test.js && node tests/workflow-validation.test.js && node tests/workflow-tiers.test.js && node tests/analyze-ci.test.js && node tests/analyze-ci-integration.test.js && node tests/setup-critical-paths.test.js && node tests/project-maturity.test.js && node tests/project-maturity-cli.test.js && node tests/package-manager-detection.test.js && node tests/check-docs.test.js && node tests/validate-command-patterns.test.js && node tests/gitleaks-binary-resolution.test.js && node tests/gitleaks-production-checksums.test.js && node tests/gitleaks-checksum-verification.test.js && node tests/gitleaks-real-binary-test.js && node tests/tier-enforcement.test.js && node tests/lazy-loader.test.js && node tests/template-content-validation.test.js && node tests/ci-environment.test.js && node tests/turborepo-detection.test.js",
|
|
23
|
+
"test": "export QAA_DEVELOPER=true && node tests/result-types.test.js && node tests/setup.test.js && node tests/integration.test.js && node tests/error-paths.test.js && node tests/error-messages.test.js && node tests/cache-manager.test.js && node tests/parallel-validation.test.js && node tests/python-integration.test.js && node tests/interactive.test.js && node tests/monorepo.test.js && node tests/template-loader.test.js && node tests/critical-fixes.test.js && node tests/interactive-routing-fix.test.js && node tests/telemetry.test.js && node tests/error-reporter.test.js && node tests/premium-dependency-monitoring.test.js && node tests/multi-language-dependency-monitoring.test.js && node tests/cli-deps-integration.test.js && node tests/deps-edge-cases.test.js && node tests/real-world-packages.test.js && node tests/validation-factory.test.js && node tests/setup-error-coverage.test.js && node tests/python-detection-sensitivity.test.js && node tests/python-parser-fixes.test.js && node tests/licensing.test.js && node tests/security-licensing.test.js && node tests/real-purchase-flow.test.js && node tests/base-validator.test.js && node tests/dependency-monitoring-basic.test.js && node tests/workflow-validation.test.js && node tests/workflow-tiers.test.js && node tests/analyze-ci.test.js && node tests/analyze-ci-integration.test.js && node tests/setup-critical-paths.test.js && node tests/project-maturity.test.js && node tests/project-maturity-cli.test.js && node tests/package-manager-detection.test.js && node tests/check-docs.test.js && node tests/validate-command-patterns.test.js && node tests/gitleaks-binary-resolution.test.js && node tests/gitleaks-production-checksums.test.js && node tests/gitleaks-checksum-verification.test.js && node tests/gitleaks-real-binary-test.js && node tests/tier-enforcement.test.js && node tests/lazy-loader.test.js && node tests/template-content-validation.test.js && node tests/ci-environment.test.js && node tests/turborepo-detection.test.js && node tests/consumer-workflow-integration.test.js",
|
|
24
24
|
"test:unit": "export QAA_DEVELOPER=true && node tests/result-types.test.js && node tests/setup.test.js && node tests/error-paths.test.js && node tests/error-messages.test.js && node tests/cache-manager.test.js && node tests/template-loader.test.js && node tests/telemetry.test.js && node tests/error-reporter.test.js && node tests/validation-factory.test.js && node tests/setup-error-coverage.test.js && node tests/licensing.test.js && node tests/security-licensing.test.js && node tests/base-validator.test.js && node tests/dependency-monitoring-basic.test.js && node tests/workflow-validation.test.js && node tests/workflow-tiers.test.js && node tests/analyze-ci.test.js && node tests/setup-critical-paths.test.js && node tests/project-maturity.test.js && node tests/package-manager-detection.test.js && node tests/check-docs.test.js && node tests/validate-command-patterns.test.js && node tests/gitleaks-binary-resolution.test.js && node tests/gitleaks-production-checksums.test.js && node tests/gitleaks-checksum-verification.test.js && node tests/lazy-loader.test.js && node tests/template-content-validation.test.js && node tests/ci-environment.test.js && node tests/turborepo-detection.test.js",
|
|
25
25
|
"test:fast": "npm run test:unit",
|
|
26
26
|
"test:medium": "npm run test:fast && npm run test:patterns && npm run test:commands",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"test:comprehensive": "npm run test:patterns && npm test && npm run test:commands && npm run test:e2e && npm run security:audit",
|
|
29
29
|
"test:real-binary": "RUN_REAL_BINARY_TEST=1 node tests/gitleaks-real-binary-test.js",
|
|
30
30
|
"test:commands": "export QAA_DEVELOPER=true && node tests/command-execution.test.js",
|
|
31
|
-
"test:patterns": "
|
|
31
|
+
"test:patterns": "bash scripts/pattern-check.sh --all",
|
|
32
32
|
"test:coverage": "c8 --reporter=html --reporter=text --reporter=lcov npm test",
|
|
33
33
|
"test:e2e": "export QAA_DEVELOPER=true && bash scripts/test-e2e-package.sh",
|
|
34
34
|
"test:all": "npm run test:patterns && npm test && npm run test:commands && npm run test:e2e",
|
|
@@ -52,7 +52,13 @@
|
|
|
52
52
|
"size:why": "size-limit --why",
|
|
53
53
|
"test:a11y": "vitest run tests/accessibility.test.js",
|
|
54
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
|
+
"test:changed": "vitest run --changed HEAD~1 --passWithNoTests",
|
|
56
|
+
"dead-code": "knip --no-exit-code || echo \"Dead code found (non-blocking)\"",
|
|
57
|
+
"dead-code:strict": "knip",
|
|
58
|
+
"pattern-check": "bash scripts/pattern-check.sh",
|
|
59
|
+
"security:scan": "bash scripts/run-semgrep.sh",
|
|
60
|
+
"security:scan:ci": "bash scripts/run-semgrep.sh --ci",
|
|
61
|
+
"license:check": "license-checker --onlyAllow \"MIT;ISC;BSD-2-Clause;BSD-3-Clause;Apache-2.0;0BSD;BlueOak-1.0.0;CC0-1.0;CC-BY-3.0;CC-BY-4.0;Unlicense;Python-2.0;MPL-2.0\" --excludePrivatePackages"
|
|
56
62
|
},
|
|
57
63
|
"keywords": [
|
|
58
64
|
"qa-architect",
|
|
@@ -74,7 +80,7 @@
|
|
|
74
80
|
"dependency-monitoring",
|
|
75
81
|
"security-audit"
|
|
76
82
|
],
|
|
77
|
-
"author": "
|
|
83
|
+
"author": "BuildProven",
|
|
78
84
|
"license": "SEE LICENSE IN LICENSE",
|
|
79
85
|
"files": [
|
|
80
86
|
"setup.js",
|
|
@@ -98,12 +104,12 @@
|
|
|
98
104
|
],
|
|
99
105
|
"repository": {
|
|
100
106
|
"type": "git",
|
|
101
|
-
"url": "git+https://github.com/
|
|
107
|
+
"url": "git+https://github.com/buildproven/qa-architect.git"
|
|
102
108
|
},
|
|
103
109
|
"bugs": {
|
|
104
|
-
"url": "https://github.com/
|
|
110
|
+
"url": "https://github.com/buildproven/qa-architect/issues"
|
|
105
111
|
},
|
|
106
|
-
"homepage": "https://
|
|
112
|
+
"homepage": "https://buildproven.ai/qa-architect",
|
|
107
113
|
"engines": {
|
|
108
114
|
"node": ">=20"
|
|
109
115
|
},
|
|
@@ -118,28 +124,33 @@
|
|
|
118
124
|
}
|
|
119
125
|
},
|
|
120
126
|
"devDependencies": {
|
|
127
|
+
"@commitlint/cli": "^19.0.0",
|
|
128
|
+
"@commitlint/config-conventional": "^19.0.0",
|
|
129
|
+
"@lhci/cli": "^0.15.1",
|
|
130
|
+
"@size-limit/file": "^11.0.0",
|
|
121
131
|
"@types/node": "^25",
|
|
132
|
+
"@typescript-eslint/eslint-plugin": "^8.9.0",
|
|
133
|
+
"@typescript-eslint/parser": "^8.9.0",
|
|
134
|
+
"@vercel/blob": "^2.2.0",
|
|
135
|
+
"@vitest/coverage-v8": "^2.1.8",
|
|
122
136
|
"actionlint": "^2.0.6",
|
|
123
|
-
"
|
|
137
|
+
"axe-core": "^4.10.0",
|
|
124
138
|
"c8": "^10.1.2",
|
|
139
|
+
"commitlint": "^20.4.1",
|
|
125
140
|
"eslint": "^9.39.2",
|
|
141
|
+
"eslint-plugin-n": "^17.24.0",
|
|
126
142
|
"eslint-plugin-security": "^3.0.1",
|
|
127
143
|
"globals": "^15.9.0",
|
|
128
144
|
"husky": "^9.1.4",
|
|
145
|
+
"knip": "^6.0.1",
|
|
146
|
+
"license-checker": "^25.0.1",
|
|
129
147
|
"lint-staged": "^15.2.10",
|
|
130
148
|
"prettier": "^3.8.0",
|
|
149
|
+
"size-limit": "^11.0.0",
|
|
131
150
|
"stylelint": "^16.26.1",
|
|
132
151
|
"stylelint-config-standard": "^37.0.0",
|
|
133
|
-
"
|
|
134
|
-
"vitest": "^2.1.8"
|
|
135
|
-
"@vitest/coverage-v8": "^2.1.8",
|
|
136
|
-
"@typescript-eslint/eslint-plugin": "^8.9.0",
|
|
137
|
-
"@typescript-eslint/parser": "^8.9.0",
|
|
138
|
-
"size-limit": "^11.0.0",
|
|
139
|
-
"@size-limit/file": "^11.0.0",
|
|
140
|
-
"@commitlint/cli": "^19.0.0",
|
|
141
|
-
"@commitlint/config-conventional": "^19.0.0",
|
|
142
|
-
"axe-core": "^4.10.0"
|
|
152
|
+
"typescript": "^5",
|
|
153
|
+
"vitest": "^2.1.8"
|
|
143
154
|
},
|
|
144
155
|
"volta": {
|
|
145
156
|
"node": "20.11.1",
|
|
@@ -185,11 +196,20 @@
|
|
|
185
196
|
],
|
|
186
197
|
"**/*.{ts,tsx}": [
|
|
187
198
|
"eslint --fix",
|
|
188
|
-
"prettier --write"
|
|
199
|
+
"prettier --write",
|
|
200
|
+
"tsc --noEmit --skipLibCheck"
|
|
189
201
|
],
|
|
190
202
|
"tests/**/*.{ts,tsx,js,jsx}": [
|
|
191
203
|
"eslint --fix",
|
|
192
204
|
"prettier --write"
|
|
205
|
+
],
|
|
206
|
+
"**/*.{js,jsx,ts,tsx,mjs,cjs,html}": [
|
|
207
|
+
"eslint --fix",
|
|
208
|
+
"prettier --write"
|
|
209
|
+
],
|
|
210
|
+
"**/*.{js,jsx,mjs,cjs,html}": [
|
|
211
|
+
"eslint --fix",
|
|
212
|
+
"prettier --write"
|
|
193
213
|
]
|
|
194
214
|
},
|
|
195
215
|
"dependencies": {
|
|
@@ -197,8 +217,18 @@
|
|
|
197
217
|
"ajv": "^8.17.1",
|
|
198
218
|
"ajv-formats": "^3.0.1",
|
|
199
219
|
"js-yaml": "^4.1.0",
|
|
200
|
-
"markdownlint-cli2": "^0.
|
|
220
|
+
"markdownlint-cli2": "^0.21.0",
|
|
201
221
|
"ora": "^8.1.1",
|
|
202
222
|
"tar": "^7.5.7"
|
|
203
|
-
}
|
|
223
|
+
},
|
|
224
|
+
"size-limit": [
|
|
225
|
+
{
|
|
226
|
+
"path": "dist/**/*.js",
|
|
227
|
+
"limit": "250 kB"
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
"path": "dist/**/*.css",
|
|
231
|
+
"limit": "50 kB"
|
|
232
|
+
}
|
|
233
|
+
]
|
|
204
234
|
}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# deploy-consumers.sh — Auto-discover and update ALL consumer repos with staged rollout
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# ./scripts/deploy-consumers.sh # Dry run (validate only)
|
|
8
|
+
# ./scripts/deploy-consumers.sh --canary-only --push # Deploy ONLY to canary, wait for CI
|
|
9
|
+
# ./scripts/deploy-consumers.sh --push # Full rollout: canary first, then rest
|
|
10
|
+
# ./scripts/deploy-consumers.sh --skip-canary --push # Emergency: bypass canary (use with caution)
|
|
11
|
+
#
|
|
12
|
+
# Staged Rollout Process:
|
|
13
|
+
# 1. Deploy to canary repo (buildproven) first
|
|
14
|
+
# 2. Wait for CI to complete on canary (up to 10 minutes)
|
|
15
|
+
# 3. If CI passes, deploy to remaining repos
|
|
16
|
+
# 4. If CI fails, abort rollout (prevents cascading failures)
|
|
17
|
+
#
|
|
18
|
+
# Auto-discovers repos by scanning ~/Projects for .github/workflows/quality.yml
|
|
19
|
+
# files that contain the WORKFLOW_MODE marker from qa-architect.
|
|
20
|
+
|
|
21
|
+
PROJECTS_DIR="$HOME/Projects"
|
|
22
|
+
QA_ARCHITECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
23
|
+
CANARY_REPO="buildproven"
|
|
24
|
+
PUSH=false
|
|
25
|
+
TIER="minimal"
|
|
26
|
+
VERBOSE=false
|
|
27
|
+
CANARY_ONLY=false
|
|
28
|
+
SKIP_CANARY=false
|
|
29
|
+
CI_TIMEOUT=600 # 10 minutes in seconds
|
|
30
|
+
CI_POLL_INTERVAL=15 # Check every 15 seconds
|
|
31
|
+
|
|
32
|
+
# Parse args
|
|
33
|
+
while [[ $# -gt 0 ]]; do
|
|
34
|
+
case "$1" in
|
|
35
|
+
--push) PUSH=true; shift ;;
|
|
36
|
+
--tier) TIER="$2"; shift 2 ;;
|
|
37
|
+
--verbose) VERBOSE=true; shift ;;
|
|
38
|
+
--canary-only) CANARY_ONLY=true; shift ;;
|
|
39
|
+
--skip-canary) SKIP_CANARY=true; shift ;;
|
|
40
|
+
--help|-h)
|
|
41
|
+
cat <<EOF
|
|
42
|
+
Usage: deploy-consumers.sh [OPTIONS]
|
|
43
|
+
|
|
44
|
+
OPTIONS:
|
|
45
|
+
--push Commit and push changes (default: dry run)
|
|
46
|
+
--canary-only Deploy ONLY to canary repo, wait for CI green
|
|
47
|
+
--skip-canary Skip canary validation (emergency use only)
|
|
48
|
+
--tier <tier> Workflow tier to apply (default: minimal)
|
|
49
|
+
--verbose Show detailed output
|
|
50
|
+
--help, -h Show this help
|
|
51
|
+
|
|
52
|
+
STAGED ROLLOUT:
|
|
53
|
+
Default behavior (--push):
|
|
54
|
+
1. Deploy to canary (buildproven)
|
|
55
|
+
2. Wait for CI to pass (up to 10 minutes)
|
|
56
|
+
3. Deploy to remaining repos
|
|
57
|
+
|
|
58
|
+
Canary-only mode (--canary-only --push):
|
|
59
|
+
Deploy to canary only, validate CI passes
|
|
60
|
+
|
|
61
|
+
Skip canary (--skip-canary --push):
|
|
62
|
+
Deploy to all repos simultaneously (emergency only)
|
|
63
|
+
|
|
64
|
+
EXAMPLES:
|
|
65
|
+
# Dry run (no changes)
|
|
66
|
+
./scripts/deploy-consumers.sh
|
|
67
|
+
|
|
68
|
+
# Canary validation
|
|
69
|
+
./scripts/deploy-consumers.sh --canary-only --push
|
|
70
|
+
|
|
71
|
+
# Full rollout (staged)
|
|
72
|
+
./scripts/deploy-consumers.sh --push
|
|
73
|
+
|
|
74
|
+
# Emergency bypass (use with caution)
|
|
75
|
+
./scripts/deploy-consumers.sh --skip-canary --push
|
|
76
|
+
EOF
|
|
77
|
+
exit 0
|
|
78
|
+
;;
|
|
79
|
+
*) echo "Unknown option: $1"; exit 1 ;;
|
|
80
|
+
esac
|
|
81
|
+
done
|
|
82
|
+
|
|
83
|
+
echo "=== QA Architect Consumer Deployment ==="
|
|
84
|
+
echo "Mode: $([ "$PUSH" = true ] && echo "PUSH (will commit + push)" || echo "DRY RUN (validate only)")"
|
|
85
|
+
echo "Tier: $TIER"
|
|
86
|
+
echo "Canary repo: $CANARY_REPO"
|
|
87
|
+
if [ "$CANARY_ONLY" = true ]; then
|
|
88
|
+
echo "⚠️ CANARY-ONLY mode: will deploy to $CANARY_REPO only"
|
|
89
|
+
elif [ "$SKIP_CANARY" = true ]; then
|
|
90
|
+
echo "⚠️ SKIP-CANARY mode: bypassing canary validation (emergency use)"
|
|
91
|
+
else
|
|
92
|
+
echo "Staged rollout: canary first, then remaining repos"
|
|
93
|
+
fi
|
|
94
|
+
echo ""
|
|
95
|
+
|
|
96
|
+
# Discover consumer repos: any repo with quality.yml containing WORKFLOW_MODE marker
|
|
97
|
+
# Excludes qa-architect itself
|
|
98
|
+
CONSUMERS=()
|
|
99
|
+
CANARY_DIR=""
|
|
100
|
+
for workflow in "$PROJECTS_DIR"/*/".github/workflows/quality.yml"; do
|
|
101
|
+
[ -f "$workflow" ] || continue
|
|
102
|
+
repo_dir="$(dirname "$(dirname "$(dirname "$workflow")")")"
|
|
103
|
+
repo_name="$(basename "$repo_dir")"
|
|
104
|
+
|
|
105
|
+
# Skip qa-architect itself (compare basenames to avoid case/symlink issues)
|
|
106
|
+
[ "$(basename "$repo_dir")" = "$(basename "$QA_ARCHITECT_DIR")" ] && continue
|
|
107
|
+
|
|
108
|
+
# Check for WORKFLOW_MODE marker (proves it was generated by qa-architect)
|
|
109
|
+
if grep -q "WORKFLOW_MODE:" "$workflow" 2>/dev/null; then
|
|
110
|
+
if [ "$repo_name" = "$CANARY_REPO" ]; then
|
|
111
|
+
CANARY_DIR="$repo_dir"
|
|
112
|
+
else
|
|
113
|
+
CONSUMERS+=("$repo_dir")
|
|
114
|
+
fi
|
|
115
|
+
fi
|
|
116
|
+
done
|
|
117
|
+
|
|
118
|
+
# Ensure canary was found
|
|
119
|
+
if [ -z "$CANARY_DIR" ]; then
|
|
120
|
+
echo "❌ ERROR: Canary repo '$CANARY_REPO' not found or doesn't have WORKFLOW_MODE marker"
|
|
121
|
+
exit 1
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
# In canary-only mode, only process canary
|
|
125
|
+
if [ "$CANARY_ONLY" = true ]; then
|
|
126
|
+
echo "Canary-only mode: processing $CANARY_REPO only"
|
|
127
|
+
CONSUMERS=()
|
|
128
|
+
else
|
|
129
|
+
echo "Discovered canary repo: $CANARY_REPO"
|
|
130
|
+
echo "Discovered ${#CONSUMERS[@]} additional consumer repos:"
|
|
131
|
+
for dir in "${CONSUMERS[@]}"; do
|
|
132
|
+
echo " - $(basename "$dir")"
|
|
133
|
+
done
|
|
134
|
+
fi
|
|
135
|
+
echo ""
|
|
136
|
+
|
|
137
|
+
# Function to wait for CI to complete on a repo
|
|
138
|
+
# Args: $1 = repo_dir, $2 = repo_name
|
|
139
|
+
wait_for_ci() {
|
|
140
|
+
local repo_dir="$1"
|
|
141
|
+
local repo_name="$2"
|
|
142
|
+
|
|
143
|
+
echo " Waiting for CI to complete on $repo_name..."
|
|
144
|
+
|
|
145
|
+
# Check if gh CLI is installed
|
|
146
|
+
if ! command -v gh &>/dev/null; then
|
|
147
|
+
echo " ⚠️ WARNING: gh CLI not installed, cannot check CI status"
|
|
148
|
+
echo " Install with: brew install gh"
|
|
149
|
+
echo " Proceeding without CI validation..."
|
|
150
|
+
return 0
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
# Get the repo owner/name for gh CLI
|
|
154
|
+
local remote_url
|
|
155
|
+
remote_url=$(cd "$repo_dir" && git remote get-url origin 2>/dev/null || echo "")
|
|
156
|
+
if [ -z "$remote_url" ]; then
|
|
157
|
+
echo " ⚠️ WARNING: No git remote found, cannot check CI status"
|
|
158
|
+
return 0
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
# Extract owner/repo from URL (handles both HTTPS and SSH)
|
|
162
|
+
local repo_slug
|
|
163
|
+
repo_slug=$(echo "$remote_url" | sed -E 's|^.*[:/]([^/]+/[^/]+)(\.git)?$|\1|')
|
|
164
|
+
|
|
165
|
+
# Get the current branch
|
|
166
|
+
local branch
|
|
167
|
+
branch=$(cd "$repo_dir" && git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "main")
|
|
168
|
+
|
|
169
|
+
echo " Checking CI status for $repo_slug on branch $branch..."
|
|
170
|
+
|
|
171
|
+
local elapsed=0
|
|
172
|
+
local status=""
|
|
173
|
+
|
|
174
|
+
while [ $elapsed -lt $CI_TIMEOUT ]; do
|
|
175
|
+
# Get latest workflow run status for quality.yml on this branch
|
|
176
|
+
status=$(gh run list --repo "$repo_slug" --workflow quality.yml --branch "$branch" --limit 1 --json status,conclusion --jq '.[0].status + ":" + (.[0].conclusion // "")' 2>/dev/null || echo "")
|
|
177
|
+
|
|
178
|
+
if [ -z "$status" ]; then
|
|
179
|
+
echo " ⚠️ No CI runs found yet (elapsed: ${elapsed}s)"
|
|
180
|
+
elif [[ "$status" == "completed:success" ]]; then
|
|
181
|
+
echo " ✅ CI PASSED on $repo_name (elapsed: ${elapsed}s)"
|
|
182
|
+
return 0
|
|
183
|
+
elif [[ "$status" == "completed:failure" ]] || [[ "$status" == "completed:cancelled" ]]; then
|
|
184
|
+
echo " ❌ CI FAILED on $repo_name (status: $status)"
|
|
185
|
+
return 1
|
|
186
|
+
else
|
|
187
|
+
echo " ⏳ CI in progress on $repo_name (status: $status, elapsed: ${elapsed}s)"
|
|
188
|
+
fi
|
|
189
|
+
|
|
190
|
+
sleep $CI_POLL_INTERVAL
|
|
191
|
+
elapsed=$((elapsed + CI_POLL_INTERVAL))
|
|
192
|
+
done
|
|
193
|
+
|
|
194
|
+
echo " ⏰ TIMEOUT: CI did not complete within ${CI_TIMEOUT}s"
|
|
195
|
+
return 1
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
# Function to deploy to a single repo
|
|
199
|
+
# Args: $1 = repo_dir, $2 = is_canary (true/false)
|
|
200
|
+
deploy_to_repo() {
|
|
201
|
+
local repo_dir="$1"
|
|
202
|
+
local is_canary="${2:-false}"
|
|
203
|
+
local repo_name="$(basename "$repo_dir")"
|
|
204
|
+
|
|
205
|
+
echo "--- $repo_name $([ "$is_canary" = true ] && echo "(CANARY)" || echo "") ---"
|
|
206
|
+
|
|
207
|
+
# Check if repo has package.json (needed for npx)
|
|
208
|
+
if [ ! -f "$repo_dir/package.json" ]; then
|
|
209
|
+
echo " SKIP: No package.json"
|
|
210
|
+
return 2 # Return code 2 = skipped
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
# Detect the existing tier from the workflow
|
|
214
|
+
local existing_tier="$TIER"
|
|
215
|
+
if grep -q "WORKFLOW_MODE: standard" "$repo_dir/.github/workflows/quality.yml" 2>/dev/null; then
|
|
216
|
+
existing_tier="standard"
|
|
217
|
+
elif grep -q "WORKFLOW_MODE: comprehensive" "$repo_dir/.github/workflows/quality.yml" 2>/dev/null; then
|
|
218
|
+
existing_tier="comprehensive"
|
|
219
|
+
elif grep -q "WORKFLOW_MODE: minimal" "$repo_dir/.github/workflows/quality.yml" 2>/dev/null; then
|
|
220
|
+
existing_tier="minimal"
|
|
221
|
+
fi
|
|
222
|
+
|
|
223
|
+
# Regenerate workflow (must cd to consumer dir — setup.js uses process.cwd())
|
|
224
|
+
echo " Regenerating workflow (tier: $existing_tier)..."
|
|
225
|
+
if (cd "$repo_dir" && QAA_DEVELOPER=true node "$QA_ARCHITECT_DIR/setup.js" --update "--workflow-${existing_tier}" \
|
|
226
|
+
2>&1 | { [ "$VERBOSE" = true ] && cat || tail -1; }); then
|
|
227
|
+
:
|
|
228
|
+
else
|
|
229
|
+
echo " FAIL: Workflow generation failed"
|
|
230
|
+
return 1
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
local workflow_file="$repo_dir/.github/workflows/quality.yml"
|
|
234
|
+
|
|
235
|
+
# Validate: no node_modules/create-qa-architect references
|
|
236
|
+
if grep -q "node_modules/create-qa-architect" "$workflow_file"; then
|
|
237
|
+
echo " FAIL: Contains node_modules/create-qa-architect references"
|
|
238
|
+
return 1
|
|
239
|
+
fi
|
|
240
|
+
|
|
241
|
+
# Validate: no section markers leaked
|
|
242
|
+
if grep -qE '\{\{(QA_ARCHITECT_ONLY|FULL_DETECTION|FULL_REPORT)_(BEGIN|END)\}\}' "$workflow_file"; then
|
|
243
|
+
echo " FAIL: Contains section markers"
|
|
244
|
+
return 1
|
|
245
|
+
fi
|
|
246
|
+
|
|
247
|
+
# Validate: valid YAML (requires node + js-yaml)
|
|
248
|
+
if ! node -e "require('$QA_ARCHITECT_DIR/node_modules/js-yaml').load(require('fs').readFileSync('$workflow_file','utf8'))" 2>/dev/null; then
|
|
249
|
+
echo " FAIL: Invalid YAML"
|
|
250
|
+
return 1
|
|
251
|
+
fi
|
|
252
|
+
|
|
253
|
+
# Remove stale devDep if present
|
|
254
|
+
if grep -q '"create-qa-architect"' "$repo_dir/package.json" 2>/dev/null; then
|
|
255
|
+
echo " Removing stale create-qa-architect devDependency..."
|
|
256
|
+
if [ "$PUSH" = true ]; then
|
|
257
|
+
(cd "$repo_dir" && npm uninstall create-qa-architect 2>/dev/null || true)
|
|
258
|
+
else
|
|
259
|
+
echo " (dry run - would remove devDep)"
|
|
260
|
+
fi
|
|
261
|
+
fi
|
|
262
|
+
|
|
263
|
+
echo " PASS: Validated"
|
|
264
|
+
|
|
265
|
+
# Commit and push if --push
|
|
266
|
+
if [ "$PUSH" = true ]; then
|
|
267
|
+
(
|
|
268
|
+
cd "$repo_dir"
|
|
269
|
+
if git diff --quiet .github/workflows/quality.yml package.json 2>/dev/null; then
|
|
270
|
+
echo " No changes to commit"
|
|
271
|
+
else
|
|
272
|
+
git add .github/workflows/quality.yml package.json package-lock.json 2>/dev/null || true
|
|
273
|
+
git commit -m "chore: regenerate qa-architect workflow (${existing_tier} tier)
|
|
274
|
+
|
|
275
|
+
Staged rollout: $([ "$is_canary" = true ] && echo "canary deployment" || echo "validated via canary")
|
|
276
|
+
Fixes node_modules/create-qa-architect fallback paths.
|
|
277
|
+
Consumers use npx create-qa-architect@latest instead of devDep." 2>/dev/null || true
|
|
278
|
+
git push 2>/dev/null && echo " Pushed" || echo " Push failed (check remote)"
|
|
279
|
+
fi
|
|
280
|
+
)
|
|
281
|
+
fi
|
|
282
|
+
|
|
283
|
+
echo ""
|
|
284
|
+
return 0
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
# Validation counters
|
|
288
|
+
PASS=0
|
|
289
|
+
FAIL=0
|
|
290
|
+
SKIPPED=0
|
|
291
|
+
|
|
292
|
+
# STAGE 1: Deploy to canary (unless --skip-canary)
|
|
293
|
+
if [ "$SKIP_CANARY" = false ]; then
|
|
294
|
+
echo "=== STAGE 1: Canary Deployment ==="
|
|
295
|
+
echo ""
|
|
296
|
+
|
|
297
|
+
if deploy_to_repo "$CANARY_DIR" true; then
|
|
298
|
+
PASS=$((PASS + 1))
|
|
299
|
+
|
|
300
|
+
# Wait for CI if in push mode
|
|
301
|
+
if [ "$PUSH" = true ]; then
|
|
302
|
+
if ! wait_for_ci "$CANARY_DIR" "$CANARY_REPO"; then
|
|
303
|
+
echo ""
|
|
304
|
+
echo "❌ ROLLOUT ABORTED: Canary CI failed or timed out"
|
|
305
|
+
echo " Fix the issue on $CANARY_REPO before rolling out to other repos"
|
|
306
|
+
exit 1
|
|
307
|
+
fi
|
|
308
|
+
fi
|
|
309
|
+
else
|
|
310
|
+
ret=$?
|
|
311
|
+
if [ $ret -eq 2 ]; then
|
|
312
|
+
SKIPPED=$((SKIPPED + 1))
|
|
313
|
+
else
|
|
314
|
+
FAIL=$((FAIL + 1))
|
|
315
|
+
echo ""
|
|
316
|
+
echo "❌ ROLLOUT ABORTED: Canary deployment failed"
|
|
317
|
+
exit 1
|
|
318
|
+
fi
|
|
319
|
+
fi
|
|
320
|
+
|
|
321
|
+
echo ""
|
|
322
|
+
echo "✅ Canary deployment successful"
|
|
323
|
+
echo ""
|
|
324
|
+
fi
|
|
325
|
+
|
|
326
|
+
# Exit early if canary-only mode
|
|
327
|
+
if [ "$CANARY_ONLY" = true ]; then
|
|
328
|
+
echo "=== Summary (Canary-Only) ==="
|
|
329
|
+
echo " Pass: $PASS"
|
|
330
|
+
echo " Fail: $FAIL"
|
|
331
|
+
echo " Skipped: $SKIPPED"
|
|
332
|
+
echo ""
|
|
333
|
+
echo "✅ Canary deployment complete. Review CI results before full rollout."
|
|
334
|
+
exit 0
|
|
335
|
+
fi
|
|
336
|
+
|
|
337
|
+
# STAGE 2: Deploy to remaining repos
|
|
338
|
+
if [ ${#CONSUMERS[@]} -gt 0 ]; then
|
|
339
|
+
echo "=== STAGE 2: Remaining Repos Deployment ==="
|
|
340
|
+
echo ""
|
|
341
|
+
|
|
342
|
+
for repo_dir in "${CONSUMERS[@]}"; do
|
|
343
|
+
if deploy_to_repo "$repo_dir" false; then
|
|
344
|
+
PASS=$((PASS + 1))
|
|
345
|
+
else
|
|
346
|
+
ret=$?
|
|
347
|
+
if [ $ret -eq 2 ]; then
|
|
348
|
+
SKIPPED=$((SKIPPED + 1))
|
|
349
|
+
else
|
|
350
|
+
FAIL=$((FAIL + 1))
|
|
351
|
+
fi
|
|
352
|
+
fi
|
|
353
|
+
done
|
|
354
|
+
fi
|
|
355
|
+
|
|
356
|
+
echo "=== Summary ==="
|
|
357
|
+
echo " Pass: $PASS"
|
|
358
|
+
echo " Fail: $FAIL"
|
|
359
|
+
echo " Skipped: $SKIPPED"
|
|
360
|
+
echo " Total: $((${#CONSUMERS[@]} + 1))" # +1 for canary
|
|
361
|
+
|
|
362
|
+
if [ "$FAIL" -gt 0 ]; then
|
|
363
|
+
echo ""
|
|
364
|
+
echo "⚠️ Some repos failed validation. Review output above."
|
|
365
|
+
exit 1
|
|
366
|
+
fi
|
|
367
|
+
|
|
368
|
+
echo ""
|
|
369
|
+
echo "✅ Deployment complete!"
|