Skip to content

feat (experimental): give every app a local git repo and deploy on merge (v2.7.204) - #27

Open
akindejuh wants to merge 1 commit into
mainfrom
feat/local-git-and-deploy-on-merge
Open

feat (experimental): give every app a local git repo and deploy on merge (v2.7.204)#27
akindejuh wants to merge 1 commit into
mainfrom
feat/local-git-and-deploy-on-merge

Conversation

@akindejuh

Copy link
Copy Markdown

Why

git_app.go has shipped a complete, hardened, unit-tested per-app git implementation since v2.6.x — but every caller was an MCP write path. In the framework and cloud editions those paths don't exist, so the code was unreachable: a locally scaffolded app got no history at all.

Worse, the scaffold shipped no .gitignore of its own, so a user's first git init && git add . committed env.yaml and a binary data.db. This PR closes that by construction.

What

benmore new now initialises the app as a real repo: framework .gitignore, initial commit, and core.hooksPath → a tracked .githooks/.

Deploy on merge. Two tracked files are scaffolded:

File Covers
.githooks/post-merge merges performed locally
.github/workflows/benmore-deploy.yml PRs merged on github.com

Merging the default branch deploys to dev. Other branches are ignored, so feature work stays local until it lands.

benmore git-init [dir] retrofits an existing app — benmore new is the rarest way an app reaches a machine; apps from pull/sync get a .benmore/ manifest but no repo.

Design decisions worth reviewing

  • Prod is deliberately not automated. benmore promote stays an explicit decision made after dev has been seen working, rather than making the riskiest deploy the one requiring the least intent.
  • Deploys route through benmore deploy, which keeps the server-side write-time validators in the path. Having the router pull from git instead would have routed around the same gate that guards every other write.
  • Hooks live in tracked .githooks/, not .git/hooks/.git is never cloned, so the behaviour has to travel with the repo to be a team workflow rather than one machine's trick.
  • git-init is additive and idempotent throughout — existing repo keeps its history and branch name, existing .gitignore/hook keeps the user's version, re-run reports "already set up". Refuses any dir without app.yaml/schema.prisma so it can't span a folder of apps.

Verification

Manually tested across: no-repo app · re-run · existing repo with history and a non-main branch name · non-app directory (refused) · hook firing after merge · feature-branch merges staying silent.

Confirmed no secrets committed (env.yaml, data.db* untracked), and that pull neither creates nor clobbers a repo while its drift guard still blocks overwrites — local git and the .benmore/ manifest coexist.

New tests cover gitEnsureIgnoreFile, writeDeployAutomation (including the executable bit, and asserting the hook never targets prod), and gitUseTrackedHooks/gitHooksPathSet.

go vet -tags sqlite_fts5 ./...   clean
go build -tags sqlite_fts5       clean
go test  -tags sqlite_fts5 ./... 456 passed, 2 failed

Both failures — TestAgentDocs_TopicsList and TestNoBareGoFuncInWorkerspredate this change. They fail identically on a clean tree with this branch stashed, and stem from files absent from the public export (backup_worker.go, the deploy docs topic).

Known gap

The generated workflow authenticates via a BENMORE_TOKEN secret. That name is unverified — the non-interactive auth path lives in cli_platform.go, outside this repo. Worth a second pair of eyes before anyone relies on the GitHub-side path; the local hook path is fully tested.

Propagation

Per AGENTS.md: CHANGELOG.md entry ✅ · docs/agent/build.md Recent row ✅ · version bump 2.7.203 → 2.7.204 ✅. The topicRecipes entry in mcp_tools_api.go could not be updated — that file is not in this repo.

🤖 Generated with Claude Code

`git_app.go` has had a complete, hardened, unit-tested per-app git
implementation since v2.6.x, but every caller was an MCP write path. In
the framework and cloud editions those paths don't exist, so the code was
unreachable: a locally scaffolded app had no history, and - because the
scaffold shipped no .gitignore of its own - a user's first `git init &&
git add .` committed env.yaml and a binary data.db. The machinery was
already there; nothing was calling it.

`benmore new` now initialises the repo, writes the protective .gitignore,
and points core.hooksPath at a tracked .githooks/ directory. Hooks live in
.githooks/ rather than .git/hooks/ because .git is never cloned - the
deploy behaviour has to travel with the repo to be a team workflow rather
than one machine's trick.

Merging into the default branch deploys to the dev instance via `benmore
deploy`, which keeps the server-side write-time validators in the path;
having the router pull from git instead would have routed around the same
gate that guards every other write. Production is deliberately not
automated - `benmore promote` stays an explicit decision made after dev
has been seen working, rather than making the riskiest deploy the one
requiring the least intent.

`benmore git-init` exists because `benmore new` is the rarest way an app
reaches a developer's machine; apps arriving via pull/sync get a manifest
but no repo. It is additive and idempotent throughout, so it is safe to
point at a directory that already holds months of work.

Verified: pull neither creates nor clobbers a repo and its drift guard
still blocks overwrites, so local git and the .benmore/ manifest coexist.
Two suite failures (TestAgentDocs_TopicsList, TestNoBareGoFuncInWorkers)
predate this change - they fail identically on a clean tree and stem from
files absent from the public export.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@akindejuh akindejuh self-assigned this Aug 27, 2026
@akindejuh akindejuh changed the title feat: give every app a local git repo and deploy on merge (v2.7.204) feat (experimental): give every app a local git repo and deploy on merge (v2.7.204) Aug 27, 2026
@akindejuh akindejuh added the enhancement New feature or request label Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant