Updating a repo¶
Modules evolve. keel update re-renders a repo's modules at their current
template versions and overlays the result onto your repository, using the
per-file hashes recorded in .scaffold.lock to tell your edits apart from
keel's.
keel update
How a file is classified¶
For each file a module would render, keel update compares the file on disk
against the hash recorded at scaffold time:
- Untouched file (on-disk hash matches the lock) → updated in place with the new render.
- User-edited file (on-disk content differs from the recorded hash) →
preserved. The new render is written alongside as
<path>.keel-newfor you to merge by hand. - Removed file (a file the module no longer renders) → reported, never deleted. Removing it is your decision.
Pass --overwrite to replace user-edited files in place instead of writing
.keel-new sidecars.
Flags¶
--dry-run— preview the classification (which files would be updated, preserved, or reported) without writing anything. External module sources are still fetched so the preview is accurate.--reconfigure— re-run the wizard and re-render all modules with fresh answers.--commit— when there are no conflicts, make a singlechore: keel updatecommit of keel's own changed files.--modules <csv>— restrict the update to a comma-separated subset of modules.--path <dir>— the repository to update (defaults to the current directory).--recipe <path>— re-apply this recipe instead of the one recorded in.scaffold.lock.
Modules the recipe gained¶
keel update re-resolves the recipe, so a module added to it since your repo was
scaffolded is applied like any other change, reported under its own heading:
added module license (1 file)
added module governance (3 files)
A file the new module renders is written when it does not exist. If you already
wrote your own — a hand-rolled CONTRIBUTING.md, say — it is treated as any other
edited file: yours is left alone and keel's render is written beside it as
.keel-new.
--modules <csv> scopes this like everything else, and --dry-run previews it.
Files the recipe no longer produces¶
When a module stops rendering a file — as security-go did when four workflows
became one security.yml — keel retracts it only if it still matches the bytes
keel wrote:
removed .github/workflows/codeql.yml (deleted)
removed .github/workflows/govulncheck.yml (edited — left in place)
If you edited it, keel never deletes it; it prints an rm you can run yourself.
This is the same rule that governs updates: an untouched file is keel's to
change, an edited one is yours.
Pointing at a moved recipe¶
A repo scaffolded from a recipe file records where that file was. If it has since
moved, keel update says so, updates the modules it already knows about, and
tells you how to do better:
warning: recipe "recipes/my-recipe.yaml" not found; updating only the modules
recorded in .scaffold.lock.
Modules added to the recipe since this repo was scaffolded cannot be
detected. Re-run with --recipe <path> to point at it.
keel update --recipe ./path/to/recipe.yaml overrides the recorded location.
Older scaffolds¶
A repo scaffolded by an older keel still updates cleanly: newly-added question
defaults are filled in automatically. But if a new required question has
no default, keel can't guess — it asks you to re-run with --reconfigure so you
can answer it.
A v1 lockfile (no per-file hashes) is read transparently and upgraded to v2 on the first update — see Lockfile.
Checking without changing anything¶
keel outdated
keel outdated reports which of a repo's modules have a newer version available
— and nothing else. It writes no files. Use it to decide whether an update is
worth running.
Upgrading a repo scaffolded before 2.0.0¶
2.0.0 brings the Go discipline modules up to date with current tooling. Four of
the changes are breaking, and keel update cannot make any of them silently — it
never deletes a file it once wrote.
- The entrypoint moved to
cmd/<name>/main.go.go-modemits the new path; the old rootmain.gois reported as no longer produced. Move your code across, then delete it.Taskfile.ymland.goreleaser.yamlboth target./cmd/<name>now, so a release built before you move will produce nothing. goimportsis replaced bygci..golangci.ymlnow enablesgofumpt+gcias formatters, withgcisections seeded tostandard,defaultandprefix(<your module path>). Runtask formatonce and commit the import reordering as its own commit.depguardnow gates dependencies. The allow-list starts at$gostdplus your own module path, so the firstgo getfails lint until you add the new dependency tolinters.settings.depguard.rules.Main.allow. That is deliberate — it makes a new dependency a decision rather than an accident. Existing third-party imports must be added in the upgrade commit.- Four security workflows became one.
codeql.yml,govulncheck.yml,dependency-review.ymlandactionlint.ymlare replaced bysecurity.yml.keel updateprints the exact commands:
Since 2.1.0 keel deletes those for you, as long as they still match the bytes keel wrote:
removed .github/workflows/actionlint.yml (deleted)
removed .github/workflows/codeql.yml (deleted)
removed .github/workflows/dependency-review.yml (deleted)
removed .github/workflows/govulncheck.yml (deleted)
removed main.go (deleted)
A file you edited is never deleted — it is listed as (edited — left in place)
and repeated afterwards with an rm you can run yourself. Until you do, that one
keeps firing alongside security.yml, on its old action pins.
Branch protection is keel's job since 2.3.0: .github/keel-settings.yml declares
the required checks and keel settings apply converges them. If you are upgrading
a repo that predates that file, keel update adds it — run keel settings apply
once afterwards. For a repo whose protection you still manage by hand, the job
names are lint, test, typos, pr-title, actionlint, dependency-review,
plus codeql and govulncheck when enabled.