dycw-actions 0.7.7__py3-none-any.whl → 0.8.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,7 +12,6 @@ from subprocess import CalledProcessError
12
12
  from typing import TYPE_CHECKING, Literal, assert_never
13
13
 
14
14
  import tomlkit
15
- from ruamel.yaml.scalarstring import LiteralScalarString
16
15
  from tomlkit import TOMLDocument, table
17
16
  from tomlkit.exceptions import NonExistentKey
18
17
  from utilities.inflect import counted_noun
@@ -24,35 +23,41 @@ from utilities.version import ParseVersionError, Version, parse_version
24
23
  from utilities.whenever import HOUR
25
24
 
26
25
  from actions import __version__
27
- from actions.action_dicts.lib import (
28
- run_action_pre_commit_dict,
29
- run_action_publish_dict,
30
- run_action_pyright_dict,
31
- run_action_pytest_dict,
32
- run_action_ruff_dict,
33
- run_action_tag_dict,
34
- )
35
- from actions.constants import PATH_THROTTLE_CACHE, YAML_INSTANCE
36
- from actions.logging import LOGGER
37
- from actions.pre_commit.conformalize_repo.constants import (
26
+ from actions.constants import (
38
27
  ACTIONS_URL,
39
28
  BUMPVERSION_TOML,
40
- CONFORMALIZE_REPO_SUB_CMD,
41
29
  COVERAGERC_TOML,
42
- DOCKERFMT_URL,
43
30
  ENVRC,
31
+ GITEA_PULL_REQUEST_YAML,
32
+ GITEA_PUSH_YAML,
44
33
  GITHUB_PULL_REQUEST_YAML,
45
34
  GITHUB_PUSH_YAML,
46
35
  GITIGNORE,
47
36
  MAX_PYTHON_VERSION,
48
- PATH_CONFIGS,
37
+ PATH_THROTTLE_CACHE,
49
38
  PRE_COMMIT_CONFIG_YAML,
50
- PRE_COMMIT_HOOKS_URL,
51
39
  PYPROJECT_TOML,
52
40
  PYRIGHTCONFIG_JSON,
53
41
  PYTEST_TOML,
54
42
  README_MD,
55
43
  RUFF_TOML,
44
+ YAML_INSTANCE,
45
+ )
46
+ from actions.logging import LOGGER
47
+ from actions.pre_commit.conformalize_repo.action_dicts import (
48
+ action_publish_package_dict,
49
+ action_pyright_dict,
50
+ action_pytest_dict,
51
+ action_ruff_dict,
52
+ action_run_hooks_dict,
53
+ action_tag_commit_dict,
54
+ update_ca_certificates_dict,
55
+ )
56
+ from actions.pre_commit.conformalize_repo.constants import (
57
+ CONFORMALIZE_REPO_SUB_CMD,
58
+ DOCKERFMT_URL,
59
+ PATH_CONFIGS,
60
+ PRE_COMMIT_HOOKS_URL,
56
61
  RUFF_URL,
57
62
  SHELLCHECK_URL,
58
63
  SHFMT_URL,
@@ -89,30 +94,35 @@ if TYPE_CHECKING:
89
94
  from collections.abc import Iterator, MutableSet
90
95
 
91
96
  from tomlkit.items import Table
92
- from utilities.types import PathLike
93
-
94
- from actions.types import StrDict
97
+ from typed_settings import Secret
98
+ from utilities.types import PathLike, StrDict
95
99
 
96
100
 
97
101
  def conformalize_repo(
98
102
  *,
103
+ ci__ca_certificates: bool = SETTINGS.ci__ca_certificates,
104
+ ci__gitea: bool = SETTINGS.ci__gitea,
105
+ ci__token: str | None = SETTINGS.ci__token,
106
+ ci__pull_request__pre_commit: bool = SETTINGS.ci__pull_request__pre_commit,
107
+ ci__pull_request__pyright: bool = SETTINGS.ci__pull_request__pyright,
108
+ ci__pull_request__pytest__macos: bool = SETTINGS.ci__pull_request__pytest__macos,
109
+ ci__pull_request__pytest__ubuntu: bool = SETTINGS.ci__pull_request__pytest__ubuntu,
110
+ ci__pull_request__pytest__windows: bool = SETTINGS.ci__pull_request__pytest__windows,
111
+ ci__pull_request__pytest__sops_age_key: str
112
+ | None = SETTINGS.ci__pull_request__pytest__sops_age_key,
113
+ ci__pull_request__ruff: bool = SETTINGS.ci__pull_request__ruff,
114
+ ci__push__publish: bool = SETTINGS.ci__push__publish,
115
+ ci__push__publish__username: str | None = SETTINGS.ci__push__publish__username,
116
+ ci__push__publish__password: Secret[str]
117
+ | None = SETTINGS.ci__push__publish__password,
118
+ ci__push__publish__publish_url: Secret[str]
119
+ | None = SETTINGS.ci__push__publish__publish_url,
120
+ ci__push__tag: bool = SETTINGS.ci__push__tag,
121
+ ci__push__tag__all: bool = SETTINGS.ci__push__tag__all,
99
122
  coverage: bool = SETTINGS.coverage,
100
123
  description: str | None = SETTINGS.description,
101
124
  envrc: bool = SETTINGS.envrc,
102
125
  envrc__uv: bool = SETTINGS.envrc__uv,
103
- envrc__uv__native_tls: bool = SETTINGS.envrc__uv__native_tls,
104
- github__pull_request__pre_commit: bool = SETTINGS.github__pull_request__pre_commit,
105
- github__pull_request__pre_commit__gitea: bool = SETTINGS.github__pull_request__pre_commit__gitea,
106
- github__pull_request__pyright: bool = SETTINGS.github__pull_request__pyright,
107
- github__pull_request__pytest__macos: bool = SETTINGS.github__pull_request__pytest__macos,
108
- github__pull_request__pytest__ubuntu: bool = SETTINGS.github__pull_request__pytest__ubuntu,
109
- github__pull_request__pytest__windows: bool = SETTINGS.github__pull_request__pytest__windows,
110
- github__pull_request__ruff: bool = SETTINGS.github__pull_request__ruff,
111
- github__push__publish: bool = SETTINGS.github__push__publish,
112
- github__push__tag: bool = SETTINGS.github__push__tag,
113
- github__push__tag__major: bool = SETTINGS.github__push__tag__major,
114
- github__push__tag__major_minor: bool = SETTINGS.github__push__tag__major_minor,
115
- github__push__tag__latest: bool = SETTINGS.github__push__tag__latest,
116
126
  gitignore: bool = SETTINGS.gitignore,
117
127
  package_name: str | None = SETTINGS.package_name,
118
128
  pre_commit__dockerfmt: bool = SETTINGS.pre_commit__dockerfmt,
@@ -122,7 +132,6 @@ def conformalize_repo(
122
132
  pre_commit__shell: bool = SETTINGS.pre_commit__shell,
123
133
  pre_commit__taplo: bool = SETTINGS.pre_commit__taplo,
124
134
  pre_commit__uv: bool = SETTINGS.pre_commit__uv,
125
- pre_commit__uv__script: str | None = SETTINGS.pre_commit__uv__script,
126
135
  pyproject: bool = SETTINGS.pyproject,
127
136
  pyproject__project__optional_dependencies__scripts: bool = SETTINGS.pyproject__project__optional_dependencies__scripts,
128
137
  pyproject__tool__uv__indexes: list[
@@ -140,27 +149,31 @@ def conformalize_repo(
140
149
  ruff: bool = SETTINGS.ruff,
141
150
  run_version_bump: bool = SETTINGS.run_version_bump,
142
151
  script: str | None = SETTINGS.script,
152
+ uv__native_tls: bool = SETTINGS.uv__native_tls,
143
153
  ) -> None:
144
154
  LOGGER.info(
145
155
  strip_and_dedent("""
146
156
  Running '%s' (version %s) with settings:
157
+ - ci__ca_certificates = %s
158
+ - ci__gitea = %s
159
+ - ci__token = %s
160
+ - ci__pull_request__pre_commit = %s
161
+ - ci__pull_request__pyright = %s
162
+ - ci__pull_request__pytest__macos = %s
163
+ - ci__pull_request__pytest__ubuntu = %s
164
+ - ci__pull_request__pytest__windows = %s
165
+ - ci__pull_request__pytest__sops_age_key = %s
166
+ - ci__pull_request__ruff = %s
167
+ - ci__push__publish = %s
168
+ - ci__push__publish__username = %s
169
+ - ci__push__publish__password = %s
170
+ - ci__push__publish__publish_url = %s
171
+ - ci__push__tag = %s
172
+ - ci__push__tag__all = %s
147
173
  - coverage = %s
148
174
  - description = %s
149
175
  - envrc = %s
150
176
  - envrc__uv = %s
151
- - envrc__uv__native_tls = %s
152
- - github__pull_request__pre_commit = %s
153
- - github__pull_request__pre_commit__gitea = %s
154
- - github__pull_request__pyright = %s
155
- - github__pull_request__pytest__macos = %s
156
- - github__pull_request__pytest__ubuntu = %s
157
- - github__pull_request__pytest__windows = %s
158
- - github__pull_request__ruff = %s
159
- - github__push__publish = %s
160
- - github__push__tag = %s
161
- - github__push__tag__major = %s
162
- - github__push__tag__major_minor = %s
163
- - github__push__tag__latest = %s
164
177
  - gitignore = %s
165
178
  - package_name = %s
166
179
  - pre_commit__dockerfmt = %s
@@ -170,7 +183,6 @@ def conformalize_repo(
170
183
  - pre_commit__shell = %s
171
184
  - pre_commit__taplo = %s
172
185
  - pre_commit__uv = %s
173
- - pre_commit__uv__script = %s
174
186
  - pyproject = %s
175
187
  - pyproject__project__optional_dependencies__scripts = %s
176
188
  - pyproject__tool__uv__indexes = %s
@@ -186,26 +198,30 @@ def conformalize_repo(
186
198
  - ruff = %s
187
199
  - run_version_bump = %s
188
200
  - script = %s
201
+ - uv__native__tls = %s
189
202
  """),
190
203
  conformalize_repo.__name__,
191
204
  __version__,
205
+ ci__ca_certificates,
206
+ ci__gitea,
207
+ ci__token,
208
+ ci__pull_request__pre_commit,
209
+ ci__pull_request__pyright,
210
+ ci__pull_request__pytest__macos,
211
+ ci__pull_request__pytest__ubuntu,
212
+ ci__pull_request__pytest__windows,
213
+ ci__pull_request__pytest__sops_age_key,
214
+ ci__pull_request__ruff,
215
+ ci__push__publish,
216
+ ci__push__publish__username,
217
+ ci__push__publish__password,
218
+ ci__push__publish__publish_url,
219
+ ci__push__tag,
220
+ ci__push__tag__all,
192
221
  coverage,
193
222
  description,
194
223
  envrc,
195
224
  envrc__uv,
196
- envrc__uv__native_tls,
197
- github__pull_request__pre_commit,
198
- github__pull_request__pre_commit__gitea,
199
- github__pull_request__pyright,
200
- github__pull_request__pytest__macos,
201
- github__pull_request__pytest__ubuntu,
202
- github__pull_request__pytest__windows,
203
- github__pull_request__ruff,
204
- github__push__publish,
205
- github__push__tag,
206
- github__push__tag__major,
207
- github__push__tag__major_minor,
208
- github__push__tag__latest,
209
225
  gitignore,
210
226
  package_name,
211
227
  pre_commit__dockerfmt,
@@ -215,7 +231,6 @@ def conformalize_repo(
215
231
  pre_commit__shell,
216
232
  pre_commit__taplo,
217
233
  pre_commit__uv,
218
- pre_commit__uv__script,
219
234
  pyproject,
220
235
  pyproject__project__optional_dependencies__scripts,
221
236
  pyproject__tool__uv__indexes,
@@ -231,6 +246,7 @@ def conformalize_repo(
231
246
  ruff,
232
247
  run_version_bump,
233
248
  script,
249
+ uv__native_tls,
234
250
  )
235
251
  modifications: set[Path] = set()
236
252
  add_bumpversion_toml(
@@ -255,53 +271,63 @@ def conformalize_repo(
255
271
  uv=pre_commit__uv,
256
272
  script=script,
257
273
  )
258
- if coverage:
259
- add_coveragerc_toml(modifications=modifications)
260
- if envrc or envrc__uv or envrc__uv__native_tls:
261
- add_envrc(
262
- modifications=modifications,
263
- uv=envrc__uv,
264
- uv__native_tls=envrc__uv__native_tls,
265
- python_version=python_version,
266
- script=script,
267
- )
268
274
  if (
269
- github__pull_request__pre_commit
270
- or github__pull_request__pre_commit__gitea
271
- or github__pull_request__pyright
272
- or github__pull_request__pytest__windows
273
- or github__pull_request__pytest__macos
274
- or github__pull_request__pytest__ubuntu
275
- or github__pull_request__ruff
275
+ ci__pull_request__pre_commit
276
+ or ci__pull_request__pyright
277
+ or ci__pull_request__pytest__macos
278
+ or ci__pull_request__pytest__ubuntu
279
+ or ci__pull_request__pytest__windows
280
+ or (ci__pull_request__pytest__sops_age_key is not None)
281
+ or ci__pull_request__ruff
276
282
  ):
277
- add_github_pull_request_yaml(
283
+ add_ci_pull_request_yaml(
284
+ certificates=ci__ca_certificates,
285
+ gitea=ci__gitea,
286
+ token=ci__token,
278
287
  modifications=modifications,
279
- pre_commit=github__pull_request__pre_commit,
280
- pre_commit__gitea=github__pull_request__pre_commit__gitea,
281
- pyright=github__pull_request__pyright,
282
- pytest__windows=github__pull_request__pytest__windows,
283
- pytest__macos=github__pull_request__pytest__macos,
284
- pytest__ubuntu=github__pull_request__pytest__ubuntu,
288
+ pre_commit=ci__pull_request__pre_commit,
289
+ pyright=ci__pull_request__pyright,
290
+ pytest__macos=ci__pull_request__pytest__macos,
291
+ pytest__ubuntu=ci__pull_request__pytest__ubuntu,
292
+ pytest__windows=ci__pull_request__pytest__windows,
293
+ pytest__sops_age_key=ci__pull_request__pytest__sops_age_key,
285
294
  pytest__timeout=pytest__timeout,
286
295
  python_version=python_version,
287
296
  repo_name=repo_name,
288
297
  ruff=ruff,
289
298
  script=script,
299
+ uv__native_tls=uv__native_tls,
290
300
  )
291
301
  if (
292
- github__push__publish
293
- or github__push__tag
294
- or github__push__tag__major_minor
295
- or github__push__tag__major
296
- or github__push__tag__latest
302
+ ci__push__publish
303
+ or (ci__push__publish__username is not None)
304
+ or (ci__push__publish__password is not None)
305
+ or (ci__push__publish__publish_url is not None)
306
+ or ci__push__tag
307
+ or ci__push__tag__all
297
308
  ):
298
- add_github_push_yaml(
309
+ add_ci_push_yaml(
310
+ certificates=ci__ca_certificates,
311
+ gitea=ci__gitea,
312
+ token=ci__token,
299
313
  modifications=modifications,
300
- publish=github__push__publish,
301
- tag=github__push__tag,
302
- tag__major_minor=github__push__tag__major_minor,
303
- tag__major=github__push__tag__major,
304
- tag__latest=github__push__tag__latest,
314
+ publish=ci__push__publish,
315
+ publish__username=ci__push__publish__username,
316
+ publish__password=ci__push__publish__password,
317
+ publish__publish_url=ci__push__publish__publish_url,
318
+ tag=ci__push__tag,
319
+ tag__all=ci__push__tag__all,
320
+ uv__native_tls=uv__native_tls,
321
+ )
322
+ if coverage:
323
+ add_coveragerc_toml(modifications=modifications)
324
+ if envrc or envrc__uv:
325
+ add_envrc(
326
+ modifications=modifications,
327
+ uv=envrc__uv,
328
+ uv__native_tls=uv__native_tls,
329
+ python_version=python_version,
330
+ script=script,
305
331
  )
306
332
  if gitignore:
307
333
  add_gitignore(modifications=modifications)
@@ -402,116 +428,27 @@ def _add_bumpversion_toml_file(path: PathLike, template: str, /) -> Table:
402
428
  ##
403
429
 
404
430
 
405
- def add_coveragerc_toml(*, modifications: MutableSet[Path] | None = None) -> None:
406
- with yield_toml_doc(COVERAGERC_TOML, modifications=modifications) as doc:
407
- html = get_table(doc, "html")
408
- html["directory"] = ".coverage/html"
409
- report = get_table(doc, "report")
410
- exclude_also = get_array(report, "exclude_also")
411
- ensure_contains(exclude_also, "@overload", "if TYPE_CHECKING:")
412
- report["fail_under"] = 100.0
413
- report["skip_covered"] = True
414
- report["skip_empty"] = True
415
- run = get_table(doc, "run")
416
- run["branch"] = True
417
- run["data_file"] = ".coverage/data"
418
- run["parallel"] = True
419
-
420
-
421
- ##
422
-
423
-
424
- def add_envrc(
425
- *,
426
- modifications: MutableSet[Path] | None = None,
427
- uv: bool = SETTINGS.envrc__uv,
428
- uv__native_tls: bool = SETTINGS.envrc__uv__native_tls,
429
- python_version: str = SETTINGS.python_version,
430
- script: str | None = SETTINGS.script,
431
- ) -> None:
432
- with yield_text_file(ENVRC, modifications=modifications) as context:
433
- shebang = strip_and_dedent("""
434
- #!/usr/bin/env sh
435
- # shellcheck source=/dev/null
436
- """)
437
- if search(escape(shebang), context.output, flags=MULTILINE) is None:
438
- context.output += f"\n\n{shebang}"
439
-
440
- echo = strip_and_dedent("""
441
- # echo
442
- echo_date() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" >&2; }
443
- """)
444
- if search(escape(echo), context.output, flags=MULTILINE) is None:
445
- context.output += f"\n\n{echo}"
446
-
447
- if uv:
448
- uv_text = _add_envrc_uv_text(
449
- native_tls=uv__native_tls, python_version=python_version, script=script
450
- )
451
- if search(escape(uv_text), context.output, flags=MULTILINE) is None:
452
- context.output += f"\n\n{echo}"
453
-
454
-
455
- def _add_envrc_uv_text(
456
- *,
457
- native_tls: bool = SETTINGS.envrc__uv__native_tls,
458
- python_version: str = SETTINGS.python_version,
459
- script: str | None = SETTINGS.script,
460
- ) -> str:
461
- lines: list[str] = [
462
- strip_and_dedent("""
463
- # uv
464
- export UV_MANAGED_PYTHON='true'
465
- """)
466
- ]
467
- if native_tls:
468
- lines.append("export UV_NATIVE_TLS='true'")
469
- lines.append(
470
- strip_and_dedent(f"""
471
- export UV_PRERELEASE='disallow'
472
- export UV_PYTHON='{python_version}'
473
- if ! command -v uv >/dev/null 2>&1; then
474
- echo_date "ERROR: 'uv' not found" && exit 1
475
- fi
476
- activate='.venv/bin/activate'
477
- if [ -f $activate ]; then
478
- . $activate
479
- else
480
- uv venv
481
- fi
482
- """)
483
- )
484
- args: list[str] = ["uv", "sync"]
485
- if script is None:
486
- args.extend(["--all-extras", "--all-groups"])
487
- args.extend(["--active", "--locked"])
488
- if script is not None:
489
- args.extend(["--script", script])
490
- lines.append(join(args))
491
- return "\n".join(lines)
492
-
493
-
494
- ##
495
-
496
-
497
- def add_github_pull_request_yaml(
431
+ def add_ci_pull_request_yaml(
498
432
  *,
433
+ certificates: bool = SETTINGS.ci__ca_certificates,
434
+ gitea: bool = SETTINGS.ci__gitea,
435
+ token: str | None = SETTINGS.ci__token,
499
436
  modifications: MutableSet[Path] | None = None,
500
- pre_commit: bool = SETTINGS.github__pull_request__pre_commit,
501
- pre_commit__gitea: bool = SETTINGS.github__pull_request__pre_commit__gitea,
502
- pyright: bool = SETTINGS.github__pull_request__pyright,
503
- pytest__macos: bool = SETTINGS.github__pull_request__pytest__macos,
504
- pytest__ubuntu: bool = SETTINGS.github__pull_request__pytest__ubuntu,
505
- pytest__windows: bool = SETTINGS.github__pull_request__pytest__windows,
437
+ pre_commit: bool = SETTINGS.ci__pull_request__pre_commit,
438
+ pyright: bool = SETTINGS.ci__pull_request__pyright,
439
+ pytest__macos: bool = SETTINGS.ci__pull_request__pytest__macos,
440
+ pytest__ubuntu: bool = SETTINGS.ci__pull_request__pytest__ubuntu,
441
+ pytest__windows: bool = SETTINGS.ci__pull_request__pytest__windows,
442
+ pytest__sops_age_key: str | None = SETTINGS.ci__pull_request__pytest__sops_age_key,
506
443
  pytest__timeout: int | None = SETTINGS.pytest__timeout,
507
444
  python_version: str = SETTINGS.python_version,
508
445
  repo_name: str | None = SETTINGS.repo_name,
509
- ruff: bool = SETTINGS.github__pull_request__ruff,
446
+ ruff: bool = SETTINGS.ci__pull_request__ruff,
510
447
  script: str | None = SETTINGS.script,
448
+ uv__native_tls: bool = SETTINGS.uv__native_tls,
511
449
  ) -> None:
512
- with yield_yaml_dict(
513
- GITHUB_PULL_REQUEST_YAML, modifications=modifications
514
- ) as dict_:
450
+ path = GITEA_PULL_REQUEST_YAML if gitea else GITHUB_PULL_REQUEST_YAML
451
+ with yield_yaml_dict(path, modifications=modifications) as dict_:
515
452
  dict_["name"] = "pull-request"
516
453
  on = get_dict(dict_, "on")
517
454
  pull_request = get_dict(on, "pull_request")
@@ -524,29 +461,36 @@ def add_github_pull_request_yaml(
524
461
  pre_commit_dict = get_dict(jobs, "pre-commit")
525
462
  pre_commit_dict["runs-on"] = "ubuntu-latest"
526
463
  steps = get_list(pre_commit_dict, "steps")
464
+ if certificates:
465
+ ensure_contains(steps, update_ca_certificates_dict("pre-commit"))
527
466
  ensure_contains(
528
467
  steps,
529
- run_action_pre_commit_dict(
530
- repos=LiteralScalarString(
531
- strip_and_dedent("""
532
- dycw/actions
533
- pre-commit/pre-commit-hooks
534
- """)
535
- ),
536
- gitea=pre_commit__gitea,
468
+ action_run_hooks_dict(
469
+ token=token, repos=["pre-commit/pre-commit-hooks"], gitea=gitea
537
470
  ),
538
471
  )
539
472
  if pyright:
540
473
  pyright_dict = get_dict(jobs, "pyright")
541
474
  pyright_dict["runs-on"] = "ubuntu-latest"
542
475
  steps = get_list(pyright_dict, "steps")
476
+ if certificates:
477
+ ensure_contains(steps, update_ca_certificates_dict("pyright"))
543
478
  ensure_contains(
544
479
  steps,
545
- run_action_pyright_dict(
546
- python_version=python_version, with_requirements=script
480
+ action_pyright_dict(
481
+ token=token,
482
+ python_version=python_version,
483
+ with_requirements=script,
484
+ native_tls=uv__native_tls,
547
485
  ),
548
486
  )
549
- if pytest__macos or pytest__ubuntu or pytest__windows:
487
+ if (
488
+ pytest__macos
489
+ or pytest__ubuntu
490
+ or pytest__windows
491
+ or (pytest__sops_age_key is not None)
492
+ or pytest__timeout
493
+ ):
550
494
  pytest_dict = get_dict(jobs, "pytest")
551
495
  env = get_dict(pytest_dict, "env")
552
496
  env["CI"] = "1"
@@ -555,11 +499,16 @@ def add_github_pull_request_yaml(
555
499
  )
556
500
  pytest_dict["runs-on"] = "${{matrix.os}}"
557
501
  steps = get_list(pytest_dict, "steps")
502
+ if certificates:
503
+ ensure_contains(steps, update_ca_certificates_dict("pytest"))
558
504
  ensure_contains(
559
505
  steps,
560
- run_action_pytest_dict(
506
+ action_pytest_dict(
507
+ token=token,
561
508
  python_version="${{matrix.python-version}}",
509
+ sops_age_key=pytest__sops_age_key,
562
510
  resolution="${{matrix.resolution}}",
511
+ native_tls=uv__native_tls,
563
512
  with_requirements=script,
564
513
  ),
565
514
  )
@@ -583,22 +532,30 @@ def add_github_pull_request_yaml(
583
532
  ruff_dict = get_dict(jobs, "ruff")
584
533
  ruff_dict["runs-on"] = "ubuntu-latest"
585
534
  steps = get_list(ruff_dict, "steps")
586
- ensure_contains(steps, run_action_ruff_dict())
535
+ if certificates:
536
+ ensure_contains(steps, update_ca_certificates_dict("steps"))
537
+ ensure_contains(steps, action_ruff_dict(token=token))
587
538
 
588
539
 
589
540
  ##
590
541
 
591
542
 
592
- def add_github_push_yaml(
543
+ def add_ci_push_yaml(
593
544
  *,
545
+ certificates: bool = SETTINGS.ci__ca_certificates,
546
+ gitea: bool = SETTINGS.ci__gitea,
547
+ token: str | None = SETTINGS.ci__token,
594
548
  modifications: MutableSet[Path] | None = None,
595
- publish: bool = SETTINGS.github__push__publish,
596
- tag: bool = SETTINGS.github__push__tag,
597
- tag__major_minor: bool = SETTINGS.github__push__tag__major_minor,
598
- tag__major: bool = SETTINGS.github__push__tag__major,
599
- tag__latest: bool = SETTINGS.github__push__tag__latest,
549
+ publish: bool = SETTINGS.ci__push__publish,
550
+ publish__username: str | None = SETTINGS.ci__push__publish__username,
551
+ publish__password: Secret[str] | None = SETTINGS.ci__push__publish__password,
552
+ publish__publish_url: Secret[str] | None = SETTINGS.ci__push__publish__publish_url,
553
+ tag: bool = SETTINGS.ci__push__tag,
554
+ tag__all: bool = SETTINGS.ci__push__tag__all,
555
+ uv__native_tls: bool = SETTINGS.uv__native_tls,
600
556
  ) -> None:
601
- with yield_yaml_dict(GITHUB_PUSH_YAML, modifications=modifications) as dict_:
557
+ path = GITEA_PUSH_YAML if gitea else GITHUB_PUSH_YAML
558
+ with yield_yaml_dict(path, modifications=modifications) as dict_:
602
559
  dict_["name"] = "push"
603
560
  on = get_dict(dict_, "on")
604
561
  push = get_dict(on, "push")
@@ -613,15 +570,28 @@ def add_github_push_yaml(
613
570
  permissions["id-token"] = "write"
614
571
  publish_dict["runs-on"] = "ubuntu-latest"
615
572
  steps = get_list(publish_dict, "steps")
616
- ensure_contains(steps, run_action_publish_dict())
617
- if tag or tag__major_minor or tag__major or tag__latest:
573
+ if certificates:
574
+ ensure_contains(steps, update_ca_certificates_dict("publish"))
575
+ ensure_contains(
576
+ steps,
577
+ action_publish_package_dict(
578
+ token=token,
579
+ username=publish__username,
580
+ password=publish__password,
581
+ publish_url=publish__publish_url,
582
+ native_tls=uv__native_tls,
583
+ ),
584
+ )
585
+ if tag:
618
586
  tag_dict = get_dict(jobs, "tag")
619
587
  tag_dict["runs-on"] = "ubuntu-latest"
620
588
  steps = get_list(tag_dict, "steps")
589
+ if certificates:
590
+ ensure_contains(steps, update_ca_certificates_dict("tag"))
621
591
  ensure_contains(
622
592
  steps,
623
- run_action_tag_dict(
624
- major_minor=tag__major_minor, major=tag__major, latest=tag__latest
593
+ action_tag_commit_dict(
594
+ major_minor=tag__all, major=tag__all, latest=tag__all
625
595
  ),
626
596
  )
627
597
 
@@ -629,6 +599,98 @@ def add_github_push_yaml(
629
599
  ##
630
600
 
631
601
 
602
+ def add_coveragerc_toml(*, modifications: MutableSet[Path] | None = None) -> None:
603
+ with yield_toml_doc(COVERAGERC_TOML, modifications=modifications) as doc:
604
+ html = get_table(doc, "html")
605
+ html["directory"] = ".coverage/html"
606
+ report = get_table(doc, "report")
607
+ exclude_also = get_array(report, "exclude_also")
608
+ ensure_contains(exclude_also, "@overload", "if TYPE_CHECKING:")
609
+ report["fail_under"] = 100.0
610
+ report["skip_covered"] = True
611
+ report["skip_empty"] = True
612
+ run = get_table(doc, "run")
613
+ run["branch"] = True
614
+ run["data_file"] = ".coverage/data"
615
+ run["parallel"] = True
616
+
617
+
618
+ ##
619
+
620
+
621
+ def add_envrc(
622
+ *,
623
+ modifications: MutableSet[Path] | None = None,
624
+ uv: bool = SETTINGS.envrc__uv,
625
+ uv__native_tls: bool = SETTINGS.uv__native_tls,
626
+ python_version: str = SETTINGS.python_version,
627
+ script: str | None = SETTINGS.script,
628
+ ) -> None:
629
+ with yield_text_file(ENVRC, modifications=modifications) as context:
630
+ shebang = strip_and_dedent("""
631
+ #!/usr/bin/env sh
632
+ # shellcheck source=/dev/null
633
+ """)
634
+ if search(escape(shebang), context.output, flags=MULTILINE) is None:
635
+ context.output += f"\n\n{shebang}"
636
+
637
+ echo = strip_and_dedent("""
638
+ # echo
639
+ echo_date() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" >&2; }
640
+ """)
641
+ if search(escape(echo), context.output, flags=MULTILINE) is None:
642
+ context.output += f"\n\n{echo}"
643
+
644
+ if uv:
645
+ uv_text = _add_envrc_uv_text(
646
+ native_tls=uv__native_tls, python_version=python_version, script=script
647
+ )
648
+ if search(escape(uv_text), context.output, flags=MULTILINE) is None:
649
+ context.output += f"\n\n{echo}"
650
+
651
+
652
+ def _add_envrc_uv_text(
653
+ *,
654
+ native_tls: bool = SETTINGS.uv__native_tls,
655
+ python_version: str = SETTINGS.python_version,
656
+ script: str | None = SETTINGS.script,
657
+ ) -> str:
658
+ lines: list[str] = [
659
+ strip_and_dedent("""
660
+ # uv
661
+ export UV_MANAGED_PYTHON='true'
662
+ """)
663
+ ]
664
+ if native_tls:
665
+ lines.append("export UV_NATIVE_TLS='true'")
666
+ lines.append(
667
+ strip_and_dedent(f"""
668
+ export UV_PRERELEASE='disallow'
669
+ export UV_PYTHON='{python_version}'
670
+ if ! command -v uv >/dev/null 2>&1; then
671
+ echo_date "ERROR: 'uv' not found" && exit 1
672
+ fi
673
+ activate='.venv/bin/activate'
674
+ if [ -f $activate ]; then
675
+ . $activate
676
+ else
677
+ uv venv
678
+ fi
679
+ """)
680
+ )
681
+ args: list[str] = ["uv", "sync"]
682
+ if script is None:
683
+ args.extend(["--all-extras", "--all-groups"])
684
+ args.extend(["--active", "--locked"])
685
+ if script is not None:
686
+ args.extend(["--script", script])
687
+ lines.append(join(args))
688
+ return "\n".join(lines)
689
+
690
+
691
+ ##
692
+
693
+
632
694
  def add_gitignore(*, modifications: MutableSet[Path] | None = None) -> None:
633
695
  with yield_text_file(GITIGNORE, modifications=modifications) as context:
634
696
  text = (PATH_CONFIGS / "gitignore").read_text()
@@ -1055,13 +1117,13 @@ def check_versions() -> None:
1055
1117
 
1056
1118
  def get_cron_job(*, repo_name: str | None = SETTINGS.repo_name) -> str:
1057
1119
  if repo_name is None:
1058
- hour = minute = 0
1120
+ minute = hour = 0
1059
1121
  else:
1060
1122
  digest = blake2b(repo_name.encode(), digest_size=8).digest()
1061
1123
  value = int.from_bytes(digest, "big")
1062
1124
  minute = value % 60
1063
1125
  hour = (value // 60) % 24
1064
- return f"{hour} {minute} * * *"
1126
+ return f"{minute} {hour} * * *"
1065
1127
 
1066
1128
 
1067
1129
  ##
@@ -1278,10 +1340,10 @@ def _yield_python_version_tuple(version: str, /) -> tuple[int, int]:
1278
1340
 
1279
1341
  __all__ = [
1280
1342
  "add_bumpversion_toml",
1343
+ "add_ci_pull_request_yaml",
1344
+ "add_ci_push_yaml",
1281
1345
  "add_coveragerc_toml",
1282
1346
  "add_envrc",
1283
- "add_github_pull_request_yaml",
1284
- "add_github_push_yaml",
1285
1347
  "add_gitignore",
1286
1348
  "add_pre_commit_config_yaml",
1287
1349
  "add_pyproject_toml",