Merge next into main: fix builds failing with "no active sessions", commit-hash image tags, dotted app names, build context secrets, layout fixes #244

Open
clawbot wants to merge 21 commits from next into main
Collaborator

On next:

  • docker build . no longer runs out of memory in RUN make test on a 4 GiB builder such as Lima's default: tests hash passwords with 1 MiB instead of 64 MiB, and upaasd still hashes with 64 MiB (#261).
  • Fixes the failed webhooker build on fsn1app1: a build now attaches a BuildKit session, so a base image not yet on the host is pulled instead of failing with "no active sessions". Build and app logs no longer carry Docker's stream framing bytes, and the deploy reads the cloned commit again (#251).
  • A failed app build reports the build's own error instead of "failed to inspect image", with the build output before it in the deploy log. upaas refuses to build on a Docker Engine older than 18.09, which cannot build with BuildKit (#234).
  • Docker creates an app volume's host directory when it does not exist yet, so a new app's first deploy no longer fails on it (#235).
  • The hint under an app's environment variable editor says changes take effect at the next deploy or rollback, instead of asking for a restart, which keeps the old values (#255).
  • Two database writes at the same moment now wait for each other instead of one failing with "database is locked" (#253).
  • Builds are tagged upaas-<app>:<short hash> of the commit built, instead of the deployment number. Images with the old numeric tags are still cleaned up (#239).
  • The build no longer passes the architecture to the linker; the startup log line reports runtime.GOARCH under the key arch instead of buildarch (#259).
  • .git/config, where a remote URL can carry a credential, no longer goes into the Docker build; the version is still stamped from git describe (#269).
  • Git-ignored files such as .env.local, *.key and data/session.key no longer go into the Docker build stages; data/ is git-ignored (#266).
  • upaas now applies an app's .dockerignore to the build context it sends, as docker build . does, so files the app leaves out, such as .git/config, no longer reach its build (#274).
  • An image built from a git clone shows its commit (git describe output) in the footer, /health and a new startup log line, instead of dev (#236).
  • App names may contain dots, such as sneak.berlin; the new and edit forms now check the name in the browser too (#260).
  • The Applications list's table stays inside its card: long repository URLs wrap, and the card scrolls sideways in a narrow window, so the Action column and Deploy buttons are never cut off (#262).
  • In a narrow window the top bar's New App and Logout buttons move to a second row instead of running into "by @sneak" (#272).
  • An app's deployments page lists its 10 most recent deployments, newest first, instead of up to 50 (#238).
  • An app's page shows its deploy branch next to the status badge in the title (#240).
  • An app's page is 84rem wide instead of 56rem, its build and container logs are twice as tall, the build log sits between Webhook URL and Environment Variables, and the container log sits above Deploy Key (#246). At 1920px the page is now wider than the site header and footer (72rem), which were left as they were.

To deploy: from an up-to-date clone (not a copy without .git), run docker compose up -d --build. Plain up -d keeps the old image. Dockerfiles that use RUN --network, and the missing volume directory, need Docker Engine 23.0 or later. The image's layers after COPY . . now rebuild on every commit. Base images from private registries still cannot be pulled by a build. An app whose .dockerignore leaves out a file its build needs now fails under upaas, as it does with docker build ..

No schema or configuration changes.

Model: opus-5-5

On `next`: - `docker build .` no longer runs out of memory in `RUN make test` on a 4 GiB builder such as Lima's default: tests hash passwords with 1 MiB instead of 64 MiB, and upaasd still hashes with 64 MiB (https://git.eeqj.de/sneak/upaas/issues/261). - Fixes the failed webhooker build on fsn1app1: a build now attaches a BuildKit session, so a base image not yet on the host is pulled instead of failing with "no active sessions". Build and app logs no longer carry Docker's stream framing bytes, and the deploy reads the cloned commit again (https://git.eeqj.de/sneak/upaas/issues/251). - A failed app build reports the build's own error instead of "failed to inspect image", with the build output before it in the deploy log. upaas refuses to build on a Docker Engine older than 18.09, which cannot build with BuildKit (https://git.eeqj.de/sneak/upaas/issues/234). - Docker creates an app volume's host directory when it does not exist yet, so a new app's first deploy no longer fails on it (https://git.eeqj.de/sneak/upaas/issues/235). - The hint under an app's environment variable editor says changes take effect at the next deploy or rollback, instead of asking for a restart, which keeps the old values (https://git.eeqj.de/sneak/upaas/issues/255). - Two database writes at the same moment now wait for each other instead of one failing with "database is locked" (https://git.eeqj.de/sneak/upaas/issues/253). - Builds are tagged `upaas-<app>:<short hash>` of the commit built, instead of the deployment number. Images with the old numeric tags are still cleaned up (https://git.eeqj.de/sneak/upaas/issues/239). - The build no longer passes the architecture to the linker; the startup log line reports `runtime.GOARCH` under the key `arch` instead of `buildarch` (https://git.eeqj.de/sneak/upaas/issues/259). - `.git/config`, where a remote URL can carry a credential, no longer goes into the Docker build; the version is still stamped from `git describe` (https://git.eeqj.de/sneak/upaas/issues/269). - Git-ignored files such as `.env.local`, `*.key` and `data/session.key` no longer go into the Docker build stages; `data/` is git-ignored (https://git.eeqj.de/sneak/upaas/issues/266). - upaas now applies an app's `.dockerignore` to the build context it sends, as `docker build .` does, so files the app leaves out, such as `.git/config`, no longer reach its build (https://git.eeqj.de/sneak/upaas/issues/274). - An image built from a git clone shows its commit (`git describe` output) in the footer, `/health` and a new startup log line, instead of `dev` (https://git.eeqj.de/sneak/upaas/issues/236). - App names may contain dots, such as `sneak.berlin`; the new and edit forms now check the name in the browser too (https://git.eeqj.de/sneak/upaas/issues/260). - The Applications list's table stays inside its card: long repository URLs wrap, and the card scrolls sideways in a narrow window, so the Action column and Deploy buttons are never cut off (https://git.eeqj.de/sneak/upaas/issues/262). - In a narrow window the top bar's New App and Logout buttons move to a second row instead of running into "by @sneak" (https://git.eeqj.de/sneak/upaas/issues/272). - An app's deployments page lists its 10 most recent deployments, newest first, instead of up to 50 (https://git.eeqj.de/sneak/upaas/issues/238). - An app's page shows its deploy branch next to the status badge in the title (https://git.eeqj.de/sneak/upaas/issues/240). - An app's page is 84rem wide instead of 56rem, its build and container logs are twice as tall, the build log sits between Webhook URL and Environment Variables, and the container log sits above Deploy Key (https://git.eeqj.de/sneak/upaas/issues/246). At 1920px the page is now wider than the site header and footer (72rem), which were left as they were. To deploy: from an up-to-date clone (not a copy without `.git`), run `docker compose up -d --build`. Plain `up -d` keeps the old image. Dockerfiles that use `RUN --network`, and the missing volume directory, need Docker Engine 23.0 or later. The image's layers after `COPY . .` now rebuild on every commit. Base images from private registries still cannot be pulled by a build. An app whose `.dockerignore` leaves out a file its build needs now fails under upaas, as it does with `docker build .`. No schema or configuration changes. Model: opus-5-5
clawbot added the merge-ready label 2026-09-29 12:07:02 +02:00
sneak was assigned by clawbot 2026-09-29 12:07:02 +02:00
clawbot added 1 commit 2026-09-29 12:07:02 +02:00
An app's deployments page listed up to 50 deployments; it now lists the
10 most recent, newest first. The query behind the page already sorted
newest first and applied the limit in SQL, so only the number changes.
A handler test creates 12 deployments with distinct start times and
checks that exactly the 10 newest are shown, in order.

Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
clawbot added 1 commit 2026-09-29 12:21:37 +02:00
Show the deploy branch in the app page title (closes #240)
Check / check (pull_request) Successful in 3m44s
211e2a4a5a
The app page shows the app's configured branch as a neutral label next
to the status badge, so it can be read without opening the edit page.
The line under the title now shows only the repository. A new test
renders the app page for an app on a non-main branch and checks the
branch is in the title row.

Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
clawbot changed title from Merge next into main: deployments page shows the 10 most recent to Merge next into main: 10 most recent deployments, deploy branch in the app page title 2026-09-29 12:22:57 +02:00
clawbot added 1 commit 2026-09-29 12:35:48 +02:00
.dockerignore left out .git, so `make build` in the image found no git
metadata and stamped `dev`. It now sends .git, and no longer leaves out
tracked files (LICENSE, README.md, ...), which git would see as deleted
and mark the version -dirty. The footer and /health already read the
same version. The startup log line that reports it, the logger's
Identify(), was never called; main now calls it. The README says to
build from a git clone.
Side effect: image layers after `COPY . .` now rebuild whenever `.git` changes.
Disclosure: merged after a rebase that changed only TODO.md; the second review gated this same tree on this base.

Model: opus-5-5
clawbot changed title from Merge next into main: 10 most recent deployments, deploy branch in the app page title to Merge next into main: 10 most recent deployments, branch in the app title, commit in the footer 2026-09-29 12:37:36 +02:00
clawbot added 1 commit 2026-09-29 12:42:58 +02:00
A build whose output ends in Docker's error line now fails with that
error, instead of going on to inspect a tag that was never created. The
build output is written to the deployment log before the failure is
recorded, so the log ends in order. Before building, upaas compares the
daemon's API version with 1.39 (Docker Engine 18.09), the first that
builds with BuildKit without experimental mode, and fails the deploy on
an older daemon instead of letting it use the legacy builder. The
README's Compose section gives the update command and the Docker Engine
versions builds need.
Disclosure: merged after a rebase that changed only TODO.md; the review gated this tree on the current next.

Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
clawbot changed title from Merge next into main: 10 most recent deployments, branch in the app title, commit in the footer to Merge next into main: build errors reported, commit in the footer, branch in the app title, 10 recent deployments 2026-09-29 12:44:24 +02:00
clawbot added 1 commit 2026-09-29 12:46:48 +02:00
The app page's content column is now at most 84rem wide instead of
56rem (max-w-4xl), set inline because the committed Tailwind CSS has no
class for that width. The build log and container log boxes are 800px
tall instead of 400px. The build log section moves to between the
webhook URL and the environment variables, and the container log
section moves to directly above the deploy key; nothing else moves. A
new handler test renders the app page and checks the width, the section
order and both log heights.
Disclosure: merged after a rebase that changed only TODO.md; the review gated this change on the next before #234, which touches no template.

Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
clawbot changed title from Merge next into main: build errors reported, commit in the footer, branch in the app title, 10 recent deployments to Merge next into main: build errors, commit in the footer, wider app page, branch in the title, 10 recent deployments 2026-09-29 12:48:16 +02:00
clawbot added 1 commit 2026-10-01 20:48:14 +02:00
Have Docker create a missing volume host path (closes #235)
Check / check (pull_request) Successful in 4m54s
d69f11f74c
Docker refused to bind-mount a host path that did not exist, so an app's
first deploy failed until someone ran mkdir on the host. buildMounts now
sets BindOptions.CreateMountpoint on every bind mount, so the Docker
daemon creates a missing host path when the container starts and leaves
an existing one alone. upaas itself cannot create it: it runs in a
container and does not see the host's filesystem. The README gains a
Volume mounts section saying so. Needs Docker Engine 23.0 or later.

Model: opus-5-5
clawbot changed title from Merge next into main: build errors, commit in the footer, wider app page, branch in the title, 10 recent deployments to Merge next into main: build errors, missing volume directories, commit in the footer, wider app page, branch in the title, 10 recent deployments 2026-10-01 20:48:30 +02:00
clawbot added 1 commit 2026-10-01 21:15:09 +02:00
Builds now attach a BuildKit session over the Docker API, as the docker
command line does, and pass its ID with the build. BuildKit fetches a base
image that is not on the host through that session; without one, Docker
Engine 27 failed the build with "no active sessions". The session is
closed when the build ends.

Container logs are now read with stdcopy, so the clone output in the build
log and the app logs no longer carry Docker's 8-byte frame headers, and the
commit is read from the clone output; the header in front of the COMMIT
line kept it from being found.

Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
clawbot changed title from Merge next into main: build errors, missing volume directories, commit in the footer, wider app page, branch in the title, 10 recent deployments to Merge next into main: fix builds failing with "no active sessions", build errors, missing volume directories, commit in the footer, wider app page 2026-10-01 21:15:26 +02:00
clawbot added 1 commit 2026-10-01 21:34:30 +02:00
An app with no volume mounts now shows, in its Volume Mounts section,
that the files it writes are lost whenever a deploy or rollback replaces
its container and that a restart keeps them. Each deploy of such an app
writes the same sentence into its log, after the webhook payload and
before the clone. The README says in one sentence that a deploy or
rollback starts a new container that keeps only the files in the app's
volume mounts.

Model: opus-5-5
clawbot added 1 commit 2026-10-01 21:51:15 +02:00
The hint under the app page's environment variable editor asked for a
container restart. Environment variables are set when a container is
created, and upaas's restart stops and starts the same container, so it
keeps the old values; deploy and rollback each create a new container.
The hint now says so in one sentence, styled with alert-warning like the
page's Volume Mounts note: its old text-amber-600 class is not in the
built stylesheet. A handler test renders the app page and checks the
hint.

Model: opus-5-5
clawbot added 1 commit 2026-10-01 22:16:17 +02:00
SQLite transactions started deferred, so two that both read and then
wrote could not both proceed, and SQLite refused the second at once with
"database is locked" without waiting. TestCreateUserRaceCondition hit
this now and then.

The database is now opened with _txlock=immediate, so each transaction
takes the write lock when it begins and a second one waits for it, and
with _busy_timeout=5000, so that wait lasts up to 5 seconds. The driver
already defaults to 5000; it is now stated in the code.

Model: opus-5-5
clawbot added 1 commit 2026-10-01 22:33:22 +02:00
Tag built images with the commit's short hash (closes #239)
Check / check (pull_request) Successful in 5m31s
9488e2faef
Builds are tagged upaas-<app>:<short hash>, git's short form of
the commit checked out, instead of the deployment number. The clone
prints the short hash and fails without one.

A redeploy of a commit gives the tag to the new image. The cleanup
after a deploy now also finds the app's untagged images among those
its deployments recorded, and removes them by ID once the app neither
runs them nor would roll back to them. It keeps every image any app
uses, since apps that build the same commit can share one.

commit_sha is now saved on update so manual deploys keep the commit
read from the clone.

Model: opus-5-5
clawbot changed title from Merge next into main: fix builds failing with "no active sessions", build errors, missing volume directories, commit in the footer, wider app page to Merge next into main: fix builds failing with "no active sessions", commit-hash image tags, build errors, volume fixes, wider app page 2026-10-01 22:33:29 +02:00
sneak added 1 commit 2026-10-02 00:58:03 +02:00
Merge branch 'main' into next
Check / check (pull_request) Successful in 5m22s
5ca1bce6fa
clawbot added 1 commit 2026-10-02 01:43:31 +02:00
The owner asked for this commit to be reverted in full
(#254 (comment)). It
removes the no-volume-mounts sentence from the app page and the deploy
log, the constant and tests behind it, the README sentence, and its
TODO.md entry. Later TODO.md entries stay. #248
stays closed as rejected.

Model: opus-5-5
clawbot added 1 commit 2026-10-02 03:21:52 +02:00
The Makefile no longer passes the architecture to the linker. The
Buildarch variable in main and the field and setter in globals are
gone; the startup log line reports runtime.GOARCH as `arch`.
CONVENTIONS.md drops Buildarch from its main, globals, logger and
Makefile examples, as the sneak/prompts conventions do.

Model: opus-5-5
clawbot added 1 commit 2026-10-02 04:43:05 +02:00
Keep .git/config out of the Docker build context (closes #269)
Check / check (pull_request) Successful in 5m3s
5c836c085d
.git goes into the build so `make build` can stamp the version, and with
it went .git/config, where a remote URL can carry a credential that then
stays in the builder stage's layers on the build host. `git describe`
does not need it, so .dockerignore now leaves it out.

Model: opus-5-5
clawbot added 1 commit 2026-10-02 05:43:41 +02:00
.dockerignore now lists every .gitignore pattern, each with **/ so Docker
matches it in every directory as git does, plus the top-level data/
directory. git-ignored secrets such as .env.local, *.key files and
data/session.key no longer reach the build stages or the build cache. A last
!.git/** line sends all of .git again, since git never applies these patterns
inside it, so a branch named like fix/session.key still resolves. No tracked
file is listed, so the version still comes from git describe without -dirty.

data/, where upaasd keeps its database and session key when run from the
checkout, is now git-ignored.

Model: opus-5-5
clawbot added 1 commit 2026-10-02 06:12:25 +02:00
Keep the Applications list's table inside its card (closes #262)
Check / check (pull_request) Successful in 5m7s
db3b47e423
Table cells never wrap, so a long repository URL made the table wider
than its card, which hides what does not fit and so cut off the Actions
column and the Deploy buttons. The repository URL now wraps within its
column, and the card scrolls sideways when the table still does not
fit. A handler test checks both. Every class used was already in the
committed static/css/tailwind.css.

Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
clawbot added 1 commit 2026-10-02 06:41:31 +02:00
Allow dots in app names (closes #260)
Check / check (pull_request) Successful in 4m19s
23f378cfde
App names may now contain dots, such as sneak.berlin: runs of
lowercase letters and numbers joined by single dots or by hyphens,
2 to 63 characters. Docker accepts every such name in the app's image
name, upaas-<name>; a dot needs a letter or number on both sides
because Docker requires it. The rule also keeps a dot off either end,
so the name is safe as a directory and log file name.

The new and edit app forms use the same pattern. Their old one was not
a valid regular expression under the flag browsers compile it with, so
browsers ignored it.

Model: opus-5-5
clawbot added 1 commit 2026-10-02 07:04:18 +02:00
The top bar's two sides were held apart only by justify-between, with
no gap and no wrapping, so at 390 px "by @sneak" ended where the New
App button began. The bar's row now wraps and has a gap: in a window
too narrow for one row, New App and Logout move to a second row; on
wider windows the bar looks as before.

Model: opus-5-5
clawbot changed title from Merge next into main: fix builds failing with "no active sessions", commit-hash image tags, build errors, volume fixes, wider app page to Merge next into main: fix builds failing with "no active sessions", commit-hash image tags, dotted app names, build context secrets, layout fixes 2026-10-02 07:10:30 +02:00
clawbot added 1 commit 2026-10-03 03:02:00 +02:00
On a build machine with 4 GiB, `docker build .` failed in `RUN make test`: the auth test binary ran out of memory, because many 64 MiB argon2id hashes ran at once under the race detector.

The memory per hash is now the auth service's `ArgonMemory` field. `New` sets the same 64 MiB and upaasd never changes it; the auth and handlers test helpers lower it to 1 MiB. One test still hashes and verifies a password at 64 MiB. The peak memory of `GOMAXPROCS=4 make test` in the build image fell from about 3.1 GiB to 1.0 GiB.

Not run on a 4 GiB arm64 machine.

Model: opus-5-5
clawbot added 1 commit 2026-10-03 03:34:41 +02:00
Docker does not apply an app's `.dockerignore` to a build context sent as a tar, which is how upaas sends it, so every file in the clone, `.git/config` included, reached the build.

upaas now reads the ignore file as `docker build` does, with the `ignorefile` reader of `github.com/moby/patternmatcher`, and leaves those files out of the tar: an ignore file named after the Dockerfile and next to it, such as `Dockerfile.dockerignore`, otherwise `.dockerignore` at the root of the clone. The Dockerfile path is read as a path inside the clone, and the Dockerfile (or the lowercase `dockerfile` Docker builds when `Dockerfile` is missing) and the ignore file always stay in. An app without an ignore file builds as before.

Not handled: a Dockerfile that is a symlink to an ignored file fails the build.

Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
All checks were successful
Check / check (pull_request) Successful in 3m47s
Required
Details
You are not authorized to merge this pull request.
This pull request can be merged automatically.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin next:next
git checkout next
Sign in to join this conversation.