Skip to content

Releases

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 07 Nov 04:45
462fcb1

Install

curl -fsSL https://raw.githubusercontent.com/Abyssinia-Labs/latem/main/install.sh | sh

The installer verifies the sha256 of the archive it downloads. That
proves the file arrived intact; it does not prove who produced it,
because the checksums come from the same server as the binary. The
signature is what proves that, and checking it takes one command:

minisign -Vm latem_0.3.0_checksums.txt -P 'RWTTZK1Cxjg51w6BUpg5KpkKhOHzUT+EDhgidVqHUHsiTr6nPjKC3esu'

That key is D73938C642AD64D3. It is also in
internal/selfupdate/trusted.pub
and compiled into every binary in this release — latem version
prints which one a binary trusts.

An agent with auto_update = true does this for itself, daily,
against the same key compiled into the binary (RUNNER_SPEC §20).

What's Changed

Full Changelog: v0.2.0...v0.3.0

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 04 Nov 04:18
76673c3

What changed

latem now runs your GitHub Actions workflows as they are written. v0.1.x ran
the jobs in .latem/jobs.yaml; v0.2.0 also plans and runs .github/workflows,
on your own machines, under GitHub's runner contract: strategy.matrix,
actions/checkout, JavaScript and composite actions, GITHUB_ENV and
GITHUB_OUTPUT, ${{ secrets.* }} and ${{ vars.* }} answered on the machine,
and a check on the commit when the job ends. .latem/jobs.yaml works as it did.

This is the first release since the repository went public, and the first that
install.sh hands a stranger that does anything useful with
latem.dev.

Upgrading from v0.1.x

The wire protocol is 2.7, and v0.1.x speaks 1.0. A control plane sends
workflow jobs only to an agent that says 2.0 or later, so a v0.1.x machine
connects and is never given one. Upgrade every machine:

curl -fsSL https://raw.githubusercontent.com/Abyssinia-Labs/latem/main/install.sh | sh
latem setup

setup keeps the config.toml you have, and rewrites the service definition
when it is an older revision, which restarts the agent. From here on,
latem update installs a release and moves a running agent onto it without
cancelling a build.

Setting a machine up

  • latem setup walks from a fresh install to a working machine: config,
    the repositories it builds, the toolchains their jobs need, registering in
    the browser, the service, and doctor. On a terminal it is a screen showing
    what this machine can build and what is in the way, and it installs what you
    agree to. It never runs sudo and never accepts a licence for you.
  • latem repo, latem secrets, latem vars edit the config and the
    secret stores, and tell the running agent. latem secrets copy --to HOST
    moves a value to another machine over ssh, on stdin, never in an argument.
  • latem workflows check --repo R says what each job needs of a machine and
    what this one lacks, read from the repository without a checkout.
  • doctor checks what your jobs need rather than everything a Mac could
    need, and warns where a runner's labels promise a toolchain it does not have.

Running

  • A machine needs no GitHub login of its own. A plan and a job bring a
    read-only token for the repository's fetches, so a machine that reboots to
    its login screen still builds.
  • A docker runner's default image is ghcr.io/abyssinia-labs/latem-ubuntu,
    pinned by digest, instead of a placeholder you had to replace.
  • Each runner is sized to the machine (capacity), and a machine's
    heartbeat says what it can build and what is in the way, which the app shows.
  • macOS updates and Xcode updates are survived, not just tolerated: the
    agent notices a changed toolchain and recovers.

Security

The threat model is unchanged and test/invariants still enforces it. Two things
worth knowing now that the repository is public: a pull request from a fork is
never planned or built on your machines by the control plane, and latem's own CI
runs on GitHub's runners, never on anybody's machine. Report a vulnerability
through the Security tab, as SECURITY.md describes.

Install

curl -fsSL https://raw.githubusercontent.com/Abyssinia-Labs/latem/main/install.sh | sh

The installer verifies the sha256 of the archive it downloads. That
proves the file arrived intact; it does not prove who produced it,
because the checksums come from the same server as the binary. The
signature is what proves that, and checking it takes one command:

minisign -Vm latem_0.2.0_checksums.txt -P 'RWTTZK1Cxjg51w6BUpg5KpkKhOHzUT+EDhgidVqHUHsiTr6nPjKC3esu'

That key is D73938C642AD64D3. It is also in
internal/selfupdate/trusted.pub
and compiled into every binary in this release — latem version
prints which one a binary trusts.

An agent with auto_update = true does this for itself, daily,
against the same key compiled into the binary (RUNNER_SPEC §20).

What's Changed

  • GitHub Actions compatibility, phases 1–4 (A42–A45), and a doctor fix by @AbyssinianGuy in #1
  • Path B: GitHub's runner contract, JavaScript actions, workflow runs (A46–A52) by @AbyssinianGuy in #2
  • actions/checkout is a step latem performs: ref, history, and the job's push credential (A53) by @AbyssinianGuy in #3
  • Plan strategy.matrix into legs, and build only a leg the file declares (A55, protocol 2.1) by @AbyssinianGuy in #4
  • Onboarding commands: setup, register in the browser, repo, secrets, vars, workflows check, reload (A54) by @AbyssinianGuy in #5
  • latem update, a once-a-day release notice, and SIGUSR2 moves the agent onto a new binary (A56) by @AbyssinianGuy in #6
  • Run latem's own CI on latem: decide platform-only steps at compile (A58), and the macOS leg uses the machine's Go by @AbyssinianGuy in #9
  • Read a repository without a checkout; setup asks which repositories to build (A57, P1) by @AbyssinianGuy in #8
  • Publish latem-ubuntu, the docker runner's default image, from CI (P0) by @AbyssinianGuy in #7
  • Say what each job needs of a machine, read from the job as written (A59, P2) by @AbyssinianGuy in #10
  • A needs-aware doctor, and a run's note on what its runner lacks (A59, P3) by @AbyssinianGuy in #11
  • A docker runner's default image is latem-ubuntu, pinned by digest (P0) by @AbyssinianGuy in #12
  • setup: install what the jobs here need, asked; [runner.native] env and path_prepend (A60) by @AbyssinianGuy in #13
  • protocol 2.2: a planned job's requires and a heartbeat's toolchains (A61) by @AbyssinianGuy in #14
  • doctor: a runner whose labels promise a toolchain its steps would not find by @AbyssinianGuy in #15
  • test: a partition cuts a connection the fake server registers after it began by @AbyssinianGuy in #16
  • system: a JDK before 9 is its 1.N, and the browser plug-in is no JDK by @AbyssinianGuy in #17
  • setup: what a clean macOS account showed it gets wrong by @AbyssinianGuy in #18
  • agent: look at the toolchains again while running by @AbyssinianGuy in #19
  • service: run the LaunchAgent as Interactive, and rewrite an old one by @AbyssinianGuy in #20
  • service install: wait for the old agent to exit before loading the new by @AbyssinianGuy in #21
  • capacity: size each runner to the machine; one native runner; install scripts need Node by @AbyssinianGuy in #22
  • Xcode updates and re-runs of merged pull requests by @AbyssinianGuy in #23
  • store: read-only latem store check for Expo apps (A65) by @AbyssinianGuy in #24
  • native: a slow xcodebuild is a busy Mac, not a broken Xcode (A67) by @AbyssinianGuy in #26
  • Linux: measure memory, and say when the service can't reach Docker by @AbyssinianGuy in #25
  • ci: run integration on a host with Docker, not a container by @AbyssinianGuy in #28
  • plan: say a job's place without any machine's config (A68) by @AbyssinianGuy in #27
  • ci: integration-host's variable is on every machine now by @AbyssinianGuy in #30
  • setup: a screen for what this machine builds, and what is in the way (A69) by @AbyssinianGuy in #31
  • protocol 2.5: a heartbeat says what setup says of the machine (A70) by @AbyssinianGuy in #32
  • readiness: read a kind job by job, and leave out what variables stop (A71) by @AbyssinianGuy in #33
  • ci: pin integration-host to the system Docker, and let doctor say why by @AbyssinianGuy in #34
  • protocol 2.6: a plan and a job bring a read-only token to fetch with (A72) by @AbyssinianGuy in #35
  • Secrets across machines (A73) by @AbyssinianGuy in #36
  • Setup reaches the running agent; platform-tools before licences (A74) by @AbyssinianGuy in #37
  • Protocol 2.7: a job that came through a restart asks for its tokens again (A75) by @AbyssinianGuy in #38
  • CI on GitHub-hosted runners, never on latem's machines by @AbyssinianGuy in #39
  • Release notes for v0.2.0 by @AbyssinianGuy in #40
  • Sign releases in the release environment by @AbyssinianGuy in #41

New Contributors

Full Changelog: v0.1.2...v0.2.0

desktop-preview (Linux x86-64 build of main)

Choose a tag to compare

@AbyssinianGuy AbyssinianGuy released this 31 Oct 02:27
0db82e9

An unsigned build of main at 284478d, for installing on the desktop's Linux side and in WSL2 while the repository is private and v0.1.2 is behind main. Not a release: latem's self-update ignores prereleases, and this tag does not start release.yml.

v0.1.2

Choose a tag to compare

@AbyssinianGuy AbyssinianGuy released this 19 Sep 16:21

What changed

Nothing about how a build runs. Every fix here is in what latem tells you
— doctor's diagnoses, a step's log line, two listings. If v0.1.1 is building
your app correctly it will carry on doing so, and you can take this at your
leisure.

The exception worth reading is the first one.

doctor --secrets could print part of a secret's value

Its contract is "names only, never values". It enumerated names with its own
parser — split each line on the first =, report the left side — which is
correct for KEY=value and wrong for every line of a quoted multi-line value,
whose interior lines are value and frequently contain =. Given a PEM in
secrets.env it printed one of the key's own lines as though it were the name
of a secret:

[FAIL] secret / QQQQINTERIORLINETWOQQQQ  file: …: line 2: unterminated double quote

This is doctor's output rather than a run log, and the redactor was never
involved — it is not a bypass, and nothing reached a broker or a build log. But
it is the one command that promises never to do this, and it was the only way it
ever had. The second parser is gone: doctor now enumerates with the parser
resolution uses, a file that does not parse yields no names and one failure
naming a line number, and that error never carries line content.

A side effect worth knowing: doctor now lists what the agent could actually
resolve. The old reader accepted export KEY= and skipped malformed lines, while
the real parser rejects such a file outright — so it could list names that could
never resolve.

doctor said why a repository refused, for the wrong reason

The repo check reported the first line of git ls-remote's output. ssh writes
Warning: Permanently added 'host' (ED25519) to the list of known hosts. on
success, so on a machine seeing a host for the first time — exactly the machine
this check is for — it announced a known-hosts warning as the failure while
Permission denied (publickey) sat two lines below, unread. Its explanation was
a fixed paragraph about launchd not inheriting an ssh agent, attached to every
failure whatever the cause, and flatly wrong for Repository not found, where
the credential worked.

The detail is now the first line that is not ssh progress. The explanation
follows the cause — no key, a repository that answered but not to this key, an
unverified host key, name resolution, an https helper, a refused connection —
and where the cause is specific enough, fix is a repair rather than another
diagnostic: a missing key answers gh auth login --git-protocol ssh instead of
git ls-remote a second time.

Smaller, in the same vein

  • doctor --fix listed prose among its commands. Two of eight lines on a Mac
    failed if pasted, one of them the remedy for the only failure. Steps a person
    performs now print under their own heading, "By hand — these are not commands".
  • A killed step was logged as "finished". A cancelled or timed-out step
    closed with finished in 3.854s — time spent waiting to die — contradicting
    the run-level line two lines below. It now reads "cancelled after" or "timed
    out after". A non-zero exit still says "finished", because it did.
  • latem service status ended in a dangling brace. It printed the first line
    of launchctl print, which is the opening of a brace block. It now summarises
    what is inside: state = running, pid 4242, or the last exit code for an agent
    that is loaded and stopped.
  • latem cache ls emitted a padded blank row and said "1 caches". The
    trailer went through the tabwriter. It is now a blank line and a sentence.
  • The App Store Connect check contradicted itself, listing all three names as
    both resolving and not resolving when a backend errored. Three states now have
    three disjoint lists.

Documentation

The README's install line pointed at v0.1.0 while the secrets section, two
hundred lines below, warned that v0.1.0 silently corrupts a Keychain-held PEM.
Install now names the current release. The latem list sample was missing the
# column, which is the build number a job reads as $LATEM_BUILD_NUMBER; both
are corrected, and # is now documented rather than merely present.

Under the hood

Every GitHub Action this repository uses moved to the node24 runtime, and an
intermittent test failure in the remote broker's sink is fixed — it awaited an
in-memory counter and then read the store on the next line, which is a race the
design deliberately allows.

Install

curl -fsSL https://raw.githubusercontent.com/Abyssinia-Labs/latem/main/install.sh | sh

The installer verifies the sha256 of the archive it downloads. That
proves the file arrived intact; it does not prove who produced it,
because the checksums come from the same server as the binary. The
signature is what proves that, and checking it takes one command:

minisign -Vm latem_0.1.2_checksums.txt -P 'RWTTZK1Cxjg51w6BUpg5KpkKhOHzUT+EDhgidVqHUHsiTr6nPjKC3esu'

That key is D73938C642AD64D3. It is also in
internal/selfupdate/trusted.pub
and compiled into every binary in this release — latem version
prints which one a binary trusts.

An agent with auto_update = true does this for itself, daily,
against the same key compiled into the binary (RUNNER_SPEC §20).

Full Changelog: v0.1.1...v0.1.2

v0.1.1

Choose a tag to compare

@AbyssinianGuy AbyssinianGuy released this 19 Sep 16:21

What changed

A multi-line secret in the macOS Keychain resolved to hex, not to a key.
This is the reason to upgrade, and it was silent in every diagnostic latem
offers.

security find-generic-password -w prints any value containing a byte outside
printable ASCII — a newline above all — as bare hex digits, with nothing marking
that it did so. v0.1.0 passed that through unchanged, so an
APP_STORE_CONNECT_KEY_P8 stored in the Keychain resolved to the literal string
2d2d2d2d2d424547494e…. It resolved, latem doctor --secrets reported [ ok ],
and the build received a key that is not a key. The failure surfaced two minutes
into the next release build, as a 401 from fastlane pilot upload — the exact
failure the App Store Connect check exists to prevent, arriving from the one
place that check does not look.

If you are on v0.1.0 and keep a PEM in the Keychain, that secret is not
reaching your builds intact.
Upgrading is the fix; nothing needs re-storing.

The provider now reads security -g, which marks an encoded value 0x<HEX>,
and decodes it. -w could not be salvaged: the encoding is unmarked and
therefore ambiguous — a secret whose literal value is deadbeef and one whose
value is the four bytes DE AD BE EF print identically under it, so any
heuristic that decoded the second would corrupt the first.

Two smaller fixes came with it. A secret larger than about 2.4 KiB would have
been truncated rather than decoded, because -g renders the bytes as hex and
again as an escaped string and the capture was bounded at 8 KiB. And output that
cannot be parsed is now an error rather than a silently wrong secret.

Documentation

latem run myapp test is four commands away from curl | sh in the README, and
an acceptance run in a fresh macOS account established that following only the
README does not get you there. Twelve gaps are fixed in this release, including
four cases where copying the README's own example produced an error:

  • The caches: example was rejected as written. A job's caches is a list
    of names referring to a top-level caches: block that the README never
    mentioned. Both examples are corrected and are now checked against the parser.
  • Giving the machine a git credential and authoring, committing and pushing
    .latem/jobs.yaml are now steps rather than assumptions. Each of them
    blocks a clean machine on its own.
  • runs_on in the job example could not be scheduled by the config latem init
    writes; the mismatch is now stated.
  • Newly documented: where secret values live and why a multi-line one needs the
    Keychain or a [secrets.commands] entry; latem list --all, without which a
    finished run is invisible; artifacts:; the twelve run states, three of which
    are not failed although all three exit 1; and that bare latem run exits
    0 for "queued", so anything checking a result wants --wait.

docs/acceptance.md §7 has the full result and docs/acceptance-pass1-findings.md
has all twenty findings verbatim. Seven remain open, all cosmetic or
message-quality, none blocking a first run.

Install

curl -fsSL https://raw.githubusercontent.com/Abyssinia-Labs/latem/main/install.sh | sh

The installer verifies the sha256 of the archive it downloads. That
proves the file arrived intact; it does not prove who produced it,
because the checksums come from the same server as the binary. The
signature is what proves that, and checking it takes one command:

minisign -Vm latem_0.1.1_checksums.txt -P 'RWTTZK1Cxjg51w6BUpg5KpkKhOHzUT+EDhgidVqHUHsiTr6nPjKC3esu'

That key is D73938C642AD64D3. It is also in
internal/selfupdate/trusted.pub
and compiled into every binary in this release — latem version
prints which one a binary trusts.

An agent with auto_update = true does this for itself, daily,
against the same key compiled into the binary (RUNNER_SPEC §20).

Full Changelog: https://github.com/Abyssinia-Labs/latem/commits/v0.1.1