Skip to content
ocx
install

Script Host API

ocx package test --script interprets a Starlark script against the materialized package environment, with no access to the host shell. The script calls the ocx.* and expect.* modules and receives typed values, and every function and value listed here is part of the v1 contract.

A script runs a binary from the package environment, asserts on the result and branches on the typed target platform.

r = ocx.run("shfmt", "--version")
expect.ok(r)
expect.contains(r.stdout, "v3")
p = ocx.target_platform
if p.os == ocx.os.Linux:
expect.eq(p.arch, ocx.arch.Amd64)

Every function is a method written with parens, while ocx.target_platform and the root paths are attributes. Returned values are typed, never anonymous dicts.

ocx.run(prog, *args, *, env=None, cwd=None, stdin=None)

Section titled “ocx.run(prog, *args, *, env=None, cwd=None, stdin=None)”

Spawns a binary from the composed package env and captures stdout, stderr, exit code and wall-clock duration. A non-zero exit code does not raise, and the script decides whether to fail.

ReturnsRunResult
prog, *argsThe first positional argument is the program, the rest are argv; splat a list with ocx.run(*cmd). Zero positional args fails the script.
envDict overlaid on the composed env for this call only. Reserved keys (PATH, OCX_HOME, the OCX_* loader vars, the OCX_AUTH_* credential family) are rejected.
cwdDefaults to the scratch root. Validated against the sandbox guard (no symlink escapes).
stdinString written to the child’s stdin.
ResolutionA bare program name resolves against the composed package directories before the host PATH. A package-shipped copy that cannot be executed fails the script instead of falling through to the host; a name the package does not ship resolves on the host.
UnspawnableA name that resolves nowhere, or a file that is not executable, fails the script outright and never yields a RunResult. Probe with ocx.exists to ask whether a bundle ships a binary.
SandboxSpawned binaries are not sandboxed: they run with normal host OS privileges, exactly as -- CMD does. The sandbox covers the ocx.* host API only.

Reads one variable from the composed package env, the same surface ocx package exec exposes. It does not read the host process env.

Returnsstr | None; None when the variable is not set
Never readableOCX_AUTH_* credentials and OCX_* resolution-affecting keys (OCX_BINARY_PIN, OCX_HOME, OCX_CONFIG, OCX_PROJECT, OCX_INDEX and the rest) return None whether or not the host sets them.
OverlayThe same keys cannot be set through the ocx.run(env=...) overlay; an attempt yields a Failed outcome.

Reflects the -p flag passed to ocx package test, the platform the package was built and tested for. The host platform may differ.

KindAttribute (no parens), a per-run constant materialized at script-engine init
TypePlatform
Platform-agnosticWith -p any or no -p, p.is_any is True and p.os / p.arch are None.

ocx.content_root / ocx.package_root / ocx.scratch_root

Section titled “ocx.content_root / ocx.package_root / ocx.scratch_root”

Path constants for the bundle’s files, its package directory and the writable scratch directory.

KindAttributes (no parens), per-run constants materialized at script-engine init
Typestr, /-normalized on every platform
content_rootThe bundle’s own file tree, read-only. ocx.read_file and ocx.exists fall back to it, so an archive whose root holds bin/ and release is read as bin/javac and release, never with a prefix naming the directory the package sits in.
package_rootThe package directory holding the bundle, read-only. Its layout is storage structure, not a script contract; use it only to build an absolute path for ocx.run.
scratch_rootThe writable scratch directory.
ocx.run(ocx.content_root + "/bin/java", "-version")

Reads a UTF-8 file within {scratch_root, content_root}.

Returnsstr, truncated at max_bytes (default 1 MiB)
pathRelative; resolved against the scratch root first, then content_root. An absolute path is rejected before anything else, including one built from ocx.content_root.
RejectedSymlink escapes and non-UTF-8 content
ocx.read_file("release") # the bundle's own `release` file
ocx.read_file(ocx.content_root + "/release") # rejected: path escapes the sandbox

Writes a file within scratch_root only. Parent directories must exist, and symlink escapes are rejected.

Checks existence within {scratch_root, content_root}, with the same guard and relative-path rule as read_file. It is the supported way to ask what a bundle contains, such as a JDK against a JRE in a variants: repository.

Returnsbool
Absent pathReturns False rather than raising.
Escaping pathFails the script.

Recursive, idempotent mkdir -p inside scratch_root.

Shapes the host functions hand back and the ocx.os / ocx.arch namespaces populate.

Returned by ocx.target_platform. str(p) returns "any" for the sentinel form or "os/arch" for the populated form, the same shape as the -p flag.

AttributeTypeDescription
is_anyboolTrue for platform-agnostic packages (Platform::Any), False for a specific OS/arch target.
osos | NoneThe target OS, or None when is_any is True.
archarch | NoneThe target CPU architecture, or None when is_any is True.

Typed enum value whose instances live in the ocx.os namespace.

  • Type: type(ocx.os.Linux) == "os"
  • Equality: equal only to another os value of the same variant, never to a string or an arch value
  • Cross-type comparison: returns False and never raises, so expect.ne(ocx.os.Linux, "linux") and expect.ne(ocx.os.Linux, ocx.arch.Amd64) both pass
  • str(value): the lowercase OCI string, such as "linux"

Typed enum value whose instances live in the ocx.arch namespace.

  • Type: type(ocx.arch.Amd64) == "arch"
  • Equality and str(): same semantics as os

Returned by ocx.run.

AttributeTypeDescription
exit_codeintChild exit code. Signal-killed children report 128 + signal (Unix convention).
stdoutstrCaptured stdout (UTF-8 lossy), truncated at 10 MiB.
stderrstrCaptured stderr (UTF-8 lossy), truncated at 10 MiB.
duration_msintWall-clock duration of the spawn, in milliseconds.
truncatedboolTrue iff stdout or stderr hit the 10 MiB capture cap.

Closed sets of typed enum constants, one per platform variant. A structural parity test catches a Rust-side variant missing from a namespace.

Constantstr(...)
ocx.os.Linux"linux"
ocx.os.Darwin"darwin"
ocx.os.Windows"windows"
ocx.os.Wasip1"wasip1"
ocx.os.Wasip2"wasip2"
Constantstr(...)
ocx.arch.Amd64"amd64"
ocx.arch.Arm64"arm64"
ocx.arch.Wasm"wasm"

Each assertion exits the script with the documented exit code on failure. The failure reads back in the --format json report under kind, a stable wire token: ok, eq, ne, true, false, contains, matches, fail.

FunctionPurpose
expect.ok(result, msg=None)Assert result.exit_code == 0. On failure, the captured stderr is included in the message automatically.
expect.eq(actual, expected, msg=None)Assert equality.
expect.ne(actual, expected, msg=None)Assert inequality.
expect.true(cond, msg=None)Assert truthiness.
expect.false(cond, msg=None)Assert falsiness.
expect.contains(haystack, needle, msg=None)Substring check for strings; membership check for lists.
expect.matches(text, pattern, msg=None)Regex match using Rust regex syntax. An invalid pattern exits with code 65.
expect.fail(msg)Unconditional failure.
fail(msg)The builtin Starlark form: same exit code, kind fail.
CodeMeaning
0All expectations passed.
1An expectation failed, expect.fail / fail() was called, or a host API returned a failure.
64Usage error — bad invocation.
65Script syntax / arity / type error, or invalid regex in expect.matches.
74I/O error — stdin read failure on --script -, scratch I/O failure.