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
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# Run Semgrep - Defensive Pattern Security Scan
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# Runs Semgrep with defensive pattern rules. Falls back to LLM analysis
|
|
6
|
+
# if Semgrep is not installed.
|
|
7
|
+
#
|
|
8
|
+
# Part of CS-090: Semgrep Integration for Patterns
|
|
9
|
+
#
|
|
10
|
+
# USAGE:
|
|
11
|
+
# ./scripts/run-semgrep.sh # Scan current directory
|
|
12
|
+
# ./scripts/run-semgrep.sh --install # Install semgrep
|
|
13
|
+
# ./scripts/run-semgrep.sh --ci # CI mode (exit codes)
|
|
14
|
+
# ./scripts/run-semgrep.sh src/ # Scan specific directory
|
|
15
|
+
# =============================================================================
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
# Colors
|
|
20
|
+
RED='\033[0;31m'
|
|
21
|
+
GREEN='\033[0;32m'
|
|
22
|
+
YELLOW='\033[1;33m'
|
|
23
|
+
BLUE='\033[0;34m'
|
|
24
|
+
NC='\033[0m'
|
|
25
|
+
|
|
26
|
+
# Configuration
|
|
27
|
+
SEMGREP_CONFIG=".semgrep/defensive-patterns.yaml"
|
|
28
|
+
CI_MODE=false
|
|
29
|
+
INSTALL_MODE=false
|
|
30
|
+
SCAN_DIR="."
|
|
31
|
+
OUTPUT_FORMAT="text"
|
|
32
|
+
OUTPUT_FILE=""
|
|
33
|
+
|
|
34
|
+
# Get script directory
|
|
35
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
36
|
+
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
37
|
+
|
|
38
|
+
# Parse arguments
|
|
39
|
+
while [[ $# -gt 0 ]]; do
|
|
40
|
+
case "$1" in
|
|
41
|
+
--install)
|
|
42
|
+
INSTALL_MODE=true
|
|
43
|
+
shift
|
|
44
|
+
;;
|
|
45
|
+
--ci)
|
|
46
|
+
CI_MODE=true
|
|
47
|
+
OUTPUT_FORMAT="json"
|
|
48
|
+
shift
|
|
49
|
+
;;
|
|
50
|
+
--json)
|
|
51
|
+
OUTPUT_FORMAT="json"
|
|
52
|
+
shift
|
|
53
|
+
;;
|
|
54
|
+
--sarif)
|
|
55
|
+
OUTPUT_FORMAT="sarif"
|
|
56
|
+
shift
|
|
57
|
+
;;
|
|
58
|
+
--output|-o)
|
|
59
|
+
OUTPUT_FILE="$2"
|
|
60
|
+
shift 2
|
|
61
|
+
;;
|
|
62
|
+
--config|-c)
|
|
63
|
+
SEMGREP_CONFIG="$2"
|
|
64
|
+
shift 2
|
|
65
|
+
;;
|
|
66
|
+
--help|-h)
|
|
67
|
+
echo "Usage: run-semgrep.sh [OPTIONS] [DIRECTORY]"
|
|
68
|
+
echo ""
|
|
69
|
+
echo "Options:"
|
|
70
|
+
echo " --install Install semgrep via pip"
|
|
71
|
+
echo " --ci CI mode (json output, proper exit codes)"
|
|
72
|
+
echo " --json Output as JSON"
|
|
73
|
+
echo " --sarif Output as SARIF (for GitHub Security)"
|
|
74
|
+
echo " --output Write output to file"
|
|
75
|
+
echo " --config Custom semgrep config (default: .semgrep/defensive-patterns.yaml)"
|
|
76
|
+
echo " --help Show this help message"
|
|
77
|
+
echo ""
|
|
78
|
+
echo "Examples:"
|
|
79
|
+
echo " run-semgrep.sh # Scan current dir"
|
|
80
|
+
echo " run-semgrep.sh --install # Install semgrep"
|
|
81
|
+
echo " run-semgrep.sh --ci --output report.json"
|
|
82
|
+
exit 0
|
|
83
|
+
;;
|
|
84
|
+
-*)
|
|
85
|
+
echo -e "${RED}Unknown option: $1${NC}"
|
|
86
|
+
exit 1
|
|
87
|
+
;;
|
|
88
|
+
*)
|
|
89
|
+
SCAN_DIR="$1"
|
|
90
|
+
shift
|
|
91
|
+
;;
|
|
92
|
+
esac
|
|
93
|
+
done
|
|
94
|
+
|
|
95
|
+
# Install semgrep if requested
|
|
96
|
+
if [[ "$INSTALL_MODE" == "true" ]]; then
|
|
97
|
+
echo -e "${BLUE}Installing Semgrep...${NC}"
|
|
98
|
+
|
|
99
|
+
if command -v pip3 &> /dev/null; then
|
|
100
|
+
pip3 install semgrep
|
|
101
|
+
elif command -v pip &> /dev/null; then
|
|
102
|
+
pip install semgrep
|
|
103
|
+
elif command -v brew &> /dev/null; then
|
|
104
|
+
brew install semgrep
|
|
105
|
+
else
|
|
106
|
+
echo -e "${RED}Error: No package manager found (pip3, pip, or brew)${NC}"
|
|
107
|
+
echo "Install manually: https://semgrep.dev/docs/getting-started/"
|
|
108
|
+
exit 1
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
echo -e "${GREEN}✓ Semgrep installed${NC}"
|
|
112
|
+
exit 0
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
# Check if semgrep is installed
|
|
116
|
+
check_semgrep() {
|
|
117
|
+
if ! command -v semgrep &> /dev/null; then
|
|
118
|
+
return 1
|
|
119
|
+
fi
|
|
120
|
+
return 0
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# Find the config file
|
|
124
|
+
find_config() {
|
|
125
|
+
# Check local project first
|
|
126
|
+
if [[ -f "$SEMGREP_CONFIG" ]]; then
|
|
127
|
+
echo "$SEMGREP_CONFIG"
|
|
128
|
+
return 0
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
# Check claude-setup
|
|
132
|
+
local setup_config="$PROJECT_ROOT/.semgrep/defensive-patterns.yaml"
|
|
133
|
+
if [[ -f "$setup_config" ]]; then
|
|
134
|
+
echo "$setup_config"
|
|
135
|
+
return 0
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
# Check home directory
|
|
139
|
+
local home_config="$HOME/.claude/.semgrep/defensive-patterns.yaml"
|
|
140
|
+
if [[ -f "$home_config" ]]; then
|
|
141
|
+
echo "$home_config"
|
|
142
|
+
return 0
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
return 1
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
# Run semgrep scan
|
|
149
|
+
run_semgrep() {
|
|
150
|
+
local config_path="$1"
|
|
151
|
+
local scan_path="$2"
|
|
152
|
+
|
|
153
|
+
echo -e "${BLUE}🔍 Running Semgrep Security Scan${NC}"
|
|
154
|
+
echo "═══════════════════════════════════════════════════════════"
|
|
155
|
+
echo ""
|
|
156
|
+
echo "Config: $config_path"
|
|
157
|
+
echo "Scanning: $scan_path"
|
|
158
|
+
echo ""
|
|
159
|
+
|
|
160
|
+
local semgrep_args=(
|
|
161
|
+
"--config" "$config_path"
|
|
162
|
+
"--exclude" "node_modules"
|
|
163
|
+
"--exclude" "dist"
|
|
164
|
+
"--exclude" "build"
|
|
165
|
+
"--exclude" ".next"
|
|
166
|
+
"--exclude" "coverage"
|
|
167
|
+
"--exclude" "*.min.js"
|
|
168
|
+
"--exclude" "*.bundle.js"
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
# Add output format
|
|
172
|
+
case "$OUTPUT_FORMAT" in
|
|
173
|
+
json)
|
|
174
|
+
semgrep_args+=("--json")
|
|
175
|
+
;;
|
|
176
|
+
sarif)
|
|
177
|
+
semgrep_args+=("--sarif")
|
|
178
|
+
;;
|
|
179
|
+
esac
|
|
180
|
+
|
|
181
|
+
# Add output file
|
|
182
|
+
if [[ -n "$OUTPUT_FILE" ]]; then
|
|
183
|
+
semgrep_args+=("--output" "$OUTPUT_FILE")
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
# Run semgrep
|
|
187
|
+
local exit_code=0
|
|
188
|
+
semgrep "${semgrep_args[@]}" "$scan_path" || exit_code=$?
|
|
189
|
+
|
|
190
|
+
echo ""
|
|
191
|
+
|
|
192
|
+
if [[ $exit_code -eq 0 ]]; then
|
|
193
|
+
echo -e "${GREEN}✅ No security issues found${NC}"
|
|
194
|
+
elif [[ $exit_code -eq 1 ]]; then
|
|
195
|
+
echo -e "${YELLOW}⚠️ Security findings detected${NC}"
|
|
196
|
+
if [[ "$CI_MODE" == "true" ]]; then
|
|
197
|
+
echo "Review findings above and fix before merging."
|
|
198
|
+
fi
|
|
199
|
+
else
|
|
200
|
+
echo -e "${RED}❌ Semgrep scan failed (exit code: $exit_code)${NC}"
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
return $exit_code
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
# Fallback LLM analysis when semgrep not available
|
|
207
|
+
fallback_llm_analysis() {
|
|
208
|
+
echo -e "${YELLOW}⚠️ Semgrep not installed - using LLM-based analysis${NC}"
|
|
209
|
+
echo ""
|
|
210
|
+
echo "For faster, more reliable scans, install Semgrep:"
|
|
211
|
+
echo " ./scripts/run-semgrep.sh --install"
|
|
212
|
+
echo ""
|
|
213
|
+
echo "Falling back to pattern-check.sh..."
|
|
214
|
+
echo ""
|
|
215
|
+
|
|
216
|
+
# Run the grep-based pattern checker
|
|
217
|
+
if [[ -f "$SCRIPT_DIR/pattern-check.sh" ]]; then
|
|
218
|
+
bash "$SCRIPT_DIR/pattern-check.sh" --all
|
|
219
|
+
return $?
|
|
220
|
+
else
|
|
221
|
+
echo -e "${RED}Error: pattern-check.sh not found${NC}"
|
|
222
|
+
return 1
|
|
223
|
+
fi
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
# Main
|
|
227
|
+
main() {
|
|
228
|
+
if ! check_semgrep; then
|
|
229
|
+
fallback_llm_analysis
|
|
230
|
+
return $?
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
local config_path
|
|
234
|
+
if ! config_path=$(find_config); then
|
|
235
|
+
echo -e "${RED}Error: Semgrep config not found at $SEMGREP_CONFIG${NC}"
|
|
236
|
+
echo ""
|
|
237
|
+
echo "Create .semgrep/defensive-patterns.yaml or specify with --config"
|
|
238
|
+
exit 1
|
|
239
|
+
fi
|
|
240
|
+
|
|
241
|
+
run_semgrep "$config_path" "$SCAN_DIR"
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
main
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# Smart Test Strategy - create-qa-architect
|
|
3
3
|
# Generated by create-qa-architect (Pro/Team/Enterprise feature)
|
|
4
|
-
# https://
|
|
4
|
+
# https://buildproven.ai/qa-architect
|
|
5
5
|
set -e
|
|
6
6
|
|
|
7
7
|
echo "🧠 Analyzing changes for optimal test strategy..."
|
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
|
|
@@ -701,9 +702,9 @@ SETUP OPTIONS:
|
|
|
701
702
|
--dry-run Preview changes without modifying files
|
|
702
703
|
|
|
703
704
|
WORKFLOW TIERS (GitHub Actions optimization):
|
|
704
|
-
--workflow-minimal Minimal CI (default) - Single Node version,
|
|
705
|
-
|
|
706
|
-
--workflow-standard Standard CI -
|
|
705
|
+
--workflow-minimal Minimal CI (default) - Single Node version, monthly security
|
|
706
|
+
Budget-first mode: detection-only CI (target <1000 min/month)
|
|
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
|
|
@@ -1161,7 +1162,7 @@ HELP:
|
|
|
1161
1162
|
if (!repoCheck.allowed) {
|
|
1162
1163
|
console.error(`\n❌ ${repoCheck.reason}`)
|
|
1163
1164
|
console.error(
|
|
1164
|
-
' Upgrade to Pro for unlimited repos: https://
|
|
1165
|
+
' Upgrade to Pro for unlimited repos: https://buildproven.ai/qa-architect'
|
|
1165
1166
|
)
|
|
1166
1167
|
process.exit(1)
|
|
1167
1168
|
}
|
|
@@ -1531,7 +1532,7 @@ HELP:
|
|
|
1531
1532
|
console.error(` • Detected maturity: ${detectedMaturity}`)
|
|
1532
1533
|
console.error(` • Error count: ${validationResult.errors.length}`)
|
|
1533
1534
|
console.error(
|
|
1534
|
-
` • https://github.com/
|
|
1535
|
+
` • https://github.com/buildproven/qa-architect/issues/new\n`
|
|
1535
1536
|
)
|
|
1536
1537
|
|
|
1537
1538
|
// Don't continue - this is a bug in the tool itself
|
|
@@ -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,50 +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)
|
|
1705
1704
|
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
const hasSlackAlerts =
|
|
1709
|
-
existingWorkflow.includes('SLACK_WEBHOOK_URL')
|
|
1710
|
-
const hasPrComments = existingWorkflow.includes(
|
|
1711
|
-
'PR_COMMENT_PLACEHOLDER'
|
|
1712
|
-
)
|
|
1705
|
+
// Inject matrix testing if enabled (for library authors)
|
|
1706
|
+
templateWorkflow = injectMatrix(templateWorkflow, matrixEnabled)
|
|
1713
1707
|
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
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
|
+
)
|
|
1718
1714
|
|
|
1719
|
-
|
|
1715
|
+
templateWorkflow = injectCollaborationSteps(templateWorkflow, {
|
|
1716
|
+
enableSlackAlerts: hasSlackAlerts,
|
|
1717
|
+
enablePrComments: hasPrComments,
|
|
1718
|
+
})
|
|
1719
|
+
|
|
1720
|
+
fs.writeFileSync(workflowFile, templateWorkflow)
|
|
1721
|
+
if (workflowMode === 'minimal') {
|
|
1720
1722
|
console.log(
|
|
1721
|
-
|
|
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.'
|
|
1722
1727
|
)
|
|
1723
1728
|
}
|
|
1729
|
+
console.log(
|
|
1730
|
+
`♻️ Updated GitHub Actions workflow to ${workflowMode} mode`
|
|
1731
|
+
)
|
|
1724
1732
|
}
|
|
1725
1733
|
}
|
|
1726
1734
|
|
|
@@ -1910,8 +1918,26 @@ const fs = require('fs')
|
|
|
1910
1918
|
const path = require('path')
|
|
1911
1919
|
const os = require('os')
|
|
1912
1920
|
|
|
1913
|
-
|
|
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 =
|
|
1914
1939
|
process.env.QAA_LICENSE_DIR || path.join(os.homedir(), '.create-qa-architect')
|
|
1940
|
+
const licenseDir = validateLicenseDir(requestedLicenseDir)
|
|
1915
1941
|
const licenseFile = path.join(licenseDir, 'license.json')
|
|
1916
1942
|
const usageFile = path.join(licenseDir, 'usage.json')
|
|
1917
1943
|
const now = new Date()
|
|
@@ -1948,7 +1974,7 @@ try {
|
|
|
1948
1974
|
const CAP = 50
|
|
1949
1975
|
if (usage.prePushRuns >= CAP) {
|
|
1950
1976
|
console.error('❌ Free tier limit reached: ' + usage.prePushRuns + '/' + CAP + ' pre-push runs this month')
|
|
1951
|
-
console.error(' Upgrade to Pro, Team, or Enterprise: https://
|
|
1977
|
+
console.error(' Upgrade to Pro, Team, or Enterprise: https://buildproven.ai/qa-architect')
|
|
1952
1978
|
process.exit(1)
|
|
1953
1979
|
}
|
|
1954
1980
|
|
|
@@ -2167,32 +2193,6 @@ echo "✅ Pre-push validation passed!"
|
|
|
2167
2193
|
: '✅ Added Shell CI GitHub Actions workflow'
|
|
2168
2194
|
)
|
|
2169
2195
|
}
|
|
2170
|
-
|
|
2171
|
-
// Copy/update Shell Quality workflow
|
|
2172
|
-
const shellQualityWorkflowFile = path.join(
|
|
2173
|
-
githubWorkflowDir,
|
|
2174
|
-
'shell-quality.yml'
|
|
2175
|
-
)
|
|
2176
|
-
if (!fs.existsSync(shellQualityWorkflowFile) || isUpdateMode) {
|
|
2177
|
-
const templateShellQualityWorkflow =
|
|
2178
|
-
templateLoader.getTemplate(
|
|
2179
|
-
templates,
|
|
2180
|
-
path.join('config', 'shell-quality.yml')
|
|
2181
|
-
) ||
|
|
2182
|
-
fs.readFileSync(
|
|
2183
|
-
path.join(__dirname, 'config/shell-quality.yml'),
|
|
2184
|
-
'utf8'
|
|
2185
|
-
)
|
|
2186
|
-
fs.writeFileSync(
|
|
2187
|
-
shellQualityWorkflowFile,
|
|
2188
|
-
templateShellQualityWorkflow
|
|
2189
|
-
)
|
|
2190
|
-
console.log(
|
|
2191
|
-
isUpdateMode
|
|
2192
|
-
? '🔄 Updated Shell Quality GitHub Actions workflow'
|
|
2193
|
-
: '✅ Added Shell Quality GitHub Actions workflow'
|
|
2194
|
-
)
|
|
2195
|
-
}
|
|
2196
2196
|
}
|
|
2197
2197
|
|
|
2198
2198
|
// Create a basic README if it doesn't exist
|
|
@@ -2250,6 +2250,54 @@ Quality checks are automated via GitHub Actions:
|
|
|
2250
2250
|
fs.unlinkSync(shellQualityWf)
|
|
2251
2251
|
staleWorkflows.push('shell-quality.yml')
|
|
2252
2252
|
}
|
|
2253
|
+
} else {
|
|
2254
|
+
// Consolidate shell CI into a single workflow to avoid duplicate runs.
|
|
2255
|
+
const shellQualityWf = path.join(
|
|
2256
|
+
githubWorkflowDir,
|
|
2257
|
+
'shell-quality.yml'
|
|
2258
|
+
)
|
|
2259
|
+
if (fs.existsSync(shellQualityWf)) {
|
|
2260
|
+
fs.unlinkSync(shellQualityWf)
|
|
2261
|
+
staleWorkflows.push('shell-quality.yml')
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2265
|
+
// Remove known duplicate CI workflow names that overlap quality.yml,
|
|
2266
|
+
// but ONLY if they were generated by qa-architect (contain a marker).
|
|
2267
|
+
const duplicateCiWorkflows = [
|
|
2268
|
+
'ci.yml',
|
|
2269
|
+
'ci.yaml',
|
|
2270
|
+
'test.yml',
|
|
2271
|
+
'test.yaml',
|
|
2272
|
+
'tests.yml',
|
|
2273
|
+
'tests.yaml',
|
|
2274
|
+
'quality-legacy.yml',
|
|
2275
|
+
'quality-legacy.yaml',
|
|
2276
|
+
]
|
|
2277
|
+
const skippedWorkflows = []
|
|
2278
|
+
for (const duplicateName of duplicateCiWorkflows) {
|
|
2279
|
+
const duplicatePath = path.join(githubWorkflowDir, duplicateName)
|
|
2280
|
+
if (fs.existsSync(duplicatePath)) {
|
|
2281
|
+
const content = fs.readFileSync(duplicatePath, 'utf8')
|
|
2282
|
+
const isQaaGenerated =
|
|
2283
|
+
content.includes('create-qa-architect') ||
|
|
2284
|
+
content.includes('qa-architect') ||
|
|
2285
|
+
content.includes('WORKFLOW_MODE:')
|
|
2286
|
+
if (isQaaGenerated) {
|
|
2287
|
+
fs.unlinkSync(duplicatePath)
|
|
2288
|
+
staleWorkflows.push(duplicateName)
|
|
2289
|
+
} else {
|
|
2290
|
+
skippedWorkflows.push(duplicateName)
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
if (skippedWorkflows.length > 0) {
|
|
2295
|
+
console.log(
|
|
2296
|
+
`⚠️ Found potential duplicate workflows: ${skippedWorkflows.join(', ')}`
|
|
2297
|
+
)
|
|
2298
|
+
console.log(
|
|
2299
|
+
' These were not created by qa-architect — review manually and remove if redundant with quality.yml'
|
|
2300
|
+
)
|
|
2253
2301
|
}
|
|
2254
2302
|
if (staleWorkflows.length > 0) {
|
|
2255
2303
|
console.log(
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## ⚠️ CRITICAL: Do Not Create Additional Workflows
|
|
4
4
|
|
|
5
|
-
This project uses **qa-architect** for quality automation with **minimal workflow mode** (
|
|
5
|
+
This project uses **qa-architect** for quality automation with **minimal workflow mode** (budget-first: <1000 min/month target).
|
|
6
6
|
|
|
7
7
|
### What This Means for AI Assistants (Claude, Copilot, etc.)
|
|
8
8
|
|
|
@@ -25,13 +25,13 @@ This project uses **qa-architect** for quality automation with **minimal workflo
|
|
|
25
25
|
|
|
26
26
|
- **Mode:** Minimal (~$0-5/mo per project)
|
|
27
27
|
- **Path filters:** Skip docs/config changes (60% fewer runs)
|
|
28
|
-
- **Security:**
|
|
28
|
+
- **Security:** Monthly schedule (not every commit)
|
|
29
29
|
- **Node version:** Single version (22)
|
|
30
30
|
|
|
31
31
|
### Why This Matters
|
|
32
32
|
|
|
33
33
|
- **Cost control:** Prevents $20-350/mo per-project bloat
|
|
34
|
-
- **Efficiency:** Path filters +
|
|
34
|
+
- **Efficiency:** Path filters + monthly security = 60-90% CI savings
|
|
35
35
|
- **Standards:** Consistent automation across all projects
|
|
36
36
|
|
|
37
37
|
### Upgrading CI (User Decision Only)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# Smart Test Strategy - {{PROJECT_NAME}}
|
|
3
3
|
# Generated by create-qa-architect (Pro/Team/Enterprise feature)
|
|
4
|
-
# https://
|
|
4
|
+
# https://buildproven.ai/qa-architect
|
|
5
5
|
set -e
|
|
6
6
|
|
|
7
7
|
echo "🧠 Analyzing changes for optimal test strategy..."
|
|
@@ -1,39 +0,0 @@
|
|
|
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
|