Prepare automated manual build and Chapter 3 draft
Build SmartPark manual / manual (push) Successful in 51s
Build SmartPark manual / manual (push) Successful in 51s
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build only from a fresh temporary copy; publish output only on success."""
|
||||
from pathlib import Path
|
||||
import datetime, os, re, shutil, subprocess, tempfile
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SOURCE = ROOT / 'latex-manual'
|
||||
OUT = ROOT / 'build' / 'manual'
|
||||
|
||||
def run(args, cwd=ROOT):
|
||||
print('+ ' + ' '.join(args), flush=True)
|
||||
subprocess.run(args, cwd=cwd, check=True)
|
||||
|
||||
def git(*args):
|
||||
return subprocess.check_output(['git', *args], cwd=ROOT, text=True).strip()
|
||||
|
||||
def main():
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
# Only remove this script's two fixed output files, so a failed run cannot
|
||||
# leave a stale deliverable looking successful.
|
||||
for name in ('SmartPark-Manual.pdf', 'build-info.txt'):
|
||||
(OUT / name).unlink(missing_ok=True)
|
||||
sha = git('rev-parse', 'HEAD')
|
||||
expected = os.environ.get('MANUAL_COMMIT', '')
|
||||
ci = os.environ.get('MANUAL_CI', '') == 'true'
|
||||
dirty = bool(git('status', '--porcelain', '--untracked-files=normal'))
|
||||
if ci and (not expected or expected != sha or dirty):
|
||||
raise RuntimeError('CI checkout must be clean and match MANUAL_COMMIT')
|
||||
number = os.environ.get('MANUAL_RUN_NUMBER', 'local')
|
||||
if ci and not number.isdigit():
|
||||
raise RuntimeError('Missing numeric Gitea run number')
|
||||
if not re.fullmatch(r'[A-Za-z0-9.-]+', number):
|
||||
raise RuntimeError('Unsafe run identifier')
|
||||
revision = sha[:12] + ('-dirty' if dirty else '')
|
||||
build_time = datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d %H:%M UTC')
|
||||
for tool in ('latexmk', 'pdflatex', 'bibtex', 'makeglossaries', 'makeindex', 'pygmentize'):
|
||||
if shutil.which(tool) is None:
|
||||
raise RuntimeError('Missing tool: ' + tool)
|
||||
with tempfile.TemporaryDirectory(prefix='manual-', dir=ROOT / 'build') as temp:
|
||||
work = Path(temp)
|
||||
shutil.copytree(SOURCE, work, dirs_exist_ok=True,
|
||||
ignore=shutil.ignore_patterns('*.aux', '*.log', '*.pdf', '*.toc', '*.out',
|
||||
'*.bbl', '*.blg', '*.fdb_latexmk', '*.fls', '*.idx', '*.ind', '*.ilg',
|
||||
'*.glo', '*.gls', '*.glg', '*.ist', '*.acn', '*.acr', '*.alg',
|
||||
'*.lof', '*.lot', '_minted-*', 'build-metadata.tex'))
|
||||
# Do not discard legitimate PDF figures: restore tracked/source PDFs
|
||||
# except the old root document output.
|
||||
for source_pdf in SOURCE.rglob('*.pdf'):
|
||||
if source_pdf == SOURCE / 'devopsAssignment.pdf':
|
||||
continue
|
||||
target = work / source_pdf.relative_to(SOURCE)
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(source_pdf, target)
|
||||
(work / 'build-metadata.tex').write_text(
|
||||
'\\renewcommand{\\BuildRevision}{' + revision + '}\n' +
|
||||
'\\renewcommand{\\BuildRun}{' + number + '}\n' +
|
||||
'\\renewcommand{\\BuildUTC}{' + build_time + '}\n', encoding='utf-8')
|
||||
command = ['latexmk', '-pdf', '-interaction=nonstopmode', '-halt-on-error',
|
||||
'-file-line-error', '-shell-escape', 'devopsAssignment.tex']
|
||||
run(command, work)
|
||||
run(['makeglossaries', 'devopsAssignment'], work)
|
||||
if (work / 'devopsAssignment.idx').stat().st_size:
|
||||
run(['makeindex', 'devopsAssignment.idx'], work)
|
||||
run(command, work)
|
||||
pdf = work / 'devopsAssignment.pdf'
|
||||
if not pdf.is_file() or pdf.stat().st_size < 5 or pdf.read_bytes()[:5] != b'%PDF-':
|
||||
raise RuntimeError('Expected nonempty PDF was not produced')
|
||||
log = (work / 'devopsAssignment.log').read_text(errors='replace')
|
||||
if 'There were undefined references' in log or 'There were undefined citations' in log:
|
||||
raise RuntimeError('PDF still contains unresolved references/citations; inspect source')
|
||||
shutil.copy2(pdf, OUT / 'SmartPark-Manual.pdf')
|
||||
info = f'commit={sha}\nrevision={revision}\nrun={number}\nutc={build_time}\n'
|
||||
info += 'mode=' + ('Gitea Actions' if ci else 'LOCAL TEST - not Actions evidence') + '\n'
|
||||
for tool, flag in [('latexmk','-v'), ('pdflatex','--version'), ('pygmentize','-V')]:
|
||||
result = subprocess.check_output([tool, flag], text=True)
|
||||
info += result
|
||||
packages = subprocess.check_output(['dpkg-query', '-W',
|
||||
'-f=${Package}\t${Version}\n', 'latexmk', 'texlive-base',
|
||||
'texlive-latex-extra', 'texlive-fonts-recommended', 'lmodern', 'python3-pygments'], text=True)
|
||||
info += '\nUbuntu package versions:\n' + packages
|
||||
(OUT / 'build-info.txt').write_text(info, encoding='utf-8')
|
||||
print(info)
|
||||
print('SUCCESS: ' + str(OUT / 'SmartPark-Manual.pdf'))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user