forgedev 1.3.0 → 1.4.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/bin/chainproof.js +1 -1
- package/bin/devforge.js +1 -1
- package/package.json +1 -1
- package/src/claude-configurator.js +114 -58
- package/src/composer.js +242 -339
- package/src/init-mode.js +14 -3
- package/src/recommender.js +85 -76
- package/src/update.js +56 -12
- package/src/utils.js +123 -25
- package/templates/base/.gitignore.template +3 -0
- package/templates/infra/k8s/k8s/deployment.yml.template +70 -0
- package/templates/infra/k8s/k8s/hpa.yml.template +24 -0
- package/templates/infra/k8s/k8s/ingress.yml.template +26 -0
- package/templates/infra/k8s/k8s/kustomization.yml.template +13 -0
- package/templates/infra/k8s/k8s/namespace.yml.template +4 -0
- package/templates/infra/k8s/k8s/networkpolicy.yml.template +41 -0
- package/templates/infra/k8s/k8s/secrets.yml.template +10 -0
- package/templates/infra/k8s/k8s/service.yml.template +15 -0
- package/templates/testing/load/k6/README.md.template +48 -0
- package/templates/testing/load/k6/load-test.js.template +57 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
apiVersion: networking.k8s.io/v1
|
|
2
|
+
kind: NetworkPolicy
|
|
3
|
+
metadata:
|
|
4
|
+
name: {{PROJECT_NAME}}
|
|
5
|
+
spec:
|
|
6
|
+
podSelector:
|
|
7
|
+
matchLabels:
|
|
8
|
+
app: {{PROJECT_NAME}}
|
|
9
|
+
policyTypes:
|
|
10
|
+
- Ingress
|
|
11
|
+
- Egress
|
|
12
|
+
ingress:
|
|
13
|
+
- from:
|
|
14
|
+
- namespaceSelector:
|
|
15
|
+
matchLabels:
|
|
16
|
+
kubernetes.io/metadata.name: ingress-nginx
|
|
17
|
+
ports:
|
|
18
|
+
- port: {{APP_PORT}}
|
|
19
|
+
egress:
|
|
20
|
+
# Database
|
|
21
|
+
- to:
|
|
22
|
+
- podSelector:
|
|
23
|
+
matchLabels:
|
|
24
|
+
app: postgres
|
|
25
|
+
ports:
|
|
26
|
+
- port: 5432
|
|
27
|
+
# DNS (UDP + TCP for large responses)
|
|
28
|
+
- to:
|
|
29
|
+
- namespaceSelector:
|
|
30
|
+
matchLabels:
|
|
31
|
+
kubernetes.io/metadata.name: kube-system
|
|
32
|
+
ports:
|
|
33
|
+
- port: 53
|
|
34
|
+
protocol: UDP
|
|
35
|
+
- port: 53
|
|
36
|
+
protocol: TCP
|
|
37
|
+
# Uncomment to allow outbound HTTPS (external APIs, OAuth, webhooks)
|
|
38
|
+
# - to: []
|
|
39
|
+
# ports:
|
|
40
|
+
# - port: 443
|
|
41
|
+
# protocol: TCP
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
apiVersion: v1
|
|
2
|
+
kind: Secret
|
|
3
|
+
metadata:
|
|
4
|
+
name: {{PROJECT_NAME}}-secrets
|
|
5
|
+
type: Opaque
|
|
6
|
+
stringData:
|
|
7
|
+
# IMPORTANT: Replace these placeholder values before deploying.
|
|
8
|
+
# In production, use sealed-secrets, external-secrets, or your cloud provider's secret manager.
|
|
9
|
+
# Do NOT commit real credentials to version control.
|
|
10
|
+
database-url: "postgresql://USER:CHANGE_ME_BEFORE_DEPLOY@postgres:5432/{{PROJECT_NAME_SNAKE}}"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Load Testing — {{PROJECT_NAME_PASCAL}}
|
|
2
|
+
|
|
3
|
+
Uses [k6](https://k6.io/) for load testing.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# macOS
|
|
9
|
+
brew install k6
|
|
10
|
+
|
|
11
|
+
# Windows (scoop)
|
|
12
|
+
scoop install k6
|
|
13
|
+
|
|
14
|
+
# Docker (no install)
|
|
15
|
+
docker run --rm -i grafana/k6 run - <load-test.js
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Run
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Smoke test (quick, single request)
|
|
22
|
+
k6 run --vus 1 --iterations 1 k6/load-test.js
|
|
23
|
+
|
|
24
|
+
# Full load test against local server
|
|
25
|
+
k6 run k6/load-test.js
|
|
26
|
+
|
|
27
|
+
# Against staging
|
|
28
|
+
k6 run -e BASE_URL=https://staging.example.com k6/load-test.js
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Thresholds
|
|
32
|
+
|
|
33
|
+
| Metric | Threshold | Meaning |
|
|
34
|
+
|--------|-----------|---------|
|
|
35
|
+
| `http_req_duration p(95)` | < 500ms | 95th percentile response time |
|
|
36
|
+
| `http_req_duration p(99)` | < 1.5s | 99th percentile response time |
|
|
37
|
+
| `http_req_failed` | < 1% | Error rate |
|
|
38
|
+
| `checks` | > 99% | Assertion pass rate |
|
|
39
|
+
|
|
40
|
+
## CI Integration
|
|
41
|
+
|
|
42
|
+
Add to your CI pipeline after deployment:
|
|
43
|
+
|
|
44
|
+
```yaml
|
|
45
|
+
- name: Load test
|
|
46
|
+
run: |
|
|
47
|
+
k6 run -e BASE_URL=${{ secrets.STAGING_URL }} k6/load-test.js
|
|
48
|
+
```
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import http from 'k6/http';
|
|
2
|
+
import { check, sleep } from 'k6';
|
|
3
|
+
|
|
4
|
+
// ─── Configuration ──────────────────────────────────────────────────
|
|
5
|
+
// Override via environment: k6 run -e BASE_URL=https://staging.example.com load-test.js
|
|
6
|
+
|
|
7
|
+
const BASE_URL = __ENV.BASE_URL || 'http://localhost:{{APP_PORT}}';
|
|
8
|
+
|
|
9
|
+
export const options = {
|
|
10
|
+
// Ramp-up pattern: smoke → load → stress → recovery
|
|
11
|
+
stages: [
|
|
12
|
+
{ duration: '30s', target: 10 }, // smoke: 10 users
|
|
13
|
+
{ duration: '1m', target: 50 }, // load: ramp to 50
|
|
14
|
+
{ duration: '2m', target: 50 }, // load: hold at 50
|
|
15
|
+
{ duration: '30s', target: 100 }, // stress: ramp to 100
|
|
16
|
+
{ duration: '1m', target: 100 }, // stress: hold at 100
|
|
17
|
+
{ duration: '30s', target: 0 }, // recovery: ramp down
|
|
18
|
+
],
|
|
19
|
+
thresholds: {
|
|
20
|
+
http_req_duration: ['p(95)<500', 'p(99)<1500'], // 95th < 500ms, 99th < 1.5s
|
|
21
|
+
http_req_failed: ['rate<0.01'], // <1% error rate
|
|
22
|
+
checks: ['rate>0.99'], // >99% checks pass
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// ─── Health check ───────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
export default function () {
|
|
29
|
+
// Liveness probe
|
|
30
|
+
const health = http.get(`${BASE_URL}/health`);
|
|
31
|
+
check(health, {
|
|
32
|
+
'health: status 200': (r) => r.status === 200,
|
|
33
|
+
'health: status ok': (r) => {
|
|
34
|
+
try { return JSON.parse(r.body).status === 'ok'; } catch { return false; }
|
|
35
|
+
},
|
|
36
|
+
'health: response < 200ms': (r) => r.timings.duration < 200,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Readiness probe
|
|
40
|
+
const healthz = http.get(`${BASE_URL}/healthz`);
|
|
41
|
+
check(healthz, {
|
|
42
|
+
'healthz: status 200': (r) => r.status === 200,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
sleep(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ─── Smoke test (quick validation) ─────────────────────────────────
|
|
49
|
+
// Run with: k6 run --exec smoke load-test.js
|
|
50
|
+
|
|
51
|
+
export function smoke() {
|
|
52
|
+
const res = http.get(`${BASE_URL}/health`);
|
|
53
|
+
check(res, {
|
|
54
|
+
'smoke: status 200': (r) => r.status === 200,
|
|
55
|
+
'smoke: response < 100ms': (r) => r.timings.duration < 100,
|
|
56
|
+
});
|
|
57
|
+
}
|