dycw-actions 0.7.1__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.
Files changed (30) hide show
  1. actions/__init__.py +1 -1
  2. actions/clean_dir/lib.py +1 -0
  3. actions/cli.py +10 -0
  4. actions/constants.py +64 -1
  5. actions/pre_commit/conformalize_repo/action_dicts.py +247 -0
  6. actions/pre_commit/conformalize_repo/cli.py +14 -14
  7. actions/pre_commit/conformalize_repo/constants.py +2 -38
  8. actions/pre_commit/conformalize_repo/lib.py +317 -242
  9. actions/pre_commit/conformalize_repo/settings.py +37 -33
  10. actions/pre_commit/touch_empty_py/lib.py +9 -1
  11. actions/pre_commit/touch_py_typed/lib.py +9 -1
  12. actions/pre_commit/update_requirements/classes.py +16 -3
  13. actions/pre_commit/update_requirements/lib.py +15 -4
  14. actions/pre_commit/utilities.py +3 -4
  15. actions/register_gitea_runner/cli.py +32 -0
  16. actions/register_gitea_runner/configs/config.yml +110 -0
  17. actions/register_gitea_runner/configs/entrypoint.sh +23 -0
  18. actions/register_gitea_runner/constants.py +23 -0
  19. actions/register_gitea_runner/lib.py +289 -0
  20. actions/register_gitea_runner/settings.py +33 -0
  21. actions/run_hooks/lib.py +13 -4
  22. actions/run_hooks/settings.py +3 -0
  23. actions/types.py +2 -2
  24. {dycw_actions-0.7.1.dist-info → dycw_actions-0.8.4.dist-info}/METADATA +4 -3
  25. {dycw_actions-0.7.1.dist-info → dycw_actions-0.8.4.dist-info}/RECORD +28 -23
  26. actions/action_dicts/constants.py +0 -8
  27. actions/action_dicts/lib.py +0 -186
  28. /actions/{action_dicts → register_gitea_runner}/__init__.py +0 -0
  29. {dycw_actions-0.7.1.dist-info → dycw_actions-0.8.4.dist-info}/WHEEL +0 -0
  30. {dycw_actions-0.7.1.dist-info → dycw_actions-0.8.4.dist-info}/entry_points.txt +0 -0
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import sys
4
4
  from contextlib import contextmanager, suppress
5
+ from hashlib import blake2b
5
6
  from itertools import product
6
7
  from pathlib import Path
7
8
  from re import MULTILINE, escape, search, sub
@@ -11,49 +12,52 @@ from subprocess import CalledProcessError
11
12
  from typing import TYPE_CHECKING, Literal, assert_never
12
13
 
13
14
  import tomlkit
14
- from ruamel.yaml.scalarstring import LiteralScalarString
15
15
  from tomlkit import TOMLDocument, table
16
16
  from tomlkit.exceptions import NonExistentKey
17
17
  from utilities.inflect import counted_noun
18
- from utilities.pathlib import get_repo_root
19
18
  from utilities.re import extract_groups
20
19
  from utilities.subprocess import ripgrep
21
20
  from utilities.text import repr_str, strip_and_dedent
21
+ from utilities.throttle import throttle
22
22
  from utilities.version import ParseVersionError, Version, parse_version
23
- from utilities.whenever import HOUR, get_now
24
- from whenever import ZonedDateTime
25
- from xdg_base_dirs import xdg_cache_home
23
+ from utilities.whenever import HOUR
26
24
 
27
25
  from actions import __version__
28
- from actions.action_dicts.lib import (
29
- run_action_pre_commit_dict,
30
- run_action_publish_dict,
31
- run_action_pyright_dict,
32
- run_action_pytest_dict,
33
- run_action_ruff_dict,
34
- run_action_tag_dict,
35
- )
36
- from actions.constants import YAML_INSTANCE
37
- from actions.logging import LOGGER
38
- from actions.pre_commit.conformalize_repo.constants import (
26
+ from actions.constants import (
39
27
  ACTIONS_URL,
40
28
  BUMPVERSION_TOML,
41
- CONFORMALIZE_REPO_SUB_CMD,
42
29
  COVERAGERC_TOML,
43
- DOCKERFMT_URL,
44
30
  ENVRC,
31
+ GITEA_PULL_REQUEST_YAML,
32
+ GITEA_PUSH_YAML,
45
33
  GITHUB_PULL_REQUEST_YAML,
46
34
  GITHUB_PUSH_YAML,
47
35
  GITIGNORE,
48
36
  MAX_PYTHON_VERSION,
49
- PATH_CONFIGS,
37
+ PATH_THROTTLE_CACHE,
50
38
  PRE_COMMIT_CONFIG_YAML,
51
- PRE_COMMIT_HOOKS_URL,
52
39
  PYPROJECT_TOML,
53
40
  PYRIGHTCONFIG_JSON,
54
41
  PYTEST_TOML,
55
42
  README_MD,
56
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,
57
61
  RUFF_URL,
58
62
  SHELLCHECK_URL,
59
63
  SHFMT_URL,
@@ -67,6 +71,7 @@ from actions.pre_commit.replace_sequence_strs.constants import (
67
71
  )
68
72
  from actions.pre_commit.touch_empty_py.constants import TOUCH_EMPTY_PY_SUB_CMD
69
73
  from actions.pre_commit.touch_py_typed.constants import TOUCH_PY_TYPED_SUB_CMD
74
+ from actions.pre_commit.update_requirements.constants import UPDATE_REQUIREMENTS_SUB_CMD
70
75
  from actions.pre_commit.utilities import (
71
76
  ensure_aot_contains,
72
77
  ensure_contains,
@@ -83,36 +88,41 @@ from actions.pre_commit.utilities import (
83
88
  yield_toml_doc,
84
89
  yield_yaml_dict,
85
90
  )
86
- from actions.utilities import logged_run, write_text
91
+ from actions.utilities import logged_run
87
92
 
88
93
  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,52 +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,
296
+ repo_name=repo_name,
287
297
  ruff=ruff,
288
298
  script=script,
299
+ uv__native_tls=uv__native_tls,
289
300
  )
290
301
  if (
291
- github__push__publish
292
- or github__push__tag
293
- or github__push__tag__major_minor
294
- or github__push__tag__major
295
- 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
296
308
  ):
297
- add_github_push_yaml(
309
+ add_ci_push_yaml(
310
+ certificates=ci__ca_certificates,
311
+ gitea=ci__gitea,
312
+ token=ci__token,
313
+ modifications=modifications,
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(
298
326
  modifications=modifications,
299
- publish=github__push__publish,
300
- tag=github__push__tag,
301
- tag__major_minor=github__push__tag__major_minor,
302
- tag__major=github__push__tag__major,
303
- tag__latest=github__push__tag__latest,
327
+ uv=envrc__uv,
328
+ uv__native_tls=uv__native_tls,
329
+ python_version=python_version,
330
+ script=script,
304
331
  )
305
332
  if gitignore:
306
333
  add_gitignore(modifications=modifications)
@@ -401,150 +428,69 @@ def _add_bumpversion_toml_file(path: PathLike, template: str, /) -> Table:
401
428
  ##
402
429
 
403
430
 
404
- def add_coveragerc_toml(*, modifications: MutableSet[Path] | None = None) -> None:
405
- with yield_toml_doc(COVERAGERC_TOML, modifications=modifications) as doc:
406
- html = get_table(doc, "html")
407
- html["directory"] = ".coverage/html"
408
- report = get_table(doc, "report")
409
- exclude_also = get_array(report, "exclude_also")
410
- ensure_contains(exclude_also, "@overload", "if TYPE_CHECKING:")
411
- report["fail_under"] = 100.0
412
- report["skip_covered"] = True
413
- report["skip_empty"] = True
414
- run = get_table(doc, "run")
415
- run["branch"] = True
416
- run["data_file"] = ".coverage/data"
417
- run["parallel"] = True
418
-
419
-
420
- ##
421
-
422
-
423
- def add_envrc(
431
+ def add_ci_pull_request_yaml(
424
432
  *,
433
+ certificates: bool = SETTINGS.ci__ca_certificates,
434
+ gitea: bool = SETTINGS.ci__gitea,
435
+ token: str | None = SETTINGS.ci__token,
425
436
  modifications: MutableSet[Path] | None = None,
426
- uv: bool = SETTINGS.envrc__uv,
427
- uv__native_tls: bool = SETTINGS.envrc__uv__native_tls,
428
- python_version: str = SETTINGS.python_version,
429
- script: str | None = SETTINGS.script,
430
- ) -> None:
431
- with yield_text_file(ENVRC, modifications=modifications) as context:
432
- shebang = strip_and_dedent("""
433
- #!/usr/bin/env sh
434
- # shellcheck source=/dev/null
435
- """)
436
- if search(escape(shebang), context.output, flags=MULTILINE) is None:
437
- context.output += f"\n\n{shebang}"
438
-
439
- echo = strip_and_dedent("""
440
- # echo
441
- echo_date() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" >&2; }
442
- """)
443
- if search(escape(echo), context.output, flags=MULTILINE) is None:
444
- context.output += f"\n\n{echo}"
445
-
446
- if uv:
447
- uv_text = _add_envrc_uv_text(
448
- native_tls=uv__native_tls, python_version=python_version, script=script
449
- )
450
- if search(escape(uv_text), context.output, flags=MULTILINE) is None:
451
- context.output += f"\n\n{echo}"
452
-
453
-
454
- def _add_envrc_uv_text(
455
- *,
456
- native_tls: bool = SETTINGS.envrc__uv__native_tls,
457
- python_version: str = SETTINGS.python_version,
458
- script: str | None = SETTINGS.script,
459
- ) -> str:
460
- lines: list[str] = [
461
- strip_and_dedent("""
462
- # uv
463
- export UV_MANAGED_PYTHON='true'
464
- """)
465
- ]
466
- if native_tls:
467
- lines.append("export UV_NATIVE_TLS='true'")
468
- lines.append(
469
- strip_and_dedent(f"""
470
- export UV_PRERELEASE='disallow'
471
- export UV_PYTHON='{python_version}'
472
- if ! command -v uv >/dev/null 2>&1; then
473
- echo_date "ERROR: 'uv' not found" && exit 1
474
- fi
475
- activate='.venv/bin/activate'
476
- if [ -f $activate ]; then
477
- . $activate
478
- else
479
- uv venv
480
- fi
481
- """)
482
- )
483
- args: list[str] = ["uv", "sync"]
484
- if script is None:
485
- args.extend(["--all-extras", "--all-groups"])
486
- args.extend(["--active", "--locked"])
487
- if script is not None:
488
- args.extend(["--script", script])
489
- lines.append(join(args))
490
- return "\n".join(lines)
491
-
492
-
493
- ##
494
-
495
-
496
- def add_github_pull_request_yaml(
497
- *,
498
- modifications: MutableSet[Path] | None = None,
499
- pre_commit: bool = SETTINGS.github__pull_request__pre_commit,
500
- pre_commit__gitea: bool = SETTINGS.github__pull_request__pre_commit__gitea,
501
- pyright: bool = SETTINGS.github__pull_request__pyright,
502
- pytest__macos: bool = SETTINGS.github__pull_request__pytest__macos,
503
- pytest__ubuntu: bool = SETTINGS.github__pull_request__pytest__ubuntu,
504
- 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,
505
443
  pytest__timeout: int | None = SETTINGS.pytest__timeout,
506
444
  python_version: str = SETTINGS.python_version,
507
- ruff: bool = SETTINGS.github__pull_request__ruff,
445
+ repo_name: str | None = SETTINGS.repo_name,
446
+ ruff: bool = SETTINGS.ci__pull_request__ruff,
508
447
  script: str | None = SETTINGS.script,
448
+ uv__native_tls: bool = SETTINGS.uv__native_tls,
509
449
  ) -> None:
510
- with yield_yaml_dict(
511
- GITHUB_PULL_REQUEST_YAML, modifications=modifications
512
- ) 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_:
513
452
  dict_["name"] = "pull-request"
514
453
  on = get_dict(dict_, "on")
515
454
  pull_request = get_dict(on, "pull_request")
516
455
  branches = get_list(pull_request, "branches")
517
456
  ensure_contains(branches, "master")
518
457
  schedule = get_list(on, "schedule")
519
- ensure_contains(schedule, {"cron": "0 0 * * *"})
458
+ ensure_contains(schedule, {"cron": get_cron_job(repo_name=repo_name)})
520
459
  jobs = get_dict(dict_, "jobs")
521
460
  if pre_commit:
522
461
  pre_commit_dict = get_dict(jobs, "pre-commit")
523
462
  pre_commit_dict["runs-on"] = "ubuntu-latest"
524
463
  steps = get_list(pre_commit_dict, "steps")
464
+ if certificates:
465
+ ensure_contains(steps, update_ca_certificates_dict("pre-commit"))
525
466
  ensure_contains(
526
467
  steps,
527
- run_action_pre_commit_dict(
528
- repos=LiteralScalarString(
529
- strip_and_dedent("""
530
- dycw/actions
531
- pre-commit/pre-commit-hooks
532
- """)
533
- ),
534
- gitea=pre_commit__gitea,
468
+ action_run_hooks_dict(
469
+ token=token, repos=["pre-commit/pre-commit-hooks"], gitea=gitea
535
470
  ),
536
471
  )
537
472
  if pyright:
538
473
  pyright_dict = get_dict(jobs, "pyright")
539
474
  pyright_dict["runs-on"] = "ubuntu-latest"
540
475
  steps = get_list(pyright_dict, "steps")
476
+ if certificates:
477
+ ensure_contains(steps, update_ca_certificates_dict("pyright"))
541
478
  ensure_contains(
542
479
  steps,
543
- run_action_pyright_dict(
544
- 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,
545
485
  ),
546
486
  )
547
- 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
+ ):
548
494
  pytest_dict = get_dict(jobs, "pytest")
549
495
  env = get_dict(pytest_dict, "env")
550
496
  env["CI"] = "1"
@@ -553,11 +499,16 @@ def add_github_pull_request_yaml(
553
499
  )
554
500
  pytest_dict["runs-on"] = "${{matrix.os}}"
555
501
  steps = get_list(pytest_dict, "steps")
502
+ if certificates:
503
+ ensure_contains(steps, update_ca_certificates_dict("pytest"))
556
504
  ensure_contains(
557
505
  steps,
558
- run_action_pytest_dict(
506
+ action_pytest_dict(
507
+ token=token,
559
508
  python_version="${{matrix.python-version}}",
509
+ sops_age_key=pytest__sops_age_key,
560
510
  resolution="${{matrix.resolution}}",
511
+ native_tls=uv__native_tls,
561
512
  with_requirements=script,
562
513
  ),
563
514
  )
@@ -581,22 +532,30 @@ def add_github_pull_request_yaml(
581
532
  ruff_dict = get_dict(jobs, "ruff")
582
533
  ruff_dict["runs-on"] = "ubuntu-latest"
583
534
  steps = get_list(ruff_dict, "steps")
584
- 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))
585
538
 
586
539
 
587
540
  ##
588
541
 
589
542
 
590
- def add_github_push_yaml(
543
+ def add_ci_push_yaml(
591
544
  *,
545
+ certificates: bool = SETTINGS.ci__ca_certificates,
546
+ gitea: bool = SETTINGS.ci__gitea,
547
+ token: str | None = SETTINGS.ci__token,
592
548
  modifications: MutableSet[Path] | None = None,
593
- publish: bool = SETTINGS.github__push__publish,
594
- tag: bool = SETTINGS.github__push__tag,
595
- tag__major_minor: bool = SETTINGS.github__push__tag__major_minor,
596
- tag__major: bool = SETTINGS.github__push__tag__major,
597
- 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,
598
556
  ) -> None:
599
- 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_:
600
559
  dict_["name"] = "push"
601
560
  on = get_dict(dict_, "on")
602
561
  push = get_dict(on, "push")
@@ -611,15 +570,28 @@ def add_github_push_yaml(
611
570
  permissions["id-token"] = "write"
612
571
  publish_dict["runs-on"] = "ubuntu-latest"
613
572
  steps = get_list(publish_dict, "steps")
614
- ensure_contains(steps, run_action_publish_dict())
615
- 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:
616
586
  tag_dict = get_dict(jobs, "tag")
617
587
  tag_dict["runs-on"] = "ubuntu-latest"
618
588
  steps = get_list(tag_dict, "steps")
589
+ if certificates:
590
+ ensure_contains(steps, update_ca_certificates_dict("tag"))
619
591
  ensure_contains(
620
592
  steps,
621
- run_action_tag_dict(
622
- 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
623
595
  ),
624
596
  )
625
597
 
@@ -627,6 +599,98 @@ def add_github_push_yaml(
627
599
  ##
628
600
 
629
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
+
630
694
  def add_gitignore(*, modifications: MutableSet[Path] | None = None) -> None:
631
695
  with yield_text_file(GITIGNORE, modifications=modifications) as context:
632
696
  text = (PATH_CONFIGS / "gitignore").read_text()
@@ -695,6 +759,7 @@ def add_pre_commit_config_yaml(
695
759
  )
696
760
  _add_pre_commit_config_repo(dict_, ACTIONS_URL, TOUCH_EMPTY_PY_SUB_CMD)
697
761
  _add_pre_commit_config_repo(dict_, ACTIONS_URL, TOUCH_PY_TYPED_SUB_CMD)
762
+ _add_pre_commit_config_repo(dict_, ACTIONS_URL, UPDATE_REQUIREMENTS_SUB_CMD)
698
763
  if ruff:
699
764
  _add_pre_commit_config_repo(
700
765
  dict_, RUFF_URL, "ruff-check", args=("add", ["--fix"])
@@ -1050,6 +1115,20 @@ def check_versions() -> None:
1050
1115
  ##
1051
1116
 
1052
1117
 
1118
+ def get_cron_job(*, repo_name: str | None = SETTINGS.repo_name) -> str:
1119
+ if repo_name is None:
1120
+ minute = hour = 0
1121
+ else:
1122
+ digest = blake2b(repo_name.encode(), digest_size=8).digest()
1123
+ value = int.from_bytes(digest, "big")
1124
+ minute = value % 60
1125
+ hour = (value // 60) % 24
1126
+ return f"{minute} {hour} * * *"
1127
+
1128
+
1129
+ ##
1130
+
1131
+
1053
1132
  def get_python_package_name(
1054
1133
  *,
1055
1134
  package_name: str | None = SETTINGS.package_name,
@@ -1123,23 +1202,18 @@ def run_bump_my_version(*, modifications: MutableSet[Path] | None = None) -> Non
1123
1202
  ##
1124
1203
 
1125
1204
 
1126
- def run_pre_commit_update(*, modifications: MutableSet[Path] | None = None) -> None:
1127
- cache = xdg_cache_home() / "conformalize" / get_repo_root().name
1128
- try:
1129
- text = cache.read_text()
1130
- except FileNotFoundError:
1131
- ...
1132
- else:
1133
- prev = ZonedDateTime.parse_iso(text.rstrip("\n"))
1134
- if prev >= (get_now() - 12 * HOUR):
1135
- return
1136
- write_text(cache, get_now().format_iso())
1205
+ def _run_pre_commit_update(*, modifications: MutableSet[Path] | None = None) -> None:
1137
1206
  current = PRE_COMMIT_CONFIG_YAML.read_text()
1138
1207
  logged_run("pre-commit", "autoupdate", print=True)
1139
1208
  if (modifications is not None) and (PRE_COMMIT_CONFIG_YAML.read_text() != current):
1140
1209
  modifications.add(PRE_COMMIT_CONFIG_YAML)
1141
1210
 
1142
1211
 
1212
+ run_pre_commit_update = throttle(
1213
+ delta=12 * HOUR, path=PATH_THROTTLE_CACHE / _run_pre_commit_update.__name__
1214
+ )(_run_pre_commit_update)
1215
+
1216
+
1143
1217
  ##
1144
1218
 
1145
1219
 
@@ -1266,10 +1340,10 @@ def _yield_python_version_tuple(version: str, /) -> tuple[int, int]:
1266
1340
 
1267
1341
  __all__ = [
1268
1342
  "add_bumpversion_toml",
1343
+ "add_ci_pull_request_yaml",
1344
+ "add_ci_push_yaml",
1269
1345
  "add_coveragerc_toml",
1270
1346
  "add_envrc",
1271
- "add_github_pull_request_yaml",
1272
- "add_github_push_yaml",
1273
1347
  "add_gitignore",
1274
1348
  "add_pre_commit_config_yaml",
1275
1349
  "add_pyproject_toml",
@@ -1278,6 +1352,7 @@ __all__ = [
1278
1352
  "add_readme_md",
1279
1353
  "add_ruff_toml",
1280
1354
  "check_versions",
1355
+ "get_cron_job",
1281
1356
  "get_python_package_name",
1282
1357
  "get_version_from_bumpversion_toml",
1283
1358
  "get_version_from_git_show",