A native deployable
On this page
A deployable is one program your app is built into. An app that declares
no deployables: has exactly one, app: a native server binary that
serves every API and runs all the background work. An app that declares
them is built as Declared deployables says.
$ jadpo build binary build/target/app/app build b_e20fc10f5474 commit 8d113e6 settings DATABASE_URL✓ built app · native in 49.1 s→ jadpo migrate apply it starts only once its database has every migration
jadpo build and jadpo build app build it. Any other name is
cli.build_arguments.
What jadpo build writes
It checks as jadpo check does, writing the same files in build/. At
the first error it stops with the same screen, ending ✗ build stopped.
Otherwise it writes the deployable's Rust into build/rust/app/: the
program's crate, jadpo-program-app, and a binary named after the
deployable, with the toolchain this Jadpo pins in rust-toolchain.toml
and the runtime crates' Cargo.lock. It compiles that in release,
offline and --locked, in the release profile its workspace's
Cargo.toml gives: fat link-time optimisation in one codegen unit at
opt-level = 3, its symbols kept. The paths it was built from are
remapped and SOURCE_DATE_EPOCH is fixed: the variable's value when it is
set, else the commit's time.
The binary is build/target/app/app, and each API's OpenAPI document and
<api>.failures.<locale>.json are copied into build/target/app/openapi/.
It is built without the runtime's testing feature.
cargo failing is toolchain.build_failed, since the Rust Jadpo writes
always compiles. A file it can't write is toolchain.artifact_write_failed.
What it compiles with
Everything a build needs comes with jadpo or is fetched by it, so a
released jadpo builds anywhere and you don't install Rust. A native
build still needs your machine's C compiler, which links the binary and
compiles ring's cryptography.
- The runtime crates' source is carried in the
jadpobinary, with theirCargo.lockand the toolchain file. - The Rust toolchain that file pins (
rustc,cargoand the standard library for your machine, and wasm32's once a Worker is built) is fetched from Rust's download server the first time a build needs it, intojadpo/toolchains/<version>-<host>/of your cache folder. Each archive is checked against the SHA-256 the release's manifest gives, and the manifest against the digest this Jadpo pins.JADPO_RUST_DIST_SERVERnames a mirror, whose manifest is checked against the.sha256beside it. - The crates the lock names come from crates.io, or whatever cargo's
own configuration puts in its place. cargo fetches them only when its
cache can't build offline, into
CARGO_HOMEwhen that is set, else the cache folder'sjadpo/cargo/, each checked against the lock's checksum.
Each fetch says what it is fetching (Fetching Rust 1.99.0 for aarch64-apple-darwin, Fetching the runtime's crates). Where stderr
animates, it is on the live line, each archive with a bar; otherwise it is
a line on stderr; under --json it says nothing. The screen's fetched
line, and --json's fetched, say what came (Rust 1.99.0 (146.9 MB) and 292 crates). A fetch that fails is toolchain.fetch_failed.
With CARGO naming a cargo, that cargo builds instead and nothing is
fetched. cargo sets CARGO for what it runs, and the gate does for this
repository's tests.
The build stamp
Every build carries a stamp saying what it is (Records and manifest):
- the build id:
b_and the first twelve hex digits of a SHA-256 over the deployable's program (what it is compiled from: its Rust and its plan, its name and its stores' migrations), the compiler's version, the target andjadpo.lock; - the commit
gitsays the project is at, and whether its tree holds changes the commit doesn't (build/and.jadpo/aside); a project git doesn't track has no commit and counts as changed; - the compiler, the deployable and the target.
The binary holds the stamp as a constant. The runtime plan it holds
records the build id where jadpo dev records the graph's digest
(Several changing calls in one
action).
Starting
The binary reads only the variables its settings name, and refuses to start when one is missing or invalid. It names each setting and its variable, never the value (Configuration and time).
It builds each connection's driver. One it can't build is listed as it
starts, and answered misconfigured when used. A database whose
connection names no url: stops the start, since only jadpo dev and
jadpo test give one an address of their own.
Each database the project keeps migrations in is compared with the migrations the build holds. A migration not applied, or one applied that the build doesn't have or that changed since, stops the start (Migrations). A database it can't reach yet is waited for, trying again after 1 second and doubling to 10, since a database that is down isn't a configuration error.
Then it listens on PORT (8080 when unset) on HOST (every IPv4
interface when unset), with a worker per core.
Running and stopping
Each worker serves requests and runs the tick, the runtime's regular pass over background work. The first SIGTERM or ctrl-c stops it: it accepts nothing more, answers the requests it is reading, lets its background work give back what it holds, and ends with code 0. A second ends it at once. A start that fails ends with code 1.
Running one tick
The binary takes one command. None, or serve, serves as above.
<deployable> tick runs one tick and ends (The
tick). Another scheduler runs it when
schedules: { trigger: ScheduleTrigger.external } says the server's own
timer never ticks.
tick reads its settings, builds its drivers and compares its stores'
migrations as serving does, but waits for no database: one it can't reach
ends it with code 1, store_unreachable. It ends with code 0, or with 1
when the tick couldn't work in a store or didn't finish, having panicked
or outlasted the drain. The first SIGTERM or ctrl-c drains it, giving back
what it holds. Any other command ends with code 2, command_unknown,
without repeating what was given.
jadpo build gives such a deployable a tick line, with the command and
how often to run it (build/target/app/app tick · each minute), and
--json a tick (command, every_ms).
What it logs
It logs one JSON object a line on standard output, its keys sorted, each
with the stamp's fields (build, commit, modified, compiler,
deployable, target):
runtime.startup_failed, with every problem that stopped the start (problems, each with aproblemcode, the setting, variable or store it is about, and amessage);runtime.waiting, while a database can't be reached;runtime.started, with the address, workers,trigger(nativeorexternal) and each connection whose driver wasn't built (unbuilt);runtime.ticked, after each tick, the server's own timer's as well as atickcommand's:ran, each schedule it ran or skipped by name with its outcome,deliveries,received,polled,deleted,alerts,problems, each store it couldn't work in,unbuiltandms;operation.failed, for a fault behind a 500, with its classification, route,request_idand.jadpoframes (Failures, Debugging and review). When the fault is a bug in Jadpo,bug_in_jadpoistrueandreportholds the issue form's address; otherwise they arefalseandnull;runtime.stopped.
The rest of a deployable's log comes with telemetry (Built-in services).
Markdown for agents: /docs/a-native-deployable.md.Built from Jadpo main at a707efb.