Skip to content

Commit 576ddb4

Browse files
Merge branch 'development' into alex_devenum_update
2 parents 35194f1 + 5a8964b commit 576ddb4

File tree

17 files changed

+1534
-138
lines changed

17 files changed

+1534
-138
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Workflow to run plugin documentation generation then create a PR with the updated changes
2+
3+
name: Plugin Documentation Generator
4+
5+
permissions:
6+
contents: write
7+
pull-requests: write
8+
9+
on:
10+
workflow_dispatch:
11+
schedule:
12+
- cron: '0 0 * * *'
13+
14+
jobs:
15+
generate_docs:
16+
runs-on: [ self-hosted ]
17+
# To disable this workflow, set DISABLE_AUTO_DOCS to 'true' in repository variables
18+
if: vars.DISABLE_AUTO_DOCS != 'true'
19+
env:
20+
HOME: /tmp/github-actions-home
21+
22+
steps:
23+
- name: Setup HOME directory
24+
run: |
25+
mkdir -p /tmp/github-actions-home
26+
export HOME=/tmp/github-actions-home
27+
28+
- name: Checkout repository
29+
uses: actions/checkout@v4
30+
with:
31+
token: ${{ secrets.GITHUB_TOKEN }}
32+
33+
- name: Install dependencies
34+
run: ./dev-setup.sh
35+
36+
- name: Run plugin documentation generator
37+
run: |
38+
source venv/bin/activate
39+
python docs/generate_plugin_doc_bundle.py \
40+
--package nodescraper.plugins.inband \
41+
--output docs/PLUGIN_DOC.md
42+
43+
- name: Format documentation with pre-commit
44+
run: |
45+
source venv/bin/activate
46+
pre-commit run --files docs/PLUGIN_DOC.md || true
47+
48+
- name: Create Pull Request
49+
uses: peter-evans/create-pull-request@v6
50+
with:
51+
token: ${{ secrets.GITHUB_TOKEN }}
52+
commit-message: "docs: Update plugin documentation [automated]"
53+
committer: "github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
54+
author: "github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
55+
branch: automated-plugin-docs-update
56+
delete-branch: true
57+
title: "docs: Update plugin documentation [automated]"
58+
body: |
59+
Automated plugin documentation update generated by workflow.
60+
61+
This PR was automatically created by the Plugin Documentation Generator workflow.
62+
labels: documentation,automated

CONTRIBUTING.md

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,41 @@ When creating a PR, use the following process.
5252
> By creating a PR, you agree to allow your contribution to be licensed under the
5353
> terms of the LICENSE.txt file.
5454
55-
### Documentation
55+
### Pre-commit hooks
5656

57-
Submit Node Scraper documentation changes to our
58-
[documentation](https://github.com/amd/node-scraper/blob/development/README.md). You must update
59-
documentation related to any new feature or API contribution.
57+
This repository uses [pre-commit](https://pre-commit.com/) to automatically format code. When you commit changes to plugin files, the hooks will:
58+
59+
1. Run code formatters (ruff, black)
60+
2. Run type checking (mypy)
61+
62+
#### Setup
63+
64+
Install pre-commit hooks after cloning the repository:
65+
66+
```bash
67+
# Activate your virtual environment
68+
source venv/bin/activate
69+
70+
# Install pre-commit hooks
71+
pre-commit install
72+
```
73+
74+
#### Usage
75+
76+
The hooks run automatically when you commit.
77+
78+
```bash
79+
# First commit attempt - hooks run and may modify files
80+
git commit -m "Add new plugin feature"
81+
82+
# If hooks modified files, stage them and commit again
83+
git add .
84+
git commit -m "Add new plugin feature"
85+
```
86+
87+
You can also run hooks manually:
88+
89+
```bash
90+
# Run all hooks on all files
91+
pre-commit run --all-files
92+
```

docs/PLUGIN_DOC.md

Lines changed: 401 additions & 67 deletions
Large diffs are not rendered by default.

docs/generate_plugin_doc_bundle.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ def extract_regexes_and_args_from_analyzer(
269269
output.append("**Analyzer Args:**")
270270
for key, value in anns.items():
271271
# Format the type annotation
272-
type_str = str(value).replace("typing.", "")
272+
type_str = format_type_annotation(value)
273273
output.append(f"- `{key}`: {type_str}")
274274

275275
return output
@@ -294,9 +294,23 @@ def bases_list(cls: type) -> List[str]:
294294
return []
295295

296296

297+
def format_type_annotation(type_ann: Any) -> str:
298+
"""
299+
Format a type annotation for documentation, removing non-deterministic content like function memory addresses.
300+
"""
301+
import re
302+
303+
type_str = str(type_ann)
304+
type_str = type_str.replace("typing.", "")
305+
type_str = re.sub(r"<function\s+(\w+)\s+at\s+0x[0-9a-fA-F]+>", r"\1", type_str)
306+
type_str = re.sub(r"<bound method\s+(\S+)\s+of\s+.+?>", r"\1", type_str)
307+
type_str = re.sub(r"<class '([^']+)'>", r"\1", type_str)
308+
return type_str
309+
310+
297311
def annotations_for_model(model_cls: type) -> List[str]:
298312
anns = get_attr(model_cls, "__annotations__", {}) or {}
299-
return [f"**{k}**: `{v}`" for k, v in anns.items()]
313+
return [f"**{k}**: `{format_type_annotation(v)}`" for k, v in anns.items()]
300314

301315

302316
def class_vars_dump(cls: type, exclude: set) -> List[str]:
@@ -458,7 +472,7 @@ def render_analyzer_args_section(args_cls: type, link_base: str, rel_root: Optio
458472

459473
anns = get_attr(args_cls, "__annotations__", {}) or {}
460474
if anns:
461-
ann_items = [f"**{k}**: `{v}`" for k, v in anns.items()]
475+
ann_items = [f"**{k}**: `{format_type_annotation(v)}`" for k, v in anns.items()]
462476
s += md_header("Annotations / fields", 3) + md_list(ann_items)
463477
return s
464478

nodescraper/cli/cli.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ def process_args(
334334
plugin_arg_index = -1
335335

336336
plugin_arg_map = {}
337+
invalid_plugins = []
337338
if plugin_arg_index != -1 and plugin_arg_index != len(raw_arg_input) - 1:
338339
top_level_args = raw_arg_input[: plugin_arg_index + 1]
339340
plugin_args = raw_arg_input[plugin_arg_index + 1 :]
@@ -344,12 +345,26 @@ def process_args(
344345
else:
345346
cur_plugin = None
346347
for arg in plugin_args:
347-
if arg in plugin_names:
348+
# Handle comma-separated plugin names (but not arguments)
349+
if not arg.startswith("-") and "," in arg:
350+
# Split comma-separated plugin names
351+
for potential_plugin in arg.split(","):
352+
potential_plugin = potential_plugin.strip()
353+
if potential_plugin in plugin_names:
354+
plugin_arg_map[potential_plugin] = []
355+
cur_plugin = potential_plugin
356+
elif potential_plugin:
357+
# Track invalid plugin names to log event later
358+
invalid_plugins.append(potential_plugin)
359+
elif arg in plugin_names:
348360
plugin_arg_map[arg] = []
349361
cur_plugin = arg
350362
elif cur_plugin:
351363
plugin_arg_map[cur_plugin].append(arg)
352-
return (top_level_args, plugin_arg_map)
364+
elif not arg.startswith("-"):
365+
# Track invalid plugin names to log event later
366+
invalid_plugins.append(arg)
367+
return (top_level_args, plugin_arg_map, invalid_plugins)
353368

354369

355370
def main(arg_input: Optional[list[str]] = None):
@@ -367,7 +382,9 @@ def main(arg_input: Optional[list[str]] = None):
367382
parser, plugin_subparser_map = build_parser(plugin_reg, config_reg)
368383

369384
try:
370-
top_level_args, plugin_arg_map = process_args(arg_input, list(plugin_subparser_map.keys()))
385+
top_level_args, plugin_arg_map, invalid_plugins = process_args(
386+
arg_input, list(plugin_subparser_map.keys())
387+
)
371388

372389
parsed_args = parser.parse_args(top_level_args)
373390
system_info = get_system_info(parsed_args)
@@ -387,6 +404,13 @@ def main(arg_input: Optional[list[str]] = None):
387404
if log_path:
388405
logger.info("Log path: %s", log_path)
389406

407+
# Log warning if invalid plugin names were provided
408+
if invalid_plugins:
409+
logger.warning(
410+
"Invalid plugin name(s) ignored: %s. Use 'describe plugin' to list available plugins.",
411+
", ".join(invalid_plugins),
412+
)
413+
390414
if parsed_args.subcmd == "summary":
391415
generate_summary(parsed_args.search_path, parsed_args.output_path, logger)
392416
sys.exit(0)

nodescraper/pluginexecutor.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,12 @@ def run_queue(self) -> list[PluginResult]:
173173
global_run_args = self.apply_global_args_to_plugin(
174174
plugin_inst, plugin_class, self.plugin_config.global_args
175175
)
176+
# Merge analysis_args and collection_args
177+
for args_key in ["analysis_args", "collection_args"]:
178+
if args_key in global_run_args and args_key in run_payload:
179+
# Merge: global args override plugin-specific args keys specified in both global and plugin-specific args
180+
run_payload[args_key].update(global_run_args[args_key])
181+
del global_run_args[args_key]
176182
run_payload.update(global_run_args)
177183
except ValueError as ve:
178184
self.logger.error(

0 commit comments

Comments
 (0)