winipedia-utils 0.2.63__py3-none-any.whl → 0.3.9__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.
- winipedia_utils/concurrent/concurrent.py +7 -2
- winipedia_utils/concurrent/multiprocessing.py +1 -2
- winipedia_utils/concurrent/multithreading.py +2 -2
- winipedia_utils/git/gitignore/config.py +54 -0
- winipedia_utils/git/gitignore/gitignore.py +5 -63
- winipedia_utils/git/pre_commit/config.py +45 -58
- winipedia_utils/git/pre_commit/hooks.py +13 -13
- winipedia_utils/git/pre_commit/run_hooks.py +2 -2
- winipedia_utils/git/workflows/base/base.py +109 -52
- winipedia_utils/git/workflows/publish.py +27 -50
- winipedia_utils/git/workflows/release.py +22 -48
- winipedia_utils/iterating/iterate.py +59 -1
- winipedia_utils/modules/class_.py +60 -10
- winipedia_utils/modules/function.py +18 -1
- winipedia_utils/modules/package.py +16 -7
- winipedia_utils/projects/poetry/config.py +122 -110
- winipedia_utils/projects/poetry/poetry.py +1 -8
- winipedia_utils/projects/project.py +7 -13
- winipedia_utils/setup.py +11 -29
- winipedia_utils/testing/config.py +95 -0
- winipedia_utils/testing/create_tests.py +4 -18
- winipedia_utils/testing/skip.py +10 -0
- winipedia_utils/testing/tests/base/fixtures/scopes/class_.py +3 -3
- winipedia_utils/testing/tests/base/fixtures/scopes/module.py +6 -4
- winipedia_utils/testing/tests/base/fixtures/scopes/session.py +28 -176
- winipedia_utils/testing/tests/base/utils/utils.py +11 -55
- winipedia_utils/text/config.py +143 -0
- winipedia_utils-0.3.9.dist-info/METADATA +324 -0
- {winipedia_utils-0.2.63.dist-info → winipedia_utils-0.3.9.dist-info}/RECORD +31 -28
- winipedia_utils/consts.py +0 -21
- winipedia_utils-0.2.63.dist-info/METADATA +0 -738
- {winipedia_utils-0.2.63.dist-info → winipedia_utils-0.3.9.dist-info}/WHEEL +0 -0
- {winipedia_utils-0.2.63.dist-info → winipedia_utils-0.3.9.dist-info}/licenses/LICENSE +0 -0
|
@@ -20,8 +20,6 @@ from typing import TYPE_CHECKING, Any, cast
|
|
|
20
20
|
|
|
21
21
|
from tqdm import tqdm
|
|
22
22
|
|
|
23
|
-
from winipedia_utils.concurrent.multiprocessing import get_spwan_pool
|
|
24
|
-
from winipedia_utils.concurrent.multithreading import imap_unordered
|
|
25
23
|
from winipedia_utils.iterating.iterate import get_len_with_default
|
|
26
24
|
from winipedia_utils.logging.logger import get_logger
|
|
27
25
|
|
|
@@ -210,6 +208,13 @@ def concurrent_loop( # noqa: PLR0913
|
|
|
210
208
|
Returns:
|
|
211
209
|
list[Any]: Results from the process_function executions
|
|
212
210
|
"""
|
|
211
|
+
from winipedia_utils.concurrent.multiprocessing import ( # noqa: PLC0415 # avoid circular import
|
|
212
|
+
get_spwan_pool,
|
|
213
|
+
)
|
|
214
|
+
from winipedia_utils.concurrent.multithreading import ( # noqa: PLC0415 # avoid circular import
|
|
215
|
+
imap_unordered,
|
|
216
|
+
)
|
|
217
|
+
|
|
213
218
|
process_args_len = get_len_with_default(process_args, process_args_len)
|
|
214
219
|
process_args = generate_process_args(
|
|
215
220
|
process_function=process_function,
|
|
@@ -15,6 +15,7 @@ from functools import wraps
|
|
|
15
15
|
from multiprocessing.pool import Pool
|
|
16
16
|
from typing import Any
|
|
17
17
|
|
|
18
|
+
from winipedia_utils.concurrent.concurrent import concurrent_loop
|
|
18
19
|
from winipedia_utils.logging.logger import get_logger
|
|
19
20
|
|
|
20
21
|
logger = get_logger(__name__)
|
|
@@ -118,8 +119,6 @@ def multiprocess_loop(
|
|
|
118
119
|
Also given functions must be pickle-able.
|
|
119
120
|
|
|
120
121
|
"""
|
|
121
|
-
from winipedia_utils.concurrent.concurrent import concurrent_loop
|
|
122
|
-
|
|
123
122
|
return concurrent_loop(
|
|
124
123
|
threading=False,
|
|
125
124
|
process_function=process_function,
|
|
@@ -15,6 +15,8 @@ from collections.abc import Callable, Generator, Iterable
|
|
|
15
15
|
from concurrent.futures import Future, ThreadPoolExecutor, as_completed
|
|
16
16
|
from typing import Any
|
|
17
17
|
|
|
18
|
+
from winipedia_utils.concurrent.concurrent import concurrent_loop
|
|
19
|
+
|
|
18
20
|
|
|
19
21
|
def get_future_results_as_completed(
|
|
20
22
|
futures: Iterable[Future[Any]],
|
|
@@ -62,8 +64,6 @@ def multithread_loop(
|
|
|
62
64
|
ThreadPoolExecutor is used for I/O-bound tasks, not for CPU-bound tasks.
|
|
63
65
|
|
|
64
66
|
"""
|
|
65
|
-
from winipedia_utils.concurrent.concurrent import concurrent_loop
|
|
66
|
-
|
|
67
67
|
return concurrent_loop(
|
|
68
68
|
threading=True,
|
|
69
69
|
process_function=process_function,
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Config utilities for .gitignore."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from winipedia_utils.text.config import ConfigFile
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class GitIgnoreConfigFile(ConfigFile):
|
|
10
|
+
"""Config file for .gitignore."""
|
|
11
|
+
|
|
12
|
+
PATH = Path(".gitignore")
|
|
13
|
+
|
|
14
|
+
IGNORE_KEY = "ignore"
|
|
15
|
+
|
|
16
|
+
def get_path(self) -> Path:
|
|
17
|
+
"""Get the path to the config file."""
|
|
18
|
+
return self.PATH
|
|
19
|
+
|
|
20
|
+
def load(self) -> dict[str, Any]:
|
|
21
|
+
"""Load the config file."""
|
|
22
|
+
return self.load_static()
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def load_static(cls) -> dict[str, Any]:
|
|
26
|
+
"""Load the config file."""
|
|
27
|
+
paths = cls.PATH.read_text().splitlines()
|
|
28
|
+
return {cls.IGNORE_KEY: paths}
|
|
29
|
+
|
|
30
|
+
def dump(self, config: dict[str, Any]) -> None:
|
|
31
|
+
"""Dump the config file."""
|
|
32
|
+
patterns = config.get(self.IGNORE_KEY, [])
|
|
33
|
+
self.path.write_text("\n".join(patterns))
|
|
34
|
+
|
|
35
|
+
def get_configs(self) -> dict[str, Any]:
|
|
36
|
+
"""Get the config."""
|
|
37
|
+
from winipedia_utils.testing.config import ( # noqa: PLC0415 # avoid circular import
|
|
38
|
+
ExperimentConfigFile,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
needed = [
|
|
42
|
+
"__pycache__/",
|
|
43
|
+
".idea/",
|
|
44
|
+
".mypy_cache/",
|
|
45
|
+
".pytest_cache/",
|
|
46
|
+
".ruff_cache/",
|
|
47
|
+
".vscode/",
|
|
48
|
+
"dist/",
|
|
49
|
+
".git/", # for walk_os_skipping_gitignore_patterns func
|
|
50
|
+
ExperimentConfigFile.PATH.name, # for executing experimental code
|
|
51
|
+
]
|
|
52
|
+
existing = self.load()[self.IGNORE_KEY]
|
|
53
|
+
needed = [p for p in needed if p not in set(existing)]
|
|
54
|
+
return {self.IGNORE_KEY: existing + needed}
|
|
@@ -12,6 +12,7 @@ from pathlib import Path
|
|
|
12
12
|
|
|
13
13
|
import pathspec
|
|
14
14
|
|
|
15
|
+
from winipedia_utils.git.gitignore.config import GitIgnoreConfigFile
|
|
15
16
|
from winipedia_utils.logging.logger import get_logger
|
|
16
17
|
|
|
17
18
|
logger = get_logger(__name__)
|
|
@@ -37,7 +38,10 @@ def path_is_in_gitignore(relative_path: str | Path) -> bool:
|
|
|
37
38
|
if is_dir and not as_posix.endswith("/"):
|
|
38
39
|
as_posix += "/"
|
|
39
40
|
|
|
40
|
-
spec = pathspec.PathSpec.from_lines(
|
|
41
|
+
spec = pathspec.PathSpec.from_lines(
|
|
42
|
+
"gitwildmatch",
|
|
43
|
+
GitIgnoreConfigFile.load_static()[GitIgnoreConfigFile.IGNORE_KEY],
|
|
44
|
+
)
|
|
41
45
|
|
|
42
46
|
return spec.match_file(as_posix)
|
|
43
47
|
|
|
@@ -72,65 +76,3 @@ def walk_os_skipping_gitignore_patterns(
|
|
|
72
76
|
valid_dirs = [d for d in dirs if not path_is_in_gitignore(rel_root / d)]
|
|
73
77
|
|
|
74
78
|
yield rel_root, valid_dirs, valid_files
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def load_gitignore() -> list[str]:
|
|
78
|
-
"""Load the .gitignore file."""
|
|
79
|
-
gitignore_path = Path(".gitignore")
|
|
80
|
-
if not gitignore_path.exists():
|
|
81
|
-
gitignore_path.touch()
|
|
82
|
-
return gitignore_path.read_text().splitlines()
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def dump_gitignore(patterns: list[str]) -> None:
|
|
86
|
-
"""Dump the given patterns to a .gitignore file (overwrites it)."""
|
|
87
|
-
gitignore_path = Path(".gitignore")
|
|
88
|
-
gitignore_path.write_text("\n".join(patterns))
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
def add_patterns_to_gitignore(patterns: list[str]) -> None:
|
|
92
|
-
"""Add the given patterns to the .gitignore file."""
|
|
93
|
-
existing_patterns = load_gitignore()
|
|
94
|
-
new_patterns = [p for p in patterns if p not in existing_patterns]
|
|
95
|
-
if new_patterns:
|
|
96
|
-
logger.info("Adding patterns to .gitignore: %s", new_patterns)
|
|
97
|
-
dump_gitignore(existing_patterns + new_patterns)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def _get_gitignore_patterns() -> list[str]:
|
|
101
|
-
"""Get the patterns that should be in the .gitignore file.
|
|
102
|
-
|
|
103
|
-
Those are the patterns that should be in there when using winipedia_utils.
|
|
104
|
-
"""
|
|
105
|
-
return [
|
|
106
|
-
"__pycache__/",
|
|
107
|
-
".idea/",
|
|
108
|
-
".mypy_cache/",
|
|
109
|
-
".pytest_cache/",
|
|
110
|
-
".ruff_cache/",
|
|
111
|
-
".vscode/",
|
|
112
|
-
"dist/",
|
|
113
|
-
"test.py", # I use this for testing code
|
|
114
|
-
".git/", # ignore the .git folder for walk_os_skipping_gitignore_patterns func
|
|
115
|
-
]
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def _get_missing_patterns() -> list[str]:
|
|
119
|
-
"""Get the patterns that are in the .gitignore file but shouldn't be."""
|
|
120
|
-
needed_patterns = _get_gitignore_patterns()
|
|
121
|
-
existing_patterns = load_gitignore()
|
|
122
|
-
return [p for p in needed_patterns if p not in existing_patterns]
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
def _gitignore_is_correct() -> bool:
|
|
126
|
-
"""Check if the .gitignore file contains all the patterns it should."""
|
|
127
|
-
missing_patterns = _get_missing_patterns()
|
|
128
|
-
return not missing_patterns
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
def _add_package_patterns_to_gitignore() -> None:
|
|
132
|
-
"""Add any missing patterns to the .gitignore file."""
|
|
133
|
-
if _gitignore_is_correct():
|
|
134
|
-
return
|
|
135
|
-
missing_patterns = _get_missing_patterns()
|
|
136
|
-
add_patterns_to_gitignore(missing_patterns)
|
|
@@ -3,68 +3,55 @@
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
|
-
import
|
|
7
|
-
|
|
6
|
+
import winipedia_utils
|
|
7
|
+
from winipedia_utils.git.pre_commit import run_hooks
|
|
8
8
|
from winipedia_utils.logging.logger import get_logger
|
|
9
|
+
from winipedia_utils.modules.module import make_obj_importpath
|
|
9
10
|
from winipedia_utils.os.os import run_subprocess
|
|
10
11
|
from winipedia_utils.projects.poetry.poetry import POETRY_RUN_ARGS
|
|
12
|
+
from winipedia_utils.projects.project import make_name_from_package
|
|
13
|
+
from winipedia_utils.text.config import YamlConfigFile
|
|
11
14
|
|
|
12
15
|
logger = get_logger(__name__)
|
|
13
16
|
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def _add_package_hook_to_pre_commit_config() -> None:
|
|
57
|
-
"""Add the winipedia-utils hook to the pre-commit config."""
|
|
58
|
-
config = load_pre_commit_config()
|
|
59
|
-
package_hook_config = _get_pre_commit_config_dict()
|
|
60
|
-
# insert at the beginning of the list
|
|
61
|
-
if not _pre_commit_config_is_correct():
|
|
62
|
-
logger.info("Adding winipedia-utils hook to pre-commit config")
|
|
63
|
-
config["repos"] = [package_hook_config, *config.get("repos", [])]
|
|
64
|
-
dump_pre_commit_config(config)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def _pre_commit_install() -> None:
|
|
68
|
-
"""Install pre-commit."""
|
|
69
|
-
logger.info("Running pre-commit install")
|
|
70
|
-
run_subprocess([*POETRY_RUN_ARGS, "pre-commit", "install"], check=True)
|
|
18
|
+
class PreCommitConfigFile(YamlConfigFile):
|
|
19
|
+
"""Config file for pre-commit."""
|
|
20
|
+
|
|
21
|
+
PATH = Path(".pre-commit-config.yaml")
|
|
22
|
+
|
|
23
|
+
def __init__(self) -> None:
|
|
24
|
+
"""Init the file."""
|
|
25
|
+
super().__init__()
|
|
26
|
+
self.install()
|
|
27
|
+
|
|
28
|
+
def get_path(self) -> Path:
|
|
29
|
+
"""Get the path to the config file."""
|
|
30
|
+
return self.PATH
|
|
31
|
+
|
|
32
|
+
def get_configs(self) -> dict[str, Any]:
|
|
33
|
+
"""Get the config."""
|
|
34
|
+
hook_name = make_name_from_package(winipedia_utils, capitalize=False)
|
|
35
|
+
return {
|
|
36
|
+
"repos": [
|
|
37
|
+
{
|
|
38
|
+
"repo": "local",
|
|
39
|
+
"hooks": [
|
|
40
|
+
{
|
|
41
|
+
"id": hook_name,
|
|
42
|
+
"name": hook_name,
|
|
43
|
+
"entry": f"python -m {make_obj_importpath(run_hooks)}",
|
|
44
|
+
"language": "system",
|
|
45
|
+
"always_run": True,
|
|
46
|
+
"pass_filenames": False,
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
def install(cls) -> None:
|
|
55
|
+
"""Installs the pre commits in the config."""
|
|
56
|
+
logger.info("Running pre-commit install")
|
|
57
|
+
run_subprocess([*POETRY_RUN_ARGS, "pre-commit", "install"], check=True)
|
|
@@ -17,7 +17,7 @@ from winipedia_utils.projects.poetry.poetry import (
|
|
|
17
17
|
)
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
def
|
|
20
|
+
def patch_version() -> list[str | Path]:
|
|
21
21
|
"""Patch the version in pyproject.toml.
|
|
22
22
|
|
|
23
23
|
This function returns the input for subprocess.run() to patch the version
|
|
@@ -26,7 +26,7 @@ def _version_patch() -> list[str | Path]:
|
|
|
26
26
|
return [POETRY_PATH, "version", "patch"]
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
def
|
|
29
|
+
def add_version_patch_to_git() -> list[str | Path]:
|
|
30
30
|
"""Add the version patch to git.
|
|
31
31
|
|
|
32
32
|
This function returns the input for subprocess.run() to add the version
|
|
@@ -35,7 +35,7 @@ def _add_version_patch_to_git() -> list[str | Path]:
|
|
|
35
35
|
return [*POETRY_RUN_ARGS, "git", "add", "pyproject.toml"]
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
def
|
|
38
|
+
def update_package_manager() -> list[str | Path]:
|
|
39
39
|
"""Update the package manager.
|
|
40
40
|
|
|
41
41
|
This function returns the input for subprocess.run() to update the package
|
|
@@ -44,7 +44,7 @@ def _update_package_manager() -> list[str | Path]:
|
|
|
44
44
|
return [POETRY_PATH, "self", "update"]
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
def
|
|
47
|
+
def install_packages() -> list[str | Path]:
|
|
48
48
|
"""Install all dependencies.
|
|
49
49
|
|
|
50
50
|
This function returns the input for subprocess.run() to install all dependencies.
|
|
@@ -52,7 +52,7 @@ def _install_packages() -> list[str | Path]:
|
|
|
52
52
|
return [POETRY_PATH, "install"]
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
def
|
|
55
|
+
def update_packages() -> list[str | Path]:
|
|
56
56
|
"""Update all dependencies.
|
|
57
57
|
|
|
58
58
|
This function returns the input for subprocess.run() to update all dependencies.
|
|
@@ -60,7 +60,7 @@ def _update_packages() -> list[str | Path]:
|
|
|
60
60
|
return [POETRY_PATH, "update"]
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
def
|
|
63
|
+
def lock_dependencies() -> list[str | Path]:
|
|
64
64
|
"""Lock the dependencies.
|
|
65
65
|
|
|
66
66
|
This function returns the input for subprocess.run() to lock the dependencies.
|
|
@@ -68,7 +68,7 @@ def _lock_dependencies() -> list[str | Path]:
|
|
|
68
68
|
return [POETRY_PATH, "lock"]
|
|
69
69
|
|
|
70
70
|
|
|
71
|
-
def
|
|
71
|
+
def check_package_manager_configs() -> list[str | Path]:
|
|
72
72
|
"""Check that poetry.lock and pyproject.toml is up to date.
|
|
73
73
|
|
|
74
74
|
This function returns the input for subprocess.run() to check that poetry.lock
|
|
@@ -77,7 +77,7 @@ def _check_configurations() -> list[str | Path]:
|
|
|
77
77
|
return [POETRY_PATH, "check", "--strict"]
|
|
78
78
|
|
|
79
79
|
|
|
80
|
-
def
|
|
80
|
+
def create_tests() -> list[str | Path]:
|
|
81
81
|
"""Create all tests for the project.
|
|
82
82
|
|
|
83
83
|
This function returns the input for subprocess.run() to create all tests.
|
|
@@ -85,7 +85,7 @@ def _creating_tests() -> list[str | Path]:
|
|
|
85
85
|
return [*POETRY_RUN_PYTHON_ARGS, "-m", "winipedia_utils.testing.create_tests"]
|
|
86
86
|
|
|
87
87
|
|
|
88
|
-
def
|
|
88
|
+
def lint_code() -> list[str | Path]:
|
|
89
89
|
"""Check the code.
|
|
90
90
|
|
|
91
91
|
This function returns the input for subprocess.run() to lint the code.
|
|
@@ -94,7 +94,7 @@ def _linting() -> list[str | Path]:
|
|
|
94
94
|
return [*POETRY_RUN_RUFF_ARGS, "check", "--fix"]
|
|
95
95
|
|
|
96
96
|
|
|
97
|
-
def
|
|
97
|
+
def format_code() -> list[str | Path]:
|
|
98
98
|
"""Format the code.
|
|
99
99
|
|
|
100
100
|
This function calls ruff format to format the code.
|
|
@@ -102,7 +102,7 @@ def _formating() -> list[str | Path]:
|
|
|
102
102
|
return [*POETRY_RUN_RUFF_ARGS, "format"]
|
|
103
103
|
|
|
104
104
|
|
|
105
|
-
def
|
|
105
|
+
def check_static_types() -> list[str | Path]:
|
|
106
106
|
"""Check the types.
|
|
107
107
|
|
|
108
108
|
This function returns the input for subprocess.run() to check the static types.
|
|
@@ -110,7 +110,7 @@ def _type_checking() -> list[str | Path]:
|
|
|
110
110
|
return [*POETRY_RUN_ARGS, "mypy"]
|
|
111
111
|
|
|
112
112
|
|
|
113
|
-
def
|
|
113
|
+
def check_security() -> list[str | Path]:
|
|
114
114
|
"""Check the security of the code.
|
|
115
115
|
|
|
116
116
|
This function returns the input for subprocess.run() to check the security of
|
|
@@ -119,7 +119,7 @@ def _security_checking() -> list[str | Path]:
|
|
|
119
119
|
return [*POETRY_RUN_ARGS, "bandit", "-c", "pyproject.toml", "-r", "."]
|
|
120
120
|
|
|
121
121
|
|
|
122
|
-
def
|
|
122
|
+
def run_tests() -> list[str | Path]:
|
|
123
123
|
"""Run the tests.
|
|
124
124
|
|
|
125
125
|
This function returns the input for subprocess.run() to run all tests.
|
|
@@ -15,7 +15,7 @@ from winipedia_utils.os.os import run_subprocess
|
|
|
15
15
|
logger = get_logger(__name__)
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
def
|
|
18
|
+
def run_all() -> None:
|
|
19
19
|
"""Import all funcs defined in hooks.py and runs them."""
|
|
20
20
|
hook_funcs = get_all_functions_from_module(hooks)
|
|
21
21
|
|
|
@@ -46,4 +46,4 @@ def _run_all_hooks() -> None:
|
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
if __name__ == "__main__":
|
|
49
|
-
|
|
49
|
+
run_all()
|
|
@@ -1,38 +1,76 @@
|
|
|
1
1
|
"""Contains base utilities for git workflows."""
|
|
2
2
|
|
|
3
|
+
from abc import abstractmethod
|
|
3
4
|
from typing import Any
|
|
4
5
|
|
|
6
|
+
from winipedia_utils.text.config import YamlConfigFile
|
|
7
|
+
from winipedia_utils.text.string import split_on_uppercase
|
|
5
8
|
|
|
6
|
-
def _get_checkout_step(
|
|
7
|
-
fetch_depth: int | None = None,
|
|
8
|
-
) -> dict[str, Any]:
|
|
9
|
-
"""Get the checkout step.
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
class Workflow(YamlConfigFile):
|
|
11
|
+
"""Base class for workflows."""
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def get_workflow_triggers(self) -> dict[str, Any]:
|
|
15
|
+
"""Get the workflow triggers."""
|
|
16
|
+
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def get_permissions(self) -> dict[str, Any]:
|
|
19
|
+
"""Get the workflow permissions."""
|
|
20
|
+
|
|
21
|
+
@abstractmethod
|
|
22
|
+
def get_jobs(self) -> dict[str, Any]:
|
|
23
|
+
"""Get the workflow jobs."""
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def get_workflow_name(cls) -> str:
|
|
27
|
+
"""Get the workflow name."""
|
|
28
|
+
return " ".join(split_on_uppercase(cls.__name__))
|
|
29
|
+
|
|
30
|
+
def get_run_name(self) -> str:
|
|
31
|
+
"""Get the workflow run name."""
|
|
32
|
+
return self.get_workflow_name() + self.get_repo_and_ref_name_formatted()
|
|
33
|
+
|
|
34
|
+
def get_configs(self) -> dict[str, Any]:
|
|
35
|
+
"""Get the workflow config."""
|
|
36
|
+
return {
|
|
37
|
+
"name": self.get_workflow_name(),
|
|
38
|
+
"on": self.get_workflow_triggers(),
|
|
39
|
+
"permissions": self.get_permissions(),
|
|
40
|
+
"run-name": self.get_run_name(),
|
|
41
|
+
"jobs": self.get_jobs(),
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def get_checkout_step(cls, fetch_depth: int | None = None) -> dict[str, Any]:
|
|
46
|
+
"""Get the checkout step.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
12
49
|
fetch_depth: The fetch depth to use. If None, no fetch depth is specified.
|
|
13
50
|
|
|
14
|
-
|
|
51
|
+
Returns:
|
|
15
52
|
The checkout step.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
53
|
+
"""
|
|
54
|
+
step: dict[str, Any] = {
|
|
55
|
+
"name": "Checkout repository",
|
|
56
|
+
"uses": "actions/checkout@v5",
|
|
57
|
+
}
|
|
58
|
+
if fetch_depth is not None:
|
|
59
|
+
step["with"] = {"fetch-depth": fetch_depth}
|
|
60
|
+
return step
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
def get_poetry_setup_steps(
|
|
64
|
+
cls,
|
|
65
|
+
*,
|
|
66
|
+
install_dependencies: bool = False,
|
|
67
|
+
fetch_depth: int | None = None,
|
|
68
|
+
configure_pipy_token: bool = False,
|
|
69
|
+
force_main_head: bool = False,
|
|
70
|
+
) -> list[dict[str, Any]]:
|
|
71
|
+
"""Get the poetry steps.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
36
74
|
install_dependencies: Whether to install dependencies.
|
|
37
75
|
fetch_depth: The fetch depth to use. If None, no fetch depth is specified.
|
|
38
76
|
configure_pipy_token: Whether to configure the pipy token.
|
|
@@ -40,38 +78,57 @@ def _get_poetry_setup_steps(
|
|
|
40
78
|
equal to the most recent commit on main. This is useful for workflows that
|
|
41
79
|
should only run on main.
|
|
42
80
|
|
|
43
|
-
|
|
81
|
+
Returns:
|
|
44
82
|
The poetry steps.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
83
|
+
"""
|
|
84
|
+
steps = [cls.get_checkout_step(fetch_depth)]
|
|
85
|
+
if force_main_head:
|
|
86
|
+
# exit with code 1 if the running branch is not main
|
|
87
|
+
steps.append(
|
|
88
|
+
{
|
|
89
|
+
"name": "Assert running on head of main",
|
|
90
|
+
"run": 'git fetch origin main --depth=1; main_sha=$(git rev-parse origin/main); if [ "$GITHUB_SHA" != "$main_sha" ]; then echo "Tag commit is not the latest commit on main."; exit 1; fi', # noqa: E501
|
|
91
|
+
}
|
|
92
|
+
)
|
|
49
93
|
steps.append(
|
|
50
94
|
{
|
|
51
|
-
"name": "
|
|
52
|
-
"
|
|
95
|
+
"name": "Setup Python",
|
|
96
|
+
"uses": "actions/setup-python@v6",
|
|
97
|
+
"with": {"python-version": "3.x"},
|
|
53
98
|
}
|
|
54
99
|
)
|
|
55
|
-
steps.append(
|
|
56
|
-
{
|
|
57
|
-
"name": "Setup Python",
|
|
58
|
-
"uses": "actions/setup-python@v6",
|
|
59
|
-
"with": {"python-version": "3.x"},
|
|
60
|
-
}
|
|
61
|
-
)
|
|
62
|
-
steps.append(
|
|
63
|
-
{
|
|
64
|
-
"name": "Install Poetry",
|
|
65
|
-
"run": "curl -sSL https://install.python-poetry.org | python3 -",
|
|
66
|
-
}
|
|
67
|
-
)
|
|
68
|
-
if configure_pipy_token:
|
|
69
100
|
steps.append(
|
|
70
101
|
{
|
|
71
|
-
"name": "
|
|
72
|
-
"run": "
|
|
102
|
+
"name": "Install Poetry",
|
|
103
|
+
"run": "curl -sSL https://install.python-poetry.org | python3 -",
|
|
73
104
|
}
|
|
74
105
|
)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
106
|
+
if configure_pipy_token:
|
|
107
|
+
steps.append(
|
|
108
|
+
{
|
|
109
|
+
"name": "Configure Poetry",
|
|
110
|
+
"run": "poetry config pypi-token.pypi ${{ secrets.PYPI_TOKEN }}",
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
if install_dependencies:
|
|
114
|
+
steps.append({"name": "Install Dependencies", "run": "poetry install"})
|
|
115
|
+
return steps
|
|
116
|
+
|
|
117
|
+
@staticmethod
|
|
118
|
+
def get_repository_name() -> str:
|
|
119
|
+
"""Get the repository name."""
|
|
120
|
+
return "${{ github.event.repository.name }}"
|
|
121
|
+
|
|
122
|
+
def get_ref_name(self) -> str:
|
|
123
|
+
"""Get the ref name."""
|
|
124
|
+
return "${{ github.ref_name }}"
|
|
125
|
+
|
|
126
|
+
@staticmethod
|
|
127
|
+
def get_repo_and_ref_name() -> str:
|
|
128
|
+
"""Get the repository name and ref name."""
|
|
129
|
+
return "${{ github.event.repository.name }}-${{ github.ref_name }}"
|
|
130
|
+
|
|
131
|
+
@classmethod
|
|
132
|
+
def get_repo_and_ref_name_formatted(cls) -> str:
|
|
133
|
+
"""Get the repository name and ref name."""
|
|
134
|
+
return f": {cls.get_repo_and_ref_name()}"
|