This is the reference for ez's commands. The README has the install steps and a quickstart; SPEC.md lists what ez guarantees, each requirement with the laws that prove it. Requirement IDs such as (EZ-VEN-1) below point at its rows.
Every subcommand needs bend and git on PATH. ez help or ez --help
prints the command list, and ez help test or ez test --help the flags of
one command. The Bend runtime keeps --bend-help, --threads, --gpu and
--gpu-build for itself; put them after a -- to hand them to ez.
Every command exits 0 on success and 1 on any failure ez detects, except
ez run and ez tool run, which exit with the program's status (EZ-OUT-1).
A command that refuses writes nothing (EZ-OUT-2).
- init
- The ledger
- Adding a dependency
- Lock
- Upgrade
- Fetch and vendoring
- Check, build and run
- Tools
- The hub
- Publish
- Doctor
- Nix
- Prove and test
ez init [name] [entry.bend] [--description TEXT] scaffolds a project the
way cargo new does: ez.toml, .gitignore, the entry, and a library under
src/ that the entry imports.
$ ez init demo --description "a demo"
$ cat main.bend
# demo: a demo
import Base
import ./src/lib.bend as Lib
def main() -> IO(Unit):
IO.print(Lib.greeting())
$ ez run
hello
- The name defaults to
appand the entry tomain.bend. - The entry opens with
# <name>: <description>. The hub describes a package by the first line of its first file by path, andmain.bendcomes beforesrc/, so this is the line the hub shows once the package is published (see Publish). With no--descriptionthe line is# <name>: TODO describe <name>, to be edited before then. A description is one line; one holding a newline is refused (EZ-INIT-1). src/lib.bendis written only with an entry at the project's top, where./src/lib.bendfinds it, and only when nothing is there yet. An entry in a directory of its own, such asez init app src/main.bend, is written alone (EZ-INIT-2).- An entry or a
src/lib.bendthat is already there is never written over, and neither gets the description then (EZ-INIT-2). - The layout is a default for new projects, not a rule: any entry the ledger
names works, and
ez doctordoes not check where files are. - It never writes over a project. In a directory that already has an
ez.tomlit exits 1 and writes nothing. - It is the only command that makes a ledger. In a directory with no
ez.toml,ez add,ez remove,ez fetchandez lock(with or without--upgrade) exit 1 and write nothing, andez check,ez buildandez runexit 1 and start nothing (EZ-LED-6). - It refuses a name or entry that a ledger cannot carry (see below), and a description holding a newline, and then writes nothing.
The .gitignore it writes holds .ez/*, !.ez/lib and .ez/lib/*, which
the vendoring allowlist needs (see Fetch and vendoring).
ez.toml is the ledger. A ledger, and a dependency vendored from a repo that
never published:
[package]
name = "myapp"
entry = "src/main.bend"
bin = "src/main.bend"
[deps]
[deps.wire]
hash = "0x7e63a5b990a375c304ed462c071214a6"
git = "https://github.com/owner/repo"
rev = "16773c0aa9914b5f04d062469d50100111eb9c9c"
tag = "v1.0"
root = "."
narHash = "sha256-..."
entry = "src/lib.bend"[deps.*]is a package. It is imported, andez fetchputs it onBEND_LIB.- A dependency with no
gitkey lives on the hub. - A CLI is a
[tools.*]pin. It is not imported and not put onBEND_LIB(see Tools). binis the file the tool commands build. With nobin, that is the entry.publish-asandversionname the package on the hub when it is published (see Publish).ez initwrites neither.rootandnarHashare there so thatez locknever has to consult anything a clone does not have (see Lock).
Paths in the ledger use forward slashes, on every system: write
src/main.bend, not src\main.bend.
The ledger is enough on its own: no command writes or reads a record of
origins beside it (there is no .ez/origins.toml).
ez init, ez add, ez remove and ez lock --upgrade write only a ledger
that reads back as what they meant to write (EZ-LED-4). Each refuses, with
exit 1 and nothing written, a ledger where:
- a name or value holds
",\or a newline; - a dependency has no hash;
- a git source names no repo or no root.
So ez init 'my"app' refuses, and so does ez remove on a hand-edited
entry = "src\main.bend". The last two cases cannot come from a ledger ez
read, so they only meet values you typed or edited in.
ez add <target> [ref] [entry.bend] [--rename NAME] vendors a git package and
records it in the ledger. On success it prints the import line for the
entry's path inside the package, the line ez publish prints (EZ-RES-2).
ez add <name>@<version> [entry.bend] records a hub package by its name
instead (see "Named imports" below).
ez add and the tool commands read a target the same way (EZ-RES-3):
- A target with
://in it, or startinggit@, is a git URL and is kept. - A target starting
/,./,../or~/is a path. owner/repo, exactly two segments of letters, digits,-,_and., neither of them.or.., ishttps://github.com/owner/repo, unless the second segment ends in.bend. Sovercel/next.jsis GitHub andsrc/main.bendis a path.- Anything else is a path. The empty word is refused.
- For
ez addonly, a path that bend reads as a hub package's<name>@<version>(a-z, 0-9 and-, 1 to 64 characters, then@and four numbers like1.0.0.0) is that package on the hub. A git URL,git@host:pathorowner/reponever is.
A relative path is recorded as it was given, relative to the project, which
is the directory ez runs in, since every command reads ez.toml from there.
An absolute one stays absolute, and a leading ~/ is expanded. ez add,
ez lock, ez fetch and ez lock --upgrade all read a relative git from
the project, so a project and a sibling repo moved together still lock to the
same bytes (EZ-LED-8).
With no ref, ez add pins (EZ-RES-1):
- the greatest semver-ish release tag on the remote (
v1.9.0beatsv2.0.0-rc1); - with no release, the greatest pre-release;
- with no semver-ish tag at all, the remote's default branch, as its
HEADnames it. A remote whoseHEADnames no branch is refused.
A named ref resolves exactly: refs/tags/<ref> first, then
refs/heads/<ref>. So a tag beats a branch of the same name, and main
never resolves to feature/main. A 40-hex ref is used as the commit without
asking the remote. The tag or branch is recorded as tag, which
ez lock --upgrade re-resolves.
With no entry, ez add reads [package] entry from that revision's
ez.toml, then [package] bin when entry is absent, then main.bend. An
entry given on the command line is used as given. It refuses:
- an entry the revision does not hold;
- a package whose imports climb out of the checkout;
- a package with a file under a directory named
license, in any case, as bend 2.0.27 does.
The package is walked from the files of the checkout itself, so its hash, the
files laid under BEND_LIB and their manifest all come from one reading of
it. Like bend --publish in 2.0.27, the walk takes along every file named
exactly LICENSE beside a file of the package, and the hash covers it.
A dependency pinned before bend 2.0.27 has a hash that does not cover its
LICENSE files. ez keeps that recorded name, as cargo and uv keep a checksum
they recorded: ez lock and ez lock --upgrade judge such a checkout
without its LICENSE files when the full manifest does not match the name.
Only a new ez add takes the new rule.
The dependency is named the way cargo names one, never after its entry (EZ-LED-7). The first rule that applies wins:
--rename NAME, likecargo add --rename, records it underNAME.NAMEmust be a TOML bare key, and a source the ledger already records under another name is refused rather than recorded twice.- A source the ledger already records keeps the name it has there, so adding it again replaces that entry.
- A target that is an ez project is named by the
[package] nameof itsez.tomlat the fetched rev, when that is a TOML bare key (letters, digits,-,_). A name that is not one falls through rather than being refused. owner/repoor a URL is named by the repository ([deps.repo], any.gitdropped).- A path is named by its directory.
A name the ledger gives to another source stops ez add with exit 1 and
leaves the ledger as it was, --rename included. Remove that entry, or add
this one with --rename under another name.
Whatever stops it, ez add writes nothing: not the ledger, not
.gitignore, and no tree, not even in BEND_LIB. With no ledger, one that
does not parse, a target that names nothing, or a --rename refused by the
ledger alone, it stops before asking the remote anything.
ez remove <name> drops the dependency from the ledger, and from the
.gitignore allowlist with it. A vendored dependency's committed tree,
.ez/lib/<hash>, is removed too, unless another dependency still names that
hash. A name the ledger does not have stops ez remove with exit 1, and it
writes nothing, as cargo remove does.
ez lock resolves every import and writes ez.lock.toml. Its output is a
function of the ledger and the committed tree, so someone who has just cloned
your repo can run ez lock and get your lock back to the byte, without
re-running ez add (EZ-DOC-3).
ez writes ez.toml and ez.lock.toml through eztoml's renderer, which writes the table each dotted header implies and no blank lines. A package's tables are headed by its bare hash:
[lock]
version = "2"
hub = "https://hub.bend-lang.com"
[packages]
[packages.0x7e63a5b990a375c304ed462c071214a6]
[packages.0x7e63a5b990a375c304ed462c071214a6.source]
kind = "git"
url = "https://github.com/owner/repo"
rev = "16773c0aa9914b5f04d062469d50100111eb9c9c"
entry = "src/lib.bend"
root = "."
narHash = "sha256-..."
tag = "v1.0"
[packages.0x7e63a5b990a375c304ed462c071214a6.files]
LICENSE = "309f5aae..."
"src/lib.bend" = "fff16a9f..."A ledger or lock in the layout ez wrote before 1.1, with a quoted hash
([packages."0x…".source]) and a blank line before each table, reads to the
same model, and the next ez lock or ez add writes it in this layout.
bootstrap.sh reads both. An ez older than 1.1 reads this layout too (its
ez fetch and ez check work), but its ez doctor calls the lock out of
date, and its ez lock writes the old layout back.
What it reads:
- the ledger;
- the
.bendfilesgit ls-fileslists, so it refuses outside a git repository; - the committed trees under
.ez/lib; - each git dependency's files at its ledger
rev; - hub content.
It takes the hub imports of every listed file and follows no local import, so
an untracked file never reaches the lock, even one a tracked file imports. It
does not record the bend that ran it, and without --upgrade it never writes
ez.toml (EZ-DOC-5).
Checks:
- Every package is checked against its
0xname and every file against its sum, whatever it was read from. - A tree already under
BEND_LIBis judged as the same bytes cloned at the ledger'srevwith the ledger'snarHash. - A git dependency whose tree is not under
BEND_LIBat the ledger'srevis fetched, checked againstnarHash, and left there once the lock is written. - A hash the ledger does not name is fetched from the hub. When the hub does not have it, the lock says the ledger does not name it and exits 1.
- A lock whose hashes, paths or values would not read back (a repeated hash,
or a
",\or newline in one) is refused, and so is a package with a file whose path holds=.
A fetch or a check that fails stops the lock with exit 1, and a lock that stops writes nothing: not the lock, and no tree.
Tool pins: ez lock records each [tools.*] pin in ez.lock.toml
([tools.bolt], say), copying rev and narHash from the ledger. A pin
missing either stops ez lock with exit 1 and names the command that fills
it, ez lock --upgrade --package bolt.
ez lock --upgrade re-pins git dependencies and [tools.*] pins, then
writes the lock. --package NAME limits that to one dependency or one tool,
and every other ledger entry keeps its rev, tag and hash (EZ-RES-6).
- A
tagis re-resolved (EZ-RES-8). - A dependency with only a
revmoves to the default branch tip when the pinned commit is an ancestor of it, and stays a commit pin. Otherwise the upgrade refuses (EZ-RES-5). - A hub dependency does not move (EZ-RES-4).
- An empty tool pin is filled, its tag re-resolved and its
narHashfilled, the way a git dependency's is. - A pinned commit whose tree no longer hashes to the pin is a drift, and stops the upgrade.
The upgrade itself fetches only the dependencies it re-pins. The lock it then
writes fetches any other git dependency whose tree is missing, the way a
plain ez lock does.
When a hash moves:
- A dependency marked
vendoris laid out again under.ez/lib/<new hash>, its old tree is removed, and the.gitignoreallowlist follows it. Any other moved tree is left underBEND_LIB. - An import line that starts
import <old>/, at column 0, is rewritten to startimport <new>/, and no other line changes (EZ-VEN-2, EZ-VEN-3).
The upgrade and the lock are one plan: ez.toml is written once, then
.gitignore, the rewritten sources and the lock. An upgrade that stops, or a
lock after it that stops, writes nothing at all.
ez fetch fills BEND_LIB from the lock, as uv sync --frozen does. Before
it lays anything it checks every file against the lock's sum, every package
against its name, and every git checkout against the narHash the lock
records (EZ-FETCH-1). It refuses with exit 1, writing nothing, when there is
no ez.toml or no lock, or when any check fails; a refused fetch lays no
tree, and removes none already under BEND_LIB.
BEND_LIB defaults to .ez/lib. bend looks in ~/.bend/lib when it is
unset, so set it when you run bend yourself.
A dependency is committed with the project when its ledger entry says
vendor = true. Its tree lives under .ez/lib/<hash>, and the hash is named
in .gitignore. Without vendor = true the tree is not committed, and
ez fetch or ez lock fills it in.
ez add records a new dependency without vendor; you set it by hand.
Adding the dependency again keeps it, lays the new tree under
.ez/lib/<hash> rather than BEND_LIB, and removes the old committed tree
when the hash moved and no other dependency names it.
The allowlist is derived from the ledger (EZ-VEN-1). After ez add,
ez remove or ez lock --upgrade:
- the
!.ez/lib/<hash>lines of.gitignoreare exactly the hashes of the dependencies markedvendor = true, each once, in the order the ledger first names them; - every other line is left as it was, blank lines included;
- a missing hash is written where the first allowlist line was, or at the end of the file, and a hash the ledger no longer vendors is dropped;
- the file is only written when this changes it.
ez counts as its own any ! line under .ez/lib/, however it is spelled (a
leading or trailing /, surrounding blanks), and writes the ones it keeps as
!.ez/lib/<hash>.
The allowlist needs .ez/*, !.ez/lib and .ez/lib/*, which ez init
writes. git cannot re-include a file under a directory it has excluded, so
under a bare .ez/ no allowlist line works, and ez replaces that line with
the three.
ez checkchecks the entry without running it.ez build [out]builds the entry to a native binary.ez run [args..]checks and runs the entry with bend, ascargo runruns a binary. The program reads ez's stdin and prints straight to the terminal as it goes, gets every word afterrun, and its status is the status of the command. The entry is the oneez.tomlnames, ormain.bendwhen it names none (EZ-TOOL-9).
The tool commands build and run another project's binary, whether that project uses ez or is a plain Bend repository:
ez tool sync build and link every pinned tool, at its lock rev
ez tool run [--entry F] <target> [-- args..]
fetch, build and run a repo's binary
ez tool install <target> [--entry F]
build the binary and link it on PATH
ez tool upgrade <target> [--entry F]
rebuild when the commit moved, refresh the link
A target is read as in Targets. ez tool run <target> passes
every word after the target to the program, dropping one leading --, so
--entry goes before the target there, as uvx takes its own options before
the command.
ezx Emerging-Patterns/bolt -- --gpu off # an ez project, built from its lock
ezx ./hello # a plain Bend repo: builds main.bend
ezx --entry src/cli.bend ./hello # another file of the same checkoutA tool the project uses is a [tools.*] pin in the ledger:
[tools.bolt]
git = "https://github.com/Emerging-Patterns/bolt"
tag = "v0.4.0"
rev = "24b497e294a08f6a83e0b08ece3795f813421b87"
root = "."
narHash = "sha256-…"
entry = "bolt/main.bend"A pin needs no hash and is never a dependency (EZ-LED-5). ez lock copies
it into the lock and ez lock --upgrade moves it (see Lock and
Upgrade). ez tool sync checks every pin against the lock before
it installs any, then builds and links each at the lock rev. It runs one
install per pin, so a later install that refuses leaves the earlier ones
installed, as cargo install a b does.
A target resolves to a commit (EZ-TOOL-4):
- A name that matches a
[tools.*]pin resolves to the rev inez.lock.toml, which has to be a full commit. Soez tool run boltuses the lock's rev, whileez tool run owner/repois the repo's HEAD. - A remote resolves to
git ls-remote <url> HEAD. - A path resolves to a clean
HEAD. A worktree with uncommitted or untracked changes (untracked files count whatever the repository's own settings hide), or a path that is not the top of a checkout, has no commit and is built every time (EZ-TOOL-3).
The built file is the pin's bin, then the pin's entry, then the file
--entry names, then the checkout's bin, then its entry, then
main.bend (EZ-TOOL-7). So --entry picks another file of an ez project, as
cargo's --bin does, but does not move a pin, which the project decided. A
built file that is not in the checkout is refused before anything is
written, with a message that names it and suggests --entry.
A checkout with no ez.toml is a plain Bend repository, and runs the way
uvx runs any package. It has no git dependencies and no lock, so ez builds
it with BEND_LIB set to a library of its own in the cache, <slug>/lib,
beside its binaries, and bend fetches the program's 0x… and
<name>@<version> hub imports into it itself. Those are content-addressed,
so bend checks each against its name. A build that fails because bend could
not load an import says so on stderr, as any failed build does.
A checkout that has an ez.toml and no ez.lock.toml is an ez project whose
author has not locked it, and is refused with a pointer to ez lock.
The checkout and the binary are cached under $XDG_CACHE_HOME/ez/tool/<slug>,
or ~/.cache/ez/tool/<slug> when that is unset.
- The slug of a URL or of
owner/repodrops a trailing.git. A URL with no host,file:///srv/repo, is cached asfile://localhost/srv/repois. A slug that is empty, absolute, or climbs with..is refused (EZ-TOOL-8). - A remote's checkout is kept per commit, under
<slug>/<rev>/src, and reused while itsrevrecord names that commit. - Every binary is kept under
<slug>/bin/<record>/, named by itskey: that commit, the built file andbend version(EZ-TOOL-2). So a pin'sbinorentry, a free run of the same repository at another commit, and a new bend each build their own binary, and none rebuilds another's.
ez tool run builds the binary and runs it, as cargo run does. It says on
stderr what it resolves and builds, and a build that fails shows bend's output
there too. Then the built program reads ez's stdin and prints straight to the
terminal as it goes, and its status is the status of the command (EZ-TOOL-5).
ezx <target> [-- args..] is ez tool run. The nix install provides it; to
make one yourself, see the README.
ez tool install fetches the lock, for an ez project, and builds
<slug>/bin/<record>/<name>.out, then links that file onto PATH as
<name>. Each long step says what it is doing before it waits, and on
success the command names what it installed and where the link is.
<name> is the package name in the target's ez.toml (bolt for bolt), or
app when the ledger names none. For a plain repository it is the built
file's name without .bend (cli for src/cli.bend), or the repository's
name, the last segment of its URL or path with .git dropped, when that is
main. A name that is not a TOML bare key is refused.
The link goes in the first of these that is set and nonempty (EZ-TOOL-1):
$EZ_TOOL_BIN/<name>$XDG_BIN_HOME/<name>~/.local/bin/<name>
The directory is created when it is missing. With none of the three, install and upgrade refuse before anything is built.
ez tool upgrade reads that commit again, builds it when no binary is kept
for its record, and writes the link again. A binary already built for that
record is left in place. Neither install nor upgrade runs the binary
(EZ-TOOL-6).
A target, ledger, fetch, build or link that fails exits 1, and a command that refuses writes nothing: no checkout, no binary, no link.
Hub packages come from https://hub.bend-lang.com, or from the hub a hub
key in the ledger's [package] table names. The lock records that hub, and
ez fetch fetches hub packages from it. ez lock does not read BEND_HUB.
A hub package can be imported by name, as bend 2.0.26 and later allow:
import <name>@<version>/file.bend as P. ez add records one:
ez add bend-tensors@0.0.0.2 # or: ez add bend-tensors@0.0.0.2 lib.bendIt asks the hub the ledger names ([package] hub) for the hash the name
names, fetches that package, checks its manifest hashes to that name, lays
it under BEND_LIB with the name's file beside it, and records a dependency
with hub = "<name>@<version>" and its hash:
[deps.bend-tensors]
hash = "0x39d8166231e68361eb37e8bef9287b8a"
hub = "bend-tensors@0.0.0.2"
entry = "tensors.bend"Then it prints the hash and the line to import it by,
import bend-tensors@0.0.0.2/tensors.bend as Tensors. The key is
--rename NAME when given, else the key ez.toml already gives that name at
any version, so adding another version replaces it, else the name before the
@. A key ez.toml gives any other dependency is refused. The word after the
name is the entry, since the version is already pinned. With none, the entry
is the package's main.bend, else its first top-level .bend file, else none
is recorded and no import line is printed. A name the hub does not have, a
package that does not hash to it, and an entry the package lacks are refused,
and a refused add writes nothing. You can also write the section by hand.
ez lock pins every name to its hash under [names] in ez.lock.toml. It asks
the hub only for a name that neither ez.toml nor the lock already records,
which happens only for names a dependency imports. A named import in your own
files must be in ez.toml, or ez lock refuses. ez lock and ez fetch
write BEND_LIB/names/<name>@<version>, the file bend reads a name from, so
bend never asks the hub at build time; ez fetch rewrites one that names
another hash and says so. ez doctor fails when a names file is missing or
names another hash. A name is a pin: ez lock --upgrade does not move it,
since the hub cannot list a name's versions.
ez publish sends the entry to the hub with bend --publish, under ez's own
0x name. Publishing is irreversible and public, so every check that can be
made before the upload is made first:
- there is a ledger that parses, and it names the entry, and the name it publishes under, if any, is one bend takes (EZ-PUB-3);
git status --porcelain --untracked-files=normalnames no path, and git could answer it (EZ-PUB-1);- the walk from the entry makes a package;
- git tracks every file of that package, unchanged (
git ls-files -vtagH), so an ignored file is refused too; - every hub package the package imports is on the hub (EZ-PUB-5).
For the last check ez reads the hub imports of the package's files, each
0x<hash>/... and each <name>@<version>/..., and asks the hub the ledger
names ([package] hub) about each, as ez lock does: the hub must serve a
manifest that hashes to the 0x name, and must resolve a name to a 0x name,
the one ez.lock.toml's [names] or ez.toml records for it when either does.
A package on the hub is built from the hub alone, so one that imports a
dependency you vendored from git, or a name the hub does not know, is refused,
as cargo publish refuses a dependency that is not on crates.io:
ez: the package imports hub packages that https://hub.bend-lang.com does not have, or could not be asked about, and a package on the hub has to build from the hub alone, so nothing was sent:
0x04b9afdd6d6a56039c5ce6dfb1e55294 (eztoml, git https://github.com/Emerging-Patterns/eztoml) is not on the hub; publish it first
Every missing import is named, with the dependency's key and origin when ez.toml records it. A hub that cannot be reached, or that answers with anything but the package or a 404, refuses too, since what cannot be checked is not sent. A package with no hub import asks the hub nothing.
Only then does bend upload. Just before it does, ez prints, on stderr, the line the hub will describe the package by:
hub description: # demo: a demo
The hub takes the first line of the package's first file by path, in plain
string order, so an uppercase letter comes before every lowercase one, and
passes over every file named LICENSE. The package's files are the entry,
the files it imports, and the LICENSE beside each, so the line is the
entry's first line unless a file it imports sorts before it: an entry
main.bend importing lib/util.bend is described by the first line of
lib/util.bend. The line comes as bend starts, not as a question: to change
it, stop bend while it mines its proof of work, edit the file, commit, and
publish again. A publish refused before the upload prints only why
(EZ-PUB-4).
bend cannot report a package's hash without
uploading it, so its answer is checked after: ez succeeds only when bend exits
0, a line of its output is exactly a 0x name, and every such line equals
ez's own hash. It then prints that hash and the import line for the entry's
path inside the package. Any other answer exits 1, with no import line
(EZ-PUB-2).
bend --publish reads the hub from BEND_HUB itself, while ez checks the
imports against the hub the ledger names. Both are
https://hub.bend-lang.com unless you set one, so a project that publishes to
another hub names it in both places.
A package can also go on the hub under a name, so that others import it as
<name>@<version> (see Named imports). Give the ledger's
[package] table both keys:
[package]
name = "tensors"
entry = "tensors.bend"
publish-as = "bend-tensors"
version = "1.2.0"ez publish then runs bend tensors.bend --publish bend-tensors@1.2.0.0,
which publishes the package and names it in one run, and on success prints
the hash and import bend-tensors@1.2.0.0/tensors.bend as Tensors. bend asks
you to bend login the first time, and the hub refuses a name that is
someone else's or a version that does not go up.
publish-asis a hub name, as bend's rule reads one: a lowercase letter, then a-z, 0-9 and-, 1 to 64 characters in all. Whether you may take a name is the hub's to decide. It is notname, which is the ledger's own and may be anything, and nothub, which is the hub's URL.versionisMAJOR.MINOR.PATCH, three numbers with no leading zeros. bend's versions have four numbers, so ez publishes1.2.0as1.2.0.0. A pre-release or build suffix, such as1.2.0-rc.1or1.2.0+abc, is refused: the hub has no way to write one.- With only one of the two keys, or with either one malformed,
ez publishrefuses with exit 1 before it asks git anything or runs bend, and says which key is missing or wrong. - With neither, it publishes by hash as above.
ez doctor reports on the toolchain and the project, and never writes to
your source (EZ-VEN-4). It reports:
- the versions of
bend, the C compiler ($CCwhen it is set, elsecc) andgit; - the ledger: the package, its entry and how many dependencies it names;
- what the lock names, and whether every locked package is under
BEND_LIB; - whether the lock is up to date: it runs a plain
ez lockwithout the network, over the ledger, the committed sources and the trees underBEND_LIB, and compares the text withez.lock.tomlbyte for byte, ascargo --lockedanduv lock --checkdo. A lock that is stale, oneez lockwould refuse to write, or one it cannot judge because a package is missing fromBEND_LIB, fails the command (EZ-VEN-6); - drift: every hash an import line names that the ledger does not, and every ledger dependency no import line names (EZ-VEN-5). It never rewrites your source to fix it.
It exits 1 when it reports any problem, after reporting everything it found.
In a flake, lib.${system} builds the program the ledger names and turns
ez.lock.toml into a BEND_LIB store path, so there is nothing to copy into
your repo:
inputs.ez.url = "github:Emerging-Patterns/ez";
# ...
ez = inputs.ez.lib.${system};
pkg = ez.mkPackage { inherit bend; src = self; };
checks.${system}.proofs = ez.mkProofs {
ez = inputs.ez.packages.${system}.default;
src = self;
name = "…-proofs";
};
lint = ez.mkLint { src = self; };mkPackagebuildsbinfromez.tomlwhen that is set, otherwiseentry, otherwisemain.bend, and wraps the binary withbendonPATH.lockis an explicit lock path.bendLib, when set, is the store path used asBEND_LIBand wins over that lock.- With neither,
BEND_LIBcomes fromsrc/ez.lock.tomlwhen that file is present.
ez.bendLib ./ez.lock.tomlis that tree on its own. Every file comes from a fixed-output derivation keyed by the sha256 the lock already records, so the build needs no network andbendnever reaches the hub.mkProofsrunsez proveover a copy ofsrc. This repo's ownproofscheck is that.mkLintbuildsboltfrom[tools.bolt]in the lock.toolPackagebuilds one locked tool, anddevPackages srcbuilds every one.mkShellputs those onPATHwhensrcis set.
None of these need inputs.bolt.
This repo's fresh check (mkFresh) is a clone with no network:
sh bootstrap.sh over that tree fetches nothing, the build in the README
builds, and ez lock with ez.lock.toml deleted writes it back byte for
byte. CI also follows the README's install steps without nix and runs
ez prove.
ez prove is the gate. It runs bend on every PROOF.bend in the tree, all
at once, and passes a proof only when the first line bend prints is exactly
ALL PROOFS CHECK, the verdict bend 2.0.32 and later print for a file with no
main. A proof that reaches an @unsafe def or foreign code, imports included,
is SOME PROOFS FAIL, so a PROOF.bend imports only modules that run nothing.
ez keeps the half of a module that runs programs or fetches over the network
in a sibling module no law imports (src/git/exec.bend beside src/git/git.bend, for
one). The gate reads the line rather than the exit status.
It prints a line for each proof and then the count, and exits 1 when any proof failed. Nothing is cached, so every run checks every proof.
ez test runs every */tests/*.bend, each as its own bend, all at once,
and holds each run's output to the #| trailer at the end of its file.
This repo's tests are the end-to-end tests under tests/, which drive real
git daemons, bend --publish and nix-build, so CI does not run them.
These variables apply to both ez prove and ez test:
EZ_CAPsets the memory cap eachbendruns under.EZ_JOBSsets how many run at once. It defaults to your cores, and never to more of them than the memory cap divides the machine into, since any onebendmay claim the whole cap.EZ_DEADLINEisez test's budget in seconds, or0for no budget. The default is five minutes for a whole run, and a run that takes longer fails on that the way a test that printed the wrong line fails.