@stim-cli/ci 1.36.0 → 1.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -36,6 +36,22 @@ device with `STIM_NO_RECORDED_BUILD`, and the failure's `remedy` names the comma
36
36
  to run first, such as `stim-ci build --platform ios --scheme App`. `build` and
37
37
  `run` must use the same Stim home: pass the same `--home`, or keep the default.
38
38
 
39
+ Every iOS and Android `run` on this machine starts the app from clean state on
40
+ the workspace's own simulator or emulator, including when the device already
41
+ has this build.
42
+ Before it installs, `run` uninstalls every other user app except XCUITest
43
+ runner apps, such as the ones agent-device installs. On iOS it also clears the
44
+ app's defaults and data container and resets the simulator keychain; on Android
45
+ it runs `pm clear` on the app. Simulator-wide settings such as privacy grants
46
+ are kept. A local simulator or emulator Stim does not own refuses with
47
+ `STIM_BAD_ARG` instead of skipping the cleanup. A run that `ios.remote` or
48
+ `android.remote` places on another machine or on EAS is not cleaned, and a
49
+ physical device is refused. `build` always compiles on this machine and ignores
50
+ `remote.build`. By default it compiles an iOS app, and an Android Debug app,
51
+ for this machine's architecture only, so when `run` can land on another
52
+ machine or on EAS, build
53
+ for that device too with `--arch all` (iOS) or `--abi all` (Android).
54
+
39
55
  `--eas-profile <name>` (iOS and Android, build only) downloads a completed EAS
40
56
  development build instead of compiling, as `stim ios --eas-profile` does; `run`
41
57
  then installs it. Stim never starts an EAS build.
@@ -93,6 +109,13 @@ run options other than those that build.
93
109
  Everything after `--` is an argument vector. No shell is inferred. To use shell
94
110
  syntax, pass a shell explicitly: `-- bash -e -o pipefail -c 'pnpm test:e2e'`.
95
111
 
112
+ Without `--`, `run` installs and launches the app, writes `run.json`, then stops the
113
+ workspace as after a passing test, with `test: null` and exit code 0. Use it when a
114
+ later step drives a Release app on the device `run.json` names; boot a simulator again
115
+ by its UDID, since cleanup shuts it down. Cleanup also stops Metro, so a Debug app has
116
+ no bundler in that later step. An explicit `--` with no command after it is
117
+ refused. The library's `runCI` behaves the same when `command` is omitted.
118
+
96
119
  The test process runs in the project directory. It receives the caller's
97
120
  environment plus these values:
98
121
 
@@ -126,7 +149,7 @@ the `test` result, optional `failure`, `diagnostics`, and `cleanup`.
126
149
  If a progress callback or writing `result.json` fails, the result includes
127
150
  `reportingError`. Reporter failures cannot interrupt cleanup or replace an earlier
128
151
  test failure; after a passing test, they return 1.
129
- Setup failure leaves `run` and `test` null. Diagnostics include the last 1000
152
+ Setup failure leaves `run` and `test` null; a run without a command leaves `test` null. Diagnostics include the last 1000
130
153
  structured records and their original directory. After cleanup, regular `.log`
131
154
  and `.ndjson` files (including `.1` rotated copies) directly inside that directory
132
155
  are copied into `logs/` and listed in `diagnostics.files`. This retains raw
@@ -1,13 +1,14 @@
1
- import { n as buildCI, t as runCI } from "./src-Gwd9CN7Z.mjs";
1
+ import { n as buildCI, t as runCI } from "./src-DJH6gv01.mjs";
2
2
  import { parseArgs } from "node:util";
3
3
  //#region src/cli.ts
4
4
  const HELP = `Usage: stim-ci build --platform <ios|android|macos> [options]
5
- stim-ci run --platform <ios|android|macos|web> [options] -- <command> [args...]
5
+ stim-ci run --platform <ios|android|macos|web> [options] [-- <command> [args...]]
6
6
 
7
7
  Build is the only step that compiles. It records the app for this checkout, platform and
8
8
  selectors, and exports it only with --export.
9
- Run installs and launches the app the last matching build recorded, runs a test command, and
10
- stops the app. It never builds; with no recorded build it refuses and names the build to run.
9
+ Run installs and launches the app the last matching build recorded, runs the test command
10
+ given after --, if any, and stops the app. It never builds; with no recorded build it refuses
11
+ and names the build to run.
11
12
  The final result is JSON on stdout; progress and test output go to stderr. No result, test or
12
13
  diagnostic file is written unless --artifacts is given.
13
14
 
@@ -110,8 +111,7 @@ function parse(argv) {
110
111
  }
111
112
  };
112
113
  }
113
- if (separator < 0) throw new Error("Separate the test command with --.");
114
- if (!command[0]) throw new Error("A test command is required after --.");
114
+ if (separator >= 0 && !command[0]) throw new Error("A test command is required after --.");
115
115
  const run = platform === "ios" ? {
116
116
  platform,
117
117
  scheme: values.scheme,
@@ -125,13 +125,13 @@ function parse(argv) {
125
125
  options: {
126
126
  ...common,
127
127
  run,
128
- command
128
+ ...separator >= 0 ? { command } : {}
129
129
  }
130
130
  };
131
131
  }
132
132
  async function main(argv = process.argv.slice(2)) {
133
133
  if (argv.length === 1 && argv[0] === "--version") {
134
- process.stdout.write(`1.36.0\n`);
134
+ process.stdout.write(`1.38.0\n`);
135
135
  return;
136
136
  }
137
137
  if (argv.length === 0 || argv[0] === "--help" || (argv[0] === "run" || argv[0] === "build") && argv[1] === "--help") {
package/dist/index.d.mts CHANGED
@@ -22,9 +22,10 @@ interface CommandResult {
22
22
  }
23
23
  //#endregion
24
24
  //#region src/build.d.ts
25
- type WithoutSignal<T> = T extends unknown ? Omit<T, "signal"> : never;
25
+ type WithoutKeys$1<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
26
26
  interface CIBuildOptions extends CIContextOptions {
27
- build: WithoutSignal<StimBuildOptions>;
27
+ /** Always compiles on this machine; `remoteBuild` is not accepted. */
28
+ build: WithoutKeys$1<StimBuildOptions, "signal" | "remoteBuild">;
28
29
  /** Also writes the app here as a portable APK (Android) or tar.gz (iOS, macOS); without it the build is only recorded for `run`. */
29
30
  exportPath?: string;
30
31
  }
@@ -56,8 +57,9 @@ declare function buildCI(input: CIBuildOptions): Promise<CIBuildResult>;
56
57
  type WithoutKeys<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
57
58
  interface CIOptions extends CIContextOptions {
58
59
  /** Selectors of the recorded build to run. `run` never builds, so build-time options such as `easProfile` belong to `buildCI`. */
59
- run: WithoutKeys<StimRunOptions, "signal" | "recordedBuild" | "easProfile">;
60
- command: readonly [string, ...string[]];
60
+ run: WithoutKeys<StimRunOptions, "signal" | "recordedBuild" | "cleanAppState" | "easProfile">;
61
+ /** Test command argv. Without it, `runCI` installs and launches the app, then stops it as after a passing test. */
62
+ command?: readonly [string, ...string[]];
61
63
  }
62
64
  interface CIFailure {
63
65
  code: string;
@@ -88,7 +90,7 @@ interface CIResult {
88
90
  error?: CIFailure;
89
91
  };
90
92
  }
91
- /** Runs an app and argv-based test command, stops its workspace or explicit slot, and retains diagnostics. */
93
+ /** Runs an app and an optional argv-based test command, stops its workspace or explicit slot, and retains diagnostics. */
92
94
  declare function runCI(options: CIOptions): Promise<CIResult>;
93
95
  //#endregion
94
96
  export { type CIBuildOptions, type CIBuildResult, CIFailure, CIOptions, CIResult, type CommandResult, buildCI, runCI };
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { n as buildCI, t as runCI } from "./src-Gwd9CN7Z.mjs";
1
+ import { n as buildCI, t as runCI } from "./src-DJH6gv01.mjs";
2
2
  export { buildCI, runCI };
@@ -201,6 +201,7 @@ async function buildCI(input) {
201
201
  attempted = true;
202
202
  result.build = await stim.build({
203
203
  ...options.build,
204
+ remoteBuild: "local",
204
205
  signal
205
206
  });
206
207
  signal?.throwIfAborted();
@@ -296,10 +297,10 @@ function commandEnvironment(result, options) {
296
297
  STIM_CI_RUN_RESULT: result.runPath ?? ""
297
298
  };
298
299
  }
299
- /** Runs an app and argv-based test command, stops its workspace or explicit slot, and retains diagnostics. */
300
+ /** Runs an app and an optional argv-based test command, stops its workspace or explicit slot, and retains diagnostics. */
300
301
  async function runCI(options) {
301
- if (!options.command[0]) throw new Error("A non-empty test command is required.");
302
- for (const argument of options.command) if (typeof argument !== "string" || argument.includes("\0")) throw new Error("Test command arguments must be strings without null bytes.");
302
+ if (options.command && !options.command[0]) throw new Error("A non-empty test command is required.");
303
+ for (const argument of options.command ?? []) if (typeof argument !== "string" || argument.includes("\0")) throw new Error("Test command arguments must be strings without null bytes.");
303
304
  const prepared = prepareCI(options);
304
305
  options = prepared.options;
305
306
  const { projectRoot, artifactsDir, timeout, signal } = prepared;
@@ -341,26 +342,30 @@ async function runCI(options) {
341
342
  result.run = await stim.run({
342
343
  ...options.run,
343
344
  recordedBuild: true,
345
+ cleanAppState: true,
344
346
  signal
345
347
  });
346
348
  signal?.throwIfAborted();
347
349
  if (result.runPath) writeJson(result.runPath, result.run);
348
- result.test = await runCommand({
349
- command: options.command,
350
- cwd: projectRoot,
351
- env: commandEnvironment(result, options),
352
- artifactsDir,
353
- signal,
354
- onOutput: onProgress
355
- });
356
- result.exitCode = result.test.exitCode ?? 1;
357
- if (result.exitCode < 0 || result.exitCode === 0 && result.test.error) result.exitCode = 1;
358
- if (result.exitCode !== 0) result.failure = {
359
- code: "STIM_CI_TEST_FAILED",
360
- message: result.test.error ?? `Test command ${result.test.signal ? `was terminated by ${result.test.signal}` : `exited with ${result.exitCode}`}.`
361
- };
350
+ if (!options.command) result.exitCode = 0;
351
+ else {
352
+ result.test = await runCommand({
353
+ command: options.command,
354
+ cwd: projectRoot,
355
+ env: commandEnvironment(result, options),
356
+ artifactsDir,
357
+ signal,
358
+ onOutput: onProgress
359
+ });
360
+ result.exitCode = result.test.exitCode ?? 1;
361
+ if (result.exitCode < 0 || result.exitCode === 0 && result.test.error) result.exitCode = 1;
362
+ if (result.exitCode !== 0) result.failure = {
363
+ code: "STIM_CI_TEST_FAILED",
364
+ message: result.test.error ?? `Test command ${result.test.signal ? `was terminated by ${result.test.signal}` : `exited with ${result.exitCode}`}.`
365
+ };
366
+ }
362
367
  } catch (error) {
363
- result.failure = failure(error, result.run ? "STIM_CI_TEST_FAILED" : "STIM_CI_SETUP_FAILED");
368
+ result.failure = failure(error, result.run && options.command ? "STIM_CI_TEST_FAILED" : "STIM_CI_SETUP_FAILED");
364
369
  if (result.failure.code === "STIM_NO_RECORDED_BUILD") result.failure.remedy = `Run ${buildCommand(options, projectRoot)} first.`;
365
370
  } finally {
366
371
  cleaningUp = true;
package/dist/stim-ci.mjs CHANGED
@@ -6,6 +6,6 @@ const minor = parts[1] ?? 0;
6
6
  if (major < 22 || major === 22 && minor < 12) {
7
7
  process.stderr.write(`stim-ci requires Node.js 22.12.0 or later; this is ${process.versions.node}.\n`);
8
8
  process.exitCode = 1;
9
- } else import("./cli-BhFWDeM1.mjs").then(({ main }) => main());
9
+ } else import("./cli-BKK3T47P.mjs").then(({ main }) => main());
10
10
  //#endregion
11
11
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stim-cli/ci",
3
- "version": "1.36.0",
3
+ "version": "1.38.0",
4
4
  "description": "Build Stim app artifacts or run Stim apps and test commands in CI with diagnostics and scoped cleanup.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "cross-spawn": "^7.0.6",
36
- "stim": "1.36.0"
36
+ "stim": "1.38.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/cross-spawn": "^6.0.6"