winipedia-utils 0.2.22__tar.gz → 0.2.47__tar.gz

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 (85) hide show
  1. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/PKG-INFO +9 -2
  2. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/README.md +8 -1
  3. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/pyproject.toml +1 -1
  4. winipedia_utils-0.2.47/winipedia_utils/git/workflows/base/base.py +47 -0
  5. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/git/workflows/publish.py +16 -12
  6. winipedia_utils-0.2.47/winipedia_utils/git/workflows/release.py +88 -0
  7. winipedia_utils-0.2.47/winipedia_utils/resources/svgs/__init__.py +1 -0
  8. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/setup.py +2 -0
  9. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/testing/assertions.py +25 -0
  10. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/testing/tests/base/fixtures/scopes/session.py +15 -1
  11. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/LICENSE +0 -0
  12. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/__init__.py +0 -0
  13. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/concurrent/__init__.py +0 -0
  14. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/concurrent/concurrent.py +0 -0
  15. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/concurrent/multiprocessing.py +0 -0
  16. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/concurrent/multithreading.py +0 -0
  17. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/consts.py +0 -0
  18. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/data/__init__.py +0 -0
  19. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/data/dataframe/__init__.py +0 -0
  20. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/data/dataframe/cleaning.py +0 -0
  21. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/data/structures/__init__.py +0 -0
  22. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/data/structures/dicts.py +0 -0
  23. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/git/__init__.py +0 -0
  24. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/git/gitignore/__init__.py +0 -0
  25. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/git/gitignore/gitignore.py +0 -0
  26. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/git/pre_commit/__init__.py +0 -0
  27. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/git/pre_commit/config.py +0 -0
  28. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/git/pre_commit/hooks.py +0 -0
  29. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/git/pre_commit/run_hooks.py +0 -0
  30. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/git/workflows/__init__.py +0 -0
  31. {winipedia_utils-0.2.22/winipedia_utils/resources → winipedia_utils-0.2.47/winipedia_utils/git/workflows/base}/__init__.py +0 -0
  32. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/iterating/__init__.py +0 -0
  33. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/iterating/iterate.py +0 -0
  34. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/logging/__init__.py +0 -0
  35. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/logging/ansi.py +0 -0
  36. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/logging/config.py +0 -0
  37. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/logging/logger.py +0 -0
  38. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/modules/__init__.py +0 -0
  39. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/modules/class_.py +0 -0
  40. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/modules/function.py +0 -0
  41. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/modules/module.py +0 -0
  42. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/modules/package.py +0 -0
  43. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/oop/__init__.py +0 -0
  44. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/oop/mixins/__init__.py +0 -0
  45. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/oop/mixins/meta.py +0 -0
  46. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/oop/mixins/mixin.py +0 -0
  47. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/os/__init__.py +0 -0
  48. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/os/os.py +0 -0
  49. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/projects/__init__.py +0 -0
  50. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/projects/poetry/__init__.py +0 -0
  51. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/projects/poetry/config.py +0 -0
  52. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/projects/poetry/poetry.py +0 -0
  53. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/projects/project.py +0 -0
  54. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/py.typed +0 -0
  55. {winipedia_utils-0.2.22/winipedia_utils/resources/svgs → winipedia_utils-0.2.47/winipedia_utils/resources}/__init__.py +0 -0
  56. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/resources/svgs/delete_garbage_can.svg +0 -0
  57. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/resources/svgs/download_arrow.svg +0 -0
  58. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/resources/svgs/exit_fullscreen_icon.svg +0 -0
  59. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/resources/svgs/fullscreen_icon.svg +0 -0
  60. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/resources/svgs/menu_icon.svg +0 -0
  61. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/resources/svgs/pause_icon.svg +0 -0
  62. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/resources/svgs/play_icon.svg +0 -0
  63. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/resources/svgs/plus_icon.svg +0 -0
  64. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/resources/svgs/svg.py +0 -0
  65. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/security/__init__.py +0 -0
  66. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/security/cryptography.py +0 -0
  67. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/security/keyring.py +0 -0
  68. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/testing/__init__.py +0 -0
  69. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/testing/convention.py +0 -0
  70. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/testing/create_tests.py +0 -0
  71. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/testing/fixtures.py +0 -0
  72. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/testing/tests/__init__.py +0 -0
  73. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/testing/tests/base/__init__.py +0 -0
  74. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/testing/tests/base/fixtures/__init__.py +0 -0
  75. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/testing/tests/base/fixtures/fixture.py +0 -0
  76. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/testing/tests/base/fixtures/scopes/__init__.py +0 -0
  77. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/testing/tests/base/fixtures/scopes/class_.py +0 -0
  78. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/testing/tests/base/fixtures/scopes/function.py +0 -0
  79. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/testing/tests/base/fixtures/scopes/module.py +0 -0
  80. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/testing/tests/base/fixtures/scopes/package.py +0 -0
  81. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/testing/tests/base/utils/__init__.py +0 -0
  82. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/testing/tests/base/utils/utils.py +0 -0
  83. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/testing/tests/conftest.py +0 -0
  84. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/text/__init__.py +0 -0
  85. {winipedia_utils-0.2.22 → winipedia_utils-0.2.47}/winipedia_utils/text/string.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: winipedia-utils
3
- Version: 0.2.22
3
+ Version: 0.2.47
4
4
  Summary: A package with many utility functions
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -126,11 +126,18 @@ Adds tool configurations in pyproject.toml for e.g.:
126
126
  - **pytest** - Test discovery and execution
127
127
  - **bandit** - Security scanning
128
128
 
129
- ### Step 6️⃣ - Create GitHub Actions Workflow
129
+ ### Step 6️⃣ - Create GitHub Actions Workflows
130
+ Sets up `.github/workflows/release.yaml` for automated versioning and publishing:
131
+ - Triggers on pushing tags in the form of v*
132
+ - Creates a release on GitHub
133
+ - Triggers publish.yaml workflow if publish.yaml exists
134
+ - Tests will fail if you do not have release.yaml
135
+
130
136
  Sets up `.github/workflows/publish.yaml` for automated PyPI publishing:
131
137
  - Triggers on GitHub releases
132
138
  - Configures Poetry with PyPI token
133
139
  - Builds and publishes package automatically
140
+ - If you do not want to publish to pypi, you can delete the file, tests will not fail bc of it and it won't be added again unless you run the setup script again or delete the .github folder/workflows folder.
134
141
 
135
142
  ### Step 7️⃣ - Create Project Root
136
143
  Creates your project's root package directory with `py.typed` marker for type hint support.
@@ -102,11 +102,18 @@ Adds tool configurations in pyproject.toml for e.g.:
102
102
  - **pytest** - Test discovery and execution
103
103
  - **bandit** - Security scanning
104
104
 
105
- ### Step 6️⃣ - Create GitHub Actions Workflow
105
+ ### Step 6️⃣ - Create GitHub Actions Workflows
106
+ Sets up `.github/workflows/release.yaml` for automated versioning and publishing:
107
+ - Triggers on pushing tags in the form of v*
108
+ - Creates a release on GitHub
109
+ - Triggers publish.yaml workflow if publish.yaml exists
110
+ - Tests will fail if you do not have release.yaml
111
+
106
112
  Sets up `.github/workflows/publish.yaml` for automated PyPI publishing:
107
113
  - Triggers on GitHub releases
108
114
  - Configures Poetry with PyPI token
109
115
  - Builds and publishes package automatically
116
+ - If you do not want to publish to pypi, you can delete the file, tests will not fail bc of it and it won't be added again unless you run the setup script again or delete the .github folder/workflows folder.
110
117
 
111
118
  ### Step 7️⃣ - Create Project Root
112
119
  Creates your project's root package directory with `py.typed` marker for type hint support.
@@ -1,7 +1,7 @@
1
1
  # Project section
2
2
  [project]
3
3
  name = "winipedia-utils"
4
- version = "0.2.22"
4
+ version = "0.2.47"
5
5
  description = "A package with many utility functions"
6
6
  readme = "README.md"
7
7
  requires-python = ">=3.12"
@@ -0,0 +1,47 @@
1
+ """Contains base utilities for git workflows."""
2
+
3
+ from typing import Any
4
+
5
+
6
+ def _get_checkout_step(fetch_depth: int | None = None) -> dict[str, Any]:
7
+ """Get the checkout step."""
8
+ step: dict[str, Any] = {
9
+ "name": "Checkout repository",
10
+ "uses": "actions/checkout@v5",
11
+ }
12
+ if fetch_depth is not None:
13
+ step["with"] = {"fetch-depth": fetch_depth}
14
+ return step
15
+
16
+
17
+ def _get_poetry_setup_steps(
18
+ *,
19
+ install_dependencies: bool = False,
20
+ fetch_depth: int | None = None,
21
+ configure_pipy_token: bool = False,
22
+ ) -> list[dict[str, Any]]:
23
+ """Get the poetry steps."""
24
+ steps = [_get_checkout_step(fetch_depth)]
25
+ steps.append(
26
+ {
27
+ "name": "Setup Python",
28
+ "uses": "actions/setup-python@v6",
29
+ "with": {"python-version": "3.x"},
30
+ }
31
+ )
32
+ steps.append(
33
+ {
34
+ "name": "Install Poetry",
35
+ "run": "curl -sSL https://install.python-poetry.org | python3 -",
36
+ }
37
+ )
38
+ if configure_pipy_token:
39
+ steps.append(
40
+ {
41
+ "name": "Configure Poetry",
42
+ "run": "poetry config pypi-token.pypi ${{ secrets.PYPI_TOKEN }}",
43
+ }
44
+ )
45
+ if install_dependencies:
46
+ steps.append({"name": "Install Dependencies", "run": "poetry install"})
47
+ return steps
@@ -8,6 +8,9 @@ from typing import Any
8
8
 
9
9
  import yaml
10
10
 
11
+ from winipedia_utils.git.workflows.base.base import _get_poetry_setup_steps
12
+ from winipedia_utils.git.workflows.release import WORKFLOW_NAME
13
+
11
14
  PUBLISH_WORKFLOW_PATH = Path(".github/workflows/publish.yaml")
12
15
 
13
16
 
@@ -31,22 +34,23 @@ def _get_publish_config() -> dict[str, Any]:
31
34
  """Dict that represents the publish workflow yaml."""
32
35
  return {
33
36
  "name": "Publish to PyPI",
34
- "on": {"release": {"types": ["published"]}},
37
+ "on": {
38
+ "workflow_run": {
39
+ "workflows": [WORKFLOW_NAME],
40
+ "types": ["completed"],
41
+ "branches": ["main"],
42
+ },
43
+ "release": {"types": ["published"]},
44
+ },
35
45
  "jobs": {
36
46
  "publish": {
37
47
  "runs-on": "ubuntu-latest",
38
48
  "steps": [
39
- {"name": "Checkout repository", "uses": "actions/checkout@v4"},
40
- {
41
- "name": "Set up Python",
42
- "uses": "actions/setup-python@v5",
43
- "with": {"python-version": "3.x"},
44
- },
45
- {"name": "Install Poetry", "run": "pip install poetry"},
46
- {
47
- "name": "Configure Poetry",
48
- "run": "poetry config pypi-token.pypi ${{ secrets.PYPI_TOKEN }}", # noqa: E501
49
- },
49
+ *(
50
+ _get_poetry_setup_steps(
51
+ configure_pipy_token=True,
52
+ )
53
+ ),
50
54
  {
51
55
  "name": "Build and publish to PyPI",
52
56
  "run": "poetry publish --build",
@@ -0,0 +1,88 @@
1
+ """Contains the release workflow.
2
+
3
+ This workflow is used to create a release on GitHub.
4
+ """
5
+
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ import yaml
10
+
11
+ from winipedia_utils.git.workflows.base.base import _get_poetry_setup_steps
12
+
13
+ RELEASE_WORKFLOW_PATH = Path(".github/workflows/release.yaml")
14
+
15
+ WORKFLOW_NAME = "Create Release"
16
+
17
+
18
+ def load_release_workflow() -> dict[str, Any]:
19
+ """Load the release workflow."""
20
+ path = RELEASE_WORKFLOW_PATH
21
+ if not path.exists():
22
+ path.parent.mkdir(parents=True, exist_ok=True)
23
+ path.touch()
24
+ return yaml.safe_load(path.read_text()) or {}
25
+
26
+
27
+ def dump_release_workflow(config: dict[str, Any]) -> None:
28
+ """Dump the release workflow."""
29
+ path = RELEASE_WORKFLOW_PATH
30
+ with path.open("w") as f:
31
+ yaml.safe_dump(config, f, sort_keys=False)
32
+
33
+
34
+ def _get_release_config() -> dict[str, Any]:
35
+ """Dict that represents the release workflow yaml."""
36
+ return {
37
+ "name": WORKFLOW_NAME,
38
+ "on": {"push": {"tags": ["v*"], "branches": ["main"]}},
39
+ "permissions": {
40
+ "contents": "write",
41
+ },
42
+ "jobs": {
43
+ "release": {
44
+ "runs-on": "ubuntu-latest",
45
+ "steps": [
46
+ *(
47
+ _get_poetry_setup_steps(
48
+ install_dependencies=True,
49
+ fetch_depth=0,
50
+ )
51
+ ),
52
+ {
53
+ "name": "Run Pre-commit Hooks",
54
+ "run": "poetry run pre-commit run --all-files",
55
+ },
56
+ {
57
+ "name": "Build Changelog",
58
+ "id": "build_changelog",
59
+ "uses": "mikepenz/release-changelog-builder-action@v5",
60
+ "with": {"token": "${{ secrets.GITHUB_TOKEN }}"},
61
+ },
62
+ {
63
+ "name": "Create GitHub Release",
64
+ "uses": "ncipollo/release-action@v1",
65
+ "with": {
66
+ "tag": "${{ github.ref_name }}",
67
+ "name": "${{ github.event.repository.name }}-${{ github.ref_name }}", # noqa: E501
68
+ "body": "${{ steps.build_changelog.outputs.changelog }}",
69
+ },
70
+ },
71
+ ],
72
+ }
73
+ },
74
+ }
75
+
76
+
77
+ def _release_config_is_correct() -> bool:
78
+ """Check if the release workflow is correct."""
79
+ config = load_release_workflow()
80
+ return bool(config == _get_release_config())
81
+
82
+
83
+ def _add_release_workflow() -> None:
84
+ """Add the release workflow."""
85
+ if _release_config_is_correct():
86
+ return
87
+ config = _get_release_config()
88
+ dump_release_workflow(config)
@@ -0,0 +1 @@
1
+ """__init__ module."""
@@ -16,6 +16,7 @@ from winipedia_utils.git.pre_commit.config import (
16
16
  )
17
17
  from winipedia_utils.git.pre_commit.run_hooks import _run_all_hooks
18
18
  from winipedia_utils.git.workflows.publish import _add_publish_workflow
19
+ from winipedia_utils.git.workflows.release import _add_release_workflow
19
20
  from winipedia_utils.logging.logger import get_logger
20
21
  from winipedia_utils.projects.poetry.config import (
21
22
  _add_configurations_to_pyproject_toml,
@@ -33,6 +34,7 @@ SETUP_STEPS = [
33
34
  _add_package_hook_to_pre_commit_config,
34
35
  _pre_commit_install,
35
36
  _add_package_patterns_to_gitignore,
37
+ _add_release_workflow,
36
38
  _add_publish_workflow,
37
39
  _add_configurations_to_pyproject_toml,
38
40
  _create_project_root,
@@ -5,6 +5,8 @@ assert statement with additional features like improved error messages and
5
5
  specialized validation logic for common testing scenarios.
6
6
  """
7
7
 
8
+ from typing import Any
9
+
8
10
 
9
11
  def assert_with_msg(expr: bool, msg: str) -> None: # noqa: FBT001
10
12
  """Assert that an expression is true with a custom error message.
@@ -21,3 +23,26 @@ def assert_with_msg(expr: bool, msg: str) -> None: # noqa: FBT001
21
23
 
22
24
  """
23
25
  assert expr, msg # noqa: S101 # nosec: B101
26
+
27
+
28
+ def assert_with_info(expr: bool, expected: Any, actual: Any, msg: str = "") -> None: # noqa: FBT001
29
+ """Assert that an expression is true with a custom error message.
30
+
31
+ wraps around assert with msg and adds the expected and actual values to the message.
32
+
33
+ Args:
34
+ expr: The expression to evaluate for truthiness
35
+ expected: The expected value
36
+ actual: The actual value
37
+ msg: The error message to display if the assertion fails
38
+
39
+ Raises:
40
+ AssertionError: If the expression evaluates to False
41
+
42
+ """
43
+ msg = f"""
44
+ Expected: {expected}
45
+ Actual: {actual}
46
+ {msg}
47
+ """
48
+ assert_with_msg(expr, msg)
@@ -18,6 +18,7 @@ from winipedia_utils.git.workflows.publish import (
18
18
  PUBLISH_WORKFLOW_PATH,
19
19
  _publish_config_is_correct,
20
20
  )
21
+ from winipedia_utils.git.workflows.release import _release_config_is_correct
21
22
  from winipedia_utils.modules.module import to_path
22
23
  from winipedia_utils.modules.package import (
23
24
  find_packages,
@@ -176,7 +177,8 @@ def _test_publish_workflow_is_correct() -> None:
176
177
  need to publish to pypi, e.g. they are binaries or private usage only or for profit.
177
178
  """
178
179
  path = PUBLISH_WORKFLOW_PATH
179
- if not path.exists():
180
+ # if folder exists but the file not then we skip this test
181
+ if path.parent.exists() and not path.exists():
180
182
  return
181
183
  assert_with_msg(
182
184
  _publish_config_is_correct(),
@@ -184,6 +186,18 @@ def _test_publish_workflow_is_correct() -> None:
184
186
  )
185
187
 
186
188
 
189
+ @autouse_session_fixture
190
+ def _test_release_workflow_is_correct() -> None:
191
+ """Verify that the release workflow is correctly defined.
192
+
193
+ This workflow is mandatory for all projects.
194
+ """
195
+ assert_with_msg(
196
+ _release_config_is_correct(),
197
+ "Release workflow is not correct.",
198
+ )
199
+
200
+
187
201
  @autouse_session_fixture
188
202
  def _test_no_namespace_packages() -> None:
189
203
  """Verify that there are no namespace packages in the project.