Squashed commit of jenkins changes
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -1,3 +1,6 @@
 | 
				
			|||||||
 | 
					venv
 | 
				
			||||||
 | 
					__pycache__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Output files
 | 
					# Output files
 | 
				
			||||||
main.pdf
 | 
					main.pdf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										28
									
								
								resources/scripts/Jenkinsfile
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								resources/scripts/Jenkinsfile
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					pipeline {
 | 
				
			||||||
 | 
						agent none
 | 
				
			||||||
 | 
						environment {
 | 
				
			||||||
 | 
							NC_CRED	= credentials("nc-jenkins-cred")
 | 
				
			||||||
 | 
							NO_TEST = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						stages {
 | 
				
			||||||
 | 
							stage("Build") {
 | 
				
			||||||
 | 
								agent {
 | 
				
			||||||
 | 
									docker {
 | 
				
			||||||
 | 
										image "git.betalupi.com/mark/latex:latest"
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								steps {
 | 
				
			||||||
 | 
									// Keep these in one "sh" directive.
 | 
				
			||||||
 | 
									// Todo: create setup files so we don't
 | 
				
			||||||
 | 
									// have to pip install manually here.
 | 
				
			||||||
 | 
									// Maybe integrate python packages into the docker container?
 | 
				
			||||||
 | 
									sh '''
 | 
				
			||||||
 | 
										python -m venv venv
 | 
				
			||||||
 | 
										source venv/bin/activate
 | 
				
			||||||
 | 
										pip install tomli webdavclient3
 | 
				
			||||||
 | 
										python resources/scripts/build.py
 | 
				
			||||||
 | 
									'''
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										149
									
								
								resources/scripts/build.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								resources/scripts/build.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,149 @@
 | 
				
			|||||||
 | 
					# Build script for CI.
 | 
				
			||||||
 | 
					# This file is run from the root of the repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import tomli
 | 
				
			||||||
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					import shutil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from webdav3.client import Client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import helpers.tex as tex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					conf_path = "resources/scripts/build.toml"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Prepare envvars
 | 
				
			||||||
 | 
					e = os.environ.copy()
 | 
				
			||||||
 | 
					e["TEXINPUTS"] = f"::/usr/share/texmf-dist/tex//"
 | 
				
			||||||
 | 
					print(Path.cwd())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# If true, rebuild everything
 | 
				
			||||||
 | 
					# without checking for changes.
 | 
				
			||||||
 | 
					if "FORCE_ALL" in e:
 | 
				
			||||||
 | 
						force_all = e["FORCE_ALL"] == "true"
 | 
				
			||||||
 | 
					else:
 | 
				
			||||||
 | 
						force_all = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if "NO_TEST" in e:
 | 
				
			||||||
 | 
						test = e["NO_TEST"] == "false"
 | 
				
			||||||
 | 
					else:
 | 
				
			||||||
 | 
						test = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Used only for debug
 | 
				
			||||||
 | 
					class FakeClient:
 | 
				
			||||||
 | 
						def __init__(self):
 | 
				
			||||||
 | 
							pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def mkdir(self, path):
 | 
				
			||||||
 | 
							print(f"Making dir {path}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def upload_sync(self, local_path, remote_path):
 | 
				
			||||||
 | 
							print(f"Sync {local_path} to {remote_path}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						def clean(self, path):
 | 
				
			||||||
 | 
							print(f"Cleaning {path}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Load configuration
 | 
				
			||||||
 | 
					with open(conf_path, mode="rb") as fp:
 | 
				
			||||||
 | 
						config = tomli.load(fp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Prepare directories
 | 
				
			||||||
 | 
					builddir = Path().absolute() / config["core"]["build_dir"]
 | 
				
			||||||
 | 
					output = Path().absolute() / config["core"]["output_dir"]
 | 
				
			||||||
 | 
					output.mkdir(parents = True, exist_ok = True)
 | 
				
			||||||
 | 
					builddir.mkdir(parents = True, exist_ok = True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if not test:
 | 
				
			||||||
 | 
						# Connect to webdav
 | 
				
			||||||
 | 
						client = Client({
 | 
				
			||||||
 | 
							"webdav_login": e["NC_CRED_USR"],
 | 
				
			||||||
 | 
							"webdav_password": e["NC_CRED_PSW"],
 | 
				
			||||||
 | 
							"webdav_hostname": e["DAV_HOSTNAME"],	# https://host:port
 | 
				
			||||||
 | 
							"webdav_root": e["DAV_ROOT"]			# the rest of the url
 | 
				
			||||||
 | 
							# client.list() will break if we do not seperate the hostname
 | 
				
			||||||
 | 
							# and the root path.
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					else:
 | 
				
			||||||
 | 
						client = FakeClient()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def upload_dir(local_path: Path):
 | 
				
			||||||
 | 
						for p in local_path.rglob("*"):
 | 
				
			||||||
 | 
							r = p.relative_to(output)
 | 
				
			||||||
 | 
							if p.is_dir():
 | 
				
			||||||
 | 
								client.mkdir(str(r))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							elif p.is_file():
 | 
				
			||||||
 | 
								client.upload_sync(  # type: ignore
 | 
				
			||||||
 | 
									remote_path = str(r),
 | 
				
			||||||
 | 
									local_path = p
 | 
				
			||||||
 | 
								)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Print envvars for debug
 | 
				
			||||||
 | 
					#print(e)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for i, item in enumerate(config["dir-of-dirs"]):
 | 
				
			||||||
 | 
						p = Path(item["path"])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for s in p.iterdir():
 | 
				
			||||||
 | 
							# s: path to directory,
 | 
				
			||||||
 | 
							# # relative to repo root
 | 
				
			||||||
 | 
							if not s.is_dir():
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							print(f"Building {s}/main.tex")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							# Make pdf
 | 
				
			||||||
 | 
							pdf_nosolutions = tex.build(
 | 
				
			||||||
 | 
								s,
 | 
				
			||||||
 | 
								mainfile = "main.tex",
 | 
				
			||||||
 | 
								jobname = f"{i}-nosols",
 | 
				
			||||||
 | 
								build_dir = builddir,
 | 
				
			||||||
 | 
								solutions = False,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								env = e,
 | 
				
			||||||
 | 
								test = test
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pdf_solutions = tex.build(
 | 
				
			||||||
 | 
								s,
 | 
				
			||||||
 | 
								mainfile = "main.tex",
 | 
				
			||||||
 | 
								jobname = f"{i}-sols",
 | 
				
			||||||
 | 
								build_dir = builddir,
 | 
				
			||||||
 | 
								solutions = True,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								env = e,
 | 
				
			||||||
 | 
								test = test
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							target_dir = output / s.parent
 | 
				
			||||||
 | 
							target_dir.mkdir(parents = True, exist_ok = True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							shutil.copy(
 | 
				
			||||||
 | 
								pdf_nosolutions,
 | 
				
			||||||
 | 
								target_dir / (s.stem + ".pdf")
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							shutil.copy(
 | 
				
			||||||
 | 
								pdf_solutions,
 | 
				
			||||||
 | 
								target_dir / (s.stem + ".sols.pdf")
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if not test:
 | 
				
			||||||
 | 
						# Delete old files
 | 
				
			||||||
 | 
						for i in client.list():
 | 
				
			||||||
 | 
							if i.endswith("/"):
 | 
				
			||||||
 | 
								i = i[:-1]
 | 
				
			||||||
 | 
							client.clean(i)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					upload_dir(output)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Remove output files so they don't persist
 | 
				
			||||||
 | 
					shutil.rmtree(output)
 | 
				
			||||||
							
								
								
									
										12
									
								
								resources/scripts/build.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								resources/scripts/build.toml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					# All paths are relative to the root of this repository.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[core]
 | 
				
			||||||
 | 
					build_dir = "_build"
 | 
				
			||||||
 | 
					output_dir = "_output"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[dir-of-dirs]]
 | 
				
			||||||
 | 
					path = "Advanced"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[dir-of-dirs]]
 | 
				
			||||||
 | 
					path = "Intermediate"
 | 
				
			||||||
							
								
								
									
										60
									
								
								resources/scripts/helpers/tex.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								resources/scripts/helpers/tex.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					import subprocess
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def build(
 | 
				
			||||||
 | 
							path: Path,
 | 
				
			||||||
 | 
							*,
 | 
				
			||||||
 | 
							solutions: bool = True,
 | 
				
			||||||
 | 
							mainfile: str = "main.tex",
 | 
				
			||||||
 | 
							jobname: str = "main",
 | 
				
			||||||
 | 
							build_dir: Path = Path("build"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							test = False,
 | 
				
			||||||
 | 
							env: dict = {},
 | 
				
			||||||
 | 
						) -> Path:
 | 
				
			||||||
 | 
						"""
 | 
				
			||||||
 | 
						Build a directory using XeTeX.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Args:
 | 
				
			||||||
 | 
							path (Path): directory to build
 | 
				
			||||||
 | 
							solutions (bool, optional): Create a handout with solutions? Defaults to True.
 | 
				
			||||||
 | 
							mainfile (str, optional): Path to main TeX file, relative to path. Defaults to "main.tex".
 | 
				
			||||||
 | 
							jobname (str, optional): TeX job name. Defaults to "main".
 | 
				
			||||||
 | 
							build_dir (Path, optional): Where to place build files. Defaults to Path("build").
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							test (bool, optional): If true, dry run. Defaults to False.
 | 
				
			||||||
 | 
							env (dict): Dictionary of environment variables.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Returns:
 | 
				
			||||||
 | 
							Path: Absolute path to output pdf.
 | 
				
			||||||
 | 
						"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						# Make temporary file for TeX build arg hack
 | 
				
			||||||
 | 
						if solutions:
 | 
				
			||||||
 | 
							with (build_dir/"tmp.tex").open("w") as f:
 | 
				
			||||||
 | 
								f.write("\\def\\argYesSolutions{1}\\input{" + mainfile + "}")
 | 
				
			||||||
 | 
						else:
 | 
				
			||||||
 | 
							with (build_dir/"tmp.tex").open("w") as f:
 | 
				
			||||||
 | 
								f.write("\\def\\argNoSolutions{1}\\input{" + mainfile + "}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cmd = subprocess.run([
 | 
				
			||||||
 | 
								"latexmk",
 | 
				
			||||||
 | 
								"-interaction=nonstopmode",
 | 
				
			||||||
 | 
								"-file-line-error",
 | 
				
			||||||
 | 
								f"-outdir={build_dir}",
 | 
				
			||||||
 | 
								"-xelatex",
 | 
				
			||||||
 | 
								f"-jobname={jobname}",
 | 
				
			||||||
 | 
								build_dir/"tmp.tex"
 | 
				
			||||||
 | 
							],
 | 
				
			||||||
 | 
							cwd = path,
 | 
				
			||||||
 | 
							env = env,
 | 
				
			||||||
 | 
							stdout = None if test else subprocess.DEVNULL,
 | 
				
			||||||
 | 
							stderr = None if test else subprocess.DEVNULL
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert cmd.returncode == 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						# Remove tmp file
 | 
				
			||||||
 | 
						(build_dir / "tmp.tex").unlink()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return build_dir / f"{jobname}.pdf"
 | 
				
			||||||
		Reference in New Issue
	
	Block a user