Skip to content

feat(template): add in_existing_repo for scaffolding into an existing repo - #13

Open
maybebyte wants to merge 4 commits into
mainfrom
fix/existing-repo-scaffolding
Open

feat(template): add in_existing_repo for scaffolding into an existing repo#13
maybebyte wants to merge 4 commits into
mainfrom
fix/existing-repo-scaffolding

Conversation

@maybebyte

Copy link
Copy Markdown
Owner

Summary

Dogfooding v0.1.1 into a subdirectory of an existing repository needed two manual repairs (a nested .git, inert CI/pre-commit/Renovate files) and a workaround for the hook-install task, which aborts the whole copy on any machine with git's core.hooksPath set. This adds a first-class in_existing_repo answer for the nested layout and makes the copy-time tasks fail closed or skip with a hint instead of rolling the copy back.

Changes

  • in_existing_repo (default false): skips git init and the hook install and omits the root-only .github/, .pre-commit-config.yaml, and renovate.json via path conditions, with an after-copy note listing what to recreate at the repository root. Rendering them nested leaves inert files whose local deletion conflicts on every later copier update, which is why omission beats documentation here.
  • The git init task now fails closed when the destination sits inside another work tree and names the answer to re-run with. Root cause: a bare git init in a subdirectory of a repository silently creates a nested repository. Copier's documented git init && copier copy … . pattern still passes because the toplevel is the destination there.
  • The hook-install task skips with a stderr hint when core.hooksPath is set (pre-commit refuses to install under it, in any scope, with no override flag), and the generated .gitignore gains .coverage. The existing install test rendered the release tag rather than HEAD, so it could not see task changes; it now goes through the HEAD-pinned fixture with git's global config isolated.

Testing

  • TMPDIR=<roomy dir> just ci on this branch: 78 passed, No known vulnerabilities found, ci: all gates passed. The four new or changed generation tests cover the hooksPath skip, the nested happy path (just ci green from the subdirectory, nothing installed into the parent's hooks), and the fail-closed abort with rollback.
  • Regression evidence: against main, on a machine whose global git config sets core.hooksPath, uv run pytest tests/test_generation.py::test_precommit_install_task_runs fails with pre-commit's Cowardly refusing to install hooks with core.hooksPath set; on this branch it passes.
  • copier copy --trust --pretend --defaults --data in_existing_repo=true . <dst> lists no .github/, .pre-commit-config.yaml, or renovate.json and prints the nested note; without the answer all three render and the note is absent.

Notes for reviewers

  • Existing downstreams get prompted for the new answer on their next copier update; the default keeps their tree unchanged. Decision: the reverse check (answer on, but not inside any repo) is not guarded; Copier's own update precondition reports the missing git repository clearly later.
  • Release plan agreed for this work: tag v0.2.0 after merge (a new answer is an Added entry), via the usual changelog-promotion PR.

`just ci` runs pytest with --cov, which writes .coverage next to the
coverage.xml the generated .gitignore already covers. A downstream had
to add the line by hand, and every local edit to a template-owned file
is a conflict waiting for the next `copier update`.
test_precommit_install_task_runs called copier.run_copy directly without
vcs_ref="HEAD", so it rendered copier's default ref, the latest release
tag, and could not observe any change to the copy-time _tasks (the same
freeze PR #4 fixed for the render fixture). It also failed on any
machine whose global git config sets core.hooksPath, because pre-commit
refuses to install hooks under it.

Route it through the render fixture and add git_global_config, which
points GIT_CONFIG_GLOBAL at a throwaway file in both os.environ and
plumbum's local.env (copier's git and task channel), so the machine's
config cannot leak into a render in either direction.
pre-commit refuses to install hooks while git's core.hooksPath is set in
any scope, and a failing _task makes copier delete the whole copy. The
task now checks the setting first and skips with a hint on stderr, so a
machine with a global hooks directory still gets a complete render; the
hooks can be installed by hand once the setting is gone. A generation
test pins the skip: the copy succeeds, no hook lands, the hint prints.
Scaffolding into a subdirectory of an existing repository used to need
two manual repairs: the unconditional `git init` created a nested repo,
and the CI workflows, .pre-commit-config.yaml and renovate.json rendered
where GitHub and Renovate never read them. Deleting those locally then
conflicts on every later `copier update` that touches them.

The new answer (default false) skips `git init` and the hook install and
omits the three root-only artifacts, with an after-copy note listing
what to recreate at the repository root. Without it, the init task now
fails closed when the destination sits inside another work tree and
names the answer to re-run with, instead of nesting a repository
silently; copier's own `git init && copier copy … .` pattern still
passes because the toplevel is the destination there.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant