winipedia-utils 0.3.41__py3-none-any.whl → 0.4.12__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.

Potentially problematic release.


This version of winipedia-utils might be problematic. Click here for more details.

Files changed (33) hide show
  1. winipedia_utils/git/github/repo/__init__.py +1 -0
  2. winipedia_utils/git/github/repo/protect.py +104 -0
  3. winipedia_utils/git/github/repo/repo.py +205 -0
  4. winipedia_utils/git/github/workflows/base/__init__.py +1 -0
  5. winipedia_utils/git/{workflows → github/workflows}/base/base.py +95 -51
  6. winipedia_utils/git/github/workflows/health_check.py +55 -0
  7. winipedia_utils/git/{workflows → github/workflows}/publish.py +11 -8
  8. winipedia_utils/git/github/workflows/release.py +45 -0
  9. winipedia_utils/git/gitignore/config.py +49 -29
  10. winipedia_utils/git/gitignore/gitignore.py +1 -1
  11. winipedia_utils/git/pre_commit/config.py +18 -13
  12. winipedia_utils/git/pre_commit/hooks.py +22 -4
  13. winipedia_utils/git/pre_commit/run_hooks.py +2 -1
  14. winipedia_utils/iterating/iterate.py +3 -4
  15. winipedia_utils/modules/module.py +2 -0
  16. winipedia_utils/modules/package.py +2 -1
  17. winipedia_utils/projects/poetry/config.py +74 -36
  18. winipedia_utils/projects/project.py +2 -2
  19. winipedia_utils/setup.py +2 -0
  20. winipedia_utils/testing/config.py +83 -29
  21. winipedia_utils/testing/tests/base/fixtures/fixture.py +36 -0
  22. winipedia_utils/testing/tests/base/fixtures/scopes/module.py +6 -5
  23. winipedia_utils/testing/tests/base/fixtures/scopes/session.py +7 -8
  24. winipedia_utils/testing/tests/base/utils/utils.py +43 -2
  25. winipedia_utils/text/config.py +84 -37
  26. {winipedia_utils-0.3.41.dist-info → winipedia_utils-0.4.12.dist-info}/METADATA +23 -8
  27. {winipedia_utils-0.3.41.dist-info → winipedia_utils-0.4.12.dist-info}/RECORD +31 -27
  28. winipedia_utils/git/workflows/health_check.py +0 -51
  29. winipedia_utils/git/workflows/release.py +0 -33
  30. /winipedia_utils/git/{workflows/base → github}/__init__.py +0 -0
  31. /winipedia_utils/git/{workflows → github/workflows}/__init__.py +0 -0
  32. {winipedia_utils-0.3.41.dist-info → winipedia_utils-0.4.12.dist-info}/WHEEL +0 -0
  33. {winipedia_utils-0.3.41.dist-info → winipedia_utils-0.4.12.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1 @@
1
+ """__init__ module."""
@@ -0,0 +1,104 @@
1
+ """Script to protect the repo and branches of a repository."""
2
+
3
+ from typing import Any
4
+
5
+ from winipedia_utils.git.github.repo.repo import (
6
+ DEFAULT_BRANCH,
7
+ DEFAULT_RULESET_NAME,
8
+ create_or_update_ruleset,
9
+ get_repo,
10
+ get_rules_payload,
11
+ )
12
+ from winipedia_utils.git.github.workflows.health_check import HealthCheckWorkflow
13
+ from winipedia_utils.modules.package import get_src_package
14
+ from winipedia_utils.projects.poetry.config import PyprojectConfigFile
15
+ from winipedia_utils.testing.tests.base.utils.utils import get_github_repo_token
16
+
17
+
18
+ def protect_repository() -> None:
19
+ """Protect the repository."""
20
+ set_secure_repo_settings()
21
+ create_or_update_default_branch_ruleset()
22
+
23
+
24
+ def set_secure_repo_settings() -> None:
25
+ """Set standard settings for the repository."""
26
+ src_pkg_name = get_src_package().__name__
27
+ owner = PyprojectConfigFile.get_main_author_name()
28
+ token = get_github_repo_token()
29
+ repo = get_repo(token, owner, src_pkg_name)
30
+
31
+ toml_description = PyprojectConfigFile.load()["project"]["description"]
32
+
33
+ repo.edit(
34
+ name=src_pkg_name,
35
+ description=toml_description,
36
+ default_branch=DEFAULT_BRANCH,
37
+ delete_branch_on_merge=True,
38
+ allow_update_branch=True,
39
+ allow_merge_commit=False,
40
+ allow_rebase_merge=True,
41
+ allow_squash_merge=True,
42
+ )
43
+
44
+
45
+ def create_or_update_default_branch_ruleset() -> None:
46
+ """Add a branch protection rule to the repository."""
47
+ create_or_update_ruleset(
48
+ **get_default_ruleset_params(),
49
+ )
50
+
51
+
52
+ def get_default_ruleset_params() -> dict[str, Any]:
53
+ """Get the default ruleset parameters."""
54
+ src_pkg_name = get_src_package().__name__
55
+ token = get_github_repo_token()
56
+
57
+ rules = get_rules_payload(
58
+ deletion={},
59
+ non_fast_forward={},
60
+ creation={},
61
+ update={},
62
+ pull_request={
63
+ "required_approving_review_count": 1,
64
+ "dismiss_stale_reviews_on_push": True,
65
+ "require_code_owner_review": True,
66
+ "require_last_push_approval": True,
67
+ "required_review_thread_resolution": True,
68
+ "automatic_copilot_code_review_enabled": False,
69
+ "allowed_merge_methods": ["merge", "squash", "rebase"],
70
+ },
71
+ required_linear_history={},
72
+ required_signatures={},
73
+ required_status_checks={
74
+ "strict_required_status_checks_policy": True,
75
+ "do_not_enforce_on_create": False,
76
+ "required_status_checks": [
77
+ {
78
+ "context": HealthCheckWorkflow.get_workflow_name(),
79
+ }
80
+ ],
81
+ },
82
+ )
83
+
84
+ return {
85
+ "owner": PyprojectConfigFile.get_main_author_name(),
86
+ "token": token,
87
+ "repo_name": src_pkg_name,
88
+ "ruleset_name": DEFAULT_RULESET_NAME,
89
+ "enforcement": "active",
90
+ "bypass_actors": [
91
+ {
92
+ "actor_id": 5,
93
+ "actor_type": "RepositoryRole",
94
+ "bypass_mode": "always",
95
+ }
96
+ ],
97
+ "target": "branch",
98
+ "conditions": {"ref_name": {"include": ["~DEFAULT_BRANCH"], "exclude": []}},
99
+ "rules": rules,
100
+ }
101
+
102
+
103
+ if __name__ == "__main__":
104
+ protect_repository()
@@ -0,0 +1,205 @@
1
+ """Contains utilities for working with GitHub repositories."""
2
+
3
+ from typing import Any, Literal
4
+
5
+ from github import Github
6
+ from github.Auth import Token
7
+ from github.Repository import Repository
8
+
9
+ from winipedia_utils.logging.logger import get_logger
10
+
11
+ logger = get_logger(__name__)
12
+
13
+ DEFAULT_BRANCH = "main"
14
+
15
+ DEFAULT_RULESET_NAME = f"{DEFAULT_BRANCH} protection"
16
+
17
+
18
+ def get_rules_payload( # noqa: PLR0913
19
+ *,
20
+ creation: dict[str, Any] | None = None,
21
+ update: dict[str, Any] | None = None,
22
+ deletion: dict[str, Any] | None = None,
23
+ required_linear_history: dict[str, Any] | None = None,
24
+ merge_queue: dict[str, Any] | None = None,
25
+ required_deployments: dict[str, Any] | None = None,
26
+ required_signatures: dict[str, Any] | None = None,
27
+ pull_request: dict[str, Any] | None = None,
28
+ required_status_checks: dict[str, Any] | None = None,
29
+ non_fast_forward: dict[str, Any] | None = None,
30
+ commit_message_pattern: dict[str, Any] | None = None,
31
+ commit_author_email_pattern: dict[str, Any] | None = None,
32
+ committer_email_pattern: dict[str, Any] | None = None,
33
+ branch_name_pattern: dict[str, Any] | None = None,
34
+ tag_name_pattern: dict[str, Any] | None = None,
35
+ file_path_restriction: dict[str, Any] | None = None,
36
+ max_file_path_length: dict[str, Any] | None = None,
37
+ file_extension_restriction: dict[str, Any] | None = None,
38
+ max_file_size: dict[str, Any] | None = None,
39
+ workflows: dict[str, Any] | None = None,
40
+ code_scanning: dict[str, Any] | None = None,
41
+ copilot_code_review: dict[str, Any] | None = None,
42
+ ) -> list[dict[str, Any]]:
43
+ """Build a rules array for a GitHub ruleset.
44
+
45
+ Args:
46
+ creation: Only allow users with bypass permission to create matching
47
+ refs.
48
+ update: Only allow users with bypass permission to update matching
49
+ refs.
50
+ deletion: Only allow users with bypass permissions to delete matching
51
+ refs.
52
+ required_linear_history: Prevent merge commits from being pushed to
53
+ matching refs.
54
+ merge_queue: Merges must be performed via a merge queue.
55
+ required_deployments: Choose which environments must be successfully
56
+ deployed to before refs can be pushed.
57
+ required_signatures: Commits pushed to matching refs must have verified
58
+ signatures.
59
+ pull_request: Require all commits be made to a non-target branch and
60
+ submitted via a pull request.
61
+ required_status_checks: Choose which status checks must pass before the
62
+ ref is updated.
63
+ non_fast_forward: Prevent users with push access from force pushing to
64
+ refs.
65
+ commit_message_pattern: Parameters to be used for the
66
+ commit_message_pattern rule.
67
+ commit_author_email_pattern: Parameters to be used for the
68
+ commit_author_email_pattern rule.
69
+ committer_email_pattern: Parameters to be used for the
70
+ committer_email_pattern rule.
71
+ branch_name_pattern: Parameters to be used for the branch_name_pattern
72
+ rule.
73
+ tag_name_pattern: Parameters to be used for the tag_name_pattern rule.
74
+ file_path_restriction: Prevent commits that include changes in
75
+ specified file and folder paths.
76
+ max_file_path_length: Prevent commits that include file paths that
77
+ exceed the specified character limit.
78
+ file_extension_restriction: Prevent commits that include files with
79
+ specified file extensions.
80
+ max_file_size: Prevent commits with individual files that exceed the
81
+ specified limit.
82
+ workflows: Require all changes made to a targeted branch to pass the
83
+ specified workflows.
84
+ code_scanning: Choose which tools must provide code scanning results
85
+ before the reference is updated.
86
+ copilot_code_review: Request Copilot code review for new pull requests
87
+ automatically.
88
+
89
+ Returns:
90
+ A list of rule objects to be used in a GitHub ruleset.
91
+ """
92
+ rules: list[dict[str, Any]] = []
93
+
94
+ rule_map = {
95
+ "creation": creation,
96
+ "update": update,
97
+ "deletion": deletion,
98
+ "required_linear_history": required_linear_history,
99
+ "merge_queue": merge_queue,
100
+ "required_deployments": required_deployments,
101
+ "required_signatures": required_signatures,
102
+ "pull_request": pull_request,
103
+ "required_status_checks": required_status_checks,
104
+ "non_fast_forward": non_fast_forward,
105
+ "commit_message_pattern": commit_message_pattern,
106
+ "commit_author_email_pattern": commit_author_email_pattern,
107
+ "committer_email_pattern": committer_email_pattern,
108
+ "branch_name_pattern": branch_name_pattern,
109
+ "tag_name_pattern": tag_name_pattern,
110
+ "file_path_restriction": file_path_restriction,
111
+ "max_file_path_length": max_file_path_length,
112
+ "file_extension_restriction": file_extension_restriction,
113
+ "max_file_size": max_file_size,
114
+ "workflows": workflows,
115
+ "code_scanning": code_scanning,
116
+ "copilot_code_review": copilot_code_review,
117
+ }
118
+
119
+ for rule_type, rule_config in rule_map.items():
120
+ if rule_config is not None:
121
+ rule_obj: dict[str, Any] = {"type": rule_type}
122
+ if rule_config: # If there are parameters
123
+ rule_obj["parameters"] = rule_config
124
+ rules.append(rule_obj)
125
+
126
+ return rules
127
+
128
+
129
+ def create_or_update_ruleset( # noqa: PLR0913
130
+ token: str,
131
+ owner: str,
132
+ repo_name: str,
133
+ *,
134
+ ruleset_name: str,
135
+ enforcement: Literal["active", "disabled", "evaluate"] = "active",
136
+ target: Literal["branch", "tag", "push"] = "branch",
137
+ bypass_actors: list[dict[str, Any]] | None = None,
138
+ conditions: dict[
139
+ Literal["ref_name"], dict[Literal["include", "exclude"], list[str]]
140
+ ]
141
+ | None = None,
142
+ rules: list[dict[str, Any]] | None = None,
143
+ ) -> Any:
144
+ """Create a ruleset for the repository."""
145
+ repo = get_repo(token, owner, repo_name)
146
+ ruleset_id = ruleset_exists(
147
+ token=token, owner=owner, repo_name=repo_name, ruleset_name=ruleset_name
148
+ )
149
+ method = "PUT" if ruleset_id else "POST"
150
+ url = f"{repo.url}/rulesets"
151
+
152
+ if ruleset_id:
153
+ url += f"/{ruleset_id}"
154
+
155
+ payload: dict[str, Any] = {
156
+ "name": ruleset_name,
157
+ "enforcement": enforcement,
158
+ "target": target,
159
+ "conditions": conditions,
160
+ "rules": rules,
161
+ }
162
+ if bypass_actors:
163
+ payload["bypass_actors"] = bypass_actors
164
+
165
+ _headers, res = repo._requester.requestJsonAndCheck( # noqa: SLF001
166
+ method,
167
+ url,
168
+ headers={
169
+ "Accept": "application/vnd.github+json",
170
+ "X-GitHub-Api-Version": "2022-11-28",
171
+ },
172
+ input=payload,
173
+ )
174
+
175
+ return res
176
+
177
+
178
+ def get_all_rulesets(token: str, owner: str, repo_name: str) -> Any:
179
+ """Get all rulesets for the repository."""
180
+ repo = get_repo(token, owner, repo_name)
181
+ url = f"{repo.url}/rulesets"
182
+ method = "GET"
183
+ _headers, res = repo._requester.requestJsonAndCheck( # noqa: SLF001
184
+ method,
185
+ url,
186
+ headers={
187
+ "Accept": "application/vnd.github+json",
188
+ "X-GitHub-Api-Version": "2022-11-28",
189
+ },
190
+ )
191
+ return res
192
+
193
+
194
+ def get_repo(token: str, owner: str, repo_name: str) -> Repository:
195
+ """Get the repository."""
196
+ auth = Token(token)
197
+ github = Github(auth=auth)
198
+ return github.get_repo(f"{owner}/{repo_name}")
199
+
200
+
201
+ def ruleset_exists(token: str, owner: str, repo_name: str, ruleset_name: str) -> int:
202
+ """Check if the main protection ruleset exists."""
203
+ rulesets = get_all_rulesets(token, owner, repo_name)
204
+ main_ruleset = next((rs for rs in rulesets if rs["name"] == ruleset_name), None)
205
+ return main_ruleset["id"] if main_ruleset else 0
@@ -0,0 +1 @@
1
+ """__init__ module."""
@@ -4,6 +4,9 @@ from abc import abstractmethod
4
4
  from pathlib import Path
5
5
  from typing import Any
6
6
 
7
+ import winipedia_utils
8
+ from winipedia_utils.modules.module import make_obj_importpath
9
+ from winipedia_utils.modules.package import get_src_package
7
10
  from winipedia_utils.text.config import YamlConfigFile
8
11
  from winipedia_utils.text.string import split_on_uppercase
9
12
 
@@ -11,22 +14,36 @@ from winipedia_utils.text.string import split_on_uppercase
11
14
  class Workflow(YamlConfigFile):
12
15
  """Base class for workflows."""
13
16
 
17
+ @classmethod
14
18
  @abstractmethod
15
- def get_workflow_triggers(self) -> dict[str, Any]:
19
+ def get_workflow_triggers(cls) -> dict[str, Any]:
16
20
  """Get the workflow triggers."""
17
21
 
22
+ @classmethod
18
23
  @abstractmethod
19
- def get_permissions(self) -> dict[str, Any]:
24
+ def get_permissions(cls) -> dict[str, Any]:
20
25
  """Get the workflow permissions."""
21
26
 
27
+ @classmethod
22
28
  @abstractmethod
23
- def get_jobs(self) -> dict[str, Any]:
29
+ def get_jobs(cls) -> dict[str, Any]:
24
30
  """Get the workflow jobs."""
25
31
 
26
- def get_path(self) -> Path:
32
+ @classmethod
33
+ def get_parent_path(cls) -> Path:
27
34
  """Get the path to the config file."""
28
- file_name = self.get_standard_job_name() + ".yaml"
29
- return Path(".github/workflows") / file_name
35
+ return Path(".github/workflows")
36
+
37
+ @classmethod
38
+ def get_configs(cls) -> dict[str, Any]:
39
+ """Get the workflow config."""
40
+ return {
41
+ "name": cls.get_workflow_name(),
42
+ "on": cls.get_workflow_triggers(),
43
+ "permissions": cls.get_permissions(),
44
+ "run-name": cls.get_run_name(),
45
+ "jobs": cls.get_jobs(),
46
+ }
30
47
 
31
48
  @classmethod
32
49
  def get_standard_job(
@@ -39,7 +56,7 @@ class Workflow(YamlConfigFile):
39
56
  ) -> dict[str, Any]:
40
57
  """Get a standard job."""
41
58
  if name is None:
42
- name = cls.get_standard_job_name()
59
+ name = cls.get_filename()
43
60
 
44
61
  if steps is None:
45
62
  steps = []
@@ -57,31 +74,15 @@ class Workflow(YamlConfigFile):
57
74
  job[name]["if"] = if_condition
58
75
  return job
59
76
 
60
- @classmethod
61
- def get_standard_job_name(cls) -> str:
62
- """Get the standard job name."""
63
- return "_".join(
64
- split_on_uppercase(cls.__name__.removesuffix(Workflow.__name__))
65
- ).lower()
66
-
67
77
  @classmethod
68
78
  def get_workflow_name(cls) -> str:
69
79
  """Get the workflow name."""
70
80
  return " ".join(split_on_uppercase(cls.__name__))
71
81
 
72
- def get_run_name(self) -> str:
82
+ @classmethod
83
+ def get_run_name(cls) -> str:
73
84
  """Get the workflow run name."""
74
- return f"{self.get_workflow_name()}"
75
-
76
- def get_configs(self) -> dict[str, Any]:
77
- """Get the workflow config."""
78
- return {
79
- "name": self.get_workflow_name(),
80
- "on": self.get_workflow_triggers(),
81
- "permissions": self.get_permissions(),
82
- "run-name": self.get_run_name(),
83
- "jobs": self.get_jobs(),
84
- }
85
+ return f"{cls.get_workflow_name()}"
85
86
 
86
87
  @classmethod
87
88
  def get_checkout_step(cls, fetch_depth: int | None = None) -> dict[str, Any]:
@@ -145,13 +146,6 @@ class Workflow(YamlConfigFile):
145
146
  "run": "curl -sSL https://install.python-poetry.org | python3 -",
146
147
  }
147
148
  )
148
- steps.append(
149
- {
150
- "name": "Extract Version from pyproject.toml",
151
- "id": "version",
152
- "run": 'version=$(poetry version -s) && echo "Project version: $version" && echo "version=v$version" >> $GITHUB_OUTPUT', # noqa: E501
153
- },
154
- )
155
149
  if configure_pipy_token:
156
150
  steps.append(
157
151
  {
@@ -163,56 +157,91 @@ class Workflow(YamlConfigFile):
163
157
  steps.append({"name": "Install Dependencies", "run": "poetry install"})
164
158
  return steps
165
159
 
166
- @staticmethod
167
- def get_release_steps() -> list[dict[str, Any]]:
160
+ @classmethod
161
+ def get_release_steps(cls) -> list[dict[str, Any]]:
168
162
  """Get the release steps."""
169
163
  return [
170
164
  {
171
165
  "name": "Create and Push Tag",
172
- "run": f"git tag {Workflow.get_version()} && git push origin {Workflow.get_version()}", # noqa: E501
166
+ "run": f"git tag {cls.get_version()} && git push origin {cls.get_version()}", # noqa: E501
173
167
  },
174
168
  {
175
169
  "name": "Build Changelog",
176
170
  "id": "build_changelog",
177
171
  "uses": "mikepenz/release-changelog-builder-action@develop",
178
- "with": {"token": "${{ secrets.GITHUB_TOKEN }}"},
172
+ "with": {"token": cls.get_github_token()},
179
173
  },
180
174
  {
181
175
  "name": "Create GitHub Release",
182
176
  "uses": "ncipollo/release-action@main",
183
177
  "with": {
184
- "tag": Workflow.get_version(),
185
- "name": Workflow.get_repo_and_version(),
178
+ "tag": cls.get_version(),
179
+ "name": cls.get_repo_and_version(),
186
180
  "body": "${{ steps.build_changelog.outputs.changelog }}",
187
181
  },
188
182
  },
189
183
  ]
190
184
 
191
- @staticmethod
192
- def get_publish_to_pypi_step() -> dict[str, Any]:
185
+ @classmethod
186
+ def get_extract_version_step(cls) -> dict[str, Any]:
187
+ """Get the extract version step."""
188
+ return {
189
+ "name": "Extract Version from pyproject.toml",
190
+ "id": "version",
191
+ "run": 'version=$(poetry version -s) && echo "Project version: $version" && echo "version=v$version" >> $GITHUB_OUTPUT', # noqa: E501
192
+ }
193
+
194
+ @classmethod
195
+ def get_publish_to_pypi_step(cls) -> dict[str, Any]:
193
196
  """Get the publish step."""
194
197
  return {"name": "Build and publish to PyPI", "run": "poetry publish --build"}
195
198
 
196
- @staticmethod
197
- def get_pre_commit_step() -> dict[str, Any]:
199
+ @classmethod
200
+ def get_pre_commit_step(cls) -> dict[str, Any]:
198
201
  """Get the pre-commit step.
199
202
 
200
203
  using pre commit in case other hooks are added later
201
204
  and bc it fails if files are changed,
202
205
  setup script shouldnt change files
203
206
  """
204
- return {
207
+ step: dict[str, Any] = {
205
208
  "name": "Run Hooks",
206
209
  "run": "poetry run pre-commit run --all-files --verbose",
207
210
  }
211
+ if get_src_package() == winipedia_utils:
212
+ step["env"] = {"REPO_TOKEN": cls.get_repo_token()}
213
+ return step
208
214
 
209
- @staticmethod
210
- def get_repository_name() -> str:
215
+ @classmethod
216
+ def get_commit_step(cls) -> dict[str, Any]:
217
+ """Get the commit step."""
218
+ return {
219
+ "name": "Commit Changes",
220
+ "run": "poetry run git commit --no-verify -m 'CI/CD: Committing possible changes to pyproject.toml and poetry.lock' && poetry run git push", # noqa: E501
221
+ }
222
+
223
+ @classmethod
224
+ def get_protect_repository_step(cls) -> dict[str, Any]:
225
+ """Get the protect repository step."""
226
+ from winipedia_utils.git.github.repo import ( # noqa: PLC0415
227
+ protect, # avoid circular import
228
+ )
229
+
230
+ return {
231
+ "name": "Protect Repository",
232
+ "run": f"poetry run python -m {make_obj_importpath(protect)}",
233
+ "env": {
234
+ "REPO_TOKEN": cls.get_repo_token(),
235
+ },
236
+ }
237
+
238
+ @classmethod
239
+ def get_repository_name(cls) -> str:
211
240
  """Get the repository name."""
212
241
  return "${{ github.event.repository.name }}"
213
242
 
214
- @staticmethod
215
- def get_ref_name() -> str:
243
+ @classmethod
244
+ def get_ref_name(cls) -> str:
216
245
  """Get the ref name."""
217
246
  return "${{ github.ref_name }}"
218
247
 
@@ -221,7 +250,22 @@ class Workflow(YamlConfigFile):
221
250
  """Get the version."""
222
251
  return "${{ steps.version.outputs.version }}"
223
252
 
224
- @staticmethod
225
- def get_repo_and_version() -> str:
253
+ @classmethod
254
+ def get_repo_and_version(cls) -> str:
226
255
  """Get the repository name and ref name."""
227
- return f"{Workflow.get_repository_name()} {Workflow.get_version()}"
256
+ return f"{cls.get_repository_name()} {cls.get_version()}"
257
+
258
+ @classmethod
259
+ def get_ownwer(cls) -> str:
260
+ """Get the repository owner."""
261
+ return "${{ github.repository_owner }}"
262
+
263
+ @classmethod
264
+ def get_github_token(cls) -> str:
265
+ """Get the GitHub token."""
266
+ return "${{ secrets.GITHUB_TOKEN }}"
267
+
268
+ @classmethod
269
+ def get_repo_token(cls) -> str:
270
+ """Get the repository token."""
271
+ return "${{ secrets.REPO_TOKEN }}"
@@ -0,0 +1,55 @@
1
+ """Contains the pull request workflow.
2
+
3
+ This workflow is used to run tests on pull requests.
4
+ """
5
+
6
+ from typing import Any
7
+
8
+ from winipedia_utils.git.github.workflows.base.base import Workflow
9
+
10
+
11
+ class HealthCheckWorkflow(Workflow):
12
+ """Pull request workflow.
13
+
14
+ This workflow is triggered by a pull request.
15
+ It runs tests on the pull request.
16
+ """
17
+
18
+ @classmethod
19
+ def get_workflow_triggers(cls) -> dict[str, Any]:
20
+ """Get the workflow triggers."""
21
+ return {
22
+ "pull_request": {
23
+ "types": ["opened", "synchronize", "reopened"],
24
+ },
25
+ "schedule": [
26
+ {
27
+ # run every day at 6 am
28
+ "cron": "0 6 * * *",
29
+ },
30
+ ],
31
+ "workflow_dispatch": {},
32
+ }
33
+
34
+ @classmethod
35
+ def get_permissions(cls) -> dict[str, Any]:
36
+ """Get the workflow permissions."""
37
+ return {}
38
+
39
+ @classmethod
40
+ def get_jobs(cls) -> dict[str, Any]:
41
+ """Get the workflow jobs."""
42
+ return {
43
+ **cls.get_standard_job(
44
+ steps=[
45
+ *(
46
+ cls.get_poetry_setup_steps(
47
+ install_dependencies=True,
48
+ )
49
+ ),
50
+ cls.get_protect_repository_step(),
51
+ cls.get_pre_commit_step(),
52
+ cls.get_extract_version_step(),
53
+ ],
54
+ ),
55
+ }
@@ -5,8 +5,8 @@ This workflow is used to publish the package to PyPI with poetry.
5
5
 
6
6
  from typing import Any
7
7
 
8
- from winipedia_utils.git.workflows.base.base import Workflow
9
- from winipedia_utils.git.workflows.release import ReleaseWorkflow
8
+ from winipedia_utils.git.github.workflows.base.base import Workflow
9
+ from winipedia_utils.git.github.workflows.release import ReleaseWorkflow
10
10
 
11
11
 
12
12
  class PublishWorkflow(Workflow):
@@ -16,7 +16,8 @@ class PublishWorkflow(Workflow):
16
16
  It publishes the package to PyPI with poetry.
17
17
  """
18
18
 
19
- def get_workflow_triggers(self) -> dict[str, Any]:
19
+ @classmethod
20
+ def get_workflow_triggers(cls) -> dict[str, Any]:
20
21
  """Get the workflow triggers."""
21
22
  return {
22
23
  "workflow_run": {
@@ -25,22 +26,24 @@ class PublishWorkflow(Workflow):
25
26
  },
26
27
  }
27
28
 
28
- def get_permissions(self) -> dict[str, Any]:
29
+ @classmethod
30
+ def get_permissions(cls) -> dict[str, Any]:
29
31
  """Get the workflow permissions."""
30
32
  return {
31
33
  "contents": "read",
32
34
  }
33
35
 
34
- def get_jobs(self) -> dict[str, Any]:
36
+ @classmethod
37
+ def get_jobs(cls) -> dict[str, Any]:
35
38
  """Get the workflow jobs."""
36
- return self.get_standard_job(
39
+ return cls.get_standard_job(
37
40
  steps=[
38
41
  *(
39
- self.get_poetry_setup_steps(
42
+ cls.get_poetry_setup_steps(
40
43
  configure_pipy_token=True,
41
44
  )
42
45
  ),
43
- self.get_publish_to_pypi_step(),
46
+ cls.get_publish_to_pypi_step(),
44
47
  ],
45
48
  if_condition="${{ github.event.workflow_run.conclusion == 'success' }}",
46
49
  )