dycw-actions 0.3.2__py3-none-any.whl → 0.6.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 (59) hide show
  1. actions/__init__.py +1 -1
  2. actions/action_dicts/constants.py +8 -0
  3. actions/action_dicts/lib.py +186 -0
  4. actions/clean_dir/cli.py +33 -0
  5. actions/clean_dir/lib.py +59 -0
  6. actions/clean_dir/settings.py +18 -0
  7. actions/cli.py +44 -6
  8. actions/conformalize_repo/cli.py +76 -0
  9. actions/conformalize_repo/configs/gitignore +244 -0
  10. actions/conformalize_repo/constants.py +72 -0
  11. actions/conformalize_repo/lib.py +1522 -0
  12. actions/conformalize_repo/settings.py +119 -0
  13. actions/constants.py +10 -0
  14. actions/format_requirements/__init__.py +1 -0
  15. actions/format_requirements/cli.py +37 -0
  16. actions/format_requirements/lib.py +121 -0
  17. actions/publish_package/__init__.py +1 -0
  18. actions/{publish → publish_package}/cli.py +6 -10
  19. actions/publish_package/doc.py +6 -0
  20. actions/{publish → publish_package}/lib.py +17 -16
  21. actions/{publish → publish_package}/settings.py +7 -7
  22. actions/random_sleep/__init__.py +1 -0
  23. actions/{sleep → random_sleep}/cli.py +6 -10
  24. actions/random_sleep/doc.py +6 -0
  25. actions/{sleep → random_sleep}/lib.py +14 -13
  26. actions/{sleep → random_sleep}/settings.py +3 -3
  27. actions/replace_sequence_strs/__init__.py +1 -0
  28. actions/replace_sequence_strs/cli.py +37 -0
  29. actions/replace_sequence_strs/lib.py +79 -0
  30. actions/run_hooks/__init__.py +1 -0
  31. actions/run_hooks/cli.py +33 -0
  32. actions/run_hooks/doc.py +6 -0
  33. actions/run_hooks/lib.py +97 -0
  34. actions/run_hooks/settings.py +24 -0
  35. actions/setup_cronjob/__init__.py +1 -0
  36. actions/setup_cronjob/cli.py +43 -0
  37. actions/setup_cronjob/configs/cron.tmpl +3 -0
  38. actions/setup_cronjob/configs/logrotate.tmpl +10 -0
  39. actions/setup_cronjob/constants.py +8 -0
  40. actions/setup_cronjob/lib.py +120 -0
  41. actions/setup_cronjob/settings.py +27 -0
  42. actions/tag_commit/__init__.py +1 -0
  43. actions/{tag → tag_commit}/cli.py +6 -10
  44. actions/tag_commit/doc.py +6 -0
  45. actions/tag_commit/lib.py +63 -0
  46. actions/{tag → tag_commit}/settings.py +3 -3
  47. actions/types.py +11 -1
  48. actions/utilities.py +68 -8
  49. dycw_actions-0.6.4.dist-info/METADATA +21 -0
  50. dycw_actions-0.6.4.dist-info/RECORD +56 -0
  51. {dycw_actions-0.3.2.dist-info → dycw_actions-0.6.4.dist-info}/WHEEL +1 -1
  52. actions/settings.py +0 -18
  53. actions/tag/lib.py +0 -62
  54. dycw_actions-0.3.2.dist-info/METADATA +0 -14
  55. dycw_actions-0.3.2.dist-info/RECORD +0 -22
  56. /actions/{publish → action_dicts}/__init__.py +0 -0
  57. /actions/{sleep → clean_dir}/__init__.py +0 -0
  58. /actions/{tag → conformalize_repo}/__init__.py +0 -0
  59. {dycw_actions-0.3.2.dist-info → dycw_actions-0.6.4.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1 @@
1
+ from __future__ import annotations
@@ -0,0 +1,33 @@
1
+ from __future__ import annotations
2
+
3
+ from rich.pretty import pretty_repr
4
+ from typed_settings import click_options
5
+ from utilities.logging import basic_config
6
+ from utilities.os import is_pytest
7
+ from utilities.text import strip_and_dedent
8
+
9
+ from actions import __version__
10
+ from actions.logging import LOGGER
11
+ from actions.run_hooks.lib import run_hooks
12
+ from actions.run_hooks.settings import Settings
13
+ from actions.utilities import LOADER
14
+
15
+
16
+ @click_options(Settings, [LOADER], show_envvars_in_help=True, argname="hooks")
17
+ def run_hooks_sub_cmd(*, hooks: Settings) -> None:
18
+ if is_pytest():
19
+ return
20
+ basic_config(obj=LOGGER)
21
+ LOGGER.info(
22
+ strip_and_dedent("""
23
+ Running '%s' (version %s) with settings:
24
+ %s
25
+ """),
26
+ run_hooks.__name__,
27
+ __version__,
28
+ pretty_repr(hooks),
29
+ )
30
+ run_hooks(repos=hooks.repos, hooks=hooks.hooks, sleep=hooks.sleep)
31
+
32
+
33
+ __all__ = ["run_hooks_sub_cmd"]
@@ -0,0 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+ DOCSTRING = "Run 'pre-commit' hooks"
4
+
5
+
6
+ __all__ = ["DOCSTRING"]
@@ -0,0 +1,97 @@
1
+ from __future__ import annotations
2
+
3
+ import time
4
+ from pathlib import Path
5
+ from re import search
6
+ from subprocess import CalledProcessError
7
+ from typing import TYPE_CHECKING, Any
8
+
9
+ from utilities.functions import ensure_class, ensure_str
10
+ from utilities.text import strip_and_dedent
11
+ from whenever import TimeDelta
12
+ from yaml import safe_load
13
+
14
+ from actions import __version__
15
+ from actions.logging import LOGGER
16
+ from actions.run_hooks.settings import SETTINGS
17
+ from actions.utilities import log_run
18
+
19
+ if TYPE_CHECKING:
20
+ from collections.abc import Iterator
21
+
22
+
23
+ def run_hooks(
24
+ *,
25
+ repos: list[str] | None = SETTINGS.repos,
26
+ hooks: list[str] | None = SETTINGS.hooks,
27
+ sleep: int = SETTINGS.sleep,
28
+ ) -> None:
29
+ LOGGER.info(
30
+ strip_and_dedent("""
31
+ Running '%s' (version %s) with settings:
32
+ - repos = %s
33
+ - hooks = %s
34
+ - sleep = %d
35
+ """),
36
+ run_hooks.__name__,
37
+ __version__,
38
+ repos,
39
+ hooks,
40
+ sleep,
41
+ )
42
+ results = {
43
+ hook: _run_hook(hook, sleep=sleep)
44
+ for hook in _yield_hooks(repos=repos, hooks=hooks)
45
+ }
46
+ failed = {hook: result for hook, result in results.items() if not result}
47
+ if len(failed) >= 1:
48
+ msg = f"Failed hook(s): {', '.join(failed)}"
49
+ raise RuntimeError(msg)
50
+
51
+
52
+ def _yield_hooks(
53
+ *,
54
+ repos: list[str] | None = SETTINGS.repos,
55
+ hooks: list[str] | None = SETTINGS.hooks,
56
+ ) -> Iterator[str]:
57
+ dict_ = safe_load(Path(".pre-commit-config.yaml").read_text())
58
+ repos_list = ensure_class(dict_["repos"], list)
59
+ results: set[str] = set()
60
+ for repo in (ensure_class(r, dict) for r in repos_list):
61
+ url = repo["repo"]
62
+ if (repos is not None) and any(search(repo_i, url) for repo_i in repos):
63
+ results.update(_yield_repo_hooks(repo))
64
+ elif hooks is not None:
65
+ for hook in _yield_repo_hooks(repo):
66
+ if any(search(hook_i, hook) for hook_i in hooks):
67
+ results.add(hook)
68
+ yield from sorted(results)
69
+
70
+
71
+ def _yield_repo_hooks(repo: dict[str, Any], /) -> Iterator[str]:
72
+ hooks = ensure_class(repo["hooks"], list)
73
+ for hook in (ensure_class(r, dict) for r in hooks):
74
+ yield ensure_str(hook["id"])
75
+
76
+
77
+ def _run_hook(hook: str, /, *, sleep: int = SETTINGS.sleep) -> bool:
78
+ LOGGER.info("Running '%s'...", hook)
79
+ try:
80
+ log_run("pre-commit", "run", "--verbose", "--all-files", hook, print=True)
81
+ except CalledProcessError:
82
+ is_success = False
83
+ else:
84
+ is_success = True
85
+ delta = TimeDelta(seconds=sleep)
86
+ LOGGER.info(
87
+ "Hook '%s' %s; sleeping for %s...",
88
+ hook,
89
+ "succeeded" if is_success else "failed",
90
+ delta,
91
+ )
92
+ time.sleep(sleep)
93
+ LOGGER.info("Finished sleeping for %s", delta)
94
+ return is_success
95
+
96
+
97
+ __all__ = ["run_hooks"]
@@ -0,0 +1,24 @@
1
+ from __future__ import annotations
2
+
3
+ from typed_settings import load_settings, option, settings
4
+
5
+ from actions.utilities import LOADER, convert_list_strs
6
+
7
+
8
+ @settings
9
+ class Settings:
10
+ repos: list[str] | None = option(
11
+ default=None,
12
+ converter=convert_list_strs,
13
+ help="The repos whose hooks are to be run",
14
+ )
15
+ hooks: list[str] | None = option(
16
+ default=None, converter=convert_list_strs, help="The hooks to be run"
17
+ )
18
+ sleep: int = option(default=1, help="Sleep in between runs")
19
+
20
+
21
+ SETTINGS = load_settings(Settings, [LOADER])
22
+
23
+
24
+ __all__ = ["SETTINGS", "Settings"]
@@ -0,0 +1 @@
1
+ from __future__ import annotations
@@ -0,0 +1,43 @@
1
+ from __future__ import annotations
2
+
3
+ from rich.pretty import pretty_repr
4
+ from typed_settings import click_options
5
+ from utilities.logging import basic_config
6
+ from utilities.os import is_pytest
7
+ from utilities.text import strip_and_dedent
8
+
9
+ from actions import __version__
10
+ from actions.logging import LOGGER
11
+ from actions.setup_cronjob.lib import setup_cronjob
12
+ from actions.setup_cronjob.settings import Settings
13
+ from actions.utilities import LOADER
14
+
15
+
16
+ @click_options(Settings, [LOADER], show_envvars_in_help=True)
17
+ def setup_cronjob_sub_cmd(settings: Settings, /) -> None:
18
+ if is_pytest():
19
+ return
20
+ basic_config(obj=LOGGER)
21
+ LOGGER.info(
22
+ strip_and_dedent("""
23
+ Running '%s' (version %s) with settings:
24
+ %s
25
+ """),
26
+ setup_cronjob.__name__,
27
+ __version__,
28
+ pretty_repr(settings),
29
+ )
30
+ setup_cronjob(
31
+ name=settings.name,
32
+ prepend_path=settings.prepend_path,
33
+ schedule=settings.schedule,
34
+ user=settings.user,
35
+ timeout=settings.timeout,
36
+ kill_after=settings.kill_after,
37
+ command=settings.command,
38
+ args=settings.args,
39
+ logs_keep=settings.logs_keep,
40
+ )
41
+
42
+
43
+ __all__ = ["setup_cronjob_sub_cmd"]
@@ -0,0 +1,3 @@
1
+ PATH=${PREPEND_PATH}/usr/local/bin:/usr/bin:/bin
2
+
3
+ ${SCHEDULE} ${USER} (echo "[$$(date '+\%Y-\%m-\%d \%H:\%M:\%S') | $$$$] Starting '${NAME}'..."; flock --nonblock --verbose /tmp/cron-${NAME}.lock timeout --kill-after=${KILL_AFTER}s --verbose ${TIMEOUT}s ${COMMAND}${SPACE}${ARGS}; echo "[$$(date '+\%Y-\%m-\%d \%H:\%M:\%S') | $$$$] Finished '${NAME}' with exit code $$?") 2>&1 | sudo tee -a /var/log/${NAME}.log
@@ -0,0 +1,10 @@
1
+ /var/log/${NAME}.log {
2
+ compress
3
+ daily
4
+ dateext
5
+ dateformat -%Y-%m-%d
6
+ delaycompress
7
+ missingok
8
+ notifempty
9
+ rotate ${ROTATE}
10
+ }
@@ -0,0 +1,8 @@
1
+ from __future__ import annotations
2
+
3
+ from actions.constants import PATH_ACTIONS
4
+
5
+ PATH_CONFIGS = PATH_ACTIONS / "setup_cronjob/configs"
6
+
7
+
8
+ __all__ = ["PATH_CONFIGS"]
@@ -0,0 +1,120 @@
1
+ from __future__ import annotations
2
+
3
+ from string import Template
4
+ from typing import TYPE_CHECKING
5
+
6
+ from utilities.platform import SYSTEM
7
+ from utilities.subprocess import chmod, chown, tee
8
+ from utilities.text import strip_and_dedent
9
+
10
+ from actions import __version__
11
+ from actions.logging import LOGGER
12
+ from actions.setup_cronjob.constants import PATH_CONFIGS
13
+ from actions.setup_cronjob.settings import SETTINGS
14
+
15
+ if TYPE_CHECKING:
16
+ from collections.abc import Sequence
17
+
18
+ from utilities.types import PathLike
19
+
20
+
21
+ def setup_cronjob(
22
+ *,
23
+ name: str = SETTINGS.name,
24
+ prepend_path: Sequence[PathLike] | None = SETTINGS.prepend_path,
25
+ schedule: str = SETTINGS.schedule,
26
+ user: str = SETTINGS.user,
27
+ timeout: int = SETTINGS.timeout,
28
+ kill_after: int = SETTINGS.kill_after,
29
+ command: PathLike = SETTINGS.command,
30
+ args: list[str] | None = SETTINGS.args,
31
+ logs_keep: int = SETTINGS.logs_keep,
32
+ ) -> None:
33
+ """Set up a cronjob & logrotate."""
34
+ LOGGER.info(
35
+ strip_and_dedent("""
36
+ Running '%s' (version %s) with settings:
37
+ - name = %s
38
+ - prepend_path = %s
39
+ - schedule = %s
40
+ - user = %s
41
+ - timeout = %d
42
+ - kill_after = %d
43
+ - command = %s
44
+ - args = %s
45
+ - logs_keep = %d
46
+ """),
47
+ setup_cronjob.__name__,
48
+ __version__,
49
+ name,
50
+ prepend_path,
51
+ schedule,
52
+ user,
53
+ timeout,
54
+ kill_after,
55
+ command,
56
+ args,
57
+ logs_keep,
58
+ )
59
+ if SYSTEM != "linux":
60
+ msg = f"System must be 'linux'; got {SYSTEM!r}"
61
+ raise TypeError(msg)
62
+ _tee_and_perms(
63
+ f"/etc/cron.d/{name}",
64
+ _get_crontab(
65
+ prepend_path=prepend_path,
66
+ schedule=schedule,
67
+ user=user,
68
+ name=name,
69
+ timeout=timeout,
70
+ kill_after=kill_after,
71
+ command=command,
72
+ args=args,
73
+ ),
74
+ )
75
+ _tee_and_perms(
76
+ f"/etc/logrotate.d/{name}", _get_logrotate(name=name, logs_keep=logs_keep)
77
+ )
78
+
79
+
80
+ def _get_crontab(
81
+ *,
82
+ prepend_path: Sequence[PathLike] | None = SETTINGS.prepend_path,
83
+ schedule: str = SETTINGS.schedule,
84
+ user: str = SETTINGS.user,
85
+ name: str = SETTINGS.name,
86
+ timeout: int = SETTINGS.timeout,
87
+ kill_after: int = SETTINGS.kill_after,
88
+ command: PathLike | None = SETTINGS.command,
89
+ args: list[str] | None = SETTINGS.args,
90
+ ) -> str:
91
+ return Template((PATH_CONFIGS / "cron.tmpl").read_text()).substitute(
92
+ PREPEND_PATH=""
93
+ if prepend_path is None
94
+ else "".join(f"{p}:" for p in prepend_path),
95
+ SCHEDULE=schedule,
96
+ USER=user,
97
+ NAME=name,
98
+ TIMEOUT=timeout,
99
+ KILL_AFTER=kill_after,
100
+ COMMAND=command,
101
+ SPACE=" " if (args is not None) and (len(args) >= 1) else "",
102
+ ARGS="" if args is None else " ".join(args),
103
+ )
104
+
105
+
106
+ def _get_logrotate(
107
+ *, name: str = SETTINGS.name, logs_keep: int = SETTINGS.logs_keep
108
+ ) -> str:
109
+ return Template((PATH_CONFIGS / "logrotate.tmpl").read_text()).substitute(
110
+ NAME=name, ROTATE=logs_keep
111
+ )
112
+
113
+
114
+ def _tee_and_perms(path: PathLike, text: str, /) -> None:
115
+ tee(path, text, sudo=True)
116
+ chown(path, sudo=True, user="root", group="root")
117
+ chmod(path, "u=rw,g=r,o=r", sudo=True)
118
+
119
+
120
+ __all__ = ["setup_cronjob"]
@@ -0,0 +1,27 @@
1
+ from __future__ import annotations
2
+
3
+ from typed_settings import load_settings, option, settings
4
+ from utilities.getpass import USER
5
+
6
+ from actions.utilities import LOADER
7
+
8
+
9
+ @settings
10
+ class Settings:
11
+ name: str = option(default="name", help="Cron job name")
12
+ prepend_path: list[str] | None = option(default=None, help="Paths to prepend")
13
+ schedule: str = option(default="* * * * *", help="Cron job schedule")
14
+ user: str = option(default=USER, help="Cron job user")
15
+ timeout: int = option(default=60, help="Seconds until timing-out the cron job")
16
+ kill_after: int = option(
17
+ default=10, help="Seconds until killing the cron job (after timeout)"
18
+ )
19
+ command: str = option(default="true", help="Command or executable script")
20
+ args: list[str] | None = option(default=None, help="Command arguments")
21
+ logs_keep: int = option(default=7, help="Number of logs to keep")
22
+
23
+
24
+ SETTINGS = load_settings(Settings, [LOADER])
25
+
26
+
27
+ __all__ = ["LOADER", "SETTINGS", "Settings"]
@@ -0,0 +1 @@
1
+ from __future__ import annotations
@@ -8,27 +8,23 @@ from utilities.text import strip_and_dedent
8
8
 
9
9
  from actions import __version__
10
10
  from actions.logging import LOGGER
11
- from actions.settings import CommonSettings
12
- from actions.tag.lib import tag_commit
13
- from actions.tag.settings import TagSettings
11
+ from actions.tag_commit.lib import tag_commit
12
+ from actions.tag_commit.settings import Settings
14
13
  from actions.utilities import LOADER
15
14
 
16
15
 
17
- @click_options(CommonSettings, [LOADER], show_envvars_in_help=True, argname="common")
18
- @click_options(TagSettings, [LOADER], show_envvars_in_help=True, argname="tag")
19
- def tag_sub_cmd(*, common: CommonSettings, tag: TagSettings) -> None:
16
+ @click_options(Settings, [LOADER], show_envvars_in_help=True, argname="tag")
17
+ def tag_commit_sub_cmd(*, tag: Settings) -> None:
20
18
  if is_pytest():
21
19
  return
22
20
  basic_config(obj=LOGGER)
23
21
  LOGGER.info(
24
22
  strip_and_dedent("""
25
- Running '%r' (version %s) with settings:
26
- %s
23
+ Running '%s' (version %s) with settings:
27
24
  %s
28
25
  """),
29
26
  tag_commit.__name__,
30
27
  __version__,
31
- pretty_repr(common),
32
28
  pretty_repr(tag),
33
29
  )
34
30
  tag_commit(
@@ -40,4 +36,4 @@ def tag_sub_cmd(*, common: CommonSettings, tag: TagSettings) -> None:
40
36
  )
41
37
 
42
38
 
43
- __all__ = ["tag_sub_cmd"]
39
+ __all__ = ["tag_commit_sub_cmd"]
@@ -0,0 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+ DOCSTRING = "Tag the latest commit"
4
+
5
+
6
+ __all__ = ["DOCSTRING"]
@@ -0,0 +1,63 @@
1
+ from __future__ import annotations
2
+
3
+ from contextlib import suppress
4
+ from subprocess import CalledProcessError
5
+
6
+ from utilities.text import strip_and_dedent
7
+ from utilities.version import parse_version
8
+
9
+ from actions import __version__
10
+ from actions.logging import LOGGER
11
+ from actions.tag_commit.settings import SETTINGS
12
+ from actions.utilities import log_run
13
+
14
+
15
+ def tag_commit(
16
+ *,
17
+ user_name: str = SETTINGS.user_name,
18
+ user_email: str = SETTINGS.user_email,
19
+ major_minor: bool = SETTINGS.major_minor,
20
+ major: bool = SETTINGS.major,
21
+ latest: bool = SETTINGS.latest,
22
+ ) -> None:
23
+ LOGGER.info(
24
+ strip_and_dedent("""
25
+ Running '%s' (version %s) with settings:
26
+ - user_name = %s
27
+ - user_email = %s
28
+ - major_minor = %s
29
+ - major = %s
30
+ - latest = %s
31
+ """),
32
+ tag_commit.__name__,
33
+ __version__,
34
+ user_name,
35
+ user_email,
36
+ major_minor,
37
+ major,
38
+ latest,
39
+ )
40
+ log_run("git", "config", "--global", "user.name", user_name)
41
+ log_run("git", "config", "--global", "user.email", user_email)
42
+ version = parse_version(
43
+ log_run("bump-my-version", "show", "current_version", return_=True)
44
+ )
45
+ _tag(str(version))
46
+ if major_minor:
47
+ _tag(f"{version.major}.{version.minor}")
48
+ if major:
49
+ _tag(str(version.major))
50
+ if latest:
51
+ _tag("latest")
52
+
53
+
54
+ def _tag(version: str, /) -> None:
55
+ with suppress(CalledProcessError):
56
+ log_run("git", "tag", "--delete", version)
57
+ with suppress(CalledProcessError):
58
+ log_run("git", "push", "--delete", "origin", version)
59
+ log_run("git", "tag", "-a", version, "HEAD", "-m", version)
60
+ log_run("git", "push", "--tags", "--force", "--set-upstream", "origin")
61
+
62
+
63
+ __all__ = ["tag_commit"]
@@ -6,7 +6,7 @@ from actions.utilities import LOADER
6
6
 
7
7
 
8
8
  @settings
9
- class TagSettings:
9
+ class Settings:
10
10
  user_name: str = option(default="github-actions-bot", help="'git' user name")
11
11
  user_email: str = option(default="noreply@github.com", help="'git' user email")
12
12
  major_minor: bool = option(default=False, help="Add the 'major.minor' tag")
@@ -14,7 +14,7 @@ class TagSettings:
14
14
  latest: bool = option(default=False, help="Add the 'latest' tag")
15
15
 
16
16
 
17
- TAG_SETTINGS = load_settings(TagSettings, [LOADER])
17
+ SETTINGS = load_settings(Settings, [LOADER])
18
18
 
19
19
 
20
- __all__ = ["TAG_SETTINGS", "TagSettings"]
20
+ __all__ = ["SETTINGS", "Settings"]
actions/types.py CHANGED
@@ -1,8 +1,18 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ from tomlkit.items import Array, Table
3
6
  from typed_settings import Secret
4
7
 
8
+ if TYPE_CHECKING:
9
+ from tomlkit.container import Container
10
+
11
+
12
+ type HasAppend = Array | list[Any]
13
+ type HasSetDefault = Container | StrDict | Table
5
14
  type SecretLike = str | Secret[str]
15
+ type StrDict = dict[str, Any]
6
16
 
7
17
 
8
- __all__ = ["SecretLike"]
18
+ __all__ = ["HasAppend", "HasSetDefault", "SecretLike", "StrDict"]
actions/utilities.py CHANGED
@@ -1,50 +1,110 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Literal, overload
3
+ from typing import TYPE_CHECKING, Literal, assert_never, overload
4
4
 
5
- from typed_settings import EnvLoader
5
+ from typed_settings import EnvLoader, Secret
6
6
  from utilities.subprocess import run
7
7
 
8
8
  from actions.logging import LOGGER
9
9
 
10
10
  if TYPE_CHECKING:
11
+ from utilities.types import StrStrMapping
12
+
11
13
  from actions.types import SecretLike
12
14
 
13
15
 
14
16
  LOADER = EnvLoader("")
15
17
 
16
18
 
17
- def empty_str_to_none(text: str, /) -> str | None:
18
- return None if text == "" else text
19
+ def convert_list_strs(
20
+ x: str | list[str] | tuple[str, ...] | None, /
21
+ ) -> list[str] | None:
22
+ match x:
23
+ case None:
24
+ return None
25
+ case list():
26
+ return x
27
+ case tuple():
28
+ return None if x == () else list(x)
29
+ case str():
30
+ return x.splitlines()
31
+ case never:
32
+ assert_never(never)
33
+
34
+
35
+ def convert_secret_str(x: SecretLike | None, /) -> Secret[str] | None:
36
+ match x:
37
+ case Secret():
38
+ match x.get_secret_value():
39
+ case None:
40
+ return None
41
+ case str() as inner:
42
+ return None if inner == "" else Secret(inner)
43
+ case never:
44
+ assert_never(never)
45
+ case str():
46
+ return None if x == "" else Secret(x)
47
+ case None:
48
+ return None
49
+ case never:
50
+ assert_never(never)
51
+
52
+
53
+ def convert_str(x: str | None, /) -> str | None:
54
+ match x:
55
+ case str():
56
+ return None if x == "" else x
57
+ case None:
58
+ return None
59
+ case never:
60
+ assert_never(never)
19
61
 
20
62
 
21
63
  @overload
22
64
  def log_run(
23
- cmd: SecretLike, /, *cmds: SecretLike, print: bool = False, return_: Literal[True]
65
+ cmd: SecretLike,
66
+ /,
67
+ *cmds: SecretLike,
68
+ env: StrStrMapping | None = None,
69
+ print: bool = False,
70
+ return_: Literal[True],
24
71
  ) -> str: ...
25
72
  @overload
26
73
  def log_run(
27
74
  cmd: SecretLike,
28
75
  /,
29
76
  *cmds: SecretLike,
77
+ env: StrStrMapping | None = None,
30
78
  print: bool = False,
31
79
  return_: Literal[False] = False,
32
80
  ) -> None: ...
33
81
  @overload
34
82
  def log_run(
35
- cmd: SecretLike, /, *cmds: SecretLike, print: bool = False, return_: bool = False
83
+ cmd: SecretLike,
84
+ /,
85
+ *cmds: SecretLike,
86
+ env: StrStrMapping | None = None,
87
+ print: bool = False,
88
+ return_: bool = False,
36
89
  ) -> str | None: ...
37
90
  def log_run(
38
91
  cmd: SecretLike,
39
92
  /,
40
93
  *cmds: SecretLike,
94
+ env: StrStrMapping | None = None,
41
95
  print: bool = False, # noqa: A002
42
96
  return_: bool = False,
43
97
  ) -> str | None:
44
98
  all_cmds = [cmd, *cmds]
45
99
  LOGGER.info("Running '%s'...", " ".join(map(str, all_cmds)))
46
100
  unwrapped = [c if isinstance(c, str) else c.get_secret_value() for c in all_cmds]
47
- return run(*unwrapped, print=print, return_=return_)
101
+ return run(*unwrapped, env=env, print=print, return_=return_, logger=LOGGER)
48
102
 
49
103
 
50
- __all__ = ["LOADER", "empty_str_to_none", "log_run"]
104
+ __all__ = [
105
+ "LOADER",
106
+ "convert_list_strs",
107
+ "convert_secret_str",
108
+ "convert_str",
109
+ "log_run",
110
+ ]
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.3
2
+ Name: dycw-actions
3
+ Version: 0.6.4
4
+ Summary: Library of actions
5
+ Requires-Dist: click>=8.3.1,<9
6
+ Requires-Dist: dycw-utilities>=0.176.3,<1
7
+ Requires-Dist: inflect>=7.5.0,<8
8
+ Requires-Dist: libcst>=1.8.6,<2
9
+ Requires-Dist: packaging>=25.0,<26
10
+ Requires-Dist: pyyaml>=6.0.3,<7
11
+ Requires-Dist: rich>=14.2.0,<15
12
+ Requires-Dist: ruamel-yaml>=0.19.0,<1
13
+ Requires-Dist: tomlkit>=0.13.3,<1
14
+ Requires-Dist: typed-settings[attrs,click]>=25.3.0,<26
15
+ Requires-Dist: xdg-base-dirs>=6.0.2,<7
16
+ Requires-Python: >=3.12
17
+ Description-Content-Type: text/markdown
18
+
19
+ # `actions`
20
+
21
+ Library of actions