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.
- actions/__init__.py +1 -1
- actions/action_dicts/constants.py +8 -0
- actions/action_dicts/lib.py +186 -0
- actions/clean_dir/cli.py +33 -0
- actions/clean_dir/lib.py +59 -0
- actions/clean_dir/settings.py +18 -0
- actions/cli.py +44 -6
- actions/conformalize_repo/cli.py +76 -0
- actions/conformalize_repo/configs/gitignore +244 -0
- actions/conformalize_repo/constants.py +72 -0
- actions/conformalize_repo/lib.py +1522 -0
- actions/conformalize_repo/settings.py +119 -0
- actions/constants.py +10 -0
- actions/format_requirements/__init__.py +1 -0
- actions/format_requirements/cli.py +37 -0
- actions/format_requirements/lib.py +121 -0
- actions/publish_package/__init__.py +1 -0
- actions/publish_package/cli.py +39 -0
- actions/publish_package/doc.py +6 -0
- actions/{publish → publish_package}/lib.py +17 -16
- actions/publish_package/settings.py +31 -0
- actions/random_sleep/__init__.py +1 -0
- actions/random_sleep/cli.py +35 -0
- actions/random_sleep/doc.py +6 -0
- actions/{sleep → random_sleep}/lib.py +14 -13
- actions/{sleep → random_sleep}/settings.py +4 -4
- actions/replace_sequence_strs/__init__.py +1 -0
- actions/replace_sequence_strs/cli.py +37 -0
- actions/replace_sequence_strs/lib.py +79 -0
- actions/run_hooks/__init__.py +1 -0
- actions/run_hooks/cli.py +33 -0
- actions/run_hooks/doc.py +6 -0
- actions/run_hooks/lib.py +97 -0
- actions/run_hooks/settings.py +24 -0
- actions/setup_cronjob/__init__.py +1 -0
- actions/setup_cronjob/cli.py +43 -0
- actions/setup_cronjob/configs/cron.tmpl +3 -0
- actions/setup_cronjob/configs/logrotate.tmpl +10 -0
- actions/setup_cronjob/constants.py +8 -0
- actions/setup_cronjob/lib.py +120 -0
- actions/setup_cronjob/settings.py +27 -0
- actions/tag_commit/__init__.py +1 -0
- actions/tag_commit/cli.py +39 -0
- actions/tag_commit/doc.py +6 -0
- actions/tag_commit/lib.py +63 -0
- actions/{tag → tag_commit}/settings.py +4 -4
- actions/types.py +18 -0
- actions/utilities.py +97 -10
- dycw_actions-0.6.4.dist-info/METADATA +21 -0
- dycw_actions-0.6.4.dist-info/RECORD +56 -0
- {dycw_actions-0.2.2.dist-info → dycw_actions-0.6.4.dist-info}/WHEEL +1 -1
- actions/publish/cli.py +0 -45
- actions/publish/settings.py +0 -35
- actions/settings.py +0 -19
- actions/sleep/cli.py +0 -39
- actions/tag/cli.py +0 -43
- actions/tag/lib.py +0 -60
- dycw_actions-0.2.2.dist-info/METADATA +0 -14
- dycw_actions-0.2.2.dist-info/RECORD +0 -21
- /actions/{publish → action_dicts}/__init__.py +0 -0
- /actions/{sleep → clean_dir}/__init__.py +0 -0
- /actions/{tag → conformalize_repo}/__init__.py +0 -0
- {dycw_actions-0.2.2.dist-info → dycw_actions-0.6.4.dist-info}/entry_points.txt +0 -0
actions/run_hooks/cli.py
ADDED
|
@@ -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"]
|
actions/run_hooks/doc.py
ADDED
actions/run_hooks/lib.py
ADDED
|
@@ -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,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,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
|
|
5
|
+
from actions.utilities import LOADER
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
@settings
|
|
9
|
-
class
|
|
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
|
-
|
|
17
|
+
SETTINGS = load_settings(Settings, [LOADER])
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
__all__ = ["
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|