Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/update-plugin-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ jobs:
source venv/bin/activate
python docs/generate_plugin_doc_bundle.py \
--package nodescraper.plugins.inband \
--output docs/PLUGIN_DOC.md
--output docs/PLUGIN_DOC.md \
--update-readme-help

- name: Clean pre-commit cache
run: |
Expand All @@ -50,7 +51,7 @@ jobs:
run: |
source venv/bin/activate
pre-commit install-hooks || true
pre-commit run --files docs/PLUGIN_DOC.md || true
pre-commit run --files docs/PLUGIN_DOC.md README.md || true

- name: Create Pull Request
uses: peter-evans/create-pull-request@v6
Expand Down
46 changes: 32 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ system debug.
- ['run-plugins' sub command](#run-plugins-sub-command)
- ['gen-plugin-config' sub command](#gen-plugin-config-sub-command)
- ['compare-runs' subcommand](#compare-runs-subcommand)
- ['show-redfish-oem-allowable' subcommand](#show-redfish-oem-allowable-subcommand)
- ['summary' sub command](#summary-sub-command)
- [Configs](#configs)
- [Global args](#global-args)
Expand Down Expand Up @@ -59,46 +58,65 @@ Sets up pre-commit hooks for code quality checks. On Debian/Ubuntu, you may need
The Node Scraper CLI can be used to run Node Scraper plugins on a target system. The following CLI
options are available:

<!-- node-scraper -h start -->
```sh
usage: node-scraper [-h] [--sys-name STRING] [--sys-location {LOCAL,REMOTE}] [--sys-interaction-level {PASSIVE,INTERACTIVE,DISRUPTIVE}] [--sys-sku STRING]
[--sys-platform STRING] [--plugin-configs [STRING ...]] [--system-config STRING] [--connection-config STRING] [--log-path STRING]
[--log-level {CRITICAL,FATAL,ERROR,WARN,WARNING,INFO,DEBUG,NOTSET}] [--gen-reference-config] [--skip-sudo]
{summary,run-plugins,describe,gen-plugin-config} ...
usage: cli.py [-h] [--version] [--sys-name STRING]
[--sys-location {LOCAL,REMOTE}]
[--sys-interaction-level {PASSIVE,INTERACTIVE,DISRUPTIVE}]
[--sys-sku STRING] [--sys-platform STRING]
[--plugin-configs [STRING ...]] [--system-config STRING]
[--connection-config STRING] [--log-path STRING]
[--log-level {CRITICAL,FATAL,ERROR,WARN,WARNING,INFO,DEBUG,NOTSET}]
[--gen-reference-config] [--skip-sudo]
{summary,run-plugins,describe,gen-plugin-config,compare-runs,show-redfish-oem-allowable}
...

node scraper CLI

positional arguments:
{summary,run-plugins,describe,gen-plugin-config}
{summary,run-plugins,describe,gen-plugin-config,compare-runs,show-redfish-oem-allowable}
Subcommands
summary Generates summary csv file
run-plugins Run a series of plugins
describe Display details on a built-in config or plugin
gen-plugin-config Generate a config for a plugin or list of plugins
compare-runs Compare datamodels from two run log directories
show-redfish-oem-allowable
Fetch OEM diagnostic allowable types from Redfish
LogService (for oem_diagnostic_types_allowable)

options:
optional arguments:
-h, --help show this help message and exit
--sys-name STRING System name (default: <my_system_name>)
--version show program's version number and exit
--sys-name STRING System name (default: <current hostname>)
--sys-location {LOCAL,REMOTE}
Location of target system (default: LOCAL)
--sys-interaction-level {PASSIVE,INTERACTIVE,DISRUPTIVE}
Specify system interaction level, used to determine the type of actions that plugins can perform (default: INTERACTIVE)
Specify system interaction level, used to determine
the type of actions that plugins can perform (default:
INTERACTIVE)
--sys-sku STRING Manually specify SKU of system (default: None)
--sys-platform STRING
Specify system platform (default: None)
--plugin-configs [STRING ...]
built-in config names or paths to plugin config JSONs. Available built-in configs: AllPlugins, NodeStatus (default: None)
built-in config names or paths to plugin config JSONs.
Available built-in configs: NodeStatus, AllPlugins
(default: None)
--system-config STRING
Path to system config json (default: None)
--connection-config STRING
Path to connection config json (default: None)
--log-path STRING Specifies local path for node scraper logs, use 'None' to disable logging (default: .)
--log-path STRING Specifies local path for node scraper logs, use 'None'
to disable logging (default: .)
--log-level {CRITICAL,FATAL,ERROR,WARN,WARNING,INFO,DEBUG,NOTSET}
Change python log level (default: INFO)
--gen-reference-config
Generate reference config from system. Writes to ./reference_config.json. (default: False)
--skip-sudo Skip plugins that require sudo permissions (default: False)

Generate reference config from system. Writes to
./reference_config.json. (default: False)
--skip-sudo Skip plugins that require sudo permissions (default:
False)
```
<!-- node-scraper -h end -->
### Execution Methods
Expand Down
73 changes: 72 additions & 1 deletion docs/generate_plugin_doc_bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,16 @@
python generate_plugin_doc_bundle.py \
--package /home/alexbara/node-scraper/nodescraper/plugins/inband \
--output PLUGIN_DOC.md
--update-readme-help

"""
import argparse
import importlib
import inspect
import os
import pkgutil
import re
import subprocess
import sys
from pathlib import Path
from typing import Any, Iterable, List, Optional, Type
Expand Down Expand Up @@ -513,6 +517,44 @@ def render_analyzer_args_section(args_cls: type, link_base: str, rel_root: Optio
return s


# Markers in README.md that bracket the node-scraper -h block (HTML comments, not rendered).
README_HELP_BLOCK_START = "<!-- node-scraper -h start -->"
README_HELP_BLOCK_END = "<!-- node-scraper -h end -->"


def update_readme_help(readme_path: Path) -> bool:
"""
Update the node-scraper -h output block in README.md.
The block must be wrapped with <!-- node-scraper -h start --> and <!-- node-scraper -h end -->.
"""
result = subprocess.run(
[sys.executable, "-m", "nodescraper.cli.cli", "-h"],
capture_output=True,
text=True,
cwd=readme_path.parent,
)
if result.returncode != 0:
return False
help_text = result.stdout.strip()
# Redact hostname in --sys-name default so README does not show machine name
help_text = re.sub(
r"(--sys-name STRING\s+System name \(default: )\S+",
r"\g<1><current hostname>)",
help_text,
)
content = readme_path.read_text(encoding="utf-8")
start_idx = content.find(README_HELP_BLOCK_START)
end_idx = content.find(README_HELP_BLOCK_END)
if start_idx == -1 or end_idx == -1 or end_idx <= start_idx:
return False
# Replace the entire bracketed block (from start marker through end marker)
block_end = end_idx + len(README_HELP_BLOCK_END)
new_block = f"{README_HELP_BLOCK_START}\n```sh\n{help_text}\n```\n{README_HELP_BLOCK_END}"
new_content = content[:start_idx] + new_block + content[block_end:]
readme_path.write_text(new_content, encoding="utf-8")
return True


def main():
ap = argparse.ArgumentParser(
description="Generate Plugin Table and detail sections with setup_link + rel-root."
Expand All @@ -521,6 +563,16 @@ def main():
"--package", default=DEFAULT_ROOT_PACKAGE, help="Dotted package or filesystem path"
)
ap.add_argument("--output", default="PLUGIN_DOC.md", help="Output Markdown file")
ap.add_argument(
"--update-readme-help",
action="store_true",
help="Update the node-scraper -h output block in README.md (run from repo root or with correct cwd)",
)
ap.add_argument(
"--readme",
default=None,
help="Path to README.md (default: README.md in current working directory)",
)
args = ap.parse_args()

root = args.package
Expand Down Expand Up @@ -601,7 +653,26 @@ def all_subclasses(cls: Type) -> set[type]:
for a in args_classes:
out.append(render_analyzer_args_section(a, LINK_BASE_DEFAULT, REL_ROOT_DEFAULT))

Path(args.output).write_text("".join(out), encoding="utf-8")
repo_root = Path(__file__).resolve().parent.parent
output_path = Path(args.output)
if not output_path.is_absolute():
output_path = repo_root / output_path
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text("".join(out), encoding="utf-8")

if args.update_readme_help:
readme_path = Path(args.readme) if args.readme else Path.cwd() / "README.md"
if not readme_path.is_file():
readme_path = Path(__file__).resolve().parent.parent / "README.md"
if readme_path.is_file():
if update_readme_help(readme_path):
print(f"Updated node-scraper -h block in {readme_path}") # noqa: T201
else:
print(f"Could not find or update -h block in {readme_path}") # noqa: T201
sys.exit(1)
else:
print(f"README not found: {readme_path}") # noqa: T201
sys.exit(1)


if __name__ == "__main__":
Expand Down
Loading