mkdocstrings-github 0.6.3__py3-none-any.whl → 0.7.0__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.
- {mkdocstrings_github-0.6.3.dist-info → mkdocstrings_github-0.7.0.dist-info}/METADATA +1 -1
- {mkdocstrings_github-0.6.3.dist-info → mkdocstrings_github-0.7.0.dist-info}/RECORD +9 -9
- mkdocstrings_handlers/github/config.py +21 -0
- mkdocstrings_handlers/github/handler.py +1 -0
- mkdocstrings_handlers/github/objects.py +56 -5
- mkdocstrings_handlers/github/rendering.py +98 -2
- mkdocstrings_handlers/github/templates/material/workflow.html.jinja +11 -0
- {mkdocstrings_github-0.6.3.dist-info → mkdocstrings_github-0.7.0.dist-info}/WHEEL +0 -0
- {mkdocstrings_github-0.6.3.dist-info → mkdocstrings_github-0.7.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
mkdocstrings_handlers/github/__init__.py,sha256=0WdFUIq4Xu2ZFtlZNIYCQSoqcx3Ot9Wv41_X_dwbFww,248
|
|
2
|
-
mkdocstrings_handlers/github/config.py,sha256=
|
|
3
|
-
mkdocstrings_handlers/github/handler.py,sha256=
|
|
4
|
-
mkdocstrings_handlers/github/objects.py,sha256=
|
|
2
|
+
mkdocstrings_handlers/github/config.py,sha256=G__jcRZkhltdwwh1YJWbx9xEdblF9mTc4_L1NEWE6Bw,7873
|
|
3
|
+
mkdocstrings_handlers/github/handler.py,sha256=W1hkFowofpsToDLzkR0MRV3ClCQckUS2VkSjfOLKYRU,8875
|
|
4
|
+
mkdocstrings_handlers/github/objects.py,sha256=2gKgrkkyBem3a0MUpffAH5C5asLk8Dqgqf6ntZuTxCo,9317
|
|
5
5
|
mkdocstrings_handlers/github/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
-
mkdocstrings_handlers/github/rendering.py,sha256=
|
|
6
|
+
mkdocstrings_handlers/github/rendering.py,sha256=N92Gbm9Qi4I2EZC69JsEixO1AdHuf1du9isJNXG4vhQ,7192
|
|
7
7
|
mkdocstrings_handlers/github/templates/material/_macros.html.jinja,sha256=1TNUgoOIxm-a1S7eiESrW1fYuzXOjrwFqXQSyA4tkkU,1576
|
|
8
8
|
mkdocstrings_handlers/github/templates/material/action.html.jinja,sha256=C7S8I9bjnrEUahyDB7CkFxtakFrr53KE3t3uyczBPzc,2864
|
|
9
9
|
mkdocstrings_handlers/github/templates/material/heading.html.jinja,sha256=wnvZpNED8Dhb935qnddeDExXN-MIUz8frRRfgq-A9VA,1396
|
|
@@ -11,8 +11,8 @@ mkdocstrings_handlers/github/templates/material/inputs.html.jinja,sha256=UE8RmfM
|
|
|
11
11
|
mkdocstrings_handlers/github/templates/material/outputs.html.jinja,sha256=Z9QD1KSALLDS9VEyugWG1BHpLCHTKYEx4TEQkKIoC3M,2693
|
|
12
12
|
mkdocstrings_handlers/github/templates/material/secrets.html.jinja,sha256=jQ_HaG2ivbZTH74pyV4BAFGQu5Vn03kA_vaEbTSABds,2839
|
|
13
13
|
mkdocstrings_handlers/github/templates/material/style.css,sha256=Nfmds-xHtPJ_IzOv5svA7ih5talHDTiQryN_n0DGdZs,1553
|
|
14
|
-
mkdocstrings_handlers/github/templates/material/workflow.html.jinja,sha256=
|
|
15
|
-
mkdocstrings_github-0.
|
|
16
|
-
mkdocstrings_github-0.
|
|
17
|
-
mkdocstrings_github-0.
|
|
18
|
-
mkdocstrings_github-0.
|
|
14
|
+
mkdocstrings_handlers/github/templates/material/workflow.html.jinja,sha256=VS3AqT0OgX6QPFCwN63tULM9tC4t6FGtOoVCixQGPl4,4543
|
|
15
|
+
mkdocstrings_github-0.7.0.dist-info/METADATA,sha256=6Tyvq8M9xxX2zLCOEA9-fzgOX_zQlUMln3dvOujSO7g,4052
|
|
16
|
+
mkdocstrings_github-0.7.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
17
|
+
mkdocstrings_github-0.7.0.dist-info/licenses/LICENSE,sha256=5enZtJ4zSp0Ps3jTqFQ4kadcx62BhgTaDNPrXWb3g3E,1069
|
|
18
|
+
mkdocstrings_github-0.7.0.dist-info/RECORD,,
|
|
@@ -21,6 +21,7 @@ logger = get_logger(__name__)
|
|
|
21
21
|
SIGNATURE_VERSION = Literal["ref", "major", "semver", "string"]
|
|
22
22
|
PARAMETERS_ORDER = Literal["alphabetical", "source"]
|
|
23
23
|
PARAMETERS_SECTION_STYLE = Literal["table", "list"]
|
|
24
|
+
STEP_DIRECTION = Literal["TB", "LR"]
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
class GitHubOptions(BaseModel):
|
|
@@ -201,6 +202,26 @@ class GitHubOptions(BaseModel):
|
|
|
201
202
|
description="Whether to add anchors to parameters in the documentation.",
|
|
202
203
|
)
|
|
203
204
|
|
|
205
|
+
# Workflow chart option
|
|
206
|
+
workflow_chart: bool = Field(
|
|
207
|
+
default=False,
|
|
208
|
+
description="""Whether to generate a Mermaid flowchart for reusable workflows.
|
|
209
|
+
|
|
210
|
+
The flowchart displays the workflow's jobs and steps in a flowchart diagram.
|
|
211
|
+
Multiple jobs are rendered as subgraphs, and calls to other workflows are visually distinct.
|
|
212
|
+
The diagram is rendered client-side in the browser using mkdocs-mermaid2.
|
|
213
|
+
""",
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
workflow_chart_step_direction: STEP_DIRECTION = Field(
|
|
217
|
+
default="LR",
|
|
218
|
+
description="""The direction of the flowchart for steps within jobs.
|
|
219
|
+
|
|
220
|
+
- `TB`: top-to-bottom layout,
|
|
221
|
+
- `LR`: left-to-right layout.
|
|
222
|
+
""",
|
|
223
|
+
)
|
|
224
|
+
|
|
204
225
|
|
|
205
226
|
class GitHubConfig(BaseModel):
|
|
206
227
|
"""Configuration options for the GitHub handler."""
|
|
@@ -173,6 +173,7 @@ class GitHubHandler(BaseHandler):
|
|
|
173
173
|
self.env.filters["group_parameters"] = rendering.group_parameters
|
|
174
174
|
self.env.filters["anchor_id"] = rendering.anchor_id
|
|
175
175
|
self.env.filters["as_string"] = rendering.as_string
|
|
176
|
+
self.env.filters["generate_mermaid_flowchart"] = rendering.generate_mermaid_flowchart
|
|
176
177
|
self.env.globals["semver_tag"] = self.semver
|
|
177
178
|
self.env.globals["major_tag"] = self.major
|
|
178
179
|
self.env.globals["git_repo"] = self.repo
|
|
@@ -2,7 +2,7 @@ import re
|
|
|
2
2
|
from dataclasses import dataclass, field
|
|
3
3
|
from enum import Enum
|
|
4
4
|
from os import PathLike
|
|
5
|
-
from typing import Any, Literal
|
|
5
|
+
from typing import Any, Literal
|
|
6
6
|
|
|
7
7
|
from ruamel.yaml import YAML
|
|
8
8
|
from ruamel.yaml.comments import CommentedMap
|
|
@@ -35,7 +35,7 @@ class Input:
|
|
|
35
35
|
required: bool = False
|
|
36
36
|
type: Literal["boolean", "number", "string"] = "string"
|
|
37
37
|
default: bool | float | int | str | None = None
|
|
38
|
-
deprecationMessage:
|
|
38
|
+
deprecationMessage: str | None = None
|
|
39
39
|
group: str = ""
|
|
40
40
|
|
|
41
41
|
|
|
@@ -55,6 +55,31 @@ class Secret:
|
|
|
55
55
|
group: str = ""
|
|
56
56
|
|
|
57
57
|
|
|
58
|
+
@dataclass
|
|
59
|
+
class Step:
|
|
60
|
+
"""Represents a step within a job."""
|
|
61
|
+
|
|
62
|
+
name: str
|
|
63
|
+
uses: str = "" # For steps that use actions
|
|
64
|
+
run: str = "" # For steps that run commands
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass
|
|
68
|
+
class Job:
|
|
69
|
+
"""Represents a job within a workflow."""
|
|
70
|
+
|
|
71
|
+
id: str
|
|
72
|
+
name: str
|
|
73
|
+
uses: str | None
|
|
74
|
+
steps: list[Step] = field(default_factory=list)
|
|
75
|
+
needs: list[str] = field(default_factory=list) # Job dependencies
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def mermaid_id(self) -> str:
|
|
79
|
+
job_id_safe = self.id.replace("-", "_").replace(".", "_")
|
|
80
|
+
return f"job_{job_id_safe}"
|
|
81
|
+
|
|
82
|
+
|
|
58
83
|
def _get_member(d: dict, key: str, error_message: str = "", default: Any = None) -> Any:
|
|
59
84
|
if key not in d:
|
|
60
85
|
if default is not None:
|
|
@@ -164,6 +189,7 @@ class Workflow:
|
|
|
164
189
|
inputs: list[Input] = field(default_factory=list)
|
|
165
190
|
secrets: list[Secret] = field(default_factory=list)
|
|
166
191
|
outputs: list[Output] = field(default_factory=list)
|
|
192
|
+
jobs: dict[str, Job] = field(default_factory=dict)
|
|
167
193
|
template: Literal["workflow.html.jinja"] = "workflow.html.jinja"
|
|
168
194
|
|
|
169
195
|
@property
|
|
@@ -221,11 +247,11 @@ class Workflow:
|
|
|
221
247
|
workflow.permissions[key] = PermissionLevel.from_label(label)
|
|
222
248
|
else:
|
|
223
249
|
raise ValueError("permissions must be a string or a dictionary")
|
|
224
|
-
for
|
|
225
|
-
if isinstance(permissions :=
|
|
250
|
+
for job_id, job_data in data.get("jobs", {}).items():
|
|
251
|
+
if isinstance(permissions := job_data.get("permissions", {}), str):
|
|
226
252
|
set_all_permissions(permissions)
|
|
227
253
|
elif isinstance(permissions, dict):
|
|
228
|
-
for key, label in
|
|
254
|
+
for key, label in job_data.get("permissions", {}).items():
|
|
229
255
|
if key in workflow.permissions:
|
|
230
256
|
permission = PermissionLevel.from_label(label)
|
|
231
257
|
if permission > workflow.permissions[key]:
|
|
@@ -235,4 +261,29 @@ class Workflow:
|
|
|
235
261
|
else:
|
|
236
262
|
raise ValueError("permissions must be a string or a dictionary")
|
|
237
263
|
|
|
264
|
+
# Parse job information for flowchart
|
|
265
|
+
job = Job(id=job_id, name=job_data.get("name", job_id), uses=job_data.get("uses", None))
|
|
266
|
+
|
|
267
|
+
# Parse job dependencies
|
|
268
|
+
needs = job_data.get("needs", [])
|
|
269
|
+
if isinstance(needs, str):
|
|
270
|
+
job.needs = [needs]
|
|
271
|
+
elif isinstance(needs, list):
|
|
272
|
+
job.needs = needs
|
|
273
|
+
|
|
274
|
+
# Parse steps
|
|
275
|
+
for step_data in job_data.get("steps", []):
|
|
276
|
+
step_name = step_data.get("name", "")
|
|
277
|
+
step_uses = step_data.get("uses", "")
|
|
278
|
+
step_run = step_data.get("run", "")
|
|
279
|
+
|
|
280
|
+
step = Step(
|
|
281
|
+
name=step_name,
|
|
282
|
+
uses=step_uses,
|
|
283
|
+
run=step_run,
|
|
284
|
+
)
|
|
285
|
+
job.steps.append(step)
|
|
286
|
+
|
|
287
|
+
workflow.jobs[job.id] = job
|
|
288
|
+
|
|
238
289
|
return workflow
|
|
@@ -6,8 +6,8 @@ from typing import TYPE_CHECKING, Sequence
|
|
|
6
6
|
|
|
7
7
|
from jinja2 import pass_context
|
|
8
8
|
|
|
9
|
-
from mkdocstrings_handlers.github.config import PARAMETERS_ORDER, GitHubOptions
|
|
10
|
-
from mkdocstrings_handlers.github.objects import Input, Output, Secret
|
|
9
|
+
from mkdocstrings_handlers.github.config import PARAMETERS_ORDER, STEP_DIRECTION, GitHubOptions
|
|
10
|
+
from mkdocstrings_handlers.github.objects import Input, Output, Secret, Workflow
|
|
11
11
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
13
13
|
from git import Repo
|
|
@@ -106,3 +106,99 @@ def as_string(value: bool | str | int | float | None) -> str:
|
|
|
106
106
|
return ""
|
|
107
107
|
case _:
|
|
108
108
|
raise TypeError(f"Unsupported type: {type(value)}")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def generate_mermaid_flowchart(workflow: Workflow, direction: STEP_DIRECTION = "TB") -> str:
|
|
112
|
+
"""Generate a Mermaid flowchart for a reusable workflow.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
workflow: The workflow object containing jobs and steps.
|
|
116
|
+
direction: The direction of steps within jobs (TB for top-to-bottom, LR for left-to-right).
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
A Mermaid flowchart diagram as a string.
|
|
120
|
+
"""
|
|
121
|
+
if not workflow.jobs:
|
|
122
|
+
return ""
|
|
123
|
+
|
|
124
|
+
lines = ["flowchart TB"]
|
|
125
|
+
|
|
126
|
+
# Track all nodes for dependency linking
|
|
127
|
+
job_start_nodes = {}
|
|
128
|
+
job_end_nodes = {}
|
|
129
|
+
|
|
130
|
+
for job in workflow.jobs.values():
|
|
131
|
+
job_id_safe = job.mermaid_id
|
|
132
|
+
|
|
133
|
+
# Check if job calls another workflow (has any steps with workflow set)
|
|
134
|
+
if job.uses is not None:
|
|
135
|
+
# Job that calls a workflow - render as a single subroutine node
|
|
136
|
+
lines.append(f' {job_id_safe}[["{job.name}"]]')
|
|
137
|
+
job_start_nodes[job.id] = job_id_safe
|
|
138
|
+
job_end_nodes[job.id] = job_id_safe
|
|
139
|
+
continue
|
|
140
|
+
|
|
141
|
+
# Regular job - render as a subgraph with steps
|
|
142
|
+
|
|
143
|
+
lines.append(f' subgraph {job_id_safe}["{job.name}"]')
|
|
144
|
+
lines.append(f" direction {direction}")
|
|
145
|
+
|
|
146
|
+
if job.steps:
|
|
147
|
+
# Filter steps that have a name
|
|
148
|
+
named_steps = [(idx, step) for idx, step in enumerate(job.steps) if step.name]
|
|
149
|
+
|
|
150
|
+
if named_steps:
|
|
151
|
+
prev_step_id = None
|
|
152
|
+
|
|
153
|
+
for idx, step in named_steps:
|
|
154
|
+
step_id = f"{job_id_safe}_step_{idx}"
|
|
155
|
+
step_name = step.name
|
|
156
|
+
|
|
157
|
+
# Escape special characters in step names
|
|
158
|
+
step_name_escaped = (
|
|
159
|
+
step_name.replace('"', """).replace("[", "[").replace("]", "]")
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Determine node style based on step type
|
|
163
|
+
if step.uses:
|
|
164
|
+
# Action uses get rounded rectangle
|
|
165
|
+
lines.append(f' {step_id}("{step_name_escaped}")')
|
|
166
|
+
else:
|
|
167
|
+
# Regular run steps get standard rectangle
|
|
168
|
+
lines.append(f' {step_id}["{step_name_escaped}"]')
|
|
169
|
+
|
|
170
|
+
# Link to previous step
|
|
171
|
+
if prev_step_id:
|
|
172
|
+
lines.append(f" {prev_step_id} --> {step_id}")
|
|
173
|
+
|
|
174
|
+
prev_step_id = step_id
|
|
175
|
+
|
|
176
|
+
# Track first and last step nodes for job dependencies
|
|
177
|
+
first_step_idx, _ = named_steps[0]
|
|
178
|
+
last_step_idx, _ = named_steps[-1]
|
|
179
|
+
first_step_id = f"{job_id_safe}_step_{first_step_idx}"
|
|
180
|
+
last_step_id = f"{job_id_safe}_step_{last_step_idx}"
|
|
181
|
+
job_start_nodes[job.id] = first_step_id
|
|
182
|
+
job_end_nodes[job.id] = last_step_id
|
|
183
|
+
else:
|
|
184
|
+
# Job with no named steps - create a single placeholder node
|
|
185
|
+
placeholder_id = f"{job_id_safe}_placeholder"
|
|
186
|
+
lines.append(f" {placeholder_id}[No named steps defined]")
|
|
187
|
+
job_start_nodes[job.id] = placeholder_id
|
|
188
|
+
job_end_nodes[job.id] = placeholder_id
|
|
189
|
+
else:
|
|
190
|
+
# Job with no steps - create a single node
|
|
191
|
+
placeholder_id = f"{job_id_safe}_placeholder"
|
|
192
|
+
lines.append(f" {placeholder_id}[No steps defined]")
|
|
193
|
+
job_start_nodes[job.id] = placeholder_id
|
|
194
|
+
job_end_nodes[job.id] = placeholder_id
|
|
195
|
+
|
|
196
|
+
lines.append(" end")
|
|
197
|
+
|
|
198
|
+
# Add job dependencies
|
|
199
|
+
for job in workflow.jobs.values():
|
|
200
|
+
for needed_job_id in job.needs:
|
|
201
|
+
needed_job = workflow.jobs[needed_job_id]
|
|
202
|
+
lines.append(f" {needed_job.mermaid_id} -.-> {job.mermaid_id}")
|
|
203
|
+
|
|
204
|
+
return "\n".join(lines)
|
|
@@ -74,6 +74,17 @@ Context:
|
|
|
74
74
|
{% endif %}
|
|
75
75
|
{% endblock description %}
|
|
76
76
|
|
|
77
|
+
{% block flowchart scoped %}
|
|
78
|
+
{% if options.workflow_chart %}
|
|
79
|
+
{% set flowchart = data | generate_mermaid_flowchart(options.workflow_chart_step_direction) %}
|
|
80
|
+
{% if flowchart %}
|
|
81
|
+
<pre class="mermaid"><code>
|
|
82
|
+
{{ flowchart }}
|
|
83
|
+
</code></pre>
|
|
84
|
+
{% endif %}
|
|
85
|
+
{% endif %}
|
|
86
|
+
{% endblock flowchart %}
|
|
87
|
+
|
|
77
88
|
{% block inputs scoped %}
|
|
78
89
|
{% if options.show_inputs %}
|
|
79
90
|
{% with inputs = data.inputs | order_parameters(options.parameters_order) | filter_parameters(required=options.show_inputs_only_required) %}
|
|
File without changes
|
{mkdocstrings_github-0.6.3.dist-info → mkdocstrings_github-0.7.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|