mirror of
https://github.com/ansible/ansible.git
synced 2026-08-02 19:51:59 +02:00
Support frontmatter in pymarkdown sanity test (#87164)
* Fix formatting of skill markdown * Support frontmatter in pymarkdown sanity test * Allow .github/ to use frontmatter
This commit is contained in:
@@ -6,21 +6,23 @@ user-invocable: true
|
||||
|
||||
# System Instructions
|
||||
|
||||
When this skill is invoked, read the AGENTS.md file from the repository root (`@../../../AGENTS.md`) and load its content into context. This provides comprehensive Ansible development guidelines.
|
||||
When this skill is invoked, read the AGENTS.md file from the repository root (`@../../../AGENTS.md`) and load its content into context.
|
||||
This provides comprehensive Ansible development guidelines.
|
||||
|
||||
After loading AGENTS.md, provide the user with confirmation that the context has been loaded and is available for answering questions or guiding development work.
|
||||
|
||||
If AGENTS.md cannot be found, inform the user and suggest they may be in a directory without the full ansible-core repository.
|
||||
|
||||
# Usage
|
||||
## Usage
|
||||
|
||||
Invoke this skill to load Ansible development context:
|
||||
|
||||
```
|
||||
```claude
|
||||
/context
|
||||
```
|
||||
|
||||
The skill provides all guidelines and conventions that would normally be in AGENTS.md, making them available even when working outside the ansible-core repository or in environments where AGENTS.md is not accessible.
|
||||
The skill provides all guidelines and conventions that would normally be in AGENTS.md,
|
||||
making them available even when working outside the ansible-core repository or in environments where AGENTS.md is not accessible.
|
||||
|
||||
## When to Use
|
||||
|
||||
@@ -31,7 +33,8 @@ The skill provides all guidelines and conventions that would normally be in AGEN
|
||||
|
||||
## What This Skill Does
|
||||
|
||||
This skill is informational only - it loads comprehensive Ansible development knowledge into context but performs no actions. After invocation, all subsequent responses will have access to:
|
||||
This skill is informational only - it loads comprehensive Ansible development knowledge into context but performs no actions.
|
||||
After invocation, all subsequent responses will have access to:
|
||||
|
||||
- Testing commands
|
||||
- PR review processes and checklists
|
||||
|
||||
@@ -6,5 +6,4 @@ labels: [core-internal]
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
|
||||
@ansibot bot_skip
|
||||
|
||||
@@ -7,19 +7,107 @@ import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import yaml
|
||||
|
||||
try:
|
||||
from yaml import CSafeLoader as SafeLoader
|
||||
except ImportError:
|
||||
from yaml import SafeLoader
|
||||
|
||||
import typing as t
|
||||
|
||||
|
||||
def main() -> None:
|
||||
paths = sys.argv[1:] or sys.stdin.read().splitlines()
|
||||
|
||||
self_dir = pathlib.Path(__file__).parent
|
||||
base_dir = self_dir.parent.parent.parent
|
||||
config_path = self_dir / 'pymarkdown.config.json'
|
||||
|
||||
# This regex is intentionally a very loose check for potential frontmatter.
|
||||
# There's no good reason for a non-frontmatter containing file to match this pattern.
|
||||
frontmatter_pattern = re.compile(r'^\s*--')
|
||||
|
||||
frontmatter_allowed_prefixes = [
|
||||
".claude/",
|
||||
".github/",
|
||||
]
|
||||
|
||||
frontmatter_paths = []
|
||||
other_paths = []
|
||||
results = []
|
||||
|
||||
for path in paths:
|
||||
content = pathlib.Path(path).read_text()
|
||||
has_frontmatter = frontmatter_pattern.search(content)
|
||||
allow_frontmatter = any(path.startswith(prefix) for prefix in frontmatter_allowed_prefixes)
|
||||
|
||||
# When potential frontmatter is detected, check it before running the document through pymarkdown.
|
||||
# This avoids treating frontmatter issues as Markdown issues, which would otherwise lead to confusing error messages.
|
||||
|
||||
if allow_frontmatter:
|
||||
if has_frontmatter and (issues := check_frontmatter(path, content)):
|
||||
results.extend(issues)
|
||||
else:
|
||||
frontmatter_paths.append(path)
|
||||
else:
|
||||
if has_frontmatter:
|
||||
results.append(f'{path}:1:1: frontmatter-not-allowed: frontmatter is not allowed in this document')
|
||||
else:
|
||||
other_paths.append(path)
|
||||
|
||||
results.extend(run_pymarkdown(frontmatter_paths, config_path, base_dir, enable_frontmatter=True))
|
||||
results.extend(run_pymarkdown(other_paths, config_path, base_dir, enable_frontmatter=False))
|
||||
|
||||
if results:
|
||||
print('\n'.join(results))
|
||||
|
||||
|
||||
def check_frontmatter(path: str, content: str) -> list[str]:
|
||||
"""
|
||||
Check frontmatter formatting.
|
||||
Use only after detecting signs of frontmatter usage.
|
||||
This supplements the frontmatter support in pymarkdown, which is less aggressive about detecting potential frontmatter.
|
||||
"""
|
||||
match = re.search(r"^(?P<content>---\n.*?)\n---\n", content, flags=re.DOTALL)
|
||||
|
||||
if not match:
|
||||
return [f"{path}:1:1: malformed-frontmatter: document appears to start with malformed frontmatter"]
|
||||
|
||||
yaml_content = match.group('content')
|
||||
|
||||
try:
|
||||
yaml.load(yaml_content, Loader=SafeLoader)
|
||||
except yaml.MarkedYAMLError as ex:
|
||||
message = ' '.join(filter(None, (ex.context, ex.problem, ex.note))).strip()
|
||||
|
||||
return [f"{path}:{ex.problem_mark.line}:{ex.problem_mark.column}: frontmatter-yaml: {message}"]
|
||||
except yaml.YAMLError as ex:
|
||||
return [f"{path}:1:1: frontmatter-yaml: {ex}"]
|
||||
|
||||
return []
|
||||
|
||||
|
||||
def run_pymarkdown(paths: list[str], config_path: pathlib.Path, base_dir: pathlib.Path, enable_frontmatter: bool) -> list[str]:
|
||||
"""Run pymarkdown on the given paths and return a list of results."""
|
||||
|
||||
if not paths:
|
||||
return []
|
||||
|
||||
extensions: list[str] = []
|
||||
|
||||
if enable_frontmatter:
|
||||
extensions.append('front-matter')
|
||||
|
||||
cmd = [
|
||||
sys.executable,
|
||||
'-m',
|
||||
'pymarkdown',
|
||||
'--config',
|
||||
pathlib.Path(__file__).parent / 'pymarkdown.config.json',
|
||||
config_path,
|
||||
'--strict-config',
|
||||
'--enable-extensions',
|
||||
','.join(extensions),
|
||||
'scan',
|
||||
] + paths
|
||||
|
||||
@@ -36,19 +124,17 @@ def main() -> None:
|
||||
sys.exit(1)
|
||||
|
||||
if not (stdout := process.stdout.strip()):
|
||||
return
|
||||
return []
|
||||
|
||||
pattern = re.compile(r'^(?P<path>[^:]*):(?P<line>[0-9]+):(?P<column>[0-9]+): (?P<code>[^:]*): (?P<message>.*) \((?P<aliases>.*)\)$')
|
||||
matches = parse_to_list_of_dict(pattern, stdout)
|
||||
|
||||
base_dir = pathlib.Path(__file__).parent.parent.parent.parent
|
||||
|
||||
for match in matches:
|
||||
match['path'] = str(pathlib.Path(match['path']).relative_to(base_dir))
|
||||
|
||||
results = [f"{match['path']}:{match['line']}:{match['column']}: {match['aliases'].split(', ')[0]}: {match['message']}" for match in matches]
|
||||
|
||||
print('\n'.join(results))
|
||||
return results
|
||||
|
||||
|
||||
def parse_to_list_of_dict(pattern: re.Pattern, value: str) -> list[dict[str, t.Any]]:
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
.claude/skills/azp-logs/SKILL.md pymarkdown!skip # Claude Code skill with YAML frontmatter
|
||||
.claude/skills/context/SKILL.md pymarkdown!skip # Claude Code skill with YAML frontmatter
|
||||
.claude/skills/review/SKILL.md pymarkdown!skip # Claude Code skill with YAML frontmatter
|
||||
.github/ISSUE_TEMPLATE/internal_issue.md pymarkdown!skip
|
||||
lib/ansible/_internal/_wrapt.py black!skip # vendored code
|
||||
lib/ansible/config/base.yml no-unwanted-files
|
||||
lib/ansible/keyword_desc.yml no-unwanted-files
|
||||
|
||||
Reference in New Issue
Block a user