Publish package
This commit is contained in:
parent
4468bcbb4a
commit
0e1ba353c1
57
.github/workflows/build.yml
vendored
57
.github/workflows/build.yml
vendored
@ -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
107
.github/workflows/ci.yml
vendored
Normal 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
|
30
.github/workflows/lints.yml
vendored
30
.github/workflows/lints.yml
vendored
@ -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
|
@ -126,6 +126,9 @@ def build_typst(source_dir: Path, out_subdir: Path) -> IndexEntry | None:
|
||||
return None
|
||||
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
|
||||
if not meta["publish_handout"]:
|
||||
return None
|
||||
@ -136,7 +139,6 @@ def build_typst(source_dir: Path, out_subdir: Path) -> IndexEntry | None:
|
||||
out = OUT_DIR / out_subdir
|
||||
out.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
file_name = sanitize_file_name(f"{meta['title']}.pdf")
|
||||
res = subprocess.run(
|
||||
[
|
||||
TYPST_PATH,
|
||||
@ -145,7 +147,7 @@ def build_typst(source_dir: Path, out_subdir: Path) -> IndexEntry | None:
|
||||
"main.typ",
|
||||
"--input",
|
||||
"show_solutions=false",
|
||||
f"{out}/{file_name}",
|
||||
f"{out}/{handout_file}",
|
||||
],
|
||||
cwd=source_dir,
|
||||
stdout=subprocess.PIPE,
|
||||
@ -158,14 +160,13 @@ def build_typst(source_dir: Path, out_subdir: Path) -> IndexEntry | None:
|
||||
# Build solutions
|
||||
if meta["publish_solutions"]:
|
||||
log(f"Building typst (solutions): {source_dir}")
|
||||
file_name = sanitize_file_name(f"{meta['title']}.sols.pdf")
|
||||
res = subprocess.run(
|
||||
[
|
||||
TYPST_PATH,
|
||||
"compile",
|
||||
"--ignore-system-fonts",
|
||||
"main.typ",
|
||||
f"{out}/{file_name}",
|
||||
f"{out}/{solutions_file}",
|
||||
],
|
||||
cwd=source_dir,
|
||||
stdout=subprocess.PIPE,
|
||||
@ -178,11 +179,9 @@ def build_typst(source_dir: Path, out_subdir: Path) -> IndexEntry | None:
|
||||
return {
|
||||
"title": meta["title"],
|
||||
"group": str(out_subdir),
|
||||
"handout_file": f"{out_subdir}/{meta['title']}.pdf",
|
||||
"handout_file": str(out / handout_file),
|
||||
"solutions_file": (
|
||||
f"{out_subdir}/{meta['title']}.sols.pdf"
|
||||
if meta["publish_solutions"]
|
||||
else None
|
||||
str(out / solutions_file) if meta["publish_solutions"] else None
|
||||
),
|
||||
}
|
||||
|
||||
@ -198,6 +197,9 @@ def build_xetex(source_dir: Path, out_subdir: Path) -> IndexEntry | None:
|
||||
return None
|
||||
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
|
||||
if not meta["publish_handout"]:
|
||||
return None
|
||||
@ -219,9 +221,8 @@ def build_xetex(source_dir: Path, out_subdir: Path) -> IndexEntry | None:
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
|
||||
file_name = sanitize_file_name(f"{meta['title']}.pdf")
|
||||
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:
|
||||
log(f"Error: {e}")
|
||||
log_error(res)
|
||||
@ -243,9 +244,8 @@ def build_xetex(source_dir: Path, out_subdir: Path) -> IndexEntry | None:
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
|
||||
file_name = sanitize_file_name(f"{meta['title']}.sols.pdf")
|
||||
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:
|
||||
log(f"Error: {e}")
|
||||
log_error(res)
|
||||
@ -256,11 +256,9 @@ def build_xetex(source_dir: Path, out_subdir: Path) -> IndexEntry | None:
|
||||
return {
|
||||
"title": meta["title"],
|
||||
"group": str(out_subdir),
|
||||
"handout_file": f"{out_subdir}/{meta['title']}.pdf",
|
||||
"handout_file": str(out / handout_file),
|
||||
"solutions_file": (
|
||||
f"{out_subdir}/{meta['title']}.sols.pdf"
|
||||
if meta["publish_solutions"]
|
||||
else None
|
||||
str(out / solutions_file) if meta["publish_solutions"] else None
|
||||
),
|
||||
}
|
||||
|
||||
@ -294,14 +292,11 @@ def build_dir(base: str, out_sub: str, index: list[IndexEntry]):
|
||||
if done:
|
||||
break
|
||||
|
||||
return index
|
||||
|
||||
|
||||
index: list[IndexEntry] = []
|
||||
|
||||
index.extend(build_dir("src/Advanced", "Advanced", index))
|
||||
index.extend(build_dir("src/Intermediate", "Intermediate", index))
|
||||
index.extend(build_dir("src/Warm-Ups", "Warm-Ups", index))
|
||||
build_dir("src/Advanced", "Advanced", index)
|
||||
build_dir("src/Intermediate", "Intermediate", index)
|
||||
build_dir("src/Warm-Ups", "Warm-Ups", index)
|
||||
|
||||
with open(OUT_DIR / "index.json", "w") as f:
|
||||
f.write(json.dumps(index))
|
95
tools/scripts/publish.py
Normal file
95
tools/scripts/publish.py
Normal file
@ -0,0 +1,95 @@
|
||||
# Publish the output of `build.py`
|
||||
# as a Gitea package.
|
||||
|
||||
from pathlib import Path
|
||||
import requests
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def del_package():
|
||||
log(f"Deleting package {PACKAGE}/{VERSION}")
|
||||
res = requests.delete(
|
||||
f"{URL}/api/packages/{USER}/generic/{PACKAGE}/{VERSION}",
|
||||
auth=AUTH,
|
||||
)
|
||||
if res.status_code != 204 and res.status_code != 404:
|
||||
log(f"Deletion failed with code {res.status_code}")
|
||||
|
||||
|
||||
# Delete if already exists
|
||||
# (important for the `latest` package)
|
||||
del_package()
|
||||
|
||||
|
||||
def upload(data, target: str):
|
||||
target = re.sub("[^A-Za-z0-9_. -]+", "", target)
|
||||
|
||||
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}")
|
||||
del_package() # Do not keep partial package if upload fails
|
||||
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 = handout["solutions_file"]
|
||||
if s_file is not None:
|
||||
s_file = SRC_DIR / s_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:
|
||||
log(f"Uploading {title} solutions")
|
||||
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")
|
@ -2,6 +2,7 @@ exclude = ["venv"]
|
||||
line-length = 88
|
||||
indent-width = 4
|
||||
target-version = "py39"
|
||||
include = ["scripts/**/*.py"]
|
||||
|
||||
[lint]
|
||||
select = ["E4", "E7", "E9", "F"]
|
Loading…
x
Reference in New Issue
Block a user