Skip to content

Instantly share code, notes, and snippets.

@lennysh
Created April 3, 2026 19:15
Show Gist options
  • Select an option

  • Save lennysh/58cadf2d8c9e59e61e3aea4899040769 to your computer and use it in GitHub Desktop.

Select an option

Save lennysh/58cadf2d8c9e59e61e3aea4899040769 to your computer and use it in GitHub Desktop.
Convert `oc explain ...` text to a commented YAML skeleton under `spec:`.
#!/usr/bin/env python3
"""
Convert `oc explain ...` text to a commented YAML skeleton under `spec:`.
Usage:
oc explain automationcontrollers.spec --api-version=... | oc-explain-to-yaml-template
oc explain ... | oc-explain-to-yaml-template --disabled
With --disabled, assignment lines are commented as "#fieldname: placeholder"
(no space after # so the key is easy to spot). Type stays a normal comment:
"# <type>".
Placeholders from explain types: string -> "", integer -> 0, boolean -> false,
Object -> {}, []string / []Object -> []
"""
from __future__ import annotations
import argparse
import re
import sys
FIELD_LINE = re.compile(r"^ ([a-zA-Z0-9_]+)\s+<([^>]+)>\s*$")
def placeholder_for_type(type_str: str) -> str:
t = type_str.strip()
if t == "string":
return '""'
if t == "integer":
return "0"
if t == "boolean":
return "false"
if t == "Object":
return "{}"
if t == "[]string":
return "[]"
if t == "[]Object":
return "[]"
return "null"
def yaml_comment_block(text: str, indent: str) -> list[str]:
lines = []
for raw in text.strip().splitlines():
line = raw.rstrip()
if not line:
continue
safe = line
if safe.startswith("#"):
safe = " " + safe
lines.append(f"{indent}# {safe}")
return lines
def parse_fields(body: str) -> list[tuple[str, str, str]]:
"""Return (name, type_str, comment_text) per field."""
if "FIELDS:" not in body:
return []
_, rest = body.split("FIELDS:", 1)
lines = rest.splitlines()
fields: list[tuple[str, str, str]] = []
name: str | None = None
typ: str | None = None
buf: list[str] = []
def flush() -> None:
nonlocal name, typ, buf
if name is not None and typ is not None:
fields.append((name, typ, "\n".join(buf)))
name = None
typ = None
buf = []
for line in lines:
m = FIELD_LINE.match(line)
if m:
flush()
name, typ = m.group(1), m.group(2)
continue
if name is None:
continue
s = line.rstrip()
if not s:
continue
if s.startswith(" "):
buf.append(s[4:])
elif s.startswith(" enum:"):
buf.append(s[2:])
else:
buf.append(s.strip())
flush()
return fields
def emit_yaml(fields: list[tuple[str, str, str]], *, disabled: bool) -> str:
out: list[str] = [
"# Generated from oc explain — fill in values.",
"# For Object fields, run: oc explain automationcontrollers.spec.<field> \\",
"# --api-version=automationcontroller.ansible.com/v1beta1",
"",
"spec:",
]
for field_name, type_str, comment in fields:
out.append("")
if comment.strip():
out.extend(yaml_comment_block(comment, " "))
ph = placeholder_for_type(type_str)
out.append(f" # <{type_str}>")
if disabled:
out.append(f" #{field_name}: {ph}")
else:
out.append(f" {field_name}: {ph}")
out.append("")
return "\n".join(out)
def main() -> int:
parser = argparse.ArgumentParser(
description="Convert oc explain output to a spec YAML skeleton.",
)
parser.add_argument(
"--disabled",
action="store_true",
help="Comment out field: value lines (#name: placeholder); type stays # <type>",
)
args = parser.parse_args()
raw = sys.stdin.read()
if not raw.strip():
print("oc-explain-to-yaml-template: empty stdin", file=sys.stderr)
return 1
fields = parse_fields(raw)
if not fields:
print(
"oc-explain-to-yaml-template: no FIELDS: section found",
file=sys.stderr,
)
return 1
sys.stdout.write(emit_yaml(fields, disabled=args.disabled))
return 0
if __name__ == "__main__":
raise SystemExit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment