Publish package
Some checks failed
CI / Typos (push) Successful in 16s
CI / Typst formatting (push) Successful in 13s
CI / Build (push) Failing after 11m28s

This commit is contained in:
Mark 2025-01-21 20:41:54 -08:00
parent 982053c93c
commit c5cdeb2c21
Signed by: Mark
GPG Key ID: C6D63995FE72FD80
6 changed files with 200 additions and 103 deletions

View File

@ -1,57 +0,0 @@
on:
push:
pull_request:
workflow_run:
name: Build and deploy
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
# Could instead install `texlive-full`, but that takes ~20 minutes.
# We'll specify the packages we need manually.
- name: "Install TeXLive"
run: |
sudo apt update
DEBIAN_FRONTEND=noninteractive \
sudo apt install --yes \
texlive texlive-xetex \
texlive-games texlive-fonts-extra texlive-latex-extra \
texlive-pictures texlive-pstricks
# Typst isn't packaged, and manual download gives us
# more control anyway.
- name: "Download Typst"
run: |
wget -q "https://github.com/typst/typst/releases/download/v0.12.0/typst-x86_64-unknown-linux-musl.tar.xz"
tar -xf "typst-x86_64-unknown-linux-musl.tar.xz"
mv "typst-x86_64-unknown-linux-musl/typst" .
rm "typst-x86_64-unknown-linux-musl.tar.xz"
rm -dr "typst-x86_64-unknown-linux-musl"
# Builds all handouts, LaTeX and Typst
- name: "Build handouts"
run: TYPST_PATH="$(pwd)/typst" python tools/build/main.py
# Upload logs, even if build fails.
# LaTeX stdout/stderr isn't always helpful.
- name: "Save LaTeX logs"
uses: actions/upload-artifact@v3
if: always()
with:
name: "LaTeX logs"
path: "**/*.log"
retention-days: 1
# Upload build output
- name: "Save output"
uses: actions/upload-artifact@v3
with:
name: "Build output"
path: "output/*"
retention-days: 10

107
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,107 @@
name: CI
on:
push:
pull_request:
workflow_dispatch:
jobs:
typos:
name: "Typos"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check typos
uses: crate-ci/typos@master
with:
config: ./tools/typos.toml
typstyle:
name: "Typst formatting"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: "Download Typstyle"
run: |
wget -q "https://github.com/Enter-tainer/typstyle/releases/download/v0.12.14/typstyle-x86_64-unknown-linux-musl"
chmod +x typstyle-x86_64-unknown-linux-musl
- name: Check typst formatting
run: |
find . -name "*.typ" -type f -print0 | xargs -0 \
./typstyle-x86_64-unknown-linux-musl --check
build:
needs:
- typos
- typstyle
name: "Build"
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
# Could instead install `texlive-full`, but that takes ~20 minutes.
# We'll specify the packages we need manually.
- name: "Install TeXLive"
run: |
sudo apt update
DEBIAN_FRONTEND=noninteractive \
sudo apt install --yes \
texlive texlive-xetex \
texlive-games texlive-fonts-extra texlive-latex-extra \
texlive-pictures texlive-pstricks \
python3-requests
# Typst isn't packaged, and manual download gives us
# more control anyway.
- name: "Download Typst"
run: |
wget -q "https://github.com/typst/typst/releases/download/v0.12.0/typst-x86_64-unknown-linux-musl.tar.xz"
tar -xf "typst-x86_64-unknown-linux-musl.tar.xz"
mv "typst-x86_64-unknown-linux-musl/typst" .
rm "typst-x86_64-unknown-linux-musl.tar.xz"
rm -dr "typst-x86_64-unknown-linux-musl"
# Builds all handouts, LaTeX and Typst
- name: "Build handouts"
run: TYPST_PATH="$(pwd)/typst" python tools/scripts/build.py
# Upload logs, even if build fails.
# LaTeX stdout/stderr isn't always helpful.
- name: "Save LaTeX logs"
uses: actions/upload-artifact@v3
if: always()
with:
name: "LaTeX logs"
path: "**/*.log"
retention-days: 14
# Upload build output
- name: "Save output"
uses: actions/upload-artifact@v3
with:
name: "Build output"
path: "output/*"
retention-days: 7
- name: "Publish package (hash)"
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
run: |
PUBLISH_USER="${{ secrets.PUBLISH_USER }}" \
PUBLISH_KEY="${{ secrets.PUBLISH_KEY }}" \
VERSION="${{ github.sha }}" \
PACKAGE="${{ vars.PACKAGE }}" \
python tools/scripts/publish.py
- name: "Publish package (latest)"
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
run: |
PUBLISH_USER="${{ secrets.PUBLISH_USER }}" \
PUBLISH_KEY="${{ secrets.PUBLISH_KEY }}" \
VERSION="latest" \
PACKAGE="${{ vars.PACKAGE }}" \
python tools/scripts/publish.py

View File

@ -1,30 +0,0 @@
on:
push:
pull_request:
name: Lints
jobs:
typos:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check typos
uses: crate-ci/typos@master
with:
config: ./tools/typos.toml
typstyle:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: "Download Typstyle"
run: |
wget -q "https://github.com/Enter-tainer/typstyle/releases/download/v0.12.14/typstyle-x86_64-unknown-linux-musl"
chmod +x typstyle-x86_64-unknown-linux-musl
- name: Check typst formatting
run: |
find . -name "*.typ" -type f -print0 | xargs -0 \
./typstyle-x86_64-unknown-linux-musl --check

View File

@ -126,6 +126,9 @@ def build_typst(source_dir: Path, out_subdir: Path) -> IndexEntry | None:
return None return None
meta = read_meta_toml(meta_path) meta = read_meta_toml(meta_path)
handout_file = sanitize_file_name(f"{meta['title']}.pdf")
solutions_file = sanitize_file_name(f"{meta['title']}.sols.pdf")
# Do nothing if not published # Do nothing if not published
if not meta["publish_handout"]: if not meta["publish_handout"]:
return None return None
@ -136,7 +139,6 @@ def build_typst(source_dir: Path, out_subdir: Path) -> IndexEntry | None:
out = OUT_DIR / out_subdir out = OUT_DIR / out_subdir
out.mkdir(parents=True, exist_ok=True) out.mkdir(parents=True, exist_ok=True)
file_name = sanitize_file_name(f"{meta['title']}.pdf")
res = subprocess.run( res = subprocess.run(
[ [
TYPST_PATH, TYPST_PATH,
@ -145,7 +147,7 @@ def build_typst(source_dir: Path, out_subdir: Path) -> IndexEntry | None:
"main.typ", "main.typ",
"--input", "--input",
"show_solutions=false", "show_solutions=false",
f"{out}/{file_name}", f"{out}/{handout_file}",
], ],
cwd=source_dir, cwd=source_dir,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
@ -158,14 +160,13 @@ def build_typst(source_dir: Path, out_subdir: Path) -> IndexEntry | None:
# Build solutions # Build solutions
if meta["publish_solutions"]: if meta["publish_solutions"]:
log(f"Building typst (solutions): {source_dir}") log(f"Building typst (solutions): {source_dir}")
file_name = sanitize_file_name(f"{meta['title']}.sols.pdf")
res = subprocess.run( res = subprocess.run(
[ [
TYPST_PATH, TYPST_PATH,
"compile", "compile",
"--ignore-system-fonts", "--ignore-system-fonts",
"main.typ", "main.typ",
f"{out}/{file_name}", f"{out}/{solutions_file}",
], ],
cwd=source_dir, cwd=source_dir,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
@ -178,11 +179,9 @@ def build_typst(source_dir: Path, out_subdir: Path) -> IndexEntry | None:
return { return {
"title": meta["title"], "title": meta["title"],
"group": str(out_subdir), "group": str(out_subdir),
"handout_file": f"{out_subdir}/{meta['title']}.pdf", "handout_file": str(out / handout_file),
"solutions_file": ( "solutions_file": (
f"{out_subdir}/{meta['title']}.sols.pdf" str(out / solutions_file) if meta["publish_solutions"] else None
if meta["publish_solutions"]
else None
), ),
} }
@ -198,6 +197,9 @@ def build_xetex(source_dir: Path, out_subdir: Path) -> IndexEntry | None:
return None return None
meta = read_meta_toml(meta_path) meta = read_meta_toml(meta_path)
handout_file = sanitize_file_name(f"{meta['title']}.pdf")
solutions_file = sanitize_file_name(f"{meta['title']}.sols.pdf")
# Do nothing if not published # Do nothing if not published
if not meta["publish_handout"]: if not meta["publish_handout"]:
return None return None
@ -219,9 +221,8 @@ def build_xetex(source_dir: Path, out_subdir: Path) -> IndexEntry | None:
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
) )
file_name = sanitize_file_name(f"{meta['title']}.pdf")
try: try:
shutil.copy(source_dir / "main.pdf", f"{out}/{file_name}") shutil.copy(source_dir / "main.pdf", f"{out}/{handout_file}")
except Exception as e: except Exception as e:
log(f"Error: {e}") log(f"Error: {e}")
log_error(res) log_error(res)
@ -243,9 +244,8 @@ def build_xetex(source_dir: Path, out_subdir: Path) -> IndexEntry | None:
stderr=subprocess.PIPE, stderr=subprocess.PIPE,
) )
file_name = sanitize_file_name(f"{meta['title']}.sols.pdf")
try: try:
shutil.copy(source_dir / "main.pdf", f"{out}/{file_name}") shutil.copy(source_dir / "main.pdf", f"{out}/{solutions_file}")
except Exception as e: except Exception as e:
log(f"Error: {e}") log(f"Error: {e}")
log_error(res) log_error(res)
@ -256,11 +256,9 @@ def build_xetex(source_dir: Path, out_subdir: Path) -> IndexEntry | None:
return { return {
"title": meta["title"], "title": meta["title"],
"group": str(out_subdir), "group": str(out_subdir),
"handout_file": f"{out_subdir}/{meta['title']}.pdf", "handout_file": str(out / handout_file),
"solutions_file": ( "solutions_file": (
f"{out_subdir}/{meta['title']}.sols.pdf" str(out / solutions_file) if meta["publish_solutions"] else None
if meta["publish_solutions"]
else None
), ),
} }

78
tools/scripts/publish.py Normal file
View File

@ -0,0 +1,78 @@
from pathlib import Path
import requests
import json
import os
URL = "https://git.betalupi.com"
USER = os.environ["PUBLISH_USER"]
PACKAGE = os.environ["PACKAGE"]
VERSION = os.environ["VERSION"]
AUTH = requests.auth.HTTPBasicAuth(USER, os.environ["PUBLISH_KEY"])
ROOT: Path = Path(os.getcwd())
SRC_DIR: Path = ROOT / "output"
def log(msg):
print(f"[PUBLISH.PY] {msg}")
log(f"Version is {VERSION}")
log(f"Package is {PACKAGE}")
log(f"Running in {ROOT}")
if not ROOT.is_dir():
log("Root is not a directory, cannot continue")
exit(1)
log(f"Source dir is {SRC_DIR}")
if not SRC_DIR.exists():
log("Source dir doesn't exist, cannot continue")
exit(1)
log(f"Deleting existing package {SRC_DIR}")
res = requests.delete(
f"{URL}/api/packages/{USER}/generic/{PACKAGE}/{VERSION}",
auth=AUTH,
)
if res.status_code != 204:
log(f"Deletion failed with code {res.status_code}, this is ok")
def upload(data, target: str):
res = requests.put(
f"{URL}/api/packages/{USER}/generic/{PACKAGE}/{VERSION}/{target}",
auth=AUTH,
data=data,
)
if res.status_code != 201:
log(f"Upload failed with code {res.status_code}")
exit(1)
return f"{URL}/api/packages/{USER}/generic/{PACKAGE}/{VERSION}/{target}"
index_file = SRC_DIR / "index.json"
with index_file.open("r") as f:
index = json.load(f)
new_index = []
for handout in index:
title = handout["title"]
group = handout["group"]
h_file = SRC_DIR / handout["handout_file"]
s_file = SRC_DIR / handout["handout_file"]
log(f"Uploading {title}")
h_url = None
s_url = None
h_url = upload(h_file.open("rb").read(), f"{group} - {title}.pdf")
if s_file is not None:
s_url = upload(s_file.open("rb").read(), f"{group} - {title}.sols.pdf")
new_index.append(
{"title": title, "group": group, "handout": h_url, "solutions": s_url}
)
upload(json.dumps(new_index), "index.json")

View File

@ -2,6 +2,7 @@ exclude = ["venv"]
line-length = 88 line-length = 88
indent-width = 4 indent-width = 4
target-version = "py39" target-version = "py39"
include = ["scripts/**/*.py"]
[lint] [lint]
select = ["E4", "E7", "E9", "F"] select = ["E4", "E7", "E9", "F"]