Repository navigation
Expand file tree
/
Copy pathcopier.yml
More file actions
208 lines (186 loc) · 8.13 KB
/
Copy pathcopier.yml
File metadata and controls
208 lines (186 loc) · 8.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# Copier template configuration for Python package projects
_subdirectory: template
_templates_suffix: .jinja
_exclude:
- ".git"
- "__pycache__"
- "*.pyc"
- ".DS_Store"
- ".venv"
# Throwaway output is configured into `.artifacts/` now. `.pytest_cache` stays
# listed because this guard is against a stray directory appearing under
# `template/`, which a tool run outside the project's own config can still create.
- ".artifacts"
- ".pytest_cache"
- "*.egg-info"
# A project's identity. Shipped once, so a new project has something to render,
# and never delivered again.
#
# These are binaries, so copier cannot three-way merge them: on every update it
# overwrites them with the template's placeholder, with no conflict, no .rej and
# no output. Nothing marks the loss -- the project's own logo is simply gone,
# and the docs render the template's wordmark under the project's name. Calling
# them "local-owned" in the update skill does not help: that is a convention for
# whoever resolves the update, and this never reaches a resolution step.
# _skip_if_exists is the only thing copier itself honours here.
_skip_if_exists:
- "docs/assets/logo.png"
- "docs/assets/logo_dark.png"
- "docs/assets/logo_light.png"
- "docs/assets/favicon.png"
# `docs/assets/made_by_stateful-y.png` is deliberately NOT here. It is the org
# wordmark, which is the template's asset rather than the project's, so it stays
# template-managed: skip-listing it would strand every project on whichever mark
# it happened to be generated with. The v0.42.0 fan-out flagged its absence as a
# gap in this list, having correctly observed that copier overwrites it on every
# run -- which is true, and is the intended behaviour here. See
# test_every_shipped_binary_is_classified, which requires each shipped binary to
# be either skip-listed or explicitly named as template-managed, so that "not in
# this list" is a recorded decision rather than an omission.
#
# Seed pages, and the same shape of problem as the logos above. Every real
# project rewrites these two wholesale -- the template's `hello("World")` tour
# and placeholder landing page survive nowhere. The template then keeps trying
# to patch them, and copier applies an update as a diff against the *template's*
# version: when the local file no longer resembles it, one shifted line rejects
# the whole hunk and the page reverts to the placeholder, with the project's
# content left only in a .rej nobody reads.
#
# v0.22.0 proved it: gating a blank line behind {% if include_examples %} was a
# whitespace-only change for projects without examples, and it still replaced a
# 244-line curated tutorial with the 74-line stub. Five of five projects with a
# customised getting-started.md were hit by that one release.
#
# Unlike the logos there is no merge to lose: a project that has rewritten these
# wants nothing the template ships in them. Seed once; never deliver again.
- "docs/index.md"
- "docs/pages/tutorials/getting-started.md"
# The gallery index goes the same way once a project curates it: yohou's lists
# six hand-written section pages, and v0.22.0 overwrote it with the generic
# one-page version. The skill's own file-classification.md already says this
# file "must never be overwritten" -- that classification is advisory and
# copier ignores it; this line is what enforces it.
- "docs/pages/examples/index.md"
# Project instructions for AI assistants. Seeded once so a new project has
# something to start from, then never delivered again. Every project rewrites this
# wholesale -- it exists to record what is true about *that* project -- so the
# template's version survives nowhere, and an update applied as a diff against the
# template's copy would reject and revert it, exactly as v0.22.0 did to five
# projects' getting-started.md. There is no merge to lose here either.
- "CLAUDE.md"
_envops:
block_start_string: "{%"
block_end_string: "%}"
variable_start_string: "{{"
variable_end_string: "}}"
comment_start_string: "{#"
comment_end_string: "#}"
keep_trailing_newline: true
# uv.lock is tracked (not gitignored) and CI/hooks run with --locked, so the
# lockfile must exist and be committed before the first push, or CI will fail.
_message_after_copy: |
Next steps:
1. cd into your new project
2. just install # creates the uv.lock (uv sync) and installs the git hooks
3. git add uv.lock && git commit # commit the lockfile — CI runs with --locked
project_name:
type: str
help: "Name of the new project (e.g., 'My Awesome Package')"
package_name:
type: str
help: "Python package name (e.g., 'my_awesome_package')"
default: "{{ project_name.lower().replace(' ', '_').replace('-', '_') }}"
project_slug:
type: str
help: "Project slug for URLs and repositories (e.g., 'my-awesome-package')"
default: "{{ project_name.lower().replace(' ', '-').replace('_', '-') }}"
description:
type: str
default: ""
help: "Short project description (one line)"
author_name:
type: str
help: "Author or maintainer name"
author_email:
type: str
help: "Author or maintainer email"
github_username:
type: str
help: "GitHub username or organization"
default: "stateful-y"
code_owner:
type: str
# Written into CODEOWNERS. A GitHub username (@user) or team (@org/team). Defaults to
# a maintainer rather than github_username, because a bare organization name is not a
# valid code owner (GitHub flags it) -- the owning org is `github_username`, but the
# reviewer must be a person or team.
help: "Default code owner for CODEOWNERS (@user or @org/team)"
default: "@gtauzin"
license:
type: str
help: "Project license"
default: "MIT"
choices:
- "Apache-2.0"
- "MIT"
- "BSD-3-Clause"
- "GPL-3.0"
- "Proprietary"
repo_visibility:
type: str
# Drives which paid, visibility-sensitive services are provisioned. Almost every
# "paid" security service (CodeQL, Codecov, GitHub-native secret scanning) is paid
# only because the repository is private -- each is free on public repos. Capturing
# visibility once lets the template default those services on for public repos and
# substitute the free OSS equivalents (ruff S, gitleaks) on private ones, instead of
# exposing one flag per service.
help: "Repository visibility (public repos get free paid-tier services; private repos get OSS equivalents)"
default: "public"
choices:
- "public"
- "private"
min_python_version:
type: str
help: "Minimum Python version"
default: "3.11"
choices:
- "3.11"
- "3.12"
- "3.13"
- "3.14"
max_python_version:
type: str
help: "Maximum Python version"
default: "3.14"
choices:
- "3.11"
- "3.12"
- "3.13"
- "3.14"
validator: "{% if max_python_version < min_python_version %}Maximum Python version must be >= minimum Python version (min: {{ min_python_version }}, max: {{ max_python_version }}){% endif %}"
include_actions:
type: bool
help: "Include GitHub Actions CI/CD workflows?"
default: true
include_codecov:
type: bool
# Codecov coverage upload. Free on public repos, a paid org plan on private ones, so
# the default follows repo_visibility. Overridable: a private project willing to pay
# can set this true. Gated on include_actions because the upload runs in CI.
help: "Upload coverage to Codecov? (free on public repos, paid on private)"
default: "{{ repo_visibility == 'public' }}"
when: "{{ include_actions }}"
renovate_preset:
type: str
# Renovate replaces Dependabot for version updates. When this is set, the generated
# renovate.json is a thin stub that extends a shared preset (e.g.
# "stateful-y/renovate-config"), so fleet-wide dependency policy lives in one repo
# instead of a copy drifting in each. When empty (the default), a full self-contained
# renovate.json is generated, which is what a standalone project wants. Renovate errors
# on a preset it cannot read, so leave this empty unless you own the named preset repo.
help: "Shared Renovate preset to extend as owner/repo (empty = self-contained config)"
default: ""
include_examples:
type: bool
help: "Include examples/ directory with marimo notebooks?"
default: true