Skip to content

Commit 7ca6f3f

Browse files
Copilotvgoehler
andauthored
feat: add version validation and auto-increment to PDF generation workflow
Agent-Logs-Url: https://github.com/TUBAF-IfI-LiaScript/VL_Softwareentwicklung/sessions/1804ece4-9a3f-46bb-8532-135e03ab4d0d Co-authored-by: vgoehler <1705385+vgoehler@users.noreply.github.com>
1 parent 85298cc commit 7ca6f3f

2 files changed

Lines changed: 103 additions & 18 deletions

File tree

.github/workflows/generate_pdfs.yml

Lines changed: 98 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,89 @@ jobs:
4646
echo "changed_files=${files}" >> "$GITHUB_OUTPUT"
4747
echo "Files to process: ${files}"
4848
49+
# -----------------------------------------------------------------------
50+
# Validate that every changed file has a version tag and auto-increment
51+
# the patch component when the corresponding release tag already exists.
52+
#
53+
# Logic:
54+
# 1. Extract version from the <!-- ... --> frontmatter.
55+
# → Fail hard if the tag is absent.
56+
# 2. Compute the release tag (same algorithm as the PDF step below).
57+
# → If the release tag already exists on GitHub, the user forgot to
58+
# bump the version. We increment the patch component in the file.
59+
# 3. Commit & push any modified files with "[skip ci]" so the PDF step
60+
# reads the correct (bumped) version from the working tree.
61+
# -----------------------------------------------------------------------
62+
- name: Check versions and auto-increment patch if tag already exists
63+
if: steps.changes.outputs.changed_files != ''
64+
run: |
65+
set -euo pipefail
66+
67+
git config user.name "github-actions[bot]"
68+
git config user.email "github-actions[bot]@users.noreply.github.com"
69+
70+
bumped_files=""
71+
error_files=""
72+
73+
for file in ${{ steps.changes.outputs.changed_files }}; do
74+
[ -f "$file" ] || continue
75+
76+
filename=$(basename "$file" .md)
77+
78+
# ── 1. Require a version tag ───────────────────────────────────
79+
version=$(grep -oP '(?m)^version:\s*\K[\d.]+' "$file" | head -1 || true)
80+
if [ -z "$version" ]; then
81+
echo "ERROR: $file has no 'version:' tag in its frontmatter." >&2
82+
error_files="$error_files $file"
83+
continue
84+
fi
85+
86+
# ── 2. Compute safe release tag ───────────────────────────────
87+
safe_tag=$(echo "$filename" \
88+
| tr '[:upper:]' '[:lower:]' \
89+
| sed -e 's/ä/ae/g' -e 's/ö/oe/g' -e 's/ü/ue/g' -e 's/ß/ss/g' \
90+
-e 's/Ä/ae/g' -e 's/Ö/oe/g' -e 's/Ü/ue/g' \
91+
-e 's/[^a-z0-9_-]//g')
92+
release_tag="${safe_tag}_v${version}"
93+
94+
# ── 3. Auto-increment patch if release tag already exists ─────
95+
if gh release view "$release_tag" > /dev/null 2>&1; then
96+
echo "Tag '$release_tag' already exists for $file – auto-incrementing patch."
97+
98+
# Increment last numeric component: e.g. 1.0.10 → 1.0.11
99+
new_version=$(echo "$version" | awk -F. '{$NF = $NF + 1; print}' OFS='.')
100+
101+
# Replace the version line inside the HTML comment frontmatter.
102+
# The sed pattern handles optional spaces around the colon.
103+
sed -i -E "s/^(version:[[:space:]]*)[0-9]+(\.[0-9]+)*/\1${new_version}/" "$file"
104+
105+
echo " $file: $version → $new_version"
106+
bumped_files="$bumped_files $file"
107+
else
108+
echo "Tag '$release_tag' is new – no version bump needed for $file."
109+
fi
110+
done
111+
112+
# ── Fail if any file was missing a version tag ───────────────────
113+
if [ -n "$error_files" ]; then
114+
echo ""
115+
echo "The following files are missing a 'version:' tag:" >&2
116+
for f in $error_files; do echo " $f" >&2; done
117+
echo "Please add 'version: X.Y.Z' to the HTML comment frontmatter." >&2
118+
exit 1
119+
fi
120+
121+
# ── Commit & push version bumps ───────────────────────────────────
122+
if [ -n "$bumped_files" ]; then
123+
git add $bumped_files
124+
git commit -m "ci: auto-increment patch version for changed courses [skip ci]"
125+
git pull --rebase origin master || true
126+
git push
127+
echo "Version bumps pushed to master."
128+
fi
129+
env:
130+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
131+
49132
- name: Set up Node.js
50133
if: steps.changes.outputs.changed_files != ''
51134
uses: actions/setup-node@v4
@@ -74,8 +157,9 @@ jobs:
74157
-e 's/Ä/ae/g' -e 's/Ö/oe/g' -e 's/Ü/ue/g' \
75158
-e 's/[^a-z0-9_-]//g')
76159
77-
# Extract version from frontmatter (e.g. "version: 1.0.10")
78-
version=$(grep -oP 'version:\s*\K[\d.]+' "$file" | head -1 || echo "unknown")
160+
# Re-read version from the file (may have been bumped in the
161+
# previous step; the version tag is guaranteed to exist by now).
162+
version=$(grep -oP '(?m)^version:\s*\K[\d.]+' "$file" | head -1)
79163
80164
asset_name="${filename}_v${version}_Documentation"
81165
release_tag="${safe_tag}_v${version}"
@@ -92,20 +176,15 @@ jobs:
92176
pdf_path="/tmp/pdf_build/${asset_name}.pdf"
93177
94178
if [ ! -f "$pdf_path" ]; then
95-
echo "WARNING: PDF not created for $file – skipping release."
96-
continue
179+
echo "ERROR: PDF not created for $file" >&2
180+
exit 1
97181
fi
98182
99-
# Create or update the GitHub Release
100-
if gh release view "$release_tag" > /dev/null 2>&1; then
101-
echo "Release $release_tag already exists – uploading updated asset."
102-
gh release upload "$release_tag" "$pdf_path" --clobber
103-
else
104-
gh release create "$release_tag" \
105-
--title "PDF – ${filename} (v${version})" \
106-
--notes "Automated PDF export for \`${file}\` – version ${version}" \
107-
"$pdf_path"
108-
fi
183+
# The version-check step guarantees the tag is new at this point.
184+
gh release create "$release_tag" \
185+
--title "PDF – ${filename} (v${version})" \
186+
--notes "Automated PDF export for \`${file}\` – version ${version}" \
187+
"$pdf_path"
109188
done
110189
env:
111190
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -124,6 +203,11 @@ jobs:
124203
with:
125204
token: ${{ secrets.GITHUB_TOKEN }}
126205

206+
# Pull any version-bump commits pushed by the generate_pdfs job so that
207+
# liaex fetches up-to-date metadata from raw.githubusercontent.com.
208+
- name: Pull latest commits (including any version bumps)
209+
run: git pull --ff-only origin master || git pull origin master
210+
127211
- name: Set up Node.js
128212
uses: actions/setup-node@v4
129213
with:

inject_pdf_links.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import re
2121
import sys
2222
from pathlib import Path
23+
from typing import Dict, Optional
2324
from urllib.parse import urlparse
2425
import posixpath
2526

@@ -143,7 +144,7 @@ def release_exists(url: str, timeout: int = 10) -> bool:
143144
# Build the course-info lookup table from JSON-LD
144145
# ---------------------------------------------------------------------------
145146

146-
def build_course_map(json_ld: dict) -> dict:
147+
def build_course_map(json_ld: dict) -> Dict[str, Dict[str, str]]:
147148
"""
148149
Returns a dict keyed by SHA-256( id_url ) with values::
149150
@@ -153,7 +154,7 @@ def build_course_map(json_ld: dict) -> dict:
153154
"pdf_url": str,
154155
}
155156
"""
156-
result: dict = {}
157+
result: Dict[str, Dict[str, str]] = {}
157158
outer_list = json_ld.get("itemListElement", [])
158159
for outer in outer_list:
159160
for item in outer.get("itemListElement", []):
@@ -176,7 +177,7 @@ def _get_cards(soup: BeautifulSoup):
176177
return soup.select("div.card-body div.row div.card-body")
177178

178179

179-
def _get_card_course_key(card, course_map: dict) -> str | None:
180+
def _get_card_course_key(card, course_map: Dict[str, Dict[str, str]]) -> Optional[str]:
180181
"""Return the course_map key for the course card, or None if not found."""
181182
link = card.find("a", href=True)
182183
if not link:
@@ -208,7 +209,7 @@ def _build_no_release_span(soup: BeautifulSoup) -> Tag:
208209
return span
209210

210211

211-
def inject_pdf_links(soup: BeautifulSoup, course_map: dict) -> int:
212+
def inject_pdf_links(soup: BeautifulSoup, course_map: Dict[str, Dict[str, str]]) -> int:
212213
"""
213214
Walk every course card, check for an existing PDF release and inject a
214215
download link. Returns the number of cards that received a link.

0 commit comments

Comments
 (0)