From 51004999e7f37ecd5758e4883ead26c97f4d1e76 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Wed, 12 Aug 2026 04:03:31 -0700 Subject: [PATCH] Stop an unscoped deploy from rebuilding the whole repository bin/build asks the local repository database which packages are already built. A build machine has no such database, so every package looks out of date: an unscoped 'bin/repo deploy' on this laptop would have built all 108 packages and published them. Verified with a dry run. deploy now refuses to run unscoped when that database is absent, and push refuses the same combination under --yes, where nobody would see the list it prints before publishing. Both are allowed on the repository host, which has the database that makes the comparison meaningful. Also states the split in the README: build, push and deploy are the three commands that may run off the repository host; everything else works on the published tree directly. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 12 ++++++++++++ bin/deploy | 20 ++++++++++++++++++++ bin/push-build | 17 +++++++++++++++++ helpers/host-helpers.sh | 10 ++++++++++ 4 files changed, 59 insertions(+) diff --git a/README.md b/README.md index ad2b051..d63b0eb 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,18 @@ bin/repo push --package nvidia-580xx-utils # Upload + publish on the host `bin/upload-prebuilt` there. Do not publish from a local checkout instead: only the repository host holds the complete repository and the signing key. +**Name the package.** `build` asks the local repository database which packages +are already built, and a build machine has no such database, so an unscoped run +treats every package as out of date and rebuilds the whole repository. `deploy` +refuses to run unscoped when that database is missing. Unscoped builds belong on +the repository host, where `bin/repo release` does the same job against a real +database. + +Every other command in `bin/` — `sign`, `promote`, `update`, `clean`, `migrate`, +`remove`, `sync`, `release` — works on the published tree directly and is meant +to run on the repository host. `build`, `push` and `deploy` are the three that +may run elsewhere. + ## Commands ### Global Flags diff --git a/bin/deploy b/bin/deploy index 622a228..cfcf171 100755 --- a/bin/deploy +++ b/bin/deploy @@ -94,6 +94,26 @@ if [[ "$PACKAGE_FLAG_GIVEN" == true && ${#PACKAGES[@]} -eq 0 ]]; then exit 1 fi +# bin/build asks the local repository database what is already built. On a build +# machine that database does not exist, so every package looks unbuilt and an +# unscoped run rebuilds the entire repository and publishes it. Deploying from +# here is for the occasional heavy package, so name it. +if [[ ${#PACKAGES[@]} -eq 0 ]] && ! on_repo_host; then + print_error "--package is required when deploying from a build machine" + echo "" + echo "There is no repository database in:" + echo " $REPO_DIR" + echo "" + echo "bin/build uses it to tell which packages are already built, so without it" + echo "every package looks out of date and this would build and publish all of" + echo "them. Name the package you came here to build:" + echo " bin/repo deploy --package " + echo "" + echo "Unscoped builds belong on the repository host, where 'bin/repo release'" + echo "does the same job against a real database." + exit 1 +fi + echo "" print_info "This will:" echo " 1. Build packages locally" diff --git a/bin/push-build b/bin/push-build index b9d2fe5..21523e8 100755 --- a/bin/push-build +++ b/bin/push-build @@ -126,6 +126,23 @@ if [[ "$PACKAGE_FLAG_GIVEN" == true && -z "$PACKAGES" ]]; then exit 1 fi +# On a build machine an unscoped build leaves the whole repository in +# build-output, because there is no local database to tell it what already +# exists. Interactively that is survivable — the confirmation below lists every +# package first — but with --yes nobody sees the list, so require an explicit +# selection instead. +if [[ -z "$PACKAGES" && "$ASSUME_YES" == true ]] && ! on_repo_host; then + print_error "--package is required to publish unattended from a build machine" + echo "" + echo "There is no repository database in $REPO_DIR, so a preceding unscoped" + echo "build would have rebuilt everything rather than only what changed, and" + echo "--yes would publish all ${#ALL_FILES[@]} of them without showing the list." + echo "" + echo "Name the packages to publish:" + echo " bin/repo push --package " + exit 1 +fi + FILES=() if [[ -z "$PACKAGES" ]]; then FILES=("${ALL_FILES[@]}") diff --git a/helpers/host-helpers.sh b/helpers/host-helpers.sh index 1d5fbe8..9f693d6 100644 --- a/helpers/host-helpers.sh +++ b/helpers/host-helpers.sh @@ -33,6 +33,16 @@ resolve_repo_host() { return 1 } +# True when this checkout holds the published repository, which in practice means +# this machine is the repository host. Every other command in bin/ works on that +# tree directly; build, push and deploy are the ones that may run elsewhere. +# +# The database is the marker rather than the directory: bin/build creates empty +# mirror directories as a side effect, so their presence proves nothing. +on_repo_host() { + [[ -f "$REPO_DIR/omarchy.db" || -f "$REPO_DIR/omarchy.db.tar.zst" ]] +} + # Shared wording so every command explains configuration the same way. print_no_repo_host() { print_error "No repository host configured"