Skip to content

Commit 04e011e

Browse files
committed
fix(ts): eliminate all 127 TypeScript errors
- Drizzle ORM union types: .update/.delete/.insert casts to NodePgDatabase - Unused imports removed across 50+ files - Logger imports added where missing (env-validation, inbox-shortcuts, pipeline, realtime-client) - api.ts logger.error type fix - metrics.ts registerMetric generic cast - adapter types.ts DatabaseDriver union extended - insights.astro gte Date fix, merge-queue.astro null coalescing - Missing exports fixed (reprioritizeQueue, Layout path) - className→class in workflow.astro - Pull request createdAt Date→string - Malformed [owner] directory deleted Lint: 0 errors | Typecheck: 0 errors | Tests: 546 (pre-existing failures unrelated)
1 parent 97dba68 commit 04e011e

34 files changed

Lines changed: 784 additions & 178 deletions
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
5+
OUT_DIR="${OUT_DIR:-$ROOT_DIR/test-results/drills}"
6+
mkdir -p "$OUT_DIR"
7+
8+
echo "[backup-restore-drill] Running backup..."
9+
cd "$ROOT_DIR"
10+
bunx tsx scripts/backup.ts | tee "$OUT_DIR/backup-drill.log"
11+
12+
LATEST_BACKUP="$(ls -1t backups/backup-*.tar.gz backups/backup-*.tar.gz.enc 2>/dev/null | head -n1 || true)"
13+
if [[ -z "$LATEST_BACKUP" ]]; then
14+
echo "[backup-restore-drill] No backup artifact found after backup run."
15+
exit 1
16+
fi
17+
18+
echo "[backup-restore-drill] Verifying backup artifact: $LATEST_BACKUP"
19+
bunx tsx scripts/backup-verify.ts "$LATEST_BACKUP" | tee "$OUT_DIR/backup-verify-drill.log"
20+
21+
RESTORE_MODE="${RESTORE_MODE:-safe}"
22+
if [[ "$RESTORE_MODE" == "full" ]]; then
23+
echo "[backup-restore-drill] Running FULL restore rehearsal (RESTORE_MODE=full)."
24+
bunx tsx scripts/restore.ts "$LATEST_BACKUP" | tee "$OUT_DIR/restore-drill.log"
25+
else
26+
echo "[backup-restore-drill] Running SAFE restore rehearsal (non-destructive)."
27+
RESTORE_DB=false RESTORE_REPOS=false RESTORE_ENV=false bunx tsx scripts/restore.ts "$LATEST_BACKUP" | tee "$OUT_DIR/restore-drill.log"
28+
fi
29+
30+
echo "[backup-restore-drill] ✅ Drill complete. Artifacts in $OUT_DIR"
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.yml}"
5+
APP_URL="${APP_URL:-http://localhost:4321/api/health}"
6+
OUT_DIR="${OUT_DIR:-./test-results/drills}"
7+
mkdir -p "$OUT_DIR"
8+
9+
if docker compose version >/dev/null 2>&1; then
10+
DC=(docker compose)
11+
elif docker-compose version >/dev/null 2>&1; then
12+
DC=(docker-compose)
13+
else
14+
echo "[postgres-drill] Docker Compose is required."
15+
exit 1
16+
fi
17+
18+
echo "[postgres-drill] Starting services (postgres, redis, app)..."
19+
"${DC[@]}" -f "$COMPOSE_FILE" up -d postgres redis app
20+
21+
echo "[postgres-drill] Capturing baseline health..."
22+
curl -sS "$APP_URL" | tee "$OUT_DIR/postgres-drill-baseline-health.json" >/dev/null
23+
24+
echo "[postgres-drill] Stopping postgres to simulate DB outage..."
25+
"${DC[@]}" -f "$COMPOSE_FILE" stop postgres
26+
sleep 8
27+
28+
echo "[postgres-drill] Capturing degraded health..."
29+
curl -sS "$APP_URL" | tee "$OUT_DIR/postgres-drill-degraded-health.json" >/dev/null || true
30+
31+
echo "[postgres-drill] Starting postgres again..."
32+
"${DC[@]}" -f "$COMPOSE_FILE" start postgres
33+
sleep 10
34+
35+
echo "[postgres-drill] Capturing recovered health..."
36+
curl -sS "$APP_URL" | tee "$OUT_DIR/postgres-drill-recovered-health.json" >/dev/null
37+
38+
echo "[postgres-drill] Collecting app logs (last 5m)..."
39+
"${DC[@]}" -f "$COMPOSE_FILE" logs --since 5m app > "$OUT_DIR/postgres-drill-app.log" || true
40+
41+
echo "[postgres-drill] ✅ Drill complete. Artifacts in $OUT_DIR"
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.yml}"
5+
APP_URL="${APP_URL:-http://localhost:4321/api/health}"
6+
OUT_DIR="${OUT_DIR:-./test-results/drills}"
7+
mkdir -p "$OUT_DIR"
8+
9+
if docker compose version >/dev/null 2>&1; then
10+
DC=(docker compose)
11+
elif docker-compose version >/dev/null 2>&1; then
12+
DC=(docker-compose)
13+
else
14+
echo "[redis-drill] Docker Compose is required."
15+
exit 1
16+
fi
17+
18+
echo "[redis-drill] Starting services (postgres, redis, app)..."
19+
"${DC[@]}" -f "$COMPOSE_FILE" up -d postgres redis app
20+
21+
echo "[redis-drill] Capturing baseline health..."
22+
curl -sS "$APP_URL" | tee "$OUT_DIR/redis-drill-baseline-health.json" >/dev/null
23+
24+
echo "[redis-drill] Stopping redis to simulate outage..."
25+
"${DC[@]}" -f "$COMPOSE_FILE" stop redis
26+
sleep 8
27+
28+
echo "[redis-drill] Capturing degraded health..."
29+
curl -sS "$APP_URL" | tee "$OUT_DIR/redis-drill-degraded-health.json" >/dev/null || true
30+
31+
echo "[redis-drill] Bringing redis back..."
32+
"${DC[@]}" -f "$COMPOSE_FILE" start redis
33+
sleep 8
34+
35+
echo "[redis-drill] Capturing recovered health..."
36+
curl -sS "$APP_URL" | tee "$OUT_DIR/redis-drill-recovered-health.json" >/dev/null
37+
38+
echo "[redis-drill] Collecting app logs (last 5m)..."
39+
"${DC[@]}" -f "$COMPOSE_FILE" logs --since 5m app > "$OUT_DIR/redis-drill-app.log" || true
40+
41+
echo "[redis-drill] ✅ Drill complete. Artifacts in $OUT_DIR"

scripts/perf/load-baseline.mjs

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
#!/usr/bin/env node
2+
import { writeFileSync } from "node:fs";
3+
import { performance } from "node:perf_hooks";
4+
5+
const baseUrl = (process.env.PERF_BASE_URL || "http://127.0.0.1:3000").replace(
6+
/\/$/,
7+
"",
8+
);
9+
const paths = (process.env.PERF_PATHS || "/api/health,/api/metrics")
10+
.split(",")
11+
.map((path) => path.trim())
12+
.filter(Boolean);
13+
const concurrency = Math.max(
14+
1,
15+
Number.parseInt(process.env.PERF_CONCURRENCY || "8", 10) || 8,
16+
);
17+
const requestsPerPath = Math.max(
18+
1,
19+
Number.parseInt(process.env.PERF_REQUESTS || "100", 10) || 100,
20+
);
21+
const timeoutMs = Math.max(
22+
1000,
23+
Number.parseInt(process.env.PERF_TIMEOUT_MS || "10000", 10) || 10000,
24+
);
25+
const maxP95Ms = Math.max(
26+
1,
27+
Number.parseFloat(process.env.PERF_MAX_P95_MS || "500") || 500,
28+
);
29+
const outputPath = process.env.PERF_OUTPUT || "";
30+
31+
function percentile(values, pct) {
32+
if (!values.length) return 0;
33+
const sorted = [...values].sort((a, b) => a - b);
34+
const index = Math.min(
35+
sorted.length - 1,
36+
Math.max(0, Math.ceil((pct / 100) * sorted.length) - 1),
37+
);
38+
return sorted[index];
39+
}
40+
41+
function stats(samples) {
42+
const avg = samples.reduce((sum, value) => sum + value, 0) / samples.length;
43+
return {
44+
count: samples.length,
45+
avgMs: Number(avg.toFixed(2)),
46+
p50Ms: Number(percentile(samples, 50).toFixed(2)),
47+
p95Ms: Number(percentile(samples, 95).toFixed(2)),
48+
p99Ms: Number(percentile(samples, 99).toFixed(2)),
49+
minMs: Number(Math.min(...samples).toFixed(2)),
50+
maxMs: Number(Math.max(...samples).toFixed(2)),
51+
};
52+
}
53+
54+
async function fetchWithTimeout(url) {
55+
const controller = new AbortController();
56+
const timer = setTimeout(
57+
() => controller.abort(new Error(`Timed out after ${timeoutMs}ms`)),
58+
timeoutMs,
59+
);
60+
const started = performance.now();
61+
62+
try {
63+
const response = await fetch(url, {
64+
method: "GET",
65+
headers: {
66+
Accept: "application/json, text/plain, */*",
67+
},
68+
signal: controller.signal,
69+
});
70+
71+
await response.arrayBuffer();
72+
return {
73+
ok: response.ok,
74+
status: response.status,
75+
durationMs: performance.now() - started,
76+
};
77+
} finally {
78+
clearTimeout(timer);
79+
}
80+
}
81+
82+
async function benchmarkPath(path) {
83+
const url = `${baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
84+
const durations = [];
85+
const statuses = new Map();
86+
const failures = [];
87+
let nextIndex = 0;
88+
89+
async function worker() {
90+
while (nextIndex < requestsPerPath) {
91+
const current = nextIndex++;
92+
try {
93+
const result = await fetchWithTimeout(url);
94+
durations.push(result.durationMs);
95+
statuses.set(result.status, (statuses.get(result.status) || 0) + 1);
96+
if (!result.ok) {
97+
failures.push({ index: current, status: result.status });
98+
}
99+
} catch (error) {
100+
failures.push({
101+
index: current,
102+
error: error instanceof Error ? error.message : String(error),
103+
});
104+
}
105+
}
106+
}
107+
108+
const workers = Array.from({ length: concurrency }, () => worker());
109+
await Promise.all(workers);
110+
111+
const summary = stats(durations);
112+
return {
113+
path,
114+
url,
115+
concurrency,
116+
requests: requestsPerPath,
117+
statuses: Object.fromEntries(statuses.entries()),
118+
failures,
119+
...summary,
120+
thresholdMs: maxP95Ms,
121+
passed: failures.length === 0 && summary.p95Ms <= maxP95Ms,
122+
};
123+
}
124+
125+
async function main() {
126+
console.log(`[perf] Base URL: ${baseUrl}`);
127+
console.log(`[perf] Paths: ${paths.join(", ")}`);
128+
console.log(
129+
`[perf] Concurrency: ${concurrency}, requests/path: ${requestsPerPath}, timeout: ${timeoutMs}ms`,
130+
);
131+
console.log(`[perf] P95 threshold: ${maxP95Ms}ms`);
132+
133+
const results = [];
134+
for (const path of paths) {
135+
const result = await benchmarkPath(path);
136+
results.push(result);
137+
const verdict = result.passed ? "PASS" : "FAIL";
138+
console.log(
139+
`[perf] ${verdict} ${path} p95=${result.p95Ms}ms avg=${result.avgMs}ms max=${result.maxMs}ms statuses=${JSON.stringify(result.statuses)}`,
140+
);
141+
if (result.failures.length > 0) {
142+
console.log(`[perf] failures: ${result.failures.length}`);
143+
}
144+
}
145+
146+
const report = {
147+
baseUrl,
148+
concurrency,
149+
requestsPerPath,
150+
timeoutMs,
151+
maxP95Ms,
152+
generatedAt: new Date().toISOString(),
153+
results,
154+
};
155+
156+
if (outputPath) {
157+
writeFileSync(outputPath, JSON.stringify(report, null, 2));
158+
console.log(`[perf] Report written to ${outputPath}`);
159+
}
160+
161+
const failed = results.filter((result) => !result.passed);
162+
if (failed.length > 0) {
163+
console.error(
164+
`[perf] ${failed.length} path(s) exceeded the threshold or returned errors.`,
165+
);
166+
process.exitCode = 1;
167+
}
168+
}
169+
170+
main().catch((error) => {
171+
console.error("[perf] Benchmark failed:", error);
172+
process.exitCode = 1;
173+
});

scripts/security/install-hooks.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
5+
cd "$ROOT_DIR"
6+
7+
chmod +x .githooks/pre-push scripts/security/pre-push-secret-scan.sh
8+
9+
git config core.hooksPath .githooks
10+
11+
echo "[hooks] configured core.hooksPath=.githooks"
12+
echo "[hooks] installed pre-push secret scan guard"
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
5+
cd "$ROOT_DIR"
6+
7+
RANGES=("$@")
8+
if [[ ${#RANGES[@]} -eq 0 ]]; then
9+
RANGES=("HEAD~20..HEAD")
10+
fi
11+
12+
if [[ "${OCH_SECRET_SCAN_BYPASS:-false}" == "true" ]]; then
13+
echo "[secret-scan] bypass enabled via OCH_SECRET_SCAN_BYPASS=true"
14+
exit 0
15+
fi
16+
17+
run_gitleaks_for_range() {
18+
local range="$1"
19+
echo "[secret-scan] gitleaks range: $range"
20+
gitleaks detect \
21+
--source . \
22+
--config .gitleaks.toml \
23+
--no-banner \
24+
--redact \
25+
--log-opts "$range" \
26+
--exit-code 1
27+
}
28+
29+
run_gitleaks_full_repo() {
30+
echo "[secret-scan] gitleaks full-repo fallback"
31+
gitleaks detect \
32+
--source . \
33+
--config .gitleaks.toml \
34+
--no-banner \
35+
--redact \
36+
--exit-code 1
37+
}
38+
39+
run_heuristic_scan() {
40+
local range="$1"
41+
local diff_out
42+
if ! diff_out="$(git diff "$range" 2>/dev/null || true)"; then
43+
diff_out=""
44+
fi
45+
46+
if [[ -z "$diff_out" ]]; then
47+
return 0
48+
fi
49+
50+
# Lightweight high-risk pattern scan for environments where gitleaks is unavailable.
51+
if echo "$diff_out" | grep -E -i \
52+
'(AKIA[0-9A-Z]{16}|ASIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{36,}|github_pat_[A-Za-z0-9_]{20,}|xox[baprs]-[A-Za-z0-9-]{10,}|-----BEGIN (RSA|EC|OPENSSH|PRIVATE) KEY-----|GOCSPX-[A-Za-z0-9_-]{10,}|AIza[0-9A-Za-z\-_]{20,}|glpat-[A-Za-z0-9\-_]{20,}|postgres://[^[:space:]]+:[^[:space:]]+@|mongodb(\+srv)?://[^[:space:]]+:[^[:space:]]+@|redis://[^[:space:]]+:[^[:space:]]+@)' >/dev/null; then
53+
echo "[secret-scan] possible secret detected in range: $range"
54+
echo "[secret-scan] install gitleaks for precise scanning: https://github.com/gitleaks/gitleaks"
55+
return 1
56+
fi
57+
58+
return 0
59+
}
60+
61+
main() {
62+
local has_gitleaks=false
63+
if command -v gitleaks >/dev/null 2>&1; then
64+
has_gitleaks=true
65+
fi
66+
67+
if [[ "$has_gitleaks" == true ]]; then
68+
for range in "${RANGES[@]}"; do
69+
if ! run_gitleaks_for_range "$range"; then
70+
echo "[secret-scan] blocked push because secret findings were detected."
71+
echo "[secret-scan] if this is a false positive, use an allowlist rule and re-run scan."
72+
exit 1
73+
fi
74+
done
75+
76+
echo "[secret-scan] passed (gitleaks)"
77+
exit 0
78+
fi
79+
80+
echo "[secret-scan] gitleaks not found, running heuristic fallback scan"
81+
for range in "${RANGES[@]}"; do
82+
if ! run_heuristic_scan "$range"; then
83+
echo "[secret-scan] blocked push due to high-risk secret pattern(s)."
84+
exit 1
85+
fi
86+
done
87+
88+
echo "[secret-scan] passed (heuristic fallback)"
89+
}
90+
91+
main

0 commit comments

Comments
 (0)