mirror of
https://github.com/prometheus/docs.git
synced 2026-02-05 06:45:01 +01:00
chore(ci): add action to test abnf syntax and examples in OM2.0 spec
Action item from OpenMetrics 2.0 WG. Signed-off-by: György Krajcsovits <gyorgy.krajcsovits@grafana.com>
This commit is contained in:
24
.github/workflows/openmetrics.yml
vendored
Normal file
24
.github/workflows/openmetrics.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
name: OpenMetrics
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'docs/specs/om/open_metrics_spec_2_0.md'
|
||||
|
||||
jobs:
|
||||
check-abnf:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- name: Set up Python 3.x
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
||||
with:
|
||||
python-version: "3.12.3"
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install abnf
|
||||
- name: Check ABNF for OpenMetrics 2.0
|
||||
run: |
|
||||
python3 scripts/check_openmetrics_spec.py docs/specs/om/open_metrics_spec_2_0.md
|
||||
@@ -399,7 +399,7 @@ Line endings MUST be signalled with line feed (\n) and MUST NOT contain carriage
|
||||
|
||||
An example of a complete exposition:
|
||||
|
||||
```
|
||||
```openmetrics
|
||||
# TYPE acme_http_router_request_seconds summary
|
||||
# UNIT acme_http_router_request_seconds seconds
|
||||
# HELP acme_http_router_request_seconds Latency though all of ACME's HTTP request router.
|
||||
|
||||
108
scripts/check_openmetrics_spec.py
Normal file
108
scripts/check_openmetrics_spec.py
Normal file
@@ -0,0 +1,108 @@
|
||||
#!/bin/env python3
|
||||
#
|
||||
# This script opens a markdown file containing the OpenMetrics specification,
|
||||
# extracts the ABNF grammar from it, and checks if the grammar is valid.
|
||||
# ABNF grammer must be enclosed in
|
||||
# ```abnf
|
||||
# exposition = metricset HASH SP eof [ LF ]
|
||||
# ...
|
||||
# ```
|
||||
# code block, and the top node must be `exposition`.
|
||||
# It also extracts examples from the OpenMetrics spec file and checks if they
|
||||
# are valid according to the grammar.
|
||||
# Exampes must be enclosed in
|
||||
# ```openmetrics
|
||||
# ... example content ...
|
||||
# ```
|
||||
# code blocks.
|
||||
|
||||
from abnf import Rule
|
||||
import sys
|
||||
|
||||
class Grammar(Rule):
|
||||
pass
|
||||
|
||||
# Start node for the OpenMetrics spec.
|
||||
start_node = 'exposition'
|
||||
|
||||
def get_spec(filename):
|
||||
with open(filename, 'r') as file:
|
||||
lines = file.readlines()
|
||||
spec = []
|
||||
collecting = False
|
||||
for line in lines:
|
||||
if collecting:
|
||||
if line.startswith('```'):
|
||||
collecting = False
|
||||
else:
|
||||
spec.append(line.strip())
|
||||
continue
|
||||
if line.startswith('```abnf'):
|
||||
if len(spec) > 0:
|
||||
raise ValueError("Multiple ABNF blocks found in the file.")
|
||||
collecting = True
|
||||
|
||||
if len(spec) == 0:
|
||||
raise ValueError("No or empty ABNF block found in the file. Wanted ```abnf ... ```.")
|
||||
return '\n'.join(spec)
|
||||
|
||||
|
||||
class example:
|
||||
def __init__(self, line_number, content):
|
||||
self.line_number = line_number
|
||||
self.content = content
|
||||
|
||||
class examples:
|
||||
"""
|
||||
Extracts examples from the OpenMetrics spec file with generator function.
|
||||
"""
|
||||
def __init__(self, filename):
|
||||
self.file = open(filename, 'r')
|
||||
self.line_number = 0
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
collecting = False
|
||||
start_line = self.line_number
|
||||
example_lines = []
|
||||
for line in self.file:
|
||||
self.line_number += 1
|
||||
if collecting:
|
||||
if line.startswith('```'):
|
||||
collecting = False
|
||||
break
|
||||
else:
|
||||
example_lines.append(line)
|
||||
elif line.startswith('```openmetrics'):
|
||||
start_line = self.line_number
|
||||
collecting = True
|
||||
if len(example_lines) > 0:
|
||||
return example(start_line, ''.join(example_lines).strip())
|
||||
|
||||
raise StopIteration("No more examples found.")
|
||||
|
||||
# Main
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: python3 check_openmetrics_spec.py <filename.md>")
|
||||
sys.exit(1)
|
||||
|
||||
filename = sys.argv[1]
|
||||
if not filename.endswith('.md'):
|
||||
print(f"Error: {filename} is not a Markdown file.")
|
||||
sys.exit(1)
|
||||
spec = get_spec(filename)
|
||||
try:
|
||||
Grammar.load_grammar(grammar=spec, strict=True)
|
||||
except Exception as e:
|
||||
print(f"Error parsing ABNF: {e}")
|
||||
sys.exit(1)
|
||||
print("ABNF parsed successfully.")
|
||||
for ex in examples(filename):
|
||||
try:
|
||||
Grammar.get(start_node).parse_all(ex.content)
|
||||
print(f"Example parsed successfully: {ex.line_number}: {ex.content[:30]}...") # Print first 30 chars
|
||||
except Exception as e:
|
||||
print(f"Error parsing example at line {ex.line_number}: {e}\nExample: {ex.content[:30]}...")
|
||||
Reference in New Issue
Block a user