dycw-actions 0.2.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 (63) 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_package/cli.py +39 -0
  19. actions/publish_package/doc.py +6 -0
  20. actions/{publish → publish_package}/lib.py +17 -16
  21. actions/publish_package/settings.py +31 -0
  22. actions/random_sleep/__init__.py +1 -0
  23. actions/random_sleep/cli.py +35 -0
  24. actions/random_sleep/doc.py +6 -0
  25. actions/{sleep → random_sleep}/lib.py +14 -13
  26. actions/{sleep → random_sleep}/settings.py +4 -4
  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_commit/cli.py +39 -0
  44. actions/tag_commit/doc.py +6 -0
  45. actions/tag_commit/lib.py +63 -0
  46. actions/{tag → tag_commit}/settings.py +4 -4
  47. actions/types.py +18 -0
  48. actions/utilities.py +97 -10
  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.2.2.dist-info → dycw_actions-0.6.4.dist-info}/WHEEL +1 -1
  52. actions/publish/cli.py +0 -45
  53. actions/publish/settings.py +0 -35
  54. actions/settings.py +0 -19
  55. actions/sleep/cli.py +0 -39
  56. actions/tag/cli.py +0 -43
  57. actions/tag/lib.py +0 -60
  58. dycw_actions-0.2.2.dist-info/METADATA +0 -14
  59. dycw_actions-0.2.2.dist-info/RECORD +0 -21
  60. /actions/{publish → action_dicts}/__init__.py +0 -0
  61. /actions/{sleep → clean_dir}/__init__.py +0 -0
  62. /actions/{tag → conformalize_repo}/__init__.py +0 -0
  63. {dycw_actions-0.2.2.dist-info → dycw_actions-0.6.4.dist-info}/entry_points.txt +0 -0
@@ -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
@@ -0,0 +1,39 @@
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.tag_commit.lib import tag_commit
12
+ from actions.tag_commit.settings import Settings
13
+ from actions.utilities import LOADER
14
+
15
+
16
+ @click_options(Settings, [LOADER], show_envvars_in_help=True, argname="tag")
17
+ def tag_commit_sub_cmd(*, tag: 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
+ tag_commit.__name__,
27
+ __version__,
28
+ pretty_repr(tag),
29
+ )
30
+ tag_commit(
31
+ user_name=tag.user_name,
32
+ user_email=tag.user_email,
33
+ major_minor=tag.major_minor,
34
+ major=tag.major,
35
+ latest=tag.latest,
36
+ )
37
+
38
+
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"]
@@ -2,11 +2,11 @@ from __future__ import annotations
2
2
 
3
3
  from typed_settings import load_settings, option, settings
4
4
 
5
- from actions.utilities import ENV_LOADER
5
+ 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, [ENV_LOADER])
17
+ SETTINGS = load_settings(Settings, [LOADER])
18
18
 
19
19
 
20
- __all__ = ["TAG_SETTINGS", "TagSettings"]
20
+ __all__ = ["SETTINGS", "Settings"]
actions/types.py ADDED
@@ -0,0 +1,18 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ from tomlkit.items import Array, Table
6
+ from typed_settings import Secret
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
14
+ type SecretLike = str | Secret[str]
15
+ type StrDict = dict[str, Any]
16
+
17
+
18
+ __all__ = ["HasAppend", "HasSetDefault", "SecretLike", "StrDict"]
actions/utilities.py CHANGED
@@ -1,23 +1,110 @@
1
1
  from __future__ import annotations
2
2
 
3
- from subprocess import check_output
3
+ from typing import TYPE_CHECKING, Literal, assert_never, overload
4
4
 
5
5
  from typed_settings import EnvLoader, Secret
6
+ from utilities.subprocess import run
6
7
 
7
8
  from actions.logging import LOGGER
8
9
 
9
- ENV_LOADER = EnvLoader("")
10
+ if TYPE_CHECKING:
11
+ from utilities.types import StrStrMapping
10
12
 
13
+ from actions.types import SecretLike
11
14
 
12
- def empty_str_to_none(text: str, /) -> str | None:
13
- return None if text == "" else text
14
15
 
16
+ LOADER = EnvLoader("")
15
17
 
16
- def log_run(*cmds: str | Secret[str]) -> str:
17
- LOGGER.info("Running '%s'...", " ".join(map(str, cmds)))
18
- return check_output(
19
- [c if isinstance(c, str) else c.get_secret_value() for c in cmds], text=True
20
- ).rstrip("\n")
21
18
 
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)
22
33
 
23
- __all__ = ["ENV_LOADER", "empty_str_to_none", "log_run"]
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)
61
+
62
+
63
+ @overload
64
+ def log_run(
65
+ cmd: SecretLike,
66
+ /,
67
+ *cmds: SecretLike,
68
+ env: StrStrMapping | None = None,
69
+ print: bool = False,
70
+ return_: Literal[True],
71
+ ) -> str: ...
72
+ @overload
73
+ def log_run(
74
+ cmd: SecretLike,
75
+ /,
76
+ *cmds: SecretLike,
77
+ env: StrStrMapping | None = None,
78
+ print: bool = False,
79
+ return_: Literal[False] = False,
80
+ ) -> None: ...
81
+ @overload
82
+ def log_run(
83
+ cmd: SecretLike,
84
+ /,
85
+ *cmds: SecretLike,
86
+ env: StrStrMapping | None = None,
87
+ print: bool = False,
88
+ return_: bool = False,
89
+ ) -> str | None: ...
90
+ def log_run(
91
+ cmd: SecretLike,
92
+ /,
93
+ *cmds: SecretLike,
94
+ env: StrStrMapping | None = None,
95
+ print: bool = False, # noqa: A002
96
+ return_: bool = False,
97
+ ) -> str | None:
98
+ all_cmds = [cmd, *cmds]
99
+ LOGGER.info("Running '%s'...", " ".join(map(str, all_cmds)))
100
+ unwrapped = [c if isinstance(c, str) else c.get_secret_value() for c in all_cmds]
101
+ return run(*unwrapped, env=env, print=print, return_=return_, logger=LOGGER)
102
+
103
+
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