forked from MichaelCade/90DaysOfDevOps
-
Notifications
You must be signed in to change notification settings - Fork 0
110 lines (93 loc) · 4.06 KB
/
Copy pathblog-post-validation.yml
File metadata and controls
110 lines (93 loc) · 4.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
name: Blog Post Validation
on:
pull_request:
paths:
- "blog/posts/*.md"
- "blog/_template.md"
permissions:
contents: read
jobs:
validate-blog-posts:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Collect changed blog posts
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
git diff --name-only "$BASE_SHA" "$HEAD_SHA" -- 'blog/posts/*.md' > changed_blog_posts.txt
echo "Changed posts:"
cat changed_blog_posts.txt || true
- name: Validate metadata and local links
run: |
python - <<'PY'
from pathlib import Path
from datetime import datetime
import re
import sys
changed_file_list = Path("changed_blog_posts.txt")
changed_files = []
if changed_file_list.exists():
changed_files = [line.strip() for line in changed_file_list.read_text(encoding="utf-8").splitlines() if line.strip()]
if not changed_files:
print("No changed blog post files detected.")
raise SystemExit(0)
required_fields = ["title", "author", "date", "tags", "summary"]
all_errors = []
for rel_path in changed_files:
path = Path(rel_path)
if not path.exists():
continue
text = path.read_text(encoding="utf-8")
lines = text.splitlines()
errors = []
if not lines or lines[0].strip() != "---":
errors.append("Missing YAML front matter opening delimiter '---'.")
else:
end_idx = None
for idx in range(1, len(lines)):
if lines[idx].strip() == "---":
end_idx = idx
break
if end_idx is None:
errors.append("Missing YAML front matter closing delimiter '---'.")
front_matter_lines = []
else:
front_matter_lines = lines[1:end_idx]
if end_idx is not None:
front_matter_text = "\n".join(front_matter_lines)
for field in required_fields:
if not re.search(rf"(?m)^{field}\s*:", front_matter_text):
errors.append(f"Missing required metadata field: {field}")
date_match = re.search(r'(?m)^date\s*:\s*"?(.*?)"?\s*$', front_matter_text)
if date_match:
date_value = date_match.group(1).strip()
try:
post_date = datetime.strptime(date_value, "%Y-%m-%d").date()
if post_date > datetime.now().date():
errors.append("Date cannot be in the future.")
except ValueError:
errors.append("Invalid date format. Expected YYYY-MM-DD.")
for link in re.findall(r"\[[^\]]+\]\(([^)]+)\)", text):
target = link.strip()
if not target or target.startswith(("http://", "https://", "mailto:", "#")):
continue
target = target.split("#", 1)[0].split("?", 1)[0]
if not target:
continue
resolved = (path.parent / target).resolve()
if not resolved.exists():
errors.append(f"Broken local link: {link}")
if errors:
all_errors.append(f"{rel_path}:")
all_errors.extend([f" - {err}" for err in errors])
if all_errors:
print("Validation failed:\n")
print("\n".join(all_errors))
sys.exit(1)
print("All changed blog posts passed metadata and local link validation.")
PY