Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ PDF_ENGINE ?= xelatex
NO_SRC := $(sort $(wildcard course/no/*.md))
EN_SRC := $(sort $(wildcard course/en/*.md))

.PHONY: all html epub pdf kindle check clean
all: check html epub pdf kindle
.PHONY: all html epub pdf kindle check validate clean
all: check html epub pdf kindle validate

build:
mkdir -p build/html/no build/html/en build/epub build/pdf build/kindle
Expand Down Expand Up @@ -35,5 +35,8 @@ kindle: epub
echo 'Calibre not installed; Kindle-compatible EPUB retained.'; \
fi

validate:
$(PYTHON) scripts/check_artifacts.py

clean:
rm -rf build
53 changes: 53 additions & 0 deletions scripts/check_artifacts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env python3
from pathlib import Path
import sys
import zipfile

errors = []

required = [
Path("build/html/no/index.html"),
Path("build/html/en/index.html"),
Path("build/epub/edunumbers-no.epub"),
Path("build/epub/edunumbers-en.epub"),
Path("build/pdf/edunumbers-no.pdf"),
Path("build/pdf/edunumbers-en.pdf"),
]

for path in required:
if not path.is_file():
errors.append(f"missing artifact: {path}")
elif path.stat().st_size == 0:
errors.append(f"empty artifact: {path}")

for path in required:
if path.suffix == ".epub" and path.is_file():
if not zipfile.is_zipfile(path):
errors.append(f"invalid EPUB ZIP container: {path}")
else:
with zipfile.ZipFile(path) as archive:
if "mimetype" not in archive.namelist():
errors.append(f"EPUB missing mimetype entry: {path}")
elif archive.read("mimetype") != b"application/epub+zip":
errors.append(f"EPUB has invalid mimetype: {path}")

for path in required:
if path.suffix == ".pdf" and path.is_file():
with path.open("rb") as stream:
if stream.read(5) != b"%PDF-":
errors.append(f"invalid PDF header: {path}")

kindle = list(Path("build/kindle").glob("*"))
if not kindle:
errors.append("no Kindle artifact produced")
for path in kindle:
if not path.is_file() or path.stat().st_size == 0:
errors.append(f"invalid Kindle artifact: {path}")

if errors:
print("Artifact validation failed:")
for error in errors:
print(f"- {error}")
sys.exit(1)

print(f"Artifact validation passed: {len(required)} core files + {len(kindle)} Kindle files")
60 changes: 45 additions & 15 deletions scripts/check_sources.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,50 @@
#!/usr/bin/env python3
from pathlib import Path
import re
import sys

required = [
Path('README.md'), Path('COURSE.md'),
Path('course/no/00-introduksjon.md'),
Path('course/en/00-introduction.md'),
Path('book/metadata-no.yaml'), Path('book/metadata-en.yaml'),
]
missing = [str(p) for p in required if not p.is_file()]
if missing:
print('Missing required M0 files:')
print('\n'.join(f'- {p}' for p in missing))
errors = []

for required in [
Path("README.md"), Path("COURSE.md"),
Path("book/metadata-no.yaml"), Path("book/metadata-en.yaml"),
]:
if not required.is_file():
errors.append(f"missing required file: {required}")

def chapters(directory):
found = {}
for path in Path(directory).glob("*.md"):
match = re.match(r"(\d\d)-", path.name)
if match:
found[int(match.group(1))] = path
return found

no = chapters("course/no")
en = chapters("course/en")

if not no:
errors.append("no Norwegian chapters found")
if not en:
errors.append("no English chapters found")

for number in sorted(set(no) | set(en)):
if number not in no:
errors.append(f"chapter {number:02d} missing Norwegian source")
if number not in en:
errors.append(f"chapter {number:02d} missing English source")

for path in list(no.values()) + list(en.values()):
text = path.read_text(encoding="utf-8")
if not text.strip():
errors.append(f"empty source: {path}")
if not re.search(r"^#\s+\S", text, re.MULTILINE):
errors.append(f"missing H1 heading: {path}")

if errors:
print("Source validation failed:")
for error in errors:
print(f"- {error}")
sys.exit(1)
for p in Path('course').rglob('*.md'):
if not p.read_text(encoding='utf-8').strip():
print(f'Empty source: {p}')
sys.exit(1)
print('M0 source checks passed')

print(f"Source validation passed: {len(no)} NO + {len(en)} EN chapters")
Loading