@framedash/cli 0.1.1 → 0.1.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/README.md +62 -5
- package/dist/commands/alerts.d.ts.map +1 -1
- package/dist/commands/alerts.js +2 -1
- package/dist/commands/alerts.js.map +1 -1
- package/dist/commands/auth.d.ts.map +1 -1
- package/dist/commands/auth.js +25 -2
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/builds.d.ts +2 -0
- package/dist/commands/builds.d.ts.map +1 -0
- package/dist/commands/builds.js +31 -0
- package/dist/commands/builds.js.map +1 -0
- package/dist/commands/content.d.ts.map +1 -1
- package/dist/commands/content.js +2 -2
- package/dist/commands/content.js.map +1 -1
- package/dist/commands/dashboard.d.ts.map +1 -1
- package/dist/commands/dashboard.js +3 -3
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/funnel.d.ts.map +1 -1
- package/dist/commands/funnel.js +7 -8
- package/dist/commands/funnel.js.map +1 -1
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +142 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +2 -0
- package/dist/commands/logout.d.ts.map +1 -0
- package/dist/commands/logout.js +79 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/map-capture.d.ts +2 -1
- package/dist/commands/map-capture.d.ts.map +1 -1
- package/dist/commands/map-capture.js +42 -12
- package/dist/commands/map-capture.js.map +1 -1
- package/dist/commands/perf-diff.d.ts +2 -0
- package/dist/commands/perf-diff.d.ts.map +1 -0
- package/dist/commands/perf-diff.js +125 -0
- package/dist/commands/perf-diff.js.map +1 -0
- package/dist/commands/query.d.ts.map +1 -1
- package/dist/commands/query.js +12 -0
- package/dist/commands/query.js.map +1 -1
- package/dist/commands/retention.d.ts.map +1 -1
- package/dist/commands/retention.js +3 -3
- package/dist/commands/retention.js.map +1 -1
- package/dist/commands/run-profile-test.d.ts +7 -0
- package/dist/commands/run-profile-test.d.ts.map +1 -0
- package/dist/commands/run-profile-test.js +503 -0
- package/dist/commands/run-profile-test.js.map +1 -0
- package/dist/commands/threshold-profiles.d.ts +2 -0
- package/dist/commands/threshold-profiles.d.ts.map +1 -0
- package/dist/commands/threshold-profiles.js +24 -0
- package/dist/commands/threshold-profiles.js.map +1 -0
- package/dist/index.js +16 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/config.d.ts +42 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +44 -14
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/create-client.d.ts +27 -1
- package/dist/lib/create-client.d.ts.map +1 -1
- package/dist/lib/create-client.js +167 -24
- package/dist/lib/create-client.js.map +1 -1
- package/dist/lib/formatters.js +93 -1
- package/dist/lib/formatters.js.map +1 -1
- package/dist/lib/logger.d.ts +2 -0
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +4 -0
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/oauth/loopback-server.d.ts +27 -0
- package/dist/lib/oauth/loopback-server.d.ts.map +1 -0
- package/dist/lib/oauth/loopback-server.js +164 -0
- package/dist/lib/oauth/loopback-server.js.map +1 -0
- package/dist/lib/oauth/manager.d.ts +36 -0
- package/dist/lib/oauth/manager.d.ts.map +1 -0
- package/dist/lib/oauth/manager.js +130 -0
- package/dist/lib/oauth/manager.js.map +1 -0
- package/dist/lib/oauth/pkce.d.ts +15 -0
- package/dist/lib/oauth/pkce.d.ts.map +1 -0
- package/dist/lib/oauth/pkce.js +24 -0
- package/dist/lib/oauth/pkce.js.map +1 -0
- package/dist/lib/oauth/token-endpoint.d.ts +39 -0
- package/dist/lib/oauth/token-endpoint.d.ts.map +1 -0
- package/dist/lib/oauth/token-endpoint.js +117 -0
- package/dist/lib/oauth/token-endpoint.js.map +1 -0
- package/dist/lib/oauth/token-store.d.ts +34 -0
- package/dist/lib/oauth/token-store.d.ts.map +1 -0
- package/dist/lib/oauth/token-store.js +164 -0
- package/dist/lib/oauth/token-store.js.map +1 -0
- package/dist/lib/perf-diff-eval.d.ts +58 -0
- package/dist/lib/perf-diff-eval.d.ts.map +1 -0
- package/dist/lib/perf-diff-eval.js +49 -0
- package/dist/lib/perf-diff-eval.js.map +1 -0
- package/dist/lib/run-command.js +2 -2
- package/dist/lib/run-command.js.map +1 -1
- package/dist/lib/run-profile-test-lib.d.ts +156 -0
- package/dist/lib/run-profile-test-lib.d.ts.map +1 -0
- package/dist/lib/run-profile-test-lib.js +171 -0
- package/dist/lib/run-profile-test-lib.js.map +1 -0
- package/dist/lib/uploader.d.ts +14 -1
- package/dist/lib/uploader.d.ts.map +1 -1
- package/dist/lib/uploader.js +26 -12
- package/dist/lib/uploader.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
2
|
+
import { constants as osConstants } from "node:os";
|
|
3
|
+
import { buildBuildComparePath, buildBuildsPath } from "@framedash/api-client";
|
|
4
|
+
import { createClient } from "../lib/create-client.js";
|
|
5
|
+
import { formatOutput } from "../lib/formatters.js";
|
|
6
|
+
import { error, log, success, warn } from "../lib/logger.js";
|
|
7
|
+
import { evaluateRegression, formatMetricDiff, isRegressionMetric, } from "../lib/perf-diff-eval.js";
|
|
8
|
+
import { runCommand } from "../lib/run-command.js";
|
|
9
|
+
import { buildEventCount, buildSessionEnv, planTreeKill, resolveProfileIdentity, SESSION_ENV_KEYS, validateSeconds, waitForIngest, } from "../lib/run-profile-test-lib.js";
|
|
10
|
+
const DEFAULT_INGEST_TIMEOUT_S = 180;
|
|
11
|
+
const DEFAULT_POLL_INTERVAL_S = 5;
|
|
12
|
+
// Generous finite default so a game that never self-quits cannot hang a CI job
|
|
13
|
+
// forever (the F53 failure mode); 0 disables the bound for interactive/local use.
|
|
14
|
+
const DEFAULT_COMMAND_TIMEOUT_S = 1800;
|
|
15
|
+
// Cap so command-timeout * 1000 stays within setTimeout's signed 32-bit delay
|
|
16
|
+
// (2^31-1 ms); a larger value would be clamped to 1ms and fire almost at once.
|
|
17
|
+
const MAX_COMMAND_TIMEOUT_S = Math.floor(2 ** 31 / 1000);
|
|
18
|
+
/** Best-effort `git` lookup; returns undefined when git is absent or fails. */
|
|
19
|
+
function gitOutput(args) {
|
|
20
|
+
try {
|
|
21
|
+
const out = execFileSync("git", args, {
|
|
22
|
+
encoding: "utf8",
|
|
23
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
24
|
+
}).trim();
|
|
25
|
+
return out.length > 0 ? out : undefined;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Parse a seconds-valued flag with a descriptive error, or fall back. */
|
|
32
|
+
function parseSeconds(value, flag, fallback, opts = {}) {
|
|
33
|
+
const result = validateSeconds(value, fallback, opts);
|
|
34
|
+
if ("error" in result) {
|
|
35
|
+
error(`--${flag} ${result.error}`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
return result.value;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Forward the project id and base URL so a CI game build that reads them from the
|
|
42
|
+
* environment targets the same project/endpoint the gate queries (they must match
|
|
43
|
+
* or the gate would read a different project's builds).
|
|
44
|
+
*
|
|
45
|
+
* Deliberately does NOT forward the API key: the gate's key needs analytics:read,
|
|
46
|
+
* while the game's SDK needs a separate events:write ingest key (the presets keep
|
|
47
|
+
* ingest a separate plane). Overwriting the child's FRAMEDASH_API_KEY would make
|
|
48
|
+
* the game send events with a read-only key and 403 at ingest, so the game keeps
|
|
49
|
+
* whatever ingest key the CI environment already set for it.
|
|
50
|
+
*/
|
|
51
|
+
function passthroughProjectEnv(config) {
|
|
52
|
+
return {
|
|
53
|
+
FRAMEDASH_PROJECT_ID: config.projectId,
|
|
54
|
+
FRAMEDASH_BASE_URL: config.baseUrl,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** Validate and resolve the regression-gate flags, exiting on a misconfig. */
|
|
58
|
+
function resolveGateOptions(values, candidateBuildId) {
|
|
59
|
+
let metric;
|
|
60
|
+
if (values.metric !== undefined) {
|
|
61
|
+
const raw = values.metric.trim();
|
|
62
|
+
if (!isRegressionMetric(raw)) {
|
|
63
|
+
error(`Invalid --metric '${raw}'. Allowed: frame_time, memory, gpu_time`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
metric = raw;
|
|
67
|
+
}
|
|
68
|
+
let thresholdPct = 0;
|
|
69
|
+
if (values.threshold !== undefined) {
|
|
70
|
+
thresholdPct = Number(values.threshold);
|
|
71
|
+
if (!Number.isFinite(thresholdPct) || thresholdPct < 0) {
|
|
72
|
+
error("--threshold must be a non-negative number (percent)");
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const baseline = values.baseline?.trim();
|
|
77
|
+
// A build compared against itself is a zero diff that would silently pass.
|
|
78
|
+
if (baseline && baseline === candidateBuildId) {
|
|
79
|
+
error("--baseline must differ from the candidate build id (the build under test).");
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
if (values["fail-on-regression"] && !baseline) {
|
|
83
|
+
error("--fail-on-regression requires --baseline (the known-good build to compare against).");
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
return { metric, thresholdPct, baseline };
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Terminate the launched process TREE by pid. A plain child kill leaves
|
|
90
|
+
* grandchildren behind (a game launched via the shell spawns helper processes),
|
|
91
|
+
* so use the platform-specific whole-tree strategy from planTreeKill: taskkill
|
|
92
|
+
* /T /F on win32, or a SIGKILL to the process group on POSIX (falling back to the
|
|
93
|
+
* direct pid if the group is already gone). planTreeKill returns a noop plan for a
|
|
94
|
+
* missing/non-positive pid (which would otherwise signal our own group or every
|
|
95
|
+
* process on POSIX). Best-effort: never throws.
|
|
96
|
+
*/
|
|
97
|
+
function killProcessTree(pid) {
|
|
98
|
+
const plan = planTreeKill(process.platform, pid);
|
|
99
|
+
if (plan.platform === "noop")
|
|
100
|
+
return;
|
|
101
|
+
if (plan.platform === "win32") {
|
|
102
|
+
spawnSync(plan.command, plan.args, { stdio: "ignore" });
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
process.kill(plan.groupPid, "SIGKILL");
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
try {
|
|
110
|
+
// -groupPid is the validated positive child pid; fall back to it directly.
|
|
111
|
+
process.kill(-plan.groupPid, "SIGKILL");
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// The process (group) is already gone; nothing left to kill.
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Spawn the profiling command (inheriting stdio) and resolve when it exits.
|
|
120
|
+
* Bounds the run with an overall timeout: on win32 the child is the shell, whose
|
|
121
|
+
* whole tree taskkill terminates; on POSIX the child is spawned detached so we
|
|
122
|
+
* can signal its process group. timeoutMs <= 0 disables the bound. Rejects only
|
|
123
|
+
* on a spawn error (ENOENT etc.).
|
|
124
|
+
*/
|
|
125
|
+
function runProfilingCommand(command, env, timeoutMs) {
|
|
126
|
+
return new Promise((resolve, reject) => {
|
|
127
|
+
const child = spawn(command, {
|
|
128
|
+
shell: true,
|
|
129
|
+
stdio: "inherit",
|
|
130
|
+
env,
|
|
131
|
+
// POSIX: run in a new process group so killProcessTree can signal the whole
|
|
132
|
+
// group. win32 has no process groups here; taskkill /T handles the tree.
|
|
133
|
+
detached: process.platform !== "win32",
|
|
134
|
+
});
|
|
135
|
+
let timedOut = false;
|
|
136
|
+
let timer;
|
|
137
|
+
// A detached POSIX child does NOT share the terminal's signals, so a Ctrl-C or
|
|
138
|
+
// CI cancellation that terminates THIS CLI would orphan the game's process
|
|
139
|
+
// group. Kill the tree on those signals first, then exit with the conventional
|
|
140
|
+
// 128 + signal-number code so the exit status still reflects the signal.
|
|
141
|
+
const forwardedSignals = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
142
|
+
const cleanup = () => {
|
|
143
|
+
if (timer)
|
|
144
|
+
clearTimeout(timer);
|
|
145
|
+
for (const s of forwardedSignals)
|
|
146
|
+
process.off(s, onSignal);
|
|
147
|
+
};
|
|
148
|
+
const onSignal = (signal) => {
|
|
149
|
+
cleanup();
|
|
150
|
+
killProcessTree(child.pid);
|
|
151
|
+
process.exit(128 + (osConstants.signals[signal] ?? 0));
|
|
152
|
+
};
|
|
153
|
+
for (const s of forwardedSignals)
|
|
154
|
+
process.on(s, onSignal);
|
|
155
|
+
if (timeoutMs > 0) {
|
|
156
|
+
timer = setTimeout(() => {
|
|
157
|
+
timedOut = true;
|
|
158
|
+
killProcessTree(child.pid);
|
|
159
|
+
}, timeoutMs);
|
|
160
|
+
}
|
|
161
|
+
child.on("error", (err) => {
|
|
162
|
+
cleanup();
|
|
163
|
+
reject(err);
|
|
164
|
+
});
|
|
165
|
+
child.on("exit", (code, signal) => {
|
|
166
|
+
cleanup();
|
|
167
|
+
resolve({ status: code, signal, timedOut });
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
/** Launch the profiling command and exit on any failure (or timeout). */
|
|
172
|
+
async function launchProfilingRun(command, env, timeoutMs) {
|
|
173
|
+
let result;
|
|
174
|
+
try {
|
|
175
|
+
result = await runProfilingCommand(command, env, timeoutMs);
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
error(`Failed to launch --command: ${err instanceof Error ? err.message : String(err)}`);
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
// A timeout is incomplete data: fail closed WITHOUT gating, same philosophy as a
|
|
182
|
+
// non-zero exit. Checked first because the kill also sets a signal on the child.
|
|
183
|
+
if (result.timedOut) {
|
|
184
|
+
error(`Profiling command exceeded the --command-timeout of ${timeoutMs / 1000}s and was killed; ` +
|
|
185
|
+
"not gating on incomplete data. If the game does not exit on its own, make your " +
|
|
186
|
+
"automation quit when the run finishes, or raise --command-timeout (0 disables the bound).");
|
|
187
|
+
process.exit(1);
|
|
188
|
+
}
|
|
189
|
+
if (result.signal) {
|
|
190
|
+
error(`Profiling command was terminated by signal ${result.signal}.`);
|
|
191
|
+
// Conventional 128 + signal number so CI sees a meaningful non-zero code.
|
|
192
|
+
process.exit(128 + (osConstants.signals[result.signal] ?? 0));
|
|
193
|
+
}
|
|
194
|
+
if (typeof result.status === "number" && result.status !== 0) {
|
|
195
|
+
error(`Profiling command exited with code ${result.status}; not gating on incomplete data.`);
|
|
196
|
+
process.exit(result.status);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Fetch the candidate build's list row (the suffix the pre-run snapshot and poll
|
|
201
|
+
* share). Scopes to the candidate buildId so the server's newest-50 cap cannot
|
|
202
|
+
* hide a re-run of an older build, and uses fresh=1 to bypass the aggregation
|
|
203
|
+
* cache so the wait reads live state (a cached, stale-low count could let an old
|
|
204
|
+
* run's data pass the wait).
|
|
205
|
+
*/
|
|
206
|
+
function fetchBuilds(client, values, buildId) {
|
|
207
|
+
return client.get(client.projectPath(buildBuildsPath({ days: values.days, buildId, fresh: true })));
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* The candidate's perf-event count right now. Read BEFORE the run so the ingest
|
|
211
|
+
* wait can require the count to grow -- a CI re-run for the same build_id must
|
|
212
|
+
* not be satisfied by the previous run's data. Fails closed if the snapshot
|
|
213
|
+
* cannot be read: without a reliable baseline the wait could pass on old data
|
|
214
|
+
* (a false-green gate), so abort before launch rather than guess 0.
|
|
215
|
+
*/
|
|
216
|
+
async function readPriorEventCount(client, values, buildId) {
|
|
217
|
+
let builds;
|
|
218
|
+
try {
|
|
219
|
+
builds = await fetchBuilds(client, values, buildId);
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
error(`Could not read the build's pre-run event count (${err instanceof Error ? err.message : String(err)}). Aborting before launch; pass --skip-wait if your SDK flushes synchronously.`);
|
|
223
|
+
process.exit(1);
|
|
224
|
+
}
|
|
225
|
+
// A non-error response that is not an array means we cannot trust the baseline
|
|
226
|
+
// (it could read as 0 and let an old run's data satisfy the wait), so fail closed.
|
|
227
|
+
if (!Array.isArray(builds)) {
|
|
228
|
+
error("The builds API returned an unexpected response (no build list); cannot establish a " +
|
|
229
|
+
"pre-run baseline. Aborting before launch; pass --skip-wait if your SDK flushes synchronously.");
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
return buildEventCount(builds, buildId);
|
|
233
|
+
}
|
|
234
|
+
/** Poll the builds list until the candidate gains fresh events; exit on timeout. */
|
|
235
|
+
async function awaitIngest(client, values, buildId, priorEventCount, timeoutMs, intervalMs) {
|
|
236
|
+
success(`Waiting up to ${timeoutMs / 1000}s for build '${buildId}' to ingest...`);
|
|
237
|
+
const landed = await waitForIngest({
|
|
238
|
+
buildId,
|
|
239
|
+
minEventCount: priorEventCount,
|
|
240
|
+
timeoutMs,
|
|
241
|
+
intervalMs,
|
|
242
|
+
now: () => Date.now(),
|
|
243
|
+
sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
244
|
+
fetchBuilds: () => fetchBuilds(client, values, buildId),
|
|
245
|
+
});
|
|
246
|
+
if (!landed) {
|
|
247
|
+
error(`Build '${buildId}' did not ingest fresh events within ${timeoutMs / 1000}s. ` +
|
|
248
|
+
"The SDK may not have flushed, or ingest/aggregation is lagging. " +
|
|
249
|
+
"Increase --ingest-timeout, or check with 'framedash builds'. " +
|
|
250
|
+
`Also confirm the game picked up the awaited build id: --build-id reaches the game via the ` +
|
|
251
|
+
`exported FRAMEDASH_BUILD_ID env var and the SDK's BeginAutomatedSessionFromEnvironment() ` +
|
|
252
|
+
`auto-session pickup -- a game that hardcodes its buildId will never match '${buildId}'.`);
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
success(`Build '${buildId}' ingested.`);
|
|
256
|
+
}
|
|
257
|
+
/** Fetch the build comparison, print it, and (with --fail-on-regression) gate. */
|
|
258
|
+
async function runRegressionGate(client, config, values, gate) {
|
|
259
|
+
const comparison = await client.get(client.projectPath(buildBuildComparePath({
|
|
260
|
+
baseline: gate.baseline,
|
|
261
|
+
candidate: gate.candidate,
|
|
262
|
+
days: values.days,
|
|
263
|
+
mapId: values.map,
|
|
264
|
+
platform: values.platform,
|
|
265
|
+
// Bypass the compare cache so a re-run gates on the just-ingested data.
|
|
266
|
+
fresh: true,
|
|
267
|
+
})));
|
|
268
|
+
// A non-error 2xx with a malformed body must fail closed, not crash the gate.
|
|
269
|
+
if (!comparison || !Array.isArray(comparison.diffs)) {
|
|
270
|
+
error("Unexpected response from the builds/compare API (no comparison data).");
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
log(config.format === "json"
|
|
274
|
+
? formatOutput(comparison, "json")
|
|
275
|
+
: formatOutput(comparison.diffs, config.format));
|
|
276
|
+
const verdict = evaluateRegression(comparison, {
|
|
277
|
+
metric: gate.metric,
|
|
278
|
+
thresholdPct: gate.thresholdPct,
|
|
279
|
+
});
|
|
280
|
+
// Report-only (no --fail-on-regression): never exit non-zero, but still warn --
|
|
281
|
+
// on a regression, AND when nothing was comparable -- so a misconfigured CI job
|
|
282
|
+
// (wrong build IDs, or a metric with no data) does not silently go green.
|
|
283
|
+
if (!values["fail-on-regression"]) {
|
|
284
|
+
if (verdict.evaluated.length === 0) {
|
|
285
|
+
warn(gate.metric
|
|
286
|
+
? `No comparable '${gate.metric}' data between these builds; the gate evaluated nothing (check the build IDs / metric).`
|
|
287
|
+
: "No comparable performance data between these builds; the gate evaluated nothing (check the build IDs).");
|
|
288
|
+
}
|
|
289
|
+
else if (verdict.failed) {
|
|
290
|
+
warn(`Regression detected (threshold ${gate.thresholdPct}%): ${verdict.offenders
|
|
291
|
+
.map(formatMetricDiff)
|
|
292
|
+
.join("; ")}. Pass --fail-on-regression to fail the build.`);
|
|
293
|
+
}
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
// Fail closed: nothing comparable means the gate is unmeasured, not passed.
|
|
297
|
+
if (verdict.evaluated.length === 0) {
|
|
298
|
+
error(gate.metric
|
|
299
|
+
? `No comparable '${gate.metric}' data between these builds; cannot evaluate a regression.`
|
|
300
|
+
: "No comparable performance data between these builds; cannot evaluate a regression.");
|
|
301
|
+
process.exit(1);
|
|
302
|
+
}
|
|
303
|
+
if (verdict.failed) {
|
|
304
|
+
error(`Performance regression detected (threshold ${gate.thresholdPct}%): ${verdict.offenders
|
|
305
|
+
.map(formatMetricDiff)
|
|
306
|
+
.join("; ")}`);
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
309
|
+
success(`No performance regression beyond ${gate.thresholdPct}% (${verdict.evaluated.length} metric(s) checked).`);
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Turnkey CI profiling gate: export the FRAMEDASH_* session contract, launch the
|
|
313
|
+
* configured game/profiling command, wait for its perf data to ingest, then run
|
|
314
|
+
* the perf-diff regression gate against a baseline build.
|
|
315
|
+
*/
|
|
316
|
+
export async function runProfileTest(args) {
|
|
317
|
+
await runCommand({
|
|
318
|
+
args,
|
|
319
|
+
help: HELP,
|
|
320
|
+
options: {
|
|
321
|
+
command: { type: "string" },
|
|
322
|
+
"build-id": { type: "string" },
|
|
323
|
+
branch: { type: "string" },
|
|
324
|
+
commit: { type: "string" },
|
|
325
|
+
scenario: { type: "string" },
|
|
326
|
+
"command-timeout": { type: "string" },
|
|
327
|
+
"ingest-timeout": { type: "string" },
|
|
328
|
+
"poll-interval": { type: "string" },
|
|
329
|
+
"skip-wait": { type: "boolean" },
|
|
330
|
+
baseline: { type: "string" },
|
|
331
|
+
metric: { type: "string" },
|
|
332
|
+
threshold: { type: "string" },
|
|
333
|
+
days: { type: "string" },
|
|
334
|
+
map: { type: "string" },
|
|
335
|
+
platform: { type: "string" },
|
|
336
|
+
"fail-on-regression": { type: "boolean" },
|
|
337
|
+
},
|
|
338
|
+
}, async ({ client, config, values }) => {
|
|
339
|
+
const command = values.command?.trim();
|
|
340
|
+
if (!command) {
|
|
341
|
+
error("--command is required (the game/profiling command to launch)");
|
|
342
|
+
process.exit(1);
|
|
343
|
+
}
|
|
344
|
+
// Resolve the build identity: explicit flags win, else fall back to git.
|
|
345
|
+
// Only spawn `git` for a field the caller did not provide.
|
|
346
|
+
const branchFlag = values.branch;
|
|
347
|
+
const commitFlag = values.commit;
|
|
348
|
+
const identity = resolveProfileIdentity({
|
|
349
|
+
buildId: values["build-id"],
|
|
350
|
+
branch: branchFlag,
|
|
351
|
+
commit: commitFlag,
|
|
352
|
+
scenario: values.scenario,
|
|
353
|
+
}, {
|
|
354
|
+
branch: branchFlag ? undefined : gitOutput(["rev-parse", "--abbrev-ref", "HEAD"]),
|
|
355
|
+
commit: commitFlag ? undefined : gitOutput(["rev-parse", "HEAD"]),
|
|
356
|
+
});
|
|
357
|
+
if (!identity) {
|
|
358
|
+
error("Could not determine a build id: pass --build-id or --commit, or run inside a git repo.");
|
|
359
|
+
process.exit(1);
|
|
360
|
+
}
|
|
361
|
+
// Validate everything up front so a misconfig fails before we spend minutes
|
|
362
|
+
// running the game only to reject the result.
|
|
363
|
+
const gate = resolveGateOptions(values, identity.buildId);
|
|
364
|
+
// Bounds only the launched game/profiling process, NOT the ingest wait
|
|
365
|
+
// (--ingest-timeout covers that separately). 0 disables the bound.
|
|
366
|
+
const commandTimeoutMs = parseSeconds(values["command-timeout"], "command-timeout", DEFAULT_COMMAND_TIMEOUT_S, {
|
|
367
|
+
allowZero: true,
|
|
368
|
+
max: MAX_COMMAND_TIMEOUT_S,
|
|
369
|
+
}) * 1000;
|
|
370
|
+
const ingestTimeoutMs = parseSeconds(values["ingest-timeout"], "ingest-timeout", DEFAULT_INGEST_TIMEOUT_S) * 1000;
|
|
371
|
+
const pollIntervalMs = parseSeconds(values["poll-interval"], "poll-interval", DEFAULT_POLL_INTERVAL_S) * 1000;
|
|
372
|
+
// The ingest snapshot/poll run in a retry loop, so they need a client that
|
|
373
|
+
// THROWS on a transient 429/5xx (the default client exits the process,
|
|
374
|
+
// which would kill the wait on a temporary blip). The gate compare keeps
|
|
375
|
+
// the default exit-on-error client (a compare failure should fail the gate).
|
|
376
|
+
const skipWait = Boolean(values["skip-wait"]);
|
|
377
|
+
const pollClient = skipWait
|
|
378
|
+
? client
|
|
379
|
+
: createClient(config.baseUrl, config.credential, config.projectId, {
|
|
380
|
+
throwOnError: true,
|
|
381
|
+
});
|
|
382
|
+
// Snapshot the candidate's event count BEFORE the run (only when we will
|
|
383
|
+
// wait), so the ingest wait can require fresh events rather than accepting
|
|
384
|
+
// a prior run's data for the same build_id.
|
|
385
|
+
const priorEventCount = skipWait
|
|
386
|
+
? 0
|
|
387
|
+
: await readPriorEventCount(pollClient, values, identity.buildId);
|
|
388
|
+
// 1) Launch the profiling command with the FRAMEDASH_* contract exported, so
|
|
389
|
+
// the SDK's BeginAutomatedSessionFromEnvironment() stamps build_id + ci.* tags
|
|
390
|
+
// onto every event (including the perf_heartbeat the gate reads).
|
|
391
|
+
success(`Running profiling build '${identity.buildId}'${identity.scenario ? ` (scenario: ${identity.scenario})` : ""}`);
|
|
392
|
+
// Build the child environment. Two safeguards:
|
|
393
|
+
// 1) The runner owns the whole FRAMEDASH_* session contract: clear any
|
|
394
|
+
// contract key it did not set this run, so the game cannot inherit stale
|
|
395
|
+
// branch/commit/scenario left in the environment by a prior CI step.
|
|
396
|
+
// 2) The gate key (analytics:read) must not be handed to the game as its key
|
|
397
|
+
// -- the game needs a separate events:write ingest key. When the gate key
|
|
398
|
+
// came from FRAMEDASH_API_KEY in the environment (no --api-key flag), strip
|
|
399
|
+
// it from the child so the game cannot inherit a read key.
|
|
400
|
+
const sessionEnv = buildSessionEnv(identity);
|
|
401
|
+
const childEnv = {
|
|
402
|
+
...process.env,
|
|
403
|
+
...passthroughProjectEnv(config),
|
|
404
|
+
...sessionEnv,
|
|
405
|
+
};
|
|
406
|
+
for (const key of SESSION_ENV_KEYS) {
|
|
407
|
+
if (!(key in sessionEnv))
|
|
408
|
+
delete childEnv[key];
|
|
409
|
+
}
|
|
410
|
+
if (!values["api-key"] && !values["api-key-file"] && process.env.FRAMEDASH_API_KEY) {
|
|
411
|
+
delete childEnv.FRAMEDASH_API_KEY;
|
|
412
|
+
warn("Removed the gate's FRAMEDASH_API_KEY (an analytics:read key) from the launched " +
|
|
413
|
+
"game's environment so it is not used as the game's ingest key. Set the game's " +
|
|
414
|
+
"events:write key separately, or pass the gate key via --api-key/--api-key-file.");
|
|
415
|
+
}
|
|
416
|
+
await launchProfilingRun(command, childEnv, commandTimeoutMs);
|
|
417
|
+
// 2) Wait for the candidate build's perf events to land (unless skipped).
|
|
418
|
+
if (!skipWait) {
|
|
419
|
+
await awaitIngest(pollClient, values, identity.buildId, priorEventCount, ingestTimeoutMs, pollIntervalMs);
|
|
420
|
+
}
|
|
421
|
+
// 3) Run the perf-diff gate when a baseline is given; otherwise the run is
|
|
422
|
+
// complete (the SDK reported the build; the caller can compare it later).
|
|
423
|
+
if (!gate.baseline) {
|
|
424
|
+
success("Profiling run complete. Pass --baseline to gate on a build-over-build regression.");
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
await runRegressionGate(client, config, values, {
|
|
428
|
+
baseline: gate.baseline,
|
|
429
|
+
candidate: identity.buildId,
|
|
430
|
+
metric: gate.metric,
|
|
431
|
+
thresholdPct: gate.thresholdPct,
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
const HELP = `Usage: framedash run-profile-test --command "<cmd>" [options]
|
|
436
|
+
|
|
437
|
+
Run an automated profiling test end-to-end for CI: export the FRAMEDASH_*
|
|
438
|
+
session contract, launch your game/profiling command, wait for its performance
|
|
439
|
+
data to ingest, then (with --baseline) gate on a build-over-build regression.
|
|
440
|
+
|
|
441
|
+
The launched command inherits these environment variables; an SDK that calls
|
|
442
|
+
BeginAutomatedSessionFromEnvironment() picks them up automatically:
|
|
443
|
+
FRAMEDASH_BUILD_ID the candidate build_id (first-class)
|
|
444
|
+
FRAMEDASH_GIT_BRANCH ci.branch attribute
|
|
445
|
+
FRAMEDASH_GIT_COMMIT ci.commit attribute
|
|
446
|
+
FRAMEDASH_TEST_SCENARIO ci.scenario attribute
|
|
447
|
+
The build id from --build-id (or the git default) reaches the game ONLY via the
|
|
448
|
+
exported FRAMEDASH_BUILD_ID env var, which BeginAutomatedSessionFromEnvironment()
|
|
449
|
+
reads. A game that hardcodes its buildId will never match the awaited build, so
|
|
450
|
+
the ingest wait will time out -- do not hardcode the SDK build id in a CI build.
|
|
451
|
+
FRAMEDASH_PROJECT_ID / FRAMEDASH_BASE_URL are forwarded so the build targets the
|
|
452
|
+
same project the gate queries. The gate uses an analytics:read key while your
|
|
453
|
+
game needs a separate events:write ingest key: pass the gate key with
|
|
454
|
+
--api-key/--api-key-file and keep the game's ingest key in the launched command's
|
|
455
|
+
FRAMEDASH_API_KEY, so the gate key does not get inherited as the game's key.
|
|
456
|
+
|
|
457
|
+
Required:
|
|
458
|
+
--command <cmd> Game/profiling command to launch (run via the shell)
|
|
459
|
+
|
|
460
|
+
Run bound (kills the launched process tree on timeout so a game that never
|
|
461
|
+
self-quits cannot hang a CI job forever; a timeout fails the run without gating):
|
|
462
|
+
--command-timeout <s> Max seconds the launched command may run (default: 1800,
|
|
463
|
+
0 disables the bound)
|
|
464
|
+
|
|
465
|
+
Build identity (each defaults as noted; exported to the child):
|
|
466
|
+
--build-id <id> Candidate build_id (default: --commit, else git HEAD)
|
|
467
|
+
--branch <name> default: git rev-parse --abbrev-ref HEAD
|
|
468
|
+
--commit <sha> default: git rev-parse HEAD
|
|
469
|
+
--scenario <name> Test scenario label (no default)
|
|
470
|
+
|
|
471
|
+
Note: the gate compares ALL of a build_id's samples in the window (the build_id
|
|
472
|
+
is the comparison unit, as in 'perf-diff'). If a CI job may re-run the SAME
|
|
473
|
+
commit, give each run a unique build_id (e.g. --build-id "$GIT_SHA-$RUN_ID") so
|
|
474
|
+
the comparison is not diluted by a previous run's samples.
|
|
475
|
+
|
|
476
|
+
Ingest wait (polls a cache-bypassing live read and waits for the build's
|
|
477
|
+
perf-event count to GROW past its pre-run value, so a re-run for the same build
|
|
478
|
+
id is not satisfied by old data):
|
|
479
|
+
--ingest-timeout <s> Max seconds to wait for fresh events (default: 180)
|
|
480
|
+
--poll-interval <s> Seconds between build-list polls (default: 5)
|
|
481
|
+
--skip-wait Skip the ingest wait (e.g. the SDK flushed synchronously)
|
|
482
|
+
|
|
483
|
+
Regression gate (runs only when --baseline is set):
|
|
484
|
+
--baseline <id> Known-good build_id to compare the candidate against
|
|
485
|
+
--metric <name> Gate on one metric: frame_time, memory, gpu_time (default: all)
|
|
486
|
+
--threshold <pct> Tolerate a regression up to this percent (default: 0)
|
|
487
|
+
--fail-on-regression Exit 1 on a regression beyond the threshold (needs --baseline)
|
|
488
|
+
--days <n> Comparison window in days: 7, 14, 30, 90 (default: 30)
|
|
489
|
+
--map <id> Restrict the comparison to one map
|
|
490
|
+
--platform <name> Restrict the comparison to one platform
|
|
491
|
+
|
|
492
|
+
Global:
|
|
493
|
+
--api-key <key> API key (or FRAMEDASH_API_KEY env)
|
|
494
|
+
--project-id <uuid> Project ID (or FRAMEDASH_PROJECT_ID env)
|
|
495
|
+
--base-url <url> API base URL (default: https://app.framedash.dev)
|
|
496
|
+
--format <fmt> Comparison output format: json, table, csv (default: json)
|
|
497
|
+
-h, --help Show help
|
|
498
|
+
|
|
499
|
+
Example (GitHub Actions gate):
|
|
500
|
+
framedash run-profile-test \\
|
|
501
|
+
--command "./Build/Game.exe -nullrhi -ExecCmds='Automation RunTest Perf'" \\
|
|
502
|
+
--scenario nightly --baseline "$BASE_SHA" --threshold 5 --fail-on-regression`;
|
|
503
|
+
//# sourceMappingURL=run-profile-test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-profile-test.js","sourceRoot":"","sources":["../../src/commands/run-profile-test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threshold-profiles.d.ts","sourceRoot":"","sources":["../../src/commands/threshold-profiles.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,iBAAiB,mCAE5B,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { formatOutput } from "../lib/formatters.js";
|
|
2
|
+
import { log } from "../lib/logger.js";
|
|
3
|
+
import { runCommand, withSubcommands } from "../lib/run-command.js";
|
|
4
|
+
const HELP = `Usage: framedash threshold-profiles <subcommand> [options]
|
|
5
|
+
|
|
6
|
+
List threshold profiles (performance budgets used by alert rules).
|
|
7
|
+
|
|
8
|
+
Subcommands:
|
|
9
|
+
list List threshold profiles
|
|
10
|
+
|
|
11
|
+
Run 'framedash threshold-profiles <subcommand> --help' for more info.`;
|
|
12
|
+
export const thresholdProfiles = withSubcommands("threshold-profiles", HELP, {
|
|
13
|
+
list: thresholdProfilesList,
|
|
14
|
+
});
|
|
15
|
+
async function thresholdProfilesList(args) {
|
|
16
|
+
await runCommand({
|
|
17
|
+
args,
|
|
18
|
+
help: "Usage: framedash threshold-profiles list [--format json|table|csv] [global options]",
|
|
19
|
+
}, async ({ client, config }) => {
|
|
20
|
+
const data = await client.get(client.projectPath("threshold-profiles"));
|
|
21
|
+
log(formatOutput(data, config.format));
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=threshold-profiles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threshold-profiles.js","sourceRoot":"","sources":["../../src/commands/threshold-profiles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEpE,MAAM,IAAI,GAAG;;;;;;;sEAOyD,CAAC;AAEvE,MAAM,CAAC,MAAM,iBAAiB,GAAG,eAAe,CAAC,oBAAoB,EAAE,IAAI,EAAE;IAC5E,IAAI,EAAE,qBAAqB;CAC3B,CAAC,CAAC;AAEH,KAAK,UAAU,qBAAqB,CAAC,IAAc;IAClD,MAAM,UAAU,CACf;QACC,IAAI;QACJ,IAAI,EAAE,qFAAqF;KAC3F,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE;QAC5B,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,oBAAoB,CAAC,CAAC,CAAC;QACxE,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACxC,CAAC,CACD,CAAC;AACH,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -6,14 +6,21 @@ const VERSION = JSON.parse(readFileSync(new URL("../package.json", import.meta.u
|
|
|
6
6
|
const HELP = `Usage: framedash <command> [options]
|
|
7
7
|
|
|
8
8
|
Commands:
|
|
9
|
-
|
|
9
|
+
login Sign in via the browser (interactive OAuth; CI should use
|
|
10
|
+
the FRAMEDASH_API_KEY env var instead)
|
|
11
|
+
logout Revoke and remove a stored browser login
|
|
12
|
+
auth Verify credentials, show their source, and list projects
|
|
10
13
|
status Show project status and key metrics
|
|
11
14
|
dashboard Show dashboard metrics
|
|
12
15
|
retention Show player retention cohorts
|
|
13
16
|
funnel Analyze event funnels
|
|
17
|
+
builds List builds seen for the project (for perf-diff)
|
|
18
|
+
perf-diff Compare two builds and gate CI on perf regressions
|
|
19
|
+
run-profile-test Run a profiling build, wait for ingest, gate on regression
|
|
14
20
|
query Execute a read-only ClickHouse query
|
|
15
21
|
alerts Manage alert rules (list, create, update, delete)
|
|
16
22
|
maps Manage maps (list, delete)
|
|
23
|
+
threshold-profiles List threshold profiles (perf budgets for alerts)
|
|
17
24
|
content Manage content registry (list, import, delete)
|
|
18
25
|
map-capture Upload captured map images
|
|
19
26
|
|
|
@@ -22,6 +29,8 @@ Global Options:
|
|
|
22
29
|
a key passed as --api-key is visible in the process
|
|
23
30
|
list and shell history.
|
|
24
31
|
--api-key-file <path> Read the API key from a file ('-' for stdin)
|
|
32
|
+
Without any API key, commands fall back to the OAuth
|
|
33
|
+
tokens stored by 'framedash login' for the base URL.
|
|
25
34
|
--project-id <uuid> Project ID (or FRAMEDASH_PROJECT_ID env)
|
|
26
35
|
--base-url <url> API base URL (default: https://app.framedash.dev)
|
|
27
36
|
--format <fmt> Output format: json, table, csv (default: json)
|
|
@@ -31,14 +40,20 @@ Global Options:
|
|
|
31
40
|
Run 'framedash <command> --help' for command-specific options.`;
|
|
32
41
|
/** Type-safe command registry — typos in export names become build-time errors. */
|
|
33
42
|
const COMMANDS = {
|
|
43
|
+
login: () => import("./commands/login.js").then((m) => m.login),
|
|
44
|
+
logout: () => import("./commands/logout.js").then((m) => m.logout),
|
|
34
45
|
auth: () => import("./commands/auth.js").then((m) => m.auth),
|
|
35
46
|
status: () => import("./commands/status.js").then((m) => m.status),
|
|
36
47
|
dashboard: () => import("./commands/dashboard.js").then((m) => m.dashboard),
|
|
37
48
|
retention: () => import("./commands/retention.js").then((m) => m.retention),
|
|
38
49
|
funnel: () => import("./commands/funnel.js").then((m) => m.funnel),
|
|
50
|
+
builds: () => import("./commands/builds.js").then((m) => m.builds),
|
|
51
|
+
"perf-diff": () => import("./commands/perf-diff.js").then((m) => m.perfDiff),
|
|
52
|
+
"run-profile-test": () => import("./commands/run-profile-test.js").then((m) => m.runProfileTest),
|
|
39
53
|
query: () => import("./commands/query.js").then((m) => m.query),
|
|
40
54
|
alerts: () => import("./commands/alerts.js").then((m) => m.alerts),
|
|
41
55
|
maps: () => import("./commands/maps.js").then((m) => m.maps),
|
|
56
|
+
"threshold-profiles": () => import("./commands/threshold-profiles.js").then((m) => m.thresholdProfiles),
|
|
42
57
|
content: () => import("./commands/content.js").then((m) => m.content),
|
|
43
58
|
"map-capture": () => import("./commands/map-capture.js").then((m) => m.mapCaptureCommand),
|
|
44
59
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAE7C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;KAC3F,OAAiB,CAAC;AAEpB,MAAM,IAAI,GAAG
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|