@testspectra/cli 1.1.8-rc.24 → 1.1.8-rc.28
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/README.md +11 -8
- package/bin/.testspectra-runner.hash +1 -0
- package/dist/commands/__tests__/doctor-filter.test.js +37 -0
- package/dist/commands/__tests__/doctor-scope.test.d.ts +1 -0
- package/dist/commands/__tests__/doctor-scope.test.js +24 -0
- package/dist/commands/__tests__/test-error-streaming.test.d.ts +1 -0
- package/dist/commands/__tests__/test-error-streaming.test.js +52 -0
- package/dist/commands/__tests__/test-scaffolding.test.d.ts +1 -0
- package/dist/commands/__tests__/test-scaffolding.test.js +32 -0
- package/dist/commands/__tests__/upgrade.test.d.ts +1 -0
- package/dist/commands/__tests__/upgrade.test.js +95 -0
- package/dist/commands/add.js +3 -3
- package/dist/commands/doctor.d.ts +9 -1
- package/dist/commands/doctor.js +16 -3
- package/dist/commands/install.js +3 -0
- package/dist/commands/test.d.ts +13 -0
- package/dist/commands/test.js +114 -22
- package/dist/commands/upgrade.d.ts +8 -1
- package/dist/commands/upgrade.js +63 -13
- package/dist/generator/architecture-doc-generator.js +1 -3
- package/dist/index.js +30 -15
- package/dist/linter/discovery.js +2 -2
- package/dist/linter/schemas/spec.schema.d.ts +6 -6
- package/dist/runner/bridge.d.ts +1 -1
- package/dist/runner/bridge.js +45 -16
- package/dist/runner/reporter.js +11 -3
- package/dist/utils/background-update-check.js +1 -8
- package/dist/utils/dependency-updates.d.ts +15 -0
- package/dist/utils/dependency-updates.js +107 -0
- package/package.json +8 -9
- package/templates/default/package.json +2 -2
- package/templates/nx/.nx/workspace-data/70094CFD-E89B-57A1-9619-2FC5F4963C54.db +0 -0
- package/templates/nx/.nx/workspace-data/70094CFD-E89B-57A1-9619-2FC5F4963C54.db-shm +0 -0
- package/templates/nx/.nx/workspace-data/70094CFD-E89B-57A1-9619-2FC5F4963C54.db-wal +0 -0
- package/templates/nx/.nx/workspace-data/d/daemon.log +983 -6269
- package/templates/nx/.nx/workspace-data/d/server-process.json +3 -0
- package/templates/nx/.nx/workspace-data/file-map.json +20 -34
- package/templates/nx/.nx/workspace-data/lockfile.hash +1 -1
- package/templates/nx/.nx/workspace-data/nx_files.nxt +0 -0
- package/templates/nx/.nx/workspace-data/project-graph.json +3 -1
- package/templates/nx/.nx/workspace-data/source-maps.json +336 -0
- package/templates/nx/package.json +2 -2
- package/templates/react-component/package.json +3 -3
- package/templates/react-component/spectra.config.ts +0 -10
- package/dist/.tsbuildinfo +0 -1
- package/dist/commands/update.d.ts +0 -3
- package/dist/commands/update.js +0 -109
- package/dist/lifecycle/__tests__/lifecycle-engine.test.js +0 -290
- package/dist/lifecycle/hook-discovery.d.ts +0 -24
- package/dist/lifecycle/hook-discovery.js +0 -60
- package/dist/lifecycle/hook-dispatcher.d.ts +0 -34
- package/dist/lifecycle/hook-dispatcher.js +0 -190
- package/dist/lifecycle/index.d.ts +0 -3
- package/dist/lifecycle/index.js +0 -3
- package/dist/lifecycle/parallel-grouping.d.ts +0 -27
- package/dist/lifecycle/parallel-grouping.js +0 -151
- /package/dist/{lifecycle/__tests__/lifecycle-engine.test.d.ts → commands/__tests__/doctor-filter.test.d.ts} +0 -0
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import os from 'os';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
/**
|
|
5
|
-
* Detects actual CPU quota available to the process, accounting for
|
|
6
|
-
* Docker and Kubernetes cgroup v1 and cgroup v2 container limits.
|
|
7
|
-
*/
|
|
8
|
-
export function detectContainerCpuQuota() {
|
|
9
|
-
try {
|
|
10
|
-
// 1. Check cgroup v2 (Unified Hierarchy): /sys/fs/cgroup/cpu.max
|
|
11
|
-
// Format: "<quota> <period>" e.g. "200000 100000" or "max 100000"
|
|
12
|
-
if (fs.existsSync('/sys/fs/cgroup/cpu.max')) {
|
|
13
|
-
const content = fs.readFileSync('/sys/fs/cgroup/cpu.max', 'utf-8').trim();
|
|
14
|
-
const [quotaStr, periodStr] = content.split(/\s+/);
|
|
15
|
-
if (quotaStr && quotaStr !== 'max' && periodStr) {
|
|
16
|
-
const quota = parseFloat(quotaStr);
|
|
17
|
-
const period = parseFloat(periodStr);
|
|
18
|
-
if (quota > 0 && period > 0) {
|
|
19
|
-
return Math.max(0.5, quota / period);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
// 2. Check cgroup v1: /sys/fs/cgroup/cpu/cpu.cfs_quota_us and cpu.cfs_period_us
|
|
24
|
-
if (fs.existsSync('/sys/fs/cgroup/cpu/cpu.cfs_quota_us') && fs.existsSync('/sys/fs/cgroup/cpu/cpu.cfs_period_us')) {
|
|
25
|
-
const quota = parseFloat(fs.readFileSync('/sys/fs/cgroup/cpu/cpu.cfs_quota_us', 'utf-8').trim());
|
|
26
|
-
const period = parseFloat(fs.readFileSync('/sys/fs/cgroup/cpu/cpu.cfs_period_us', 'utf-8').trim());
|
|
27
|
-
if (quota > 0 && period > 0) {
|
|
28
|
-
return Math.max(0.5, quota / period);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
catch { }
|
|
33
|
-
// 3. Fallback to host CPU count
|
|
34
|
-
if (typeof os.availableParallelism === 'function') {
|
|
35
|
-
try {
|
|
36
|
-
return os.availableParallelism();
|
|
37
|
-
}
|
|
38
|
-
catch { }
|
|
39
|
-
}
|
|
40
|
-
return os.cpus()?.length || 2;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Detects available memory in bytes, respecting container cgroup memory limits
|
|
44
|
-
* to prevent Linux OOM (Out of Memory) kills.
|
|
45
|
-
*/
|
|
46
|
-
export function detectAvailableMemoryBytes() {
|
|
47
|
-
try {
|
|
48
|
-
// 1. Check cgroup v2: /sys/fs/cgroup/memory.max and /sys/fs/cgroup/memory.current
|
|
49
|
-
if (fs.existsSync('/sys/fs/cgroup/memory.max')) {
|
|
50
|
-
const maxStr = fs.readFileSync('/sys/fs/cgroup/memory.max', 'utf-8').trim();
|
|
51
|
-
if (maxStr !== 'max') {
|
|
52
|
-
const limit = parseFloat(maxStr);
|
|
53
|
-
let usage = 0;
|
|
54
|
-
if (fs.existsSync('/sys/fs/cgroup/memory.current')) {
|
|
55
|
-
usage = parseFloat(fs.readFileSync('/sys/fs/cgroup/memory.current', 'utf-8').trim()) || 0;
|
|
56
|
-
}
|
|
57
|
-
if (limit > 0) {
|
|
58
|
-
return Math.max(0, limit - usage);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
// 2. Check cgroup v1: /sys/fs/cgroup/memory/memory.limit_in_bytes
|
|
63
|
-
if (fs.existsSync('/sys/fs/cgroup/memory/memory.limit_in_bytes')) {
|
|
64
|
-
const limit = parseFloat(fs.readFileSync('/sys/fs/cgroup/memory/memory.limit_in_bytes', 'utf-8').trim());
|
|
65
|
-
// cgroup v1 sets a huge number (e.g. 9223372036854771712) when no limit is enforced
|
|
66
|
-
if (limit > 0 && limit < 1e15) {
|
|
67
|
-
let usage = 0;
|
|
68
|
-
if (fs.existsSync('/sys/fs/cgroup/memory/memory.usage_in_bytes')) {
|
|
69
|
-
usage = parseFloat(fs.readFileSync('/sys/fs/cgroup/memory/memory.usage_in_bytes', 'utf-8').trim()) || 0;
|
|
70
|
-
}
|
|
71
|
-
return Math.max(0, limit - usage);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
catch { }
|
|
76
|
-
// 3. Fallback to host available memory (estimating reclaimable cache/buffer on modern OS)
|
|
77
|
-
try {
|
|
78
|
-
const free = os.freemem();
|
|
79
|
-
const total = os.totalmem();
|
|
80
|
-
return Math.max(free, total * 0.4);
|
|
81
|
-
}
|
|
82
|
-
catch {
|
|
83
|
-
return 1024 * 1024 * 1024; // 1 GB fallback
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Dynamically computes optimal worker concurrency respecting:
|
|
88
|
-
* 1. Explicit user concurrency flag (--concurrency <N> or config)
|
|
89
|
-
* 2. Real container cgroups CPU quota & host CPU count
|
|
90
|
-
* 3. Low-CPU safety ratio (clamping <=2 CPUs to 1 worker, 3-4 CPUs to 2 workers)
|
|
91
|
-
* 4. Memory-aware guard (~600MB RAM budget per Chromium instance to prevent OOM kills)
|
|
92
|
-
* 5. Target items count & maximum worker cap
|
|
93
|
-
*/
|
|
94
|
-
export function calculateWorkerCount(targetItems, options) {
|
|
95
|
-
const opts = typeof options === 'number' ? { maxWorkersCap: options } : options || {};
|
|
96
|
-
const itemCount = targetItems.length;
|
|
97
|
-
if (itemCount === 0)
|
|
98
|
-
return 1;
|
|
99
|
-
// 1. Explicit User Concurrency has absolute priority
|
|
100
|
-
if (opts.userConcurrency && opts.userConcurrency > 0) {
|
|
101
|
-
return Math.min(opts.userConcurrency, itemCount);
|
|
102
|
-
}
|
|
103
|
-
// 2. Determine CPU Quota (cgroups aware)
|
|
104
|
-
const cpuCount = opts.cpuQuotaOverride !== undefined ? opts.cpuQuotaOverride : detectContainerCpuQuota();
|
|
105
|
-
// 3. Low-CPU Safety Ratio:
|
|
106
|
-
// - CPU <= 2: Max 1 worker (prevents severe thrashing on 2-vCPU CI runners)
|
|
107
|
-
// - CPU 3-4: Max 2 workers
|
|
108
|
-
// - CPU > 4: cpuCount - 2 (leaves 2 cores for OS/IDE/DevTools)
|
|
109
|
-
let cpuBasedWorkers = 1;
|
|
110
|
-
if (cpuCount <= 2) {
|
|
111
|
-
cpuBasedWorkers = 1;
|
|
112
|
-
}
|
|
113
|
-
else if (cpuCount <= 4) {
|
|
114
|
-
cpuBasedWorkers = 2;
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
cpuBasedWorkers = Math.max(1, Math.floor(cpuCount - 2));
|
|
118
|
-
}
|
|
119
|
-
// 4. Memory-Aware Guard (Anti-OOM):
|
|
120
|
-
// Allocate ~600MB per Chromium browser instance
|
|
121
|
-
const MEMORY_PER_WORKER_BYTES = 600 * 1024 * 1024;
|
|
122
|
-
const availableMemBytes = opts.memoryBytesOverride !== undefined ? opts.memoryBytesOverride : detectAvailableMemoryBytes();
|
|
123
|
-
const memBasedWorkers = Math.max(1, Math.floor(availableMemBytes / MEMORY_PER_WORKER_BYTES));
|
|
124
|
-
// 5. Apply Maximum Cap and Item Count Clamping
|
|
125
|
-
const maxCap = opts.maxWorkersCap || 6;
|
|
126
|
-
return Math.max(1, Math.min(cpuBasedWorkers, memBasedWorkers, maxCap, itemCount));
|
|
127
|
-
}
|
|
128
|
-
export function groupSpecs(specPaths, mode = 'suite') {
|
|
129
|
-
if (mode === 'testcase') {
|
|
130
|
-
return [...specPaths];
|
|
131
|
-
}
|
|
132
|
-
// Group by suite directory
|
|
133
|
-
const suiteGroups = new Map();
|
|
134
|
-
for (const specPath of specPaths) {
|
|
135
|
-
const normalized = specPath.replace(/\\/g, '/');
|
|
136
|
-
const parts = normalized.split('/');
|
|
137
|
-
let suiteKey = 'default';
|
|
138
|
-
const specsIdx = parts.findIndex((p) => p === 'specs' || p === 'suites');
|
|
139
|
-
if (specsIdx !== -1 && specsIdx + 1 < parts.length) {
|
|
140
|
-
suiteKey = parts.slice(0, specsIdx + 2).join('/');
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
suiteKey = path.dirname(normalized);
|
|
144
|
-
}
|
|
145
|
-
if (!suiteGroups.has(suiteKey)) {
|
|
146
|
-
suiteGroups.set(suiteKey, []);
|
|
147
|
-
}
|
|
148
|
-
suiteGroups.get(suiteKey).push(specPath);
|
|
149
|
-
}
|
|
150
|
-
return Array.from(suiteGroups.values());
|
|
151
|
-
}
|
|
File without changes
|