Skip to content

Update Project Templates

When this repo's template files change (new hooks, new egress rules, new sections), consuming projects need to pick up those changes. Renovate handles individual version pins in .pre-commit-config.yaml and .mise.toml, but structural changes — new files, new sections, new hook entries — require the update script.

Basic usage

# Interactive (prompts per differing file)
./scripts/update-project.sh /path/to/my-infra-project

# Report only — no writes
./scripts/update-project.sh /path/to/my-infra-project --dry-run

# Auto-apply all diffs (creates .bak for each replaced file)
./scripts/update-project.sh /path/to/my-infra-project --yes

--dry-run and --yes are mutually exclusive — passing both exits with an error.

Status values

The status reported per file depends on the mode. NEW and OK apply in all modes; the action statuses differ between --dry-run and the interactive/--yes modes.

Status Mode Meaning
NEW all File does not exist in the project; copied from upstream
OK all Byte-identical to upstream; nothing to do
DIFF --dry-run Differs from upstream; would change (reported, not written)
UPDATE interactive / --yes Overwritten with upstream (a timestamped .bak was saved)
KEEP interactive / --yes The project's version was kept unchanged
SKIP interactive / --yes The file was skipped (kept, but tracked separately in the summary)

Interactive mode (differing files)

When a file differs, the script shows a short diff preview and prompts:

  Action: [k]eep mine  [o]verwrite  [d]iff (full)  [s]kip >
Answer Effect
k (or Enter) Keep the project's version unchanged — Enter selects this default
o Overwrite with upstream; saves a timestamped .bak alongside the file
d Show the full diff via less -R if available, otherwise raw diff -u, then prompt again
s Skip this file (kept, but counted separately in the summary)

If stdin is not a TTY, the script cannot prompt and falls back to keeping the project's version (the k action) for every differing file.

Overwrite safety

Existing files are never overwritten without an explicit o answer in interactive mode or --yes. Every overwrite first copies the current file to <path>.bak.<timestamp> alongside it, so the project's prior version is always recoverable.

Marker-based sync for AGENTS.md and CLAUDE.md

AGENTS.md and CLAUDE.md use <!-- BASE:START --> / <!-- BASE:END --> fences. For these two files the update script syncs only the section between the markers — project content outside the markers is never touched.

If a project's copy has no markers (old format), the script falls back to the full-file interactive update and prints a migration hint.

Managed files

The script syncs ten files, grouped as required, recommended, editor config, and optional:

File Sync mode
.devcontainer/devcontainer.json Full file
.devcontainer/egress-allowlist.txt Full file
.devcontainer/proxy.yaml Full file
.devcontainer/env.sh Full file
.gitignore Full file
.pre-commit-config.yaml Full file
AGENTS.md Base section only
CLAUDE.md Base section only
.editorconfig Full file
.mise.toml Full file

After updating devcontainer.json

If the run changes .devcontainer/devcontainer.json (outside --dry-run), the script prints a reminder: in each open VS Code window for the project, run Dev Containers: Rebuild Container to pick up the change.