Skip to content

Commit dbb6b99

Browse files
feat: Deploy a catalog
1 parent 3c9f24f commit dbb6b99

5 files changed

Lines changed: 266 additions & 0 deletions

File tree

.github/workflows/gh-pages.yaml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: Deploy the catalog
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
workflow_dispatch:
8+
release:
9+
types:
10+
- published
11+
12+
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
13+
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
14+
concurrency:
15+
group: "pages"
16+
cancel-in-progress: false
17+
18+
jobs:
19+
build:
20+
runs-on: ubuntu-latest
21+
steps:
22+
- uses: actions/checkout@v5
23+
- uses: typst-community/setup-typst@v4
24+
with:
25+
typst-version: v0.14.0-rc.2
26+
- uses: actions/configure-pages@v5
27+
- run: python scripts/build_catalog.py
28+
env:
29+
GH_TOKEN: ${{ github.token }}
30+
- uses: actions/upload-pages-artifact@v3
31+
with:
32+
path: ./dist
33+
34+
deploy:
35+
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
36+
permissions:
37+
contents: read
38+
pages: write
39+
id-token: write
40+
environment:
41+
name: github-pages
42+
url: ${{ steps.deployment.outputs.page_url }}
43+
runs-on: ubuntu-latest
44+
needs: build
45+
steps:
46+
- uses: actions/deploy-pages@v4
47+
id: deployment

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Generated
2+
/dist/

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@
22

33
Unofficial builds of [Typst](https://typst.app/home) artifacts for development purposes.
44

5+
<!-- included by catalog.typ — start -->
6+
57
- **Artifacts:**
68

79
- `docs` [Documentation](https://typst.app/docs/) (see [below](#additional-explanation-for-docs) for explanation)
810
- `typst` [Typst](https://typst.app/open-source/#download)
911
- `package-check` [Typst package check](https://github.com/typst/package-check)
1012
- `hayagriva` [Hayagriva](https://github.com/typst/hayagriva)
1113

14+
A machine-readable sorted catalog of published artifacts is available at [`catalog.json`](https://typst-community.github.io/dev-builds/catalog.json).
15+
1216
- **Platform:** Only x86_64 Linux (musl) and Windows binaries are offered for CLI programs.
1317

1418
You can open an issue if you really need binaries for other platforms.
@@ -29,6 +33,8 @@ Unofficial builds of [Typst](https://typst.app/home) artifacts for development p
2933
You can build a docs website from these files yourself by leveraging [typst-docs-web](https://github.com/typst-community/typst-docs-web) and [other](https://ydx-2147483647.github.io/best-of-typst/#docs-infra) unofficial tools.
3034
The base URL for the files is set to `/DOCS-BASE/`. Please replace the string with your actual base URL. For example, [`sd '/DOCS-BASE/' '/' docs.json`](https://webinstall.dev/sd/).
3135

36+
<!-- included by catalog.typ — end -->
37+
3238
## Debugging recipes
3339

3440
_The following commands are for debugging build scripts, and are not intended for the general public._

scripts/build_catalog.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
"""Build a catalog for published artifacts."""
2+
3+
# /// script
4+
# requires-python = ">=3.12"
5+
# dependencies = [
6+
# "packaging",
7+
# ]
8+
# ///
9+
10+
import json
11+
import re
12+
from os import getenv
13+
from pathlib import Path
14+
from subprocess import run
15+
from typing import Literal
16+
17+
from packaging.version import Version
18+
19+
URL_PREFIX = (
20+
f"{getenv('GITHUB_SERVER_URL', default='https://github.com')}"
21+
f"/{getenv('GITHUB_REPOSITORY', default='typst-community/dev-builds')}"
22+
"/releases/tag/"
23+
)
24+
25+
TAG_PATTERN = re.compile(
26+
r"(?P<artifact>[-a-z]+)-(?P<revision>v[-.0-9rc]+|main\.\d{4}-\d{2}-\d{2}\.[0-9a-f]{6,})"
27+
)
28+
29+
type RawReleaseMeta = Literal["name", "tagName", "publishedAt"]
30+
type ReleaseMeta = Literal[
31+
"name", "publishedAt", "revision", "releaseTag", "releaseUrl"
32+
]
33+
34+
35+
def get_raw_releases() -> list[dict[RawReleaseMeta, str]]:
36+
result = run(
37+
["gh", "release", "list", "--json", "name,tagName,publishedAt"],
38+
check=True,
39+
capture_output=True,
40+
text=True,
41+
)
42+
return json.loads(result.stdout)
43+
44+
45+
def generate_catalog(
46+
raw_releases: list[dict[RawReleaseMeta, str]],
47+
) -> dict[str, list[dict[ReleaseMeta, str]]]:
48+
catalog: dict[str, list[dict[ReleaseMeta, str]]] = {
49+
# Specify the order of artifacts
50+
"docs": [],
51+
"typst": [],
52+
"package-check": [],
53+
"hayagriva": [],
54+
}
55+
56+
for r in raw_releases:
57+
m = TAG_PATTERN.fullmatch(r["tagName"])
58+
assert m is not None, f"failed to parse “{r['tagName']}”"
59+
match = m.groupdict()
60+
61+
release: dict[ReleaseMeta, str] = {
62+
"name": r["name"],
63+
"publishedAt": r["publishedAt"],
64+
"revision": match["revision"],
65+
"releaseTag": r["tagName"],
66+
"releaseUrl": f"{URL_PREFIX}{r['tagName']}",
67+
}
68+
69+
catalog[match["artifact"]].append(release)
70+
71+
for v in catalog.values():
72+
v.sort(key=key_for_release, reverse=True)
73+
74+
return catalog
75+
76+
77+
def key_for_release(it: dict[ReleaseMeta, str]) -> tuple[int | str | Version, ...]:
78+
if it["revision"].startswith("v"):
79+
version = Version(it["revision"])
80+
return 1, version
81+
else:
82+
return 0, it["revision"], it["publishedAt"]
83+
84+
85+
if __name__ == "__main__":
86+
root_dir = Path(__file__).parent.parent
87+
dist_dir = root_dir / "dist"
88+
dist_dir.mkdir(exist_ok=True)
89+
90+
raw_releases = get_raw_releases()
91+
catalog = generate_catalog(raw_releases)
92+
93+
catalog_json = json.dumps(
94+
{
95+
"version": "0.1.0",
96+
"artifacts": catalog,
97+
},
98+
indent=2,
99+
ensure_ascii=False,
100+
)
101+
(dist_dir / "catalog.json").write_text(catalog_json, encoding="utf-8")
102+
103+
run(
104+
[
105+
"typst",
106+
"compile",
107+
root_dir / "scripts/catalog.typ",
108+
dist_dir / "index.html",
109+
"--features=html",
110+
f"--root={root_dir}",
111+
],
112+
check=True,
113+
)

scripts/catalog.typ

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#set document(title: "Typst dev builds")
2+
3+
#html.style(
4+
```css
5+
body {
6+
background-color: white;
7+
color: black;
8+
9+
max-width: 40em;
10+
margin: 0 auto;
11+
padding-inline: 1em;
12+
13+
font-family: system-ui;
14+
}
15+
@media (prefers-color-scheme: dark) {
16+
body {
17+
background-color: #282828;
18+
color: white;
19+
}
20+
}
21+
22+
h1 {
23+
margin-block: 2em;
24+
text-align: center;
25+
}
26+
27+
p {
28+
line-height: 1.5;
29+
}
30+
31+
li {
32+
margin-block: 0.3em;
33+
}
34+
35+
a {
36+
color: #1464CC;
37+
text-decoration: none;
38+
}
39+
a:visited {
40+
color: #681da8;
41+
}
42+
a:hover {
43+
text-decoration: underline;
44+
}
45+
@media (prefers-color-scheme: dark) {
46+
a {
47+
color: #9fc1f9;
48+
}
49+
a:visited {
50+
color: #c58af9;
51+
}
52+
}
53+
54+
code {
55+
background: #e6e6e6;
56+
padding: 0.15em 0.35em;
57+
border-radius: 4px;
58+
}
59+
@media (prefers-color-scheme: dark) {
60+
code {
61+
background: #4a4a4a;
62+
}
63+
}
64+
```.text,
65+
)
66+
67+
#title()
68+
69+
Unofficial builds of #link("https://typst.app/home")[Typst] artifacts for development purposes.
70+
71+
#outline()
72+
73+
= Introduction
74+
#{
75+
let full = read("/README.md")
76+
let start = "<!-- included by catalog.typ — start -->"
77+
let end = "<!-- included by catalog.typ — end -->"
78+
79+
let readme = full.slice(full.position(start) + start.len(), full.position(end))
80+
81+
import "@preview/cmarker:0.1.6": render
82+
render(readme)
83+
}
84+
85+
= Artifacts in GitHub Releases
86+
#{
87+
let catalog = json("/dist/catalog.json")
88+
assert.eq(catalog.version, "0.1.0")
89+
90+
for (name, releases) in catalog.artifacts {
91+
[== #raw(name)]
92+
list(
93+
..releases.map(r => list.item(
94+
link(r.releaseUrl, r.revision),
95+
)),
96+
)
97+
}
98+
}

0 commit comments

Comments
 (0)