Skip to content

Commit

Permalink
feat(runtime): two-tier subprocess API (denoland#11618)
Browse files Browse the repository at this point in the history
  • Loading branch information
crowlKats committed Apr 20, 2022
1 parent 8b25807 commit 8a7539c
Show file tree
Hide file tree
Showing 13 changed files with 1,323 additions and 5 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ jobs:
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
key: 8-cargo-home-${{ matrix.os }}-${{ hashFiles('Cargo.lock') }}
key: 9-cargo-home-${{ matrix.os }}-${{ hashFiles('Cargo.lock') }}

# In main branch, always creates fresh cache
- name: Cache build output (main)
Expand All @@ -252,7 +252,7 @@ jobs:
!./target/*/*.zip
!./target/*/*.tar.gz
key: |
8-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ github.sha }}
9-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-${{ github.sha }}
# Restore cache from the latest 'main' branch build.
- name: Cache build output (PR)
Expand All @@ -268,7 +268,7 @@ jobs:
!./target/*/*.tar.gz
key: never_saved
restore-keys: |
8-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-
9-cargo-target-${{ matrix.os }}-${{ matrix.profile }}-
# Don't save cache after building PRs or branches other than 'main'.
- name: Skip save cache (PR)
Expand Down
6 changes: 6 additions & 0 deletions cli/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ const UNSTABLE_DENO_PROPS: &[&str] = &[
"umask",
"utime",
"utimeSync",
"spawnChild",
"Child",
"spawn",
"spawnSync",
"ChildStatus",
"SpawnOutput",
];

static MSG_MISSING_PROPERTY_DENO: Lazy<Regex> = Lazy::new(|| {
Expand Down
139 changes: 139 additions & 0 deletions cli/dts/lib.deno.unstable.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1361,6 +1361,145 @@ declare namespace Deno {
export function upgradeHttp(
request: Request,
): Promise<[Deno.Conn, Uint8Array]>;

export interface SpawnOptions {
/** Arguments to pass to the process. */
args?: string[];
/**
* The working directory of the process.
* If not specified, the cwd of the parent process is used.
*/
cwd?: string | URL;
/**
* Clear environmental variables from parent process.
* Doesn't guarantee that only `opt.env` variables are present,
* as the OS may set environmental variables for processes.
*/
clearEnv?: boolean;
/** Environmental variables to pass to the subprocess. */
env?: Record<string, string>;
/**
* Sets the child process’s user ID. This translates to a setuid call
* in the child process. Failure in the setuid call will cause the spawn to fail.
*/
uid?: number;
/** Similar to `uid`, but sets the group ID of the child process. */
gid?: number;

/** Defaults to "null". */
stdin?: "piped" | "inherit" | "null";
/** Defaults to "piped". */
stdout?: "piped" | "inherit" | "null";
/** Defaults to "piped". */
stderr?: "piped" | "inherit" | "null";
}

/**
* Spawns a child process.
*
* If stdin is set to "piped", the stdin WritableStream needs to be closed manually.
*
* ```ts
* const child = Deno.spawnChild(Deno.execPath(), {
* args: [
* "eval",
* "console.log('Hello World')",
* ],
* stdin: "piped",
* });
*
* // open a file and pipe the subprocess output to it.
* child.stdout.pipeTo(Deno.openSync("output").writable);
*
* // manually close stdin
* child.stdin.close();
* const status = await child.status;
* ```
*/
export function spawnChild<T extends SpawnOptions = SpawnOptions>(
command: string | URL,
options?: T,
): Child<T>;

export class Child<T extends SpawnOptions> {
readonly stdin: T["stdin"] extends "piped" ? WritableStream<Uint8Array>
: null;
readonly stdout: T["stdout"] extends "inherit" | "null" ? null
: ReadableStream<Uint8Array>;
readonly stderr: T["stderr"] extends "inherit" | "null" ? null
: ReadableStream<Uint8Array>;

readonly pid: number;
/** Get the status of the child. */
readonly status: Promise<ChildStatus>;

/** Waits for the child to exit completely, returning all its output and status. */
output(): Promise<SpawnOutput<T>>;
/** Kills the process with given Signal. */
kill(signo: Signal): void;
}

/**
* Executes a subprocess, waiting for it to finish and
* collecting all of its output.
* The stdio options are ignored.
*
* ```ts
* const { status, stdout, stderr } = await Deno.spawn(Deno.execPath(), {
* args: [
* "eval",
* "console.log('hello'); console.error('world')",
* ],
* });
* console.assert(status.code === 0);
* console.assert("hello\n" === new TextDecoder().decode(stdout));
* console.assert("world\n" === new TextDecoder().decode(stderr));
* ```
*/
export function spawn<T extends SpawnOptions = SpawnOptions>(
command: string | URL,
options?: T,
): Promise<SpawnOutput<T>>;

/**
* Synchronously executes a subprocess, waiting for it to finish and
* collecting all of its output.
* The stdio options are ignored.
*
* * ```ts
* const { status, stdout, stderr } = Deno.spawnSync(Deno.execPath(), {
* args: [
* "eval",
* "console.log('hello'); console.error('world')",
* ],
* });
* console.assert(status.code === 0);
* console.assert("hello\n" === new TextDecoder().decode(stdout));
* console.assert("world\n" === new TextDecoder().decode(stderr));
* ```
*/
export function spawnSync<T extends SpawnOptions = SpawnOptions>(
command: string | URL,
options?: T,
): SpawnOutput<T>;

export type ChildStatus =
| {
success: true;
code: 0;
signal: null;
}
| {
success: false;
code: number;
signal: number | null;
};

export interface SpawnOutput<T extends SpawnOptions> {
status: ChildStatus;
stdout: T["stdout"] extends "inherit" | "null" ? null : Uint8Array;
stderr: T["stderr"] extends "inherit" | "null" ? null : Uint8Array;
}
}

declare function fetch(
Expand Down
Loading

0 comments on commit 8a7539c

Please sign in to comment.