Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ jobs:
# What the install workflow's targeted applies add for every
# `moved` block, read from the files above.
- run: scripts/terraform-moved-targets.test.sh
# Where a deploy finds its CLI: the install, then weft's release,
# then a build, with a fake web standing in for both.
- run: scripts/release-cli.test.sh

# The name the main ruleset requires: green only when every Rust check
# above is, and the Terraform the install runs validates. It runs whatever happened above (unless the run was
Expand Down Expand Up @@ -290,9 +293,11 @@ jobs:
# The container's pure parts, on the standard library alone.
- run: python3 catalog/postgres/database/images/credential/test_bootstrap.py

# Kilo Code's Tangle plugin (`tangle/kilo-code/`): its tests are
# TypeScript run straight by `node --test`, which strips the types
# itself from Node 22.18 on, so they need no install either.
# The Tangle plugins and hooks that decide what a specialist may run:
# Kilo Code's and OpenCode's tests are TypeScript run straight by
# `node`, which strips the types itself from Node 22.18 on, and Claude
# Code's and Codex's are Python on the standard library, so none of
# them needs an install.
tangle:
name: Tangle plugin tests
runs-on: ubuntu-latest
Expand All @@ -302,6 +307,9 @@ jobs:
with:
node-version: 24
- run: node --test tangle/kilo-code/tests/*.test.ts
- run: node tangle/opencode/tests/specialist-commands.test.ts
- run: python3 tangle/claude-code/tests/test_specialist_commands.py
- run: python3 tangle/codex/tests/test_specialist_commands.py

# The browser extension ships to three stores gated only on a version
# bump, so its checks belong in the same gate as everything else JS.
Expand Down
58 changes: 51 additions & 7 deletions .github/workflows/install-gcp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
# WEFT_BROKER_MAX_COPIES (the same for the broker;
# 4 when unset). Each copy of those two takes an
# equal share of the database's connections.
# WEFT_DISPATCHER_MEMORY, WEFT_BROKER_MEMORY,
# WEFT_LISTENER_MEMORY, WEFT_SUPERVISOR_MEMORY (the
# memory of each copy of that role, from 512Mi to
# 4Gi; 1Gi for the dispatcher and 512Mi for the
# others when unset), WEFT_HOLDER_MEMORY (each
# holder's, at least 512Mi, 512Mi when unset).
# repository secrets WEFT_DATABASE_URL (the Postgres the install keeps
# everything in; not needed with the variable
# WEFT_DATABASE set to cloud_sql, which gives the
Expand Down Expand Up @@ -95,13 +101,19 @@ jobs:

# The optional sizes, handed to Terraform only when set: an unset
# repository variable reads as an empty string, which Terraform
# refuses for a number, so an unset one leaves Terraform's default.
# refuses for a number or a memory size, so an unset one leaves
# Terraform's default.
# SYNC: these TF_VAR_* names <-> deploy/terraform/gcp/variables.tf
- name: the optional sizes
env:
CLOUD_SQL_MAX_CONNECTIONS: ${{ vars.WEFT_CLOUD_SQL_MAX_CONNECTIONS }}
DISPATCHER_MAX_COPIES: ${{ vars.WEFT_DISPATCHER_MAX_COPIES }}
BROKER_MAX_COPIES: ${{ vars.WEFT_BROKER_MAX_COPIES }}
DISPATCHER_MEMORY: ${{ vars.WEFT_DISPATCHER_MEMORY }}
BROKER_MEMORY: ${{ vars.WEFT_BROKER_MEMORY }}
LISTENER_MEMORY: ${{ vars.WEFT_LISTENER_MEMORY }}
SUPERVISOR_MEMORY: ${{ vars.WEFT_SUPERVISOR_MEMORY }}
HOLDER_MEMORY: ${{ vars.WEFT_HOLDER_MEMORY }}
run: |
set_if_given() {
if [ -n "$2" ]; then
Expand All @@ -111,6 +123,11 @@ jobs:
set_if_given TF_VAR_cloud_sql_max_connections "$CLOUD_SQL_MAX_CONNECTIONS"
set_if_given TF_VAR_dispatcher_max_copies "$DISPATCHER_MAX_COPIES"
set_if_given TF_VAR_broker_max_copies "$BROKER_MAX_COPIES"
set_if_given TF_VAR_dispatcher_memory "$DISPATCHER_MEMORY"
set_if_given TF_VAR_broker_memory "$BROKER_MEMORY"
set_if_given TF_VAR_listener_memory "$LISTENER_MEMORY"
set_if_given TF_VAR_supervisor_memory "$SUPERVISOR_MEMORY"
set_if_given TF_VAR_holder_memory "$HOLDER_MEMORY"

- uses: actions/checkout@v7

Expand Down Expand Up @@ -210,12 +227,16 @@ jobs:
exit 1
fi

# The runtime image and the worker builder base this checkout names,
# pushed to the install's registry. Each is taken from the install's
# registry if there, else copied from the release's (the same tag is
# the same contents), else built. A fork carrying its own changes
# hashes to new tags and gets exactly its own runtime, made from the
# `weft-runtime` built above instead of compiled again in Docker.
# The runtime image, the worker builder base and the standard worker
# this checkout names, pushed to the install's registry. Each is taken
# from the install's registry if there, else copied from the
# release's (the same tag is the same contents), else built. A fork
# carrying its own changes hashes to new tags and gets exactly its
# own runtime, made from the `weft-runtime` built above instead of
# compiled again in Docker. The standard worker is the one every
# project whose nodes are all the standard library's runs, so the
# first of them after this update finds it ready and builds nothing
# (crates/weft-dispatcher/src/build/mod.rs, `built_whole`).
- name: build the runtime's images
env:
BUILT_HERE: ${{ steps.release.outputs.found != 'yes' }}
Expand Down Expand Up @@ -426,6 +447,29 @@ jobs:
gcloud secrets versions access latest --secret "$key_secret" --project "${{ vars.GCP_PROJECT_ID }}" \
| "$GITHUB_WORKSPACE/target/release/weft" catalog preload --dispatcher "$url" --key-stdin

# The CLI this run used (the release's for this exact source, or the
# one built above), stored in the install under that source's tree, so
# a project's deploy downloads it (`GET /install/cli/<tree>`, through
# scripts/release-cli.sh) instead of compiling it: a fork's CLI exists
# nowhere else. The deploy runs on the same runner as this job
# (ubuntu-24.04, x86_64), so this binary runs there. The sha256 goes
# last, since it is what says the CLI is whole, and only this source's
# CLI is kept: once the install runs it, no deploy asks for another.
# This workflow's account writes the bucket as the project's Owner.
# SYNC: install/cli/<tree>/{weft,sha256} <-> crates/weft-core/src/install.rs (install_cli_keys)
# SYNC: runs-on <-> crates/weft-cli/templates/ci/gcp.yml (runs-on)
- name: store the weft CLI in the install
working-directory: deploy/terraform/gcp
run: |
bucket=$(terraform output -raw files_bucket)
tree=$(git -C "$GITHUB_WORKSPACE" rev-parse 'HEAD^{tree}')
cli="$GITHUB_WORKSPACE/target/release/weft"
sha256sum "$cli" | cut -d' ' -f1 > "$RUNNER_TEMP/cli.sha256"
gcloud storage cp "$cli" "gs://$bucket/install/cli/$tree/weft"
gcloud storage cp "$RUNNER_TEMP/cli.sha256" "gs://$bucket/install/cli/$tree/sha256"
gcloud storage ls "gs://$bucket/install/cli/" | { grep -v "/install/cli/$tree/\$" || true; } \
| xargs -r gcloud storage rm --recursive

# Written to the run's log as well as its summary: `gh run view`
# shows the log, never the summary, and whoever installs from a
# terminal needs the address and the key command from there.
Expand Down
84 changes: 84 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,90 @@ What changed between versions of weft that you need to know about when you
update. To update, run `weft maintenance on`, pull and run `./setup.sh` again,
then run `weft maintenance off`.

## 2026-11-01 (0.2.285)

**What breaks after this update:**

**A database's password is handed over once to whoever takes it.** `weft infra
env` writing it into a file, or the card's copy button, now hands it over the
way a run storing it does: the card shows it as handed over from then on, and
its Reset button mints a new one.

**`weft infra env --as` names the secret it writes.** On a card with more than
one secret (a database with a `login` role), `--as` used to take whichever one
still showed its value; it now refuses and lists them, and `--set
NAME=Label` picks one.

**Tangle's specialists write `cd` and ffmpeg a little differently.** After
`weft tangle update` in a project, a specialist changes folder only as `cd
./<folder>`, and names each ffmpeg or ffprobe input's format right before it
(`ffmpeg -f mp4 -i clip.mp4 ...`); any other form is refused, with a message
showing this one. In return they may now search the project with `grep` and
`rg`, look at media with `ffprobe` and make it with `ffmpeg` into their own
scratch folder, read a database with a read-only `psql` transaction (`psql
<connection> -c "BEGIN TRANSACTION READ ONLY" -c "<one SELECT, SHOW or
EXPLAIN>"`), and stop a run they started with `weft stop`, without asking you.
The commands they may run are now read the way your shell reads them, bash or
zsh, so a line that only looked harmless is refused.

**On Linux, weft's internal port no longer listens on every address of your
machine.** It listens on loopback and on the one address Docker gives your
containers for this machine, which weft asks Docker each time it starts, so
nothing on your network reaches it any more.

**The rest:**

**A PostgresDatabase makes the roles your program needs.** Since the program
signs in as `app`, which is no superuser, a database that kept tenants apart
with roles of its own could no longer make them, `set role` to them, or let a
site sign in as one, and a setup step writing past a `FORCE` row-level
security policy was refused. List them in the node's new `roles` input: on
every start the database makes each one, grants it to `app` (so `set role`
works), lets one marked `login` sign in through the pooler with a password of
its own, handed out once like the program's (`weft infra env <node> --into
.env --set SITE_PASSWORD="<role> password"` takes it, and its Reset button on
the card mints a new one), and lets one marked
`bypassRowLevelSecurity` write past every policy. Roles you made by hand keep
what they own; list them under the same names and the database takes them
over, except that a `login` role gets a password of its own, so whatever signs
in as it needs the new one from the card. Taking a role off the list takes back
what it gave: `app` can no longer `set role` to it, and it signs in and passes
row-level security no more; the role and what it owns stay. Changing the list
restarts the database.

**A cloud dispatcher fits in its memory, and keeps the memory you give it.**
On Cloud Run a role's files are held in its memory, and the dispatcher kept
up to 1GiB of a build's files there, and 64 programs whatever their size;
past 512Mi a resync failed with `Service Unavailable`. It now keeps at most
64MiB of those files there and about 90MiB of programs. The dispatcher gets
1Gi by default, and each role's memory is a repository variable on your fork
(`WEFT_DISPATCHER_MEMORY`, `WEFT_BROKER_MEMORY`, `WEFT_LISTENER_MEMORY`,
`WEFT_SUPERVISOR_MEMORY`, and `WEFT_HOLDER_MEMORY` for the holders): a value set by hand in the Cloud Run console used
to be put back to 512Mi by every install run, and now the install sets yours.

**A fork's deploy no longer compiles the weft CLI.** The install workflow now
keeps the CLI it used in your install, and the deploy workflow downloads it
from there first, checking its sha256, before weft's release and before
compiling it. Run `weft ci add` again in each project to get the new deploy
workflow, and the install workflow once, to store the CLI.

**The first project of standard nodes after a cloud update starts at once.**
The install already built the standard worker, and that project built it
again, waiting about three minutes; it now runs on the one the install built.

**A node can hand a stored file to its project's own container.** On a local
install an `internal_link` points at `127.0.0.1`, which inside a container is
the container itself, so a download from there failed. A node's code now asks
for `container_link(&file)`, which a project's own container opens on any
install.

**`weft daemon start` on a Mac says why launchd refused.** The error now
carries everything `launchctl` printed and its exit status, where it used to
end at `launchctl bootstrap failed:`. What fails before the runtime even
starts (a secrets file it cannot read, a binary gone) now lands in the
install's log, and so does a runtime's panic after its log has rolled over,
which used to go to a file nobody reads.

## 2026-11-01 (0.2.284)

**What breaks after this update:**
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading