@keepsnappy/agent 0.1.0-alpha.12 → 0.1.0-alpha.13
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 +44 -44
- package/dist/appium-driver.js +7 -7
- package/dist/keepsnappy.js +80 -80
- package/examples/farm/flows/{perflab → keepsnappy}/begin-run.yaml +1 -1
- package/examples/farm/flows/{perflab → keepsnappy}/end-run.yaml +4 -4
- package/examples/farm/flows/keepsnappy/next-token.js +7 -0
- package/examples/farm/flows/{perflab/run-perflab.yaml → keepsnappy/run-keepsnappy.yaml} +1 -1
- package/examples/farm/flows/{perflab → keepsnappy}/send-markers.js +3 -3
- package/examples/farm/flows/keepsnappy/step-markers.yaml +13 -0
- package/examples/farm/flows/keepsnappy/tokens.js +2 -0
- package/examples/farm/workflows/{perflab-measure.yml → keepsnappy-measure.yml} +17 -17
- package/examples/farm/workflows/perf-device-run.fragment.yml +5 -5
- package/examples/github-actions.yml +14 -14
- package/examples/keepsnappy.yml +2 -2
- package/examples/maestro-steps/e2e-community-and-live-chat.yaml +10 -10
- package/maestro/{perflab → keepsnappy}/send-markers.js +3 -3
- package/maestro/{perflab → keepsnappy}/step.js +6 -6
- package/maestro/keepsnappy/step.yaml +11 -0
- package/package.json +1 -1
- package/examples/farm/flows/perflab/next-token.js +0 -7
- package/examples/farm/flows/perflab/step-markers.yaml +0 -13
- package/examples/farm/flows/perflab/tokens.js +0 -2
- package/maestro/perflab/step.yaml +0 -11
package/dist/keepsnappy.js
CHANGED
|
@@ -413,7 +413,7 @@ function validateConfig(raw, opts = {}) {
|
|
|
413
413
|
}
|
|
414
414
|
const pin = strList("steps.pin", steps.pin);
|
|
415
415
|
const output = section("output", ["dir"], false);
|
|
416
|
-
const outDir = str("output.dir", output.dir, false, "
|
|
416
|
+
const outDir = str("output.dir", output.dir, false, "keepsnappy-out");
|
|
417
417
|
let timeoutSeconds;
|
|
418
418
|
if (raw.timeout_seconds !== undefined) {
|
|
419
419
|
if (typeof raw.timeout_seconds !== "number" || raw.timeout_seconds <= 0)
|
|
@@ -519,12 +519,12 @@ import { join as join11, resolve as resolve3 } from "node:path";
|
|
|
519
519
|
|
|
520
520
|
// apps/agent/src/data-source.ts
|
|
521
521
|
import { join as join2 } from "node:path";
|
|
522
|
-
var
|
|
522
|
+
var KEEPSNAPPY_LOG = "Documents/keepsnappy/log.txt";
|
|
523
523
|
var SAFE_FILE = /^[\w.+:-]+$/;
|
|
524
524
|
var profilePath = (fileName) => {
|
|
525
525
|
if (!SAFE_FILE.test(fileName) || fileName.includes(".."))
|
|
526
526
|
throw new Error(`unsafe profile file name: ${fileName}`);
|
|
527
|
-
return `Documents/
|
|
527
|
+
return `Documents/keepsnappy/${fileName}`;
|
|
528
528
|
};
|
|
529
529
|
function parseSimctlDevices(json, udid) {
|
|
530
530
|
let data;
|
|
@@ -561,7 +561,7 @@ class IosSimulatorDataSource {
|
|
|
561
561
|
return this.deps.fs.readFile(join2(await this.dataDir(bundleId), profilePath(fileName)));
|
|
562
562
|
}
|
|
563
563
|
async fetchLog(bundleId) {
|
|
564
|
-
return this.deps.fs.readFile(join2(await this.dataDir(bundleId),
|
|
564
|
+
return this.deps.fs.readFile(join2(await this.dataDir(bundleId), KEEPSNAPPY_LOG));
|
|
565
565
|
}
|
|
566
566
|
async describeDevice() {
|
|
567
567
|
const r = await this.deps.spawner.run("xcrun", ["simctl", "list", "devices", this.udid === "booted" ? "booted" : "", "-j"].filter(Boolean));
|
|
@@ -580,7 +580,7 @@ class IosDeviceDataSource {
|
|
|
580
580
|
}
|
|
581
581
|
async fetchLog(bundleId) {
|
|
582
582
|
await this.deps.fs.mkdir(this.scratchDir);
|
|
583
|
-
const dest = join2(this.scratchDir, "
|
|
583
|
+
const dest = join2(this.scratchDir, "keepsnappy.log");
|
|
584
584
|
const r = await this.deps.spawner.run("xcrun", [
|
|
585
585
|
"devicectl",
|
|
586
586
|
"device",
|
|
@@ -593,7 +593,7 @@ class IosDeviceDataSource {
|
|
|
593
593
|
"--domain-identifier",
|
|
594
594
|
bundleId,
|
|
595
595
|
"--source",
|
|
596
|
-
|
|
596
|
+
KEEPSNAPPY_LOG,
|
|
597
597
|
"--destination",
|
|
598
598
|
dest
|
|
599
599
|
]);
|
|
@@ -614,7 +614,7 @@ class IosDeviceDataSource {
|
|
|
614
614
|
"--domain-identifier",
|
|
615
615
|
bundleId,
|
|
616
616
|
"--source",
|
|
617
|
-
|
|
617
|
+
KEEPSNAPPY_LOG,
|
|
618
618
|
"--destination",
|
|
619
619
|
dest
|
|
620
620
|
]);
|
|
@@ -651,7 +651,7 @@ class IosDeviceDataSource {
|
|
|
651
651
|
// apps/agent/src/flows.ts
|
|
652
652
|
import { basename, dirname, join as join3 } from "node:path";
|
|
653
653
|
var FLOW_FILE = /\.ya?ml$/i;
|
|
654
|
-
var NOT_FLOWS = new Set(["config.yaml", "config.yml", "keepsnappy.yml", "keepsnappy.yaml", "
|
|
654
|
+
var NOT_FLOWS = new Set(["config.yaml", "config.yml", "keepsnappy.yml", "keepsnappy.yaml", "keepsnappy.yml", "keepsnappy.yaml"]);
|
|
655
655
|
var flowStem = (path) => basename(path).replace(FLOW_FILE, "");
|
|
656
656
|
function flowTags(text) {
|
|
657
657
|
const head = text.split(/^---\s*$/m)[0] ?? "";
|
|
@@ -1193,20 +1193,20 @@ var MEASURES = {
|
|
|
1193
1193
|
worse: "none",
|
|
1194
1194
|
at: ["screen", "launch", "press", "drag", "other"],
|
|
1195
1195
|
producer: "derive",
|
|
1196
|
-
label: "Time of the
|
|
1197
|
-
short: "time of the
|
|
1196
|
+
label: "Time of the KeepSnappy SDK in the window",
|
|
1197
|
+
short: "time of the KeepSnappy SDK",
|
|
1198
1198
|
decimals: 1,
|
|
1199
|
-
what: "Time the
|
|
1199
|
+
what: "Time the KeepSnappy SDK spent on the JavaScript thread after each screen update in the window (reading the update, its profile and, when on, the React export); evidence only, never decides a result. Clock time, so it also holds pauses for garbage collection or other threads while the SDK ran. Not known when the window had more updates than the SDK keeps times for"
|
|
1200
1200
|
},
|
|
1201
1201
|
sdk_hook_commits: {
|
|
1202
1202
|
unit: "count",
|
|
1203
1203
|
worse: "none",
|
|
1204
1204
|
at: ["screen", "launch", "press", "drag", "other"],
|
|
1205
1205
|
producer: "derive",
|
|
1206
|
-
label: "Screen updates the
|
|
1206
|
+
label: "Screen updates the KeepSnappy SDK read",
|
|
1207
1207
|
short: "screen updates the SDK read",
|
|
1208
1208
|
decimals: 0,
|
|
1209
|
-
what: "How many screen updates the
|
|
1209
|
+
what: "How many screen updates the KeepSnappy SDK read in the window, the ones its time is summed over"
|
|
1210
1210
|
},
|
|
1211
1211
|
sdk_hook_capped: {
|
|
1212
1212
|
unit: "count",
|
|
@@ -1216,17 +1216,17 @@ var MEASURES = {
|
|
|
1216
1216
|
label: "Windows with too many updates to time the SDK",
|
|
1217
1217
|
short: "windows the SDK time is not known for",
|
|
1218
1218
|
decimals: 0,
|
|
1219
|
-
what: "Windows of this place with more screen updates than the
|
|
1219
|
+
what: "Windows of this place with more screen updates than the KeepSnappy SDK keeps times for (5000), so its time there is not given"
|
|
1220
1220
|
},
|
|
1221
1221
|
js_thread_app_ms: {
|
|
1222
1222
|
unit: "ms",
|
|
1223
1223
|
worse: "none",
|
|
1224
1224
|
at: ["screen", "launch", "press", "drag", "other"],
|
|
1225
1225
|
producer: "derive",
|
|
1226
|
-
label: "JavaScript thread CPU time without the
|
|
1226
|
+
label: "JavaScript thread CPU time without the KeepSnappy SDK",
|
|
1227
1227
|
short: "JavaScript thread time without the SDK",
|
|
1228
1228
|
decimals: 0,
|
|
1229
|
-
what: "JavaScript thread CPU time in the window minus the time the
|
|
1229
|
+
what: "JavaScript thread CPU time in the window minus the time the KeepSnappy SDK spent after screen updates (not below 0); evidence only, never decides a result. Approximate either way: the thread time is CPU time in 10 ms steps on Android, the SDK time is clock time and includes pauses inside it"
|
|
1230
1230
|
},
|
|
1231
1231
|
main_thread_ms: {
|
|
1232
1232
|
unit: "ms",
|
|
@@ -4205,7 +4205,7 @@ function netMetrics(windows) {
|
|
|
4205
4205
|
// packages/derive/dist/src/sampler.js
|
|
4206
4206
|
var obj6 = (v) => typeof v === "object" && v !== null ? v : {};
|
|
4207
4207
|
var num6 = (v) => typeof v === "number" && Number.isFinite(v) ? v : undefined;
|
|
4208
|
-
var SAMPLE_MARK = "
|
|
4208
|
+
var SAMPLE_MARK = "KEEPSNAPPY sample {";
|
|
4209
4209
|
function parseSamples(logText) {
|
|
4210
4210
|
const out = [];
|
|
4211
4211
|
for (const line of logText.split(`
|
|
@@ -4215,7 +4215,7 @@ function parseSamples(logText) {
|
|
|
4215
4215
|
continue;
|
|
4216
4216
|
let raw;
|
|
4217
4217
|
try {
|
|
4218
|
-
raw = JSON.parse(line.slice(sp + 1 + "
|
|
4218
|
+
raw = JSON.parse(line.slice(sp + 1 + "KEEPSNAPPY sample ".length));
|
|
4219
4219
|
} catch {
|
|
4220
4220
|
continue;
|
|
4221
4221
|
}
|
|
@@ -5184,7 +5184,7 @@ function parseMarkersNdjson(text) {
|
|
|
5184
5184
|
return out;
|
|
5185
5185
|
}
|
|
5186
5186
|
// packages/derive/dist/src/window-log.js
|
|
5187
|
-
var WINDOW_MARK = "
|
|
5187
|
+
var WINDOW_MARK = "KEEPSNAPPY window {";
|
|
5188
5188
|
function parseWindows(logText) {
|
|
5189
5189
|
const out = [];
|
|
5190
5190
|
for (const line of logText.split(`
|
|
@@ -5197,7 +5197,7 @@ function parseWindows(logText) {
|
|
|
5197
5197
|
continue;
|
|
5198
5198
|
let raw;
|
|
5199
5199
|
try {
|
|
5200
|
-
raw = JSON.parse(line.slice(sp + 1 + "
|
|
5200
|
+
raw = JSON.parse(line.slice(sp + 1 + "KEEPSNAPPY window ".length));
|
|
5201
5201
|
} catch {
|
|
5202
5202
|
continue;
|
|
5203
5203
|
}
|
|
@@ -5294,21 +5294,21 @@ async function uploadRuns(deps, outDir, runs, opts) {
|
|
|
5294
5294
|
skipped.push({ run_id: r.run_id, why: r.invalid_reason || "invalid" });
|
|
5295
5295
|
continue;
|
|
5296
5296
|
}
|
|
5297
|
-
const log = await deps.fs.readFile(join4(outDir, r.run_id, "
|
|
5297
|
+
const log = await deps.fs.readFile(join4(outDir, r.run_id, "keepsnappy.log"));
|
|
5298
5298
|
const windows = windowsInRun(parseWindows(log), r.t0, r.t1);
|
|
5299
5299
|
if (windows.length === 0) {
|
|
5300
5300
|
skipped.push({ run_id: r.run_id, why: "no-windows" });
|
|
5301
5301
|
continue;
|
|
5302
5302
|
}
|
|
5303
5303
|
const externalId = `${opts.externalPrefix}-${r.run_id}`;
|
|
5304
|
-
const created = await call(f, opts.ingestUrl, "/v1/runs", { "x-
|
|
5304
|
+
const created = await call(f, opts.ingestUrl, "/v1/runs", { "x-keepsnappy-key": opts.key }, {
|
|
5305
5305
|
external_id: externalId,
|
|
5306
5306
|
started_at: r.t0,
|
|
5307
5307
|
ended_at: r.t1,
|
|
5308
5308
|
device: { ...opts.device?.model ? { model: opts.device.model } : {}, ...opts.device?.os ? { os: opts.device.os } : {}, ...opts.device?.id ? { id: opts.device.id } : {}, exclusive: true },
|
|
5309
5309
|
meta: {
|
|
5310
5310
|
arm: r.arm,
|
|
5311
|
-
driver: opts.driver ?? "
|
|
5311
|
+
driver: opts.driver ?? "keepsnappy-run",
|
|
5312
5312
|
...opts.meta,
|
|
5313
5313
|
...r.scenario ? { scenario: r.scenario } : {},
|
|
5314
5314
|
...opts.runSetId ? { run_set_id: opts.runSetId } : {}
|
|
@@ -5318,16 +5318,16 @@ async function uploadRuns(deps, outDir, runs, opts) {
|
|
|
5318
5318
|
const token = String(created.run_token ?? "");
|
|
5319
5319
|
if (!runId || !token)
|
|
5320
5320
|
throw new UploadError("POST /v1/runs: response has no run_id/run_token");
|
|
5321
|
-
const items = windows.map((w) => `${w.endWall}
|
|
5321
|
+
const items = windows.map((w) => `${w.endWall} KEEPSNAPPY window ${JSON.stringify(w.raw)}`);
|
|
5322
5322
|
for (let i = 0;i * size < items.length; i++) {
|
|
5323
|
-
await call(f, opts.ingestUrl, "/v1/windows", { "x-
|
|
5323
|
+
await call(f, opts.ingestUrl, "/v1/windows", { "x-keepsnappy-key": opts.key, "x-keepsnappy-run-token": token }, {
|
|
5324
5324
|
batch_id: `${externalId}-${String(i + 1).padStart(4, "0")}`,
|
|
5325
5325
|
items: items.slice(i * size, (i + 1) * size)
|
|
5326
5326
|
});
|
|
5327
5327
|
}
|
|
5328
|
-
const samples = samplesInRun(parseSamples(log), r.t0, r.t1).map((x) => `${x.wall}
|
|
5328
|
+
const samples = samplesInRun(parseSamples(log), r.t0, r.t1).map((x) => `${x.wall} KEEPSNAPPY sample ${JSON.stringify(x.raw)}`);
|
|
5329
5329
|
for (let i = 0;i * size < samples.length; i++) {
|
|
5330
|
-
await call(f, opts.ingestUrl, "/v1/windows", { "x-
|
|
5330
|
+
await call(f, opts.ingestUrl, "/v1/windows", { "x-keepsnappy-key": opts.key, "x-keepsnappy-run-token": token }, {
|
|
5331
5331
|
batch_id: `${externalId}-s${String(i + 1).padStart(4, "0")}`,
|
|
5332
5332
|
items: samples.slice(i * size, (i + 1) * size)
|
|
5333
5333
|
});
|
|
@@ -5339,7 +5339,7 @@ async function uploadRuns(deps, outDir, runs, opts) {
|
|
|
5339
5339
|
for (const m of markers) {
|
|
5340
5340
|
await call(f, opts.ingestUrl, "/v1/markers", { authorization: `Bearer ${token}` }, { step: m.step, phase: m.phase, t_host: m.t_host });
|
|
5341
5341
|
}
|
|
5342
|
-
await call(f, opts.ingestUrl, `/v1/runs/${runId}/finish`, { "x-
|
|
5342
|
+
await call(f, opts.ingestUrl, `/v1/runs/${runId}/finish`, { "x-keepsnappy-key": opts.key }, {});
|
|
5343
5343
|
uploaded++;
|
|
5344
5344
|
}
|
|
5345
5345
|
return { uploaded, skipped };
|
|
@@ -5570,7 +5570,7 @@ function movedCounts(sc) {
|
|
|
5570
5570
|
}
|
|
5571
5571
|
var addCounts = (xs) => xs.reduce((a, c) => ({ worse: a.worse + c.worse, warnings: a.warnings + c.warnings, better: a.better + c.better, noChange: a.noChange + c.noChange, inconclusive: a.inconclusive + c.inconclusive }), { worse: 0, warnings: 0, better: 0, noChange: 0, inconclusive: 0 });
|
|
5572
5572
|
var setupLine = (h) => h?.measured_differently?.length ? `Measured differently (${h.measured_differently.join(", ")}) — ${NOT_BLOCKING}` : null;
|
|
5573
|
-
var keyHeader = (t) => ({ "x-
|
|
5573
|
+
var keyHeader = (t) => ({ "x-keepsnappy-key": t.key });
|
|
5574
5574
|
async function createRunSet(t, body) {
|
|
5575
5575
|
const r = await call(t.fetch ?? fetch, t.url, "/v1/run-sets", keyHeader(t), body);
|
|
5576
5576
|
return { run_set_id: String(r.run_set_id), dashboard_url: String(r.dashboard_url) };
|
|
@@ -7628,7 +7628,7 @@ function summarizeCpuProfile(raw, sessionId, name) {
|
|
|
7628
7628
|
};
|
|
7629
7629
|
}
|
|
7630
7630
|
|
|
7631
|
-
// packages/profile-analysis/dist/src/
|
|
7631
|
+
// packages/profile-analysis/dist/src/keepsnappy/hermes.js
|
|
7632
7632
|
var HERMES_ROOT = "[root]";
|
|
7633
7633
|
function hermesIdleAsCdp(profile) {
|
|
7634
7634
|
const hasParent = new Set;
|
|
@@ -8205,7 +8205,7 @@ function selectTaskCards(profile, durationMs, taskSet, classes) {
|
|
|
8205
8205
|
};
|
|
8206
8206
|
}
|
|
8207
8207
|
|
|
8208
|
-
// packages/profile-analysis/dist/src/
|
|
8208
|
+
// packages/profile-analysis/dist/src/keepsnappy/keys.js
|
|
8209
8209
|
function keyPath(path) {
|
|
8210
8210
|
const normalized = path.replace(/\\/g, "/");
|
|
8211
8211
|
const marker = normalized.lastIndexOf("node_modules/");
|
|
@@ -8228,9 +8228,9 @@ function functionKey(frame, options = {}) {
|
|
|
8228
8228
|
return `${name}@${compactLocation(frame.url, frame.lineNumber, frame.columnNumber)}`;
|
|
8229
8229
|
}
|
|
8230
8230
|
|
|
8231
|
-
// packages/profile-analysis/dist/src/
|
|
8232
|
-
var MEASURING_NAME = "(
|
|
8233
|
-
var SDK_PATH = /(?:^|\/)(?:node_modules\/)?@
|
|
8231
|
+
// packages/profile-analysis/dist/src/keepsnappy/measuring.js
|
|
8232
|
+
var MEASURING_NAME = "(KeepSnappy measuring)";
|
|
8233
|
+
var SDK_PATH = /(?:^|\/)(?:node_modules\/)?@keepsnappy\/react-native\//;
|
|
8234
8234
|
var SDK_FUNCTION_NAMES = new Set([
|
|
8235
8235
|
"recordCommit",
|
|
8236
8236
|
"recordUnmount",
|
|
@@ -8263,8 +8263,8 @@ var SDK_FUNCTION_NAMES = new Set([
|
|
|
8263
8263
|
"finalizeWindow",
|
|
8264
8264
|
"windowAggregate",
|
|
8265
8265
|
"perfLog",
|
|
8266
|
-
"
|
|
8267
|
-
"
|
|
8266
|
+
"handleKeepSnappyDeepLink",
|
|
8267
|
+
"isKeepSnappyUrl",
|
|
8268
8268
|
"logStartup",
|
|
8269
8269
|
"addStartupSection",
|
|
8270
8270
|
"extraStartupSections",
|
|
@@ -8343,7 +8343,7 @@ function foldMeasuring(profile) {
|
|
|
8343
8343
|
return { profile: { ...profile, nodes, samples }, measuringId };
|
|
8344
8344
|
}
|
|
8345
8345
|
|
|
8346
|
-
// packages/profile-analysis/dist/src/
|
|
8346
|
+
// packages/profile-analysis/dist/src/keepsnappy/task-cards.js
|
|
8347
8347
|
var REACT_INTERNALS = new Set([
|
|
8348
8348
|
"propagateParentContextChanges",
|
|
8349
8349
|
"lazilyPropagateParentContextChanges",
|
|
@@ -8481,7 +8481,7 @@ function finishCard(card, measuringMs, profileStartMs) {
|
|
|
8481
8481
|
const cleaned = { ...card, boundaryFrames, culprits: keep(card.culprits) };
|
|
8482
8482
|
const { title, location } = taskCardTitle(cleaned);
|
|
8483
8483
|
const what = card.boundaries === "measured" ? "task" : "stretch of uninterrupted JS work";
|
|
8484
|
-
const measuring = measuringMs >= 1 ? ` (${formatMs(measuringMs)} of it
|
|
8484
|
+
const measuring = measuringMs >= 1 ? ` (${formatMs(measuringMs)} of it KeepSnappy's own measuring)` : "";
|
|
8485
8485
|
return {
|
|
8486
8486
|
...cleaned,
|
|
8487
8487
|
headline: `${title} — a ${formatMs(card.durationMs)} ${what}${measuring}`,
|
|
@@ -8693,7 +8693,7 @@ function originClassTable(frames, origins) {
|
|
|
8693
8693
|
}
|
|
8694
8694
|
// packages/profile-analysis/dist/src/tracesift/lib/card-prompt.js
|
|
8695
8695
|
var DRAWABLE3 = new Set(SHOWN_CLASSES);
|
|
8696
|
-
// packages/profile-analysis/dist/src/
|
|
8696
|
+
// packages/profile-analysis/dist/src/keepsnappy/slice.js
|
|
8697
8697
|
function sliceCpuProfile(profile, window, options = {}) {
|
|
8698
8698
|
const offsetUs = (options.clockOffsetMs ?? 0) * 1000;
|
|
8699
8699
|
const t0Us = window.t0Ms * 1000 - offsetUs;
|
|
@@ -8765,10 +8765,10 @@ function pruneNodes(nodes, samples) {
|
|
|
8765
8765
|
return out;
|
|
8766
8766
|
});
|
|
8767
8767
|
}
|
|
8768
|
-
// packages/profile-analysis/dist/src/
|
|
8768
|
+
// packages/profile-analysis/dist/src/keepsnappy/source-map.js
|
|
8769
8769
|
var BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
8770
8770
|
var BASE64_VALUE = new Map([...BASE64].map((char, index) => [char, index]));
|
|
8771
|
-
// packages/profile-analysis/dist/src/
|
|
8771
|
+
// packages/profile-analysis/dist/src/keepsnappy/symbolicate.js
|
|
8772
8772
|
var FRAMEWORK_PACKAGES = new Set([
|
|
8773
8773
|
"react",
|
|
8774
8774
|
"react-native",
|
|
@@ -8777,9 +8777,9 @@ var FRAMEWORK_PACKAGES = new Set([
|
|
|
8777
8777
|
"scheduler",
|
|
8778
8778
|
"metro-runtime"
|
|
8779
8779
|
]);
|
|
8780
|
-
// packages/profile-analysis/dist/src/
|
|
8780
|
+
// packages/profile-analysis/dist/src/keepsnappy/composition.js
|
|
8781
8781
|
var HOST_PRIMITIVES = new Set(["View", "Text", "Image", "TextInput", "ScrollView"]);
|
|
8782
|
-
// packages/profile-analysis/dist/src/
|
|
8782
|
+
// packages/profile-analysis/dist/src/keepsnappy/summary.js
|
|
8783
8783
|
var DEFAULT_TOP_N = 10;
|
|
8784
8784
|
var NOT_WORK = new Set(["(root)", "[root]", "(idle)", "(program)"]);
|
|
8785
8785
|
var MAX_CALLERS2 = 3;
|
|
@@ -9020,7 +9020,7 @@ async function extract(deps, outDir, ri, memWindow, clockOffsetMs) {
|
|
|
9020
9020
|
let logText = "";
|
|
9021
9021
|
let cpuProfiles = new Map;
|
|
9022
9022
|
try {
|
|
9023
|
-
const log = await deps.fs.readFile(join6(outDir, r.run_id, "
|
|
9023
|
+
const log = await deps.fs.readFile(join6(outDir, r.run_id, "keepsnappy.log"));
|
|
9024
9024
|
logText = log;
|
|
9025
9025
|
cpuProfiles = await readCpuProfiles(deps, join6(outDir, r.run_id), log);
|
|
9026
9026
|
windows = windowsInRun(parseWindows(log), r.t0, r.t1);
|
|
@@ -9141,7 +9141,7 @@ ${r.stderr}` : ""}`);
|
|
|
9141
9141
|
try {
|
|
9142
9142
|
const text = await source.fetchLog(opts.bundleId);
|
|
9143
9143
|
logText = text;
|
|
9144
|
-
await fs.writeFile(join7(dir, "
|
|
9144
|
+
await fs.writeFile(join7(dir, "keepsnappy.log"), text);
|
|
9145
9145
|
windows = windowsInRun(parseWindows(text), t0, t1);
|
|
9146
9146
|
samples = samplesInRun(parseSamples(text), t0, t1);
|
|
9147
9147
|
cpuProfiles = await pullCpuProfiles(source, opts.bundleId, text, pulled);
|
|
@@ -9150,7 +9150,7 @@ ${r.stderr}` : ""}`);
|
|
|
9150
9150
|
await saveCpuProfiles(deps, dir, cpuProfiles);
|
|
9151
9151
|
} catch (e) {
|
|
9152
9152
|
if (!reason)
|
|
9153
|
-
reason = `no-
|
|
9153
|
+
reason = `no-keepsnappy-log: ${e.message}`;
|
|
9154
9154
|
}
|
|
9155
9155
|
const markers = opts.markers?.forRun(p.runId) ?? [];
|
|
9156
9156
|
if (markers.length)
|
|
@@ -9294,7 +9294,7 @@ async function writeComparisonDataset(deps, baselineDir, outDir) {
|
|
|
9294
9294
|
`);
|
|
9295
9295
|
return dir;
|
|
9296
9296
|
}
|
|
9297
|
-
async function
|
|
9297
|
+
async function keepsnappyRun(deps, source, opts) {
|
|
9298
9298
|
const { runs, rows: raw } = await measure(deps, source, undefined, {
|
|
9299
9299
|
arms: [{ name: "A" }],
|
|
9300
9300
|
repeat: opts.repetitions,
|
|
@@ -9321,7 +9321,7 @@ async function replayRuns(deps, sources, outDir, scenario) {
|
|
|
9321
9321
|
const runs = [];
|
|
9322
9322
|
for (const src of sources) {
|
|
9323
9323
|
const text = await deps.fs.readFile(join9(src.dir, "runs.ndjson")).catch(() => {
|
|
9324
|
-
throw new Error(`--replay ${src.dir}: no runs.ndjson (not a
|
|
9324
|
+
throw new Error(`--replay ${src.dir}: no runs.ndjson (not a keepsnappy out dir)`);
|
|
9325
9325
|
});
|
|
9326
9326
|
for (const line of text.split(`
|
|
9327
9327
|
`)) {
|
|
@@ -9332,7 +9332,7 @@ async function replayRuns(deps, sources, outDir, scenario) {
|
|
|
9332
9332
|
continue;
|
|
9333
9333
|
const id = `${String(runs.length + 1).padStart(3, "0")}-A`;
|
|
9334
9334
|
await deps.fs.mkdir(join9(outDir, id));
|
|
9335
|
-
for (const f of ["
|
|
9335
|
+
for (const f of ["keepsnappy.log", "markers.ndjson", "test.log"]) {
|
|
9336
9336
|
const body = await deps.fs.readFile(join9(src.dir, r.run_id, f)).catch(() => null);
|
|
9337
9337
|
if (body !== null)
|
|
9338
9338
|
await deps.fs.writeFile(join9(outDir, id, f), body);
|
|
@@ -9350,7 +9350,7 @@ async function replayRuns(deps, sources, outDir, scenario) {
|
|
|
9350
9350
|
}
|
|
9351
9351
|
|
|
9352
9352
|
// apps/agent/src/remote-verdict.ts
|
|
9353
|
-
var RUN_INFO = "
|
|
9353
|
+
var RUN_INFO = "keepsnappy-run.json";
|
|
9354
9354
|
async function remoteVerdict(deps, outDir, api, opts = {}) {
|
|
9355
9355
|
let info = {};
|
|
9356
9356
|
try {
|
|
@@ -9401,7 +9401,7 @@ ${output.table}`);
|
|
|
9401
9401
|
}
|
|
9402
9402
|
|
|
9403
9403
|
// apps/agent/src/defaults.ts
|
|
9404
|
-
var DEFAULT_API_URL = "https://keepsnappy-api-
|
|
9404
|
+
var DEFAULT_API_URL = "https://keepsnappy-api-922918702810.europe-west1.run.app";
|
|
9405
9405
|
|
|
9406
9406
|
// apps/agent/src/journeys.ts
|
|
9407
9407
|
import { isAbsolute, relative, resolve as resolve2, sep } from "node:path";
|
|
@@ -9579,7 +9579,7 @@ async function runCommand(deps, o, positionals, rt = { env: process.env, print:
|
|
|
9579
9579
|
}
|
|
9580
9580
|
}
|
|
9581
9581
|
deps.log(`running ${flows.length} flow${flows.length === 1 ? "" : "s"} x${repeat}: ${flows.map((f) => f.name).join(", ")}`);
|
|
9582
|
-
const outcome = await
|
|
9582
|
+
const outcome = await keepsnappyRun(deps, source, {
|
|
9583
9583
|
config,
|
|
9584
9584
|
flows,
|
|
9585
9585
|
outDir: out,
|
|
@@ -9605,7 +9605,7 @@ ${outcome.summary}`);
|
|
|
9605
9605
|
device: { ...device, ...config.device && config.device !== "booted" ? { id: config.device } : {} },
|
|
9606
9606
|
meta: { device_provider: provider, ...git.commit ? { commit_sha: git.commit } : {}, ...git.branch ? { branch: git.branch } : {} },
|
|
9607
9607
|
...runSet ? { runSetId: runSet.run_set_id } : {},
|
|
9608
|
-
driver: config.flows.runner === "maestro" && positionals.length === 0 ? "maestro" : "
|
|
9608
|
+
driver: config.flows.runner === "maestro" && positionals.length === 0 ? "maestro" : "keepsnappy-run",
|
|
9609
9609
|
...api.fetch ? { fetch: api.fetch } : {}
|
|
9610
9610
|
}).catch(infra(`results are saved in ${out}, but the upload failed`));
|
|
9611
9611
|
rt.print(`uploaded ${up.uploaded} runs to ${api.url}${up.skipped.length ? ` (${up.skipped.length} skipped: ${up.skipped.map((x) => `${x.run_id} ${x.why}`).join(", ")})` : ""}`);
|
|
@@ -9643,7 +9643,7 @@ var infra = (what) => (e) => {
|
|
|
9643
9643
|
};
|
|
9644
9644
|
async function writeResult(deps, out, rt, runSet, summary, branch) {
|
|
9645
9645
|
const url = summary?.dashboard_url ?? runSet.dashboard_url;
|
|
9646
|
-
await deps.fs.writeFile(join11(out, "
|
|
9646
|
+
await deps.fs.writeFile(join11(out, "keepsnappy-result.json"), `${JSON.stringify({ run_set_id: runSet.run_set_id, dashboard_url: url, state: summary?.verdicts?.state ?? null, summary }, null, 2)}
|
|
9647
9647
|
`);
|
|
9648
9648
|
const step = rt.env.GITHUB_STEP_SUMMARY;
|
|
9649
9649
|
if (step)
|
|
@@ -9770,7 +9770,7 @@ import { dirname as dirname3, join as join13, resolve as resolve5 } from "node:p
|
|
|
9770
9770
|
// packages/appium-driver/dist/src/links.js
|
|
9771
9771
|
var ALERT_POLL_MS = 250;
|
|
9772
9772
|
var RUN_DEFAULTS = { ttl: 3600, profile: 1, diag: 1 };
|
|
9773
|
-
var route = (scheme) => `${scheme}devtools/
|
|
9773
|
+
var route = (scheme) => `${scheme}devtools/keepsnappy`;
|
|
9774
9774
|
function parseSdkArgs(args) {
|
|
9775
9775
|
return args.split("&").filter(Boolean).map((kv) => {
|
|
9776
9776
|
const i = kv.indexOf("=");
|
|
@@ -9980,7 +9980,7 @@ class Screen {
|
|
|
9980
9980
|
// packages/appium-driver/dist/src/platform.js
|
|
9981
9981
|
var modelOf2 = (device) => device.replace(/-[^-]*$/, "");
|
|
9982
9982
|
var versionOf = (device) => device.replace(/^.*-/, "");
|
|
9983
|
-
var FLUSH_ID = "
|
|
9983
|
+
var FLUSH_ID = "keepsnappy.flushed";
|
|
9984
9984
|
function parseBroadcastAnswer(out, n) {
|
|
9985
9985
|
const data = /data="([^"]*)"/.exec(String(out ?? ""))?.[1];
|
|
9986
9986
|
return data !== undefined && data.startsWith(`gc:${n} `) ? data : null;
|
|
@@ -10042,7 +10042,7 @@ var ANDROID = {
|
|
|
10042
10042
|
uploadFields: {},
|
|
10043
10043
|
async gcCommand(driver, n) {
|
|
10044
10044
|
const out = await driver.execute("mobile: broadcast", {
|
|
10045
|
-
action: `${driver.bundleId}.
|
|
10045
|
+
action: `${driver.bundleId}.keepsnappy.COMMAND`,
|
|
10046
10046
|
package: driver.bundleId,
|
|
10047
10047
|
extras: [["s", "cmd", "gc"], ["i", "n", String(n)]],
|
|
10048
10048
|
flags: "0x10000000"
|
|
@@ -10069,7 +10069,7 @@ import { existsSync, readFileSync, statSync } from "node:fs";
|
|
|
10069
10069
|
import { dirname as dirname2, join as join12, relative as relative2, resolve as resolve4 } from "node:path";
|
|
10070
10070
|
import { fileURLToPath } from "node:url";
|
|
10071
10071
|
function driverVersion() {
|
|
10072
|
-
const built = "0.1.0-alpha.
|
|
10072
|
+
const built = "0.1.0-alpha.13";
|
|
10073
10073
|
if (built)
|
|
10074
10074
|
return built;
|
|
10075
10075
|
let dir = dirname2(fileURLToPath(import.meta.url));
|
|
@@ -10328,7 +10328,7 @@ function linksCommand(cmd, argv, rt) {
|
|
|
10328
10328
|
rt.print(activationLink(o.scheme, o.token, set.join("&")));
|
|
10329
10329
|
return 0;
|
|
10330
10330
|
}
|
|
10331
|
-
var maestroLinkFields = (sdkArgs = "") => ({
|
|
10331
|
+
var maestroLinkFields = (sdkArgs = "") => ({ keepsnappyLinkArgs: linkArgs(sdkArgs) });
|
|
10332
10332
|
|
|
10333
10333
|
// apps/agent/src/farm.ts
|
|
10334
10334
|
var STATE_VERSION = 1;
|
|
@@ -10355,17 +10355,17 @@ function instrumentFlags(f) {
|
|
|
10355
10355
|
const journey = f["journey-version"] ?? hash;
|
|
10356
10356
|
return { ...driver ? { "driver-version": driver } : {}, ...journey ? { "journey-version": journey } : {} };
|
|
10357
10357
|
}
|
|
10358
|
-
var TOKENS_STUB = "// Replaced by `keepsnappy farm open` for one upload. Never commit real tokens.\noutput.
|
|
10358
|
+
var TOKENS_STUB = "// Replaced by `keepsnappy farm open` for one upload. Never commit real tokens.\noutput.keepsnappyTokens = {};\n";
|
|
10359
10359
|
var JOURNEY = /^[A-Za-z0-9][A-Za-z0-9._-]{0,199}$/;
|
|
10360
10360
|
var DEFAULT_TTL = 21600;
|
|
10361
10361
|
var POLL_MS = 5000;
|
|
10362
10362
|
function tokensJs(api, runSetId, tokens, links = maestroLinkFields()) {
|
|
10363
10363
|
return [
|
|
10364
10364
|
`// Generated by \`keepsnappy farm open\` for run set ${runSetId}. Overlaid into the suite for one upload; never commit.`,
|
|
10365
|
-
`output.
|
|
10366
|
-
`output.
|
|
10367
|
-
`output.
|
|
10368
|
-
`output.
|
|
10365
|
+
`output.keepsnappyApi = ${JSON.stringify(api.replace(/\/+$/, ""))};`,
|
|
10366
|
+
`output.keepsnappyRunSet = ${JSON.stringify(runSetId)};`,
|
|
10367
|
+
`output.keepsnappyTokens = ${JSON.stringify(tokens)};`,
|
|
10368
|
+
`output.keepsnappyLinkArgs = ${JSON.stringify(links.keepsnappyLinkArgs)};`,
|
|
10369
10369
|
""
|
|
10370
10370
|
].join(`
|
|
10371
10371
|
`);
|
|
@@ -10443,7 +10443,7 @@ async function farmOpen(deps, f, rt) {
|
|
|
10443
10443
|
if (!Number.isInteger(ttl) || ttl < 60)
|
|
10444
10444
|
problems.push("--ttl-seconds must be a whole number >= 60");
|
|
10445
10445
|
if (!f["tokens-js"])
|
|
10446
|
-
problems.push("--tokens-js <path> is required (the file the suite overlay puts at flows/
|
|
10446
|
+
problems.push("--tokens-js <path> is required (the file the suite overlay puts at flows/keepsnappy/tokens.js)");
|
|
10447
10447
|
if (!f.state)
|
|
10448
10448
|
problems.push("--state <path> is required (read by keepsnappy farm close)");
|
|
10449
10449
|
if (problems.length)
|
|
@@ -10712,7 +10712,7 @@ async function closeRunSet(deps, f, rt, state, statePath) {
|
|
|
10712
10712
|
await deps.fs.writeFile(statePath, `${JSON.stringify(state2, null, 2)}
|
|
10713
10713
|
`);
|
|
10714
10714
|
await deps.fs.mkdir(out);
|
|
10715
|
-
await deps.fs.writeFile(join13(out, "
|
|
10715
|
+
await deps.fs.writeFile(join13(out, "keepsnappy-result.json"), `${JSON.stringify({ run_set_id: state.run_set_id, dashboard_url: summary.dashboard_url, state: state2.result.state, session: { status: session.status, message: session.message }, summary }, null, 2)}
|
|
10716
10716
|
`);
|
|
10717
10717
|
await githubOutputs(deps, rt, { state: state2.result.state ?? "none", dashboard_url: summary.dashboard_url, run_set_id: state.run_set_id });
|
|
10718
10718
|
const step = rt.env.GITHUB_STEP_SUMMARY;
|
|
@@ -10893,7 +10893,7 @@ class BrowserStack {
|
|
|
10893
10893
|
}
|
|
10894
10894
|
async runMaestro(b) {
|
|
10895
10895
|
const appUrl = await this.uploadApp(b.app, b.customId);
|
|
10896
|
-
const work = join15(this.o.tmpDir ?? "/tmp", `
|
|
10896
|
+
const work = join15(this.o.tmpDir ?? "/tmp", `keepsnappy-suite-${(this.o.uniqueId ?? randomUUID)()}`);
|
|
10897
10897
|
let suiteUrl;
|
|
10898
10898
|
try {
|
|
10899
10899
|
const zip = await this.zipSuite(b, work);
|
|
@@ -10911,8 +10911,8 @@ class BrowserStack {
|
|
|
10911
10911
|
testSuite: suiteUrl,
|
|
10912
10912
|
devices: [b.device],
|
|
10913
10913
|
setEnvVariables: { ...b.env, RUN_ID: b.runId },
|
|
10914
|
-
buildTag: `
|
|
10915
|
-
customBuildName: `
|
|
10914
|
+
buildTag: `keepsnappy-${b.runId}`,
|
|
10915
|
+
customBuildName: `keepsnappy ${b.runId} ${b.platform}`,
|
|
10916
10916
|
execute: [b.execute],
|
|
10917
10917
|
deviceLogs: true,
|
|
10918
10918
|
...b.localIdentifier ? { local: "true", localIdentifier: b.localIdentifier } : {},
|
|
@@ -11045,7 +11045,7 @@ function nodeFarmRunPorts(deps, rt) {
|
|
|
11045
11045
|
if (!user || !key)
|
|
11046
11046
|
throw new ConfigError(["BROWSERSTACK_USERNAME and BROWSERSTACK_ACCESS_KEY must be set"]);
|
|
11047
11047
|
const tmp = rt.env.RUNNER_TEMP ?? rt.env.TMPDIR ?? "/tmp";
|
|
11048
|
-
const dir = join16(tmp, "
|
|
11048
|
+
const dir = join16(tmp, "keepsnappy-bs-local");
|
|
11049
11049
|
return {
|
|
11050
11050
|
apps: new DefaultAppResolver(deps, rt.env),
|
|
11051
11051
|
provider: new BrowserStack(deps, { credentials: { user, key }, tmpDir: tmp, ...rt.fetch ? { fetch: rt.fetch } : {}, ...rt.sleep ? { sleep: rt.sleep } : {}, ...rt.now ? { now: rt.now } : {} }),
|
|
@@ -11158,10 +11158,10 @@ function printPlan(p, f, rt) {
|
|
|
11158
11158
|
line("keepsnappy farm run: dry run, no request is made and nothing is written");
|
|
11159
11159
|
line(` 1. validate: ${p.journeys.join(", ")} x ${p.repeat} on ${p.device} (${p.platform}), driver ${p.driver}`);
|
|
11160
11160
|
line(` 2. resolve the app: ${p.app.startsWith("eas:") ? `eas-cli build:view ${p.app.slice(4)}, download into ${p.out}` : p.app.startsWith("bs://") ? "bs:// passes through" : `${p.app} (local file)`}`);
|
|
11161
|
-
line(` 3. farm open -> ${join16(p.out, "
|
|
11161
|
+
line(` 3. farm open -> ${join16(p.out, "keepsnappy-tokens.js")}, ${join16(p.out, "keepsnappy-farm.json")} (POST /v1/run-sets, then POST /v1/runs x ${p.journeys.length * p.repeat})`);
|
|
11162
11162
|
line(` 4. tunnel: ${p.ports.length ? `BrowserStackLocal for bs-local.com ports ${p.ports.join(", ")}, identifier = run id` : "none (no --tunnel-for)"}`);
|
|
11163
11163
|
if (p.driver === "maestro") {
|
|
11164
|
-
line(` 5. maestro on BrowserStack: upload app, zip ${f["suite-dir"]} with the tokens at ${f["tokens-path"] ?? "flows/
|
|
11164
|
+
line(` 5. maestro on BrowserStack: upload app, zip ${f["suite-dir"]} with the tokens at ${f["tokens-path"] ?? "flows/keepsnappy/tokens.js"}, upload suite, POST build (execute ${f.execute}), poll`);
|
|
11165
11165
|
} else {
|
|
11166
11166
|
line(` 5. appium: ${f["journey-module"] ? `built-in driver, journey module ${f["journey-module"]}` : `node ${f["driver-script"]}`} --platform ${p.platform} --device ... --tokens-js ... --journeys ... --repeat ${p.repeat}${p.driverArgs.length ? ` ${p.driverArgs.map((a) => substitute(a, {})).join(" ")}` : ""}`);
|
|
11167
11167
|
}
|
|
@@ -11180,7 +11180,7 @@ async function runDriver2(deps, rt, f, p, ports, ctx) {
|
|
|
11180
11180
|
app: ctx.app,
|
|
11181
11181
|
...f["custom-id"] ? { customId: f["custom-id"] } : {},
|
|
11182
11182
|
suiteDir: resolve6(f["suite-dir"]),
|
|
11183
|
-
tokensPath: f["tokens-path"] ?? "flows/
|
|
11183
|
+
tokensPath: f["tokens-path"] ?? "flows/keepsnappy/tokens.js",
|
|
11184
11184
|
tokensJs: ctx.tokensJs,
|
|
11185
11185
|
execute: f.execute,
|
|
11186
11186
|
runId: ctx.runId,
|
|
@@ -11251,7 +11251,7 @@ async function runDriver2(deps, rt, f, p, ports, ctx) {
|
|
|
11251
11251
|
}
|
|
11252
11252
|
async function farmRun(deps, f, rt, ports) {
|
|
11253
11253
|
const p = planFarmRun(f);
|
|
11254
|
-
const statePath = join16(p.out, "
|
|
11254
|
+
const statePath = join16(p.out, "keepsnappy-farm.json");
|
|
11255
11255
|
if (await deps.fs.exists(statePath))
|
|
11256
11256
|
throw new ConfigError([`${statePath} exists: a run set was opened here before; use another --out or remove ${p.out} (close it first with keepsnappy farm close)`]);
|
|
11257
11257
|
if (f["dry-run"]) {
|
|
@@ -11259,7 +11259,7 @@ async function farmRun(deps, f, rt, ports) {
|
|
|
11259
11259
|
return EXIT.ok;
|
|
11260
11260
|
}
|
|
11261
11261
|
const farm = ports ?? nodeFarmRunPorts(deps, rt);
|
|
11262
|
-
const tokensJs = join16(p.out, "
|
|
11262
|
+
const tokensJs = join16(p.out, "keepsnappy-tokens.js");
|
|
11263
11263
|
await deps.fs.mkdir(p.out);
|
|
11264
11264
|
const resolved = await farm.apps.resolve(p.app, { outDir: p.out });
|
|
11265
11265
|
const commit = f.commit ?? resolved.commit ?? undefined;
|
|
@@ -11287,7 +11287,7 @@ async function farmRun(deps, f, rt, ports) {
|
|
|
11287
11287
|
return openCode;
|
|
11288
11288
|
opened = true;
|
|
11289
11289
|
const state = JSON.parse(await deps.fs.readFile(statePath));
|
|
11290
|
-
const runId = f["run-id"] ?? `
|
|
11290
|
+
const runId = f["run-id"] ?? `keepsnappy-${p.platform}-${state.run_set_id.slice(0, 8)}`;
|
|
11291
11291
|
const ctx = { runId, out: p.out, tokensJs, runSetId: state.run_set_id, app: resolved.app };
|
|
11292
11292
|
try {
|
|
11293
11293
|
await deps.spawner.run("rm", ["-f", join16(p.out, `browserstack.${p.platform}.json`)]);
|
|
@@ -11371,7 +11371,7 @@ async function put(f, upload, body) {
|
|
|
11371
11371
|
async function uploadSourcemaps(opts) {
|
|
11372
11372
|
const f = opts.fetch ?? fetch;
|
|
11373
11373
|
const read = opts.readFile ?? ((p) => readFile4(p, "utf8"));
|
|
11374
|
-
const headers = { "x-
|
|
11374
|
+
const headers = { "x-keepsnappy-key": opts.key };
|
|
11375
11375
|
const out = [];
|
|
11376
11376
|
for (const file of opts.files) {
|
|
11377
11377
|
let parsed;
|
|
@@ -11625,7 +11625,7 @@ async function get(t, path, query = {}) {
|
|
|
11625
11625
|
for (const [k, v] of Object.entries(query))
|
|
11626
11626
|
if (v !== undefined && v !== "")
|
|
11627
11627
|
u.searchParams.set(k, String(v));
|
|
11628
|
-
const r = await (t.fetch ?? fetch)(u, { headers: { "x-
|
|
11628
|
+
const r = await (t.fetch ?? fetch)(u, { headers: { "x-keepsnappy-key": t.key, accept: "application/json" } });
|
|
11629
11629
|
const text = await r.text();
|
|
11630
11630
|
const body = text ? JSON.parse(text) : {};
|
|
11631
11631
|
if (!r.ok) {
|
|
@@ -11767,7 +11767,7 @@ var USAGE = `keepsnappy <command>
|
|
|
11767
11767
|
farm close --state <file> [--wait [--fail-on-worse]] [--bs-build browserstack.ios.json] [--settle-seconds 60] [--max-wait-seconds 300] [--out DIR]
|
|
11768
11768
|
farm run --app <eas:ID|file.ipa|bs://ID> --journeys a,b [--repeat 5] --device "iPhone 15 Pro-17" [--platform ios|android] [--driver maestro|appium]
|
|
11769
11769
|
[--tunnel-for localhost:PORT ...] [--env KEY=VALUE ...] (values may use {tunnel:PORT} {run_id} {out} {tokens_js} {run_set_id})
|
|
11770
|
-
maestro: --suite-dir DIR --execute FLOW [--tokens-path flows/
|
|
11770
|
+
maestro: --suite-dir DIR --execute FLOW [--tokens-path flows/keepsnappy/tokens.js] [--maestro-version V]
|
|
11771
11771
|
appium: --journey-module FILE | --driver-script FILE [--driver-arg ARG ...]
|
|
11772
11772
|
[--branch NAME --commit SHA] [--wait [--fail-on-worse]] [--out DIR] [--dry-run] (open + tunnel + device + close; docs/plans/54a)
|
|
11773
11773
|
extract <out-dir> --ri-capture observations.ndjson [--mem-window NAME]
|
|
@@ -11899,7 +11899,7 @@ async function main2(argv) {
|
|
|
11899
11899
|
if (platform === "ios-device" && !o.device)
|
|
11900
11900
|
throw new Error("--device <udid> is required for ios-device");
|
|
11901
11901
|
const arms = parseArmEnv(o["arm-env"], parseArms(o.arms));
|
|
11902
|
-
const out = resolve7(o.out ?? `
|
|
11902
|
+
const out = resolve7(o.out ?? `keepsnappy-${new Date().toISOString().replace(/[:.]/g, "-")}`);
|
|
11903
11903
|
const udid = o.device ?? "booted";
|
|
11904
11904
|
const source = platform === "ios-sim" ? new IosSimulatorDataSource(deps, udid) : new IosDeviceDataSource(deps, udid, resolve7(out, ".scratch"));
|
|
11905
11905
|
const installer = platform === "ios-sim" ? new IosSimulatorInstaller(deps, udid) : new IosDeviceInstaller(deps, udid);
|
|
@@ -13,5 +13,5 @@ appId: com.gigaverse.appdev
|
|
|
13
13
|
id: ${output.elements.home.screen}
|
|
14
14
|
timeout: 30000
|
|
15
15
|
# The settings come from tokens.js (`keepsnappy farm open`): the run defaults, never written by hand here (REQ-FARM-28).
|
|
16
|
-
- openLink: gigaversedev://devtools/
|
|
16
|
+
- openLink: gigaversedev://devtools/keepsnappy?activate=1&${output.keepsnappyLinkArgs}&runToken=${output.keepsnappyToken}
|
|
17
17
|
- runFlow: ../../subflows/confirm-open-link.yaml
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# End of one measured iteration: the SDK sends everything still unsent (nothing is sent while a run is active), then
|
|
2
|
-
# shows `
|
|
2
|
+
# shows `keepsnappy.flushed` (ok | error | off). `activate=0` makes the next launch dormant again.
|
|
3
3
|
appId: com.gigaverse.appdev
|
|
4
4
|
---
|
|
5
5
|
- runScript: send-markers.js # the iteration's step markers, recorded by step.js, sent now that it has ended
|
|
6
|
-
- openLink: gigaversedev://devtools/
|
|
6
|
+
- openLink: gigaversedev://devtools/keepsnappy?flush=1&activate=0
|
|
7
7
|
- runFlow: ../../subflows/confirm-open-link.yaml
|
|
8
8
|
- extendedWaitUntil:
|
|
9
9
|
visible:
|
|
10
|
-
id:
|
|
10
|
+
id: keepsnappy.flushed
|
|
11
11
|
timeout: 60000
|
|
12
12
|
optional: true # a dormant iteration (activation failed) shows nothing; farm close marks it invalid
|
|
13
13
|
- assertVisible:
|
|
14
|
-
id:
|
|
14
|
+
id: keepsnappy.flushed
|
|
15
15
|
text: ok
|
|
16
16
|
optional: true
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Hands out the run tokens of one journey in order: iteration i of JOURNEY gets output.keepsnappyTokens[JOURNEY][i].
|
|
2
|
+
// ES5 (Maestro runScript). An empty token (more iterations than tokens) leaves the SDK dormant: no data, no harm.
|
|
3
|
+
var list = (output.keepsnappyTokens || {})[JOURNEY] || [];
|
|
4
|
+
output.keepsnappyIdx = output.keepsnappyIdx || {};
|
|
5
|
+
var i = output.keepsnappyIdx[JOURNEY] || 0;
|
|
6
|
+
output.keepsnappyToken = list[i] || '';
|
|
7
|
+
output.keepsnappyIdx[JOURNEY] = i + 1;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# e2e/flows/
|
|
1
|
+
# e2e/flows/keepsnappy/run-keepsnappy.yaml (plan 36 D10). One BrowserStack session runs every iteration: the session is the
|
|
2
2
|
# noise block. Untagged, so smoke/regression never select it; the dispatcher names it with --execute.
|
|
3
3
|
# `times` must equal `keepsnappy farm open --repeat`. One begin/journey/end triple per journey listed in --journeys.
|
|
4
4
|
appId: com.gigaverse.appdev
|