Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# editorconfig.org
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 120

[*.md]
trim_trailing_whitespace = false
2 changes: 1 addition & 1 deletion .github/workflows/unit-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@ jobs:
- run: npm run lint
name: Run linter
- run: npm run format:check
name: Run Prettier check
name: Run format check
- run: npm run test
name: Run unit tests
37 changes: 0 additions & 37 deletions .releaserc

This file was deleted.

10 changes: 0 additions & 10 deletions eslint.config.mjs

This file was deleted.

10 changes: 5 additions & 5 deletions lib/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as semver from 'semver';
import {spawn} from 'node:child_process';
import {Readable} from 'node:stream';

import * as semver from 'semver';

export const DEFAULT_EXEC_TIMEOUT = 10 * 60 * 1000; // ms
export const SIM_RUNTIME_NAME = 'com.apple.CoreSimulator.SimRuntime.';

Expand Down Expand Up @@ -61,10 +62,9 @@ export async function convertPlistToJson(plistInput: string): Promise<any> {
});
} catch (err) {
plutilProcess.kill(9);
throw new Error(
`Failed to convert plist to JSON: ${err instanceof Error ? err.message : String(err)}`,
{cause: err},
);
throw new Error(`Failed to convert plist to JSON: ${err instanceof Error ? err.message : String(err)}`, {
cause: err,
});
} finally {
plutilProcess.removeAllListeners();
inputStream.removeAllListeners();
Expand Down
35 changes: 13 additions & 22 deletions lib/simctl.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {exec as tpExec, SubProcess} from 'teen_process';
import which from 'which';
import {log, LOG_PREFIX} from './logger.js';

import {DEFAULT_EXEC_TIMEOUT, getXcrunBinary} from './helpers.js';
import {exec as tpExec, SubProcess} from 'teen_process';
import {log, LOG_PREFIX} from './logger.js';
import * as addmediaCommands from './subcommands/addmedia.js';
import * as appinfoCommands from './subcommands/appinfo.js';
import * as bootCommands from './subcommands/boot.js';
Expand All @@ -10,23 +11,23 @@ import * as createCommands from './subcommands/create.js';
import * as deleteCommands from './subcommands/delete.js';
import * as eraseCommands from './subcommands/erase.js';
import * as getappcontainerCommands from './subcommands/get_app_container.js';
import * as envCommands from './subcommands/getenv.js';
import * as installCommands from './subcommands/install.js';
import * as ioCommands from './subcommands/io.js';
import * as keychainCommands from './subcommands/keychain.js';
import * as launchCommands from './subcommands/launch.js';
import * as listCommands from './subcommands/list.js';
import * as locationCommands from './subcommands/location.js';
import * as openurlCommands from './subcommands/openurl.js';
import * as pbcopyCommands from './subcommands/pbcopy.js';
import * as pbpasteCommands from './subcommands/pbpaste.js';
import * as privacyCommands from './subcommands/privacy.js';
import * as pushCommands from './subcommands/push.js';
import * as envCommands from './subcommands/getenv.js';
import * as shutdownCommands from './subcommands/shutdown.js';
import * as spawnCommands from './subcommands/spawn.js';
import * as terminateCommands from './subcommands/terminate.js';
import * as uiCommands from './subcommands/ui.js';
import * as uninstallCommands from './subcommands/uninstall.js';
import * as locationCommands from './subcommands/location.js';
import type {XCRun, ExecOpts, SimctlOpts, ExecResult} from './types.js';

const SIMCTL_ENV_PREFIX = 'SIMCTL_CHILD_';
Expand Down Expand Up @@ -113,8 +114,7 @@ export class Simctl {
requireUdid(commandName: string | null = null): string {
if (!this.udid) {
throw new Error(
`udid is required to be set for ` +
(commandName ? `the '${commandName}' command` : 'this simctl command'),
`udid is required to be set for ` + (commandName ? `the '${commandName}' command` : 'this simctl command'),
);
}
return this.udid;
Expand All @@ -130,10 +130,7 @@ export class Simctl {
try {
this.xcrun.path = await which(xcrunBinary);
} catch {
throw new Error(
`${xcrunBinary} tool has not been found in PATH. ` +
`Are Xcode developers tools installed?`,
);
throw new Error(`${xcrunBinary} tool has not been found in PATH. Are Xcode developers tools installed?`);
}
}
if (!this.xcrun.path) {
Expand Down Expand Up @@ -163,12 +160,7 @@ export class Simctl {
timeout,
} = opts ?? ({} as T);
// run a particular simctl command
const args = [
'simctl',
...(this.devicesSetPath ? ['--set', this.devicesSetPath] : []),
subcommand,
...initialArgs,
];
const args = ['simctl', ...(this.devicesSetPath ? ['--set', this.devicesSetPath] : []), subcommand, ...initialArgs];
// Prefix all passed in environment variables with 'SIMCTL_CHILD_', simctl
// will then pass these to the child (spawned) process.
const envWithPrefixedKeys = Object.fromEntries(
Expand All @@ -190,17 +182,16 @@ export class Simctl {
try {
let execArgs: [string, string[], any];
if (architectures?.length) {
const archArgs = (Array.isArray(architectures) ? architectures : [architectures]).flatMap(
(arch) => ['-arch', arch],
);
const archArgs = (Array.isArray(architectures) ? architectures : [architectures]).flatMap((arch) => [
'-arch',
arch,
]);
execArgs = ['arch', [...archArgs, xcrun, ...args], execOpts];
} else {
execArgs = [xcrun, args, execOpts];
}
// We know what we are doing here - the type system can't handle the dynamic nature
return (
asynchronous ? new SubProcess(...execArgs) : await tpExec(...execArgs)
) as ExecResult<T>;
return (asynchronous ? new SubProcess(...execArgs) : await tpExec(...execArgs)) as ExecResult<T>;
} catch (e: any) {
if (!this.logErrors || !logErrors) {
// if we don't want to see the errors, just throw and allow the calling
Expand Down
8 changes: 3 additions & 5 deletions lib/subcommands/addmedia.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type {Simctl} from '../simctl.js';
import type {TeenProcessExecResult} from 'teen_process';

import type {Simctl} from '../simctl.js';

/**
* Add the particular media file to Simulator's library.
* It is required that Simulator is in _booted_ state.
Expand All @@ -12,10 +13,7 @@ import type {TeenProcessExecResult} from 'teen_process';
* returns non-zero return code.
* @throws {Error} If the `udid` instance property is unset
*/
export async function addMedia(
this: Simctl,
filePath: string,
): Promise<TeenProcessExecResult<string>> {
export async function addMedia(this: Simctl, filePath: string): Promise<TeenProcessExecResult<string>> {
return await this.exec('addmedia', {
args: [this.requireUdid('addmedia'), filePath],
});
Expand Down
16 changes: 5 additions & 11 deletions lib/subcommands/appinfo.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {convertPlistToJson} from '../helpers.js';
import type {Simctl} from '../simctl.js';
import type {AppInfo} from '../types.js';
import {convertPlistToJson} from '../helpers.js';

/**
* Get information about an app installed on the simulator
Expand All @@ -24,19 +24,13 @@ export async function appInfo(this: Simctl, bundleId: string): Promise<AppInfo>
try {
result = await convertPlistToJson(stdout);
} catch (err) {
throw new Error(
`Cannot retrieve app info for ${bundleId}: ${err instanceof Error ? err.message : String(err)}`,
{cause: err},
);
throw new Error(`Cannot retrieve app info for ${bundleId}: ${err instanceof Error ? err.message : String(err)}`, {
cause: err,
});
}
}

if (
!result ||
typeof result !== 'object' ||
Array.isArray(result) ||
!('ApplicationType' in result)
) {
if (!result || typeof result !== 'object' || Array.isArray(result) || !('ApplicationType' in result)) {
throw new Error(`App with bundle identifier "${bundleId}" not found. Is it installed?`);
}

Expand Down
19 changes: 5 additions & 14 deletions lib/subcommands/bootstatus.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {log, LOG_PREFIX} from '../logger.js';
import {waitForCondition} from 'asyncbox';
import type {SubProcess} from 'teen_process';

import {log, LOG_PREFIX} from '../logger.js';
import type {Simctl} from '../simctl.js';
import type {BootMonitorOptions} from '../types.js';
import type {SubProcess} from 'teen_process';

/**
* Start monitoring for boot status of the particular Simulator.
Expand All @@ -16,18 +17,8 @@ import type {SubProcess} from 'teen_process';
* property is not set.
* @throws {Error} If the `udid` instance property is unset
*/
export async function startBootMonitor(
this: Simctl,
opts: BootMonitorOptions = {},
): Promise<SubProcess> {
const {
timeout = 240000,
onWaitingDataMigration,
onWaitingSystemApp,
onFinished,
onError,
shouldPreboot,
} = opts;
export async function startBootMonitor(this: Simctl, opts: BootMonitorOptions = {}): Promise<SubProcess> {
const {timeout = 240000, onWaitingDataMigration, onWaitingSystemApp, onFinished, onError, shouldPreboot} = opts;
const udid = this.requireUdid('bootstatus');

const status: string[] = [];
Expand Down
7 changes: 3 additions & 4 deletions lib/subcommands/create.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {log, LOG_PREFIX} from '../logger.js';
import {retryInterval} from 'asyncbox';

import {SIM_RUNTIME_NAME, normalizeVersion} from '../helpers.js';
import {log, LOG_PREFIX} from '../logger.js';
import type {Simctl} from '../simctl.js';
import type {SimCreationOpts} from '../types.js';

Expand Down Expand Up @@ -58,9 +59,7 @@ export async function createDevice(
// add modified versions, since modern Xcodes use this, then the bare
// versions, to accomodate older Xcodes
runtimeIds.push(
...potentialRuntimeIds.map(
(id) => `${SIM_RUNTIME_NAME}${platform}-${id.replace(/\./g, '-')}`,
),
...potentialRuntimeIds.map((id) => `${SIM_RUNTIME_NAME}${platform}-${id.replace(/\./g, '-')}`),
...potentialRuntimeIds,
);
}
Expand Down
1 change: 1 addition & 0 deletions lib/subcommands/erase.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {retryInterval} from 'asyncbox';

import type {Simctl} from '../simctl.js';

/**
Expand Down
8 changes: 5 additions & 3 deletions lib/subcommands/io.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {rimraf} from 'rimraf';
import {randomUUID} from 'node:crypto';
import path from 'node:path';
import os from 'node:os';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';

import {rimraf} from 'rimraf';

import type {Simctl} from '../simctl.js';

/**
Expand Down
18 changes: 6 additions & 12 deletions lib/subcommands/keychain.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import os from 'node:os';
import fs from 'node:fs/promises';
import {randomUUID} from 'node:crypto';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';

import {rimraf} from 'rimraf';

import type {Simctl} from '../simctl.js';
import type {CertOptions} from '../types.js';

Expand All @@ -18,11 +20,7 @@ import type {CertOptions} from '../types.js';
* or there was an error while adding the certificate
* @throws {Error} If the `udid` instance property is unset
*/
export async function addRootCertificate(
this: Simctl,
cert: string | Buffer,
opts: CertOptions = {},
): Promise<void> {
export async function addRootCertificate(this: Simctl, cert: string | Buffer, opts: CertOptions = {}): Promise<void> {
const {raw = false} = opts;
const execMethod = async (certPath: string) =>
await this.exec('keychain', {
Expand All @@ -47,11 +45,7 @@ export async function addRootCertificate(
* or there was an error while adding the certificate
* @throws {Error} If the `udid` instance property is unset
*/
export async function addCertificate(
this: Simctl,
cert: string | Buffer,
opts: CertOptions = {},
): Promise<void> {
export async function addCertificate(this: Simctl, cert: string | Buffer, opts: CertOptions = {}): Promise<void> {
const {raw = false} = opts;
const execMethod = async (certPath: string) =>
await this.exec('keychain', {
Expand Down
7 changes: 2 additions & 5 deletions lib/subcommands/launch.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {retryInterval} from 'asyncbox';

import type {Simctl} from '../simctl.js';

/**
Expand All @@ -15,11 +16,7 @@ import type {Simctl} from '../simctl.js';
* returns non-zero return code.
* @throws {Error} If the `udid` instance property is unset
*/
export async function launchApp(
this: Simctl,
bundleId: string,
tries: number = 5,
): Promise<string> {
export async function launchApp(this: Simctl, bundleId: string, tries: number = 5): Promise<string> {
const result = await retryInterval(tries, 1000, async () => {
const {stdout} = await this.exec('launch', {
args: [this.requireUdid('launch'), bundleId],
Expand Down
Loading