sqzass.git

sqzass

~ static site generator in Rust
git clone https://github.com/debuman/sqzass.git
commit 506a2c5792ff841760b5b8c88fefdd8b370446e0
parent 4e8d25d449d5f22ec264209d9548a7d73d0933f2
Author: sqzer <110329244+sqzer-x@users.noreply.github.com>
Date: Sat, 25 Jul 2026 22:55:25 +0900

Add a release workflow and an AUR PKGBUILD

Tagging `v*` builds two targets and attaches them to the release:
x86_64-unknown-linux-musl and aarch64-apple-darwin, each with a checksum beside
it because otherwise nobody downloading has a way to check anything.

The musl job is not only a build target. Whether this project can link
statically against musl is what dictated its dependency choices — syntect is
pinned to its pure-Rust regex engine specifically because the default is an
Oniguruma C binding, and that binding is what breaks here. That reasoning has
been written down since the beginning and never once executed. Now it runs on
every release.

Each job then runs the binary it produced: `init`, `build`, and a check that
the page exists. A static build that links and dies at startup is a real
failure mode, and "the compiler was happy" is not evidence that a binary works.

The workflow also runs on workflow_dispatch, where it uploads artifacts and
creates no release. Finding out whether a target builds should not require an
irreversible act; tags are the only thing that publishes.

fail-fast is off. If macOS breaks, the Linux archive is still worth having
while the cause is found.

The PKGBUILD builds from the tag tarball rather than shipping a binary. Building
a Rust tool from source is normal on Arch, and two packages invite the question
of which one is current. Its checksums are SKIP because they cannot be computed
before a tag exists — packaging/aur/README.md gives the order to do it in, and
says plainly that publishing needs an AUR account and an SSH key, which is not
something to automate from here.

Nothing is released yet. The docs say so rather than describing binaries that
do not exist.

Diffstat:
A.github/workflows/release.yml | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdocs/content/start/installation.ko.md | 19++++++++++++++++++-
Mdocs/content/start/installation.md | 20+++++++++++++++++++-
Apackaging/aur/PKGBUILD | 38++++++++++++++++++++++++++++++++++++++
Apackaging/aur/README.md | 39+++++++++++++++++++++++++++++++++++++++
5 files changed, 202 insertion(+), 2 deletion(-)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
@@ -0,0 +1,88 @@
+name: Release
+
+on:
+  push:
+    tags: ["v*"]
+  # 태그를 붙이기 전에 빌드가 되는지 확인할 수 있어야 한다. 수동 실행은 아티팩트만
+  # 남기고 릴리스는 만들지 않는다 — 되돌릴 수 없는 건 태그가 있을 때만 한다.
+  workflow_dispatch:
+
+permissions:
+  contents: write
+
+env:
+  CARGO_TERM_COLOR: always
+
+jobs:
+  build:
+    strategy:
+      # 한 타깃이 깨져도 나머지는 끝까지 간다. 어느 하나가 실패했다고 전부 없는
+      # 것보다, 되는 것만이라도 손에 쥐고 원인을 보는 편이 낫다.
+      fail-fast: false
+      matrix:
+        include:
+          # musl 정적 링크. 이게 되는지가 이 프로젝트의 의존성 선택을 지배했다 —
+          # syntect를 default-fancy(순수 Rust)로 고정한 이유가 Oniguruma의 C
+          # 바인딩이 여기서 깨지기 때문이다. 이 잡이 그 결정의 검증이다.
+          - os: ubuntu-latest
+            target: x86_64-unknown-linux-musl
+          - os: macos-latest
+            target: aarch64-apple-darwin
+
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v7
+
+      - uses: dtolnay/rust-toolchain@stable
+        with:
+          targets: ${{ matrix.target }}
+
+      - uses: Swatinem/rust-cache@v2
+        with:
+          key: ${{ matrix.target }}
+
+      - name: Install the musl toolchain
+        if: contains(matrix.target, 'musl')
+        run: sudo apt-get update && sudo apt-get install -y musl-tools
+
+      - name: Build
+        run: cargo build --release --locked --target ${{ matrix.target }}
+
+      # 빌드된 바이너리가 실제로 사이트를 만드는지 본다. 링크만 성공하고 런타임에
+      # 죽는 조합이 있으므로(정적 빌드에서 특히), 여기서 한 번 돌려 본다.
+      - name: The binary builds a site
+        run: |
+          ./target/${{ matrix.target }}/release/sqzass init "$RUNNER_TEMP/smoke"
+          ./target/${{ matrix.target }}/release/sqzass build -i "$RUNNER_TEMP/smoke" --json
+          test -f "$RUNNER_TEMP/smoke/public/index.html"
+
+      - name: Package
+        id: package
+        shell: bash
+        run: |
+          name="sqzass-${{ github.ref_type == 'tag' && github.ref_name || 'dev' }}-${{ matrix.target }}"
+          mkdir -p "dist/$name"
+          cp "target/${{ matrix.target }}/release/sqzass" "dist/$name/"
+          cp README.md LICENSE "dist/$name/"
+          tar -C dist -czf "dist/$name.tar.gz" "$name"
+          # 체크섬을 같이 낸다. 없으면 받는 쪽이 확인할 방법이 없다.
+          (cd dist && shasum -a 256 "$name.tar.gz" > "$name.tar.gz.sha256")
+          echo "archive=dist/$name.tar.gz" >> "$GITHUB_OUTPUT"
+
+      - uses: actions/upload-artifact@v5
+        with:
+          name: ${{ matrix.target }}
+          path: |
+            ${{ steps.package.outputs.archive }}
+            ${{ steps.package.outputs.archive }}.sha256
+
+      # 릴리스는 태그일 때만. 수동 실행은 "빌드가 되는지"를 확인하는 용도이고,
+      # 그때 릴리스가 생기면 확인이 부작용을 갖게 된다.
+      - name: Attach to the release
+        if: github.ref_type == 'tag'
+        uses: softprops/action-gh-release@v2
+        with:
+          files: |
+            ${{ steps.package.outputs.archive }}
+            ${{ steps.package.outputs.archive }}.sha256
+          generate_release_notes: true
diff --git a/docs/content/start/installation.ko.md b/docs/content/start/installation.ko.md
@@ -25,8 +25,25 @@ cargo build --release
 | Rust | 1.97 이상 |
 | 그 외 | — |
 
+## 미리 빌드된 바이너리
+
+[릴리스](https://github.com/sqzer-x/sqzass/releases)마다 정적 링크된
+`x86_64-unknown-linux-musl` 빌드와 `aarch64-apple-darwin` 빌드가 붙습니다. 각각
+`.sha256`이 옆에 있습니다.
+
+```bash
+curl -LO https://github.com/sqzer-x/sqzass/releases/latest/download/sqzass-v0.1.0-x86_64-unknown-linux-musl.tar.gz
+tar xzf sqzass-*.tar.gz
+sudo install -m755 sqzass-*/sqzass /usr/local/bin/
+```
+
+리눅스 빌드는 정적이라 glibc를 요구하지 않고, 빌드한 배포판보다 오래된 배포판에서도
+돕니다. 이 프로젝트가 syntect를 순수 Rust 정규식 엔진으로 고정한 이유가 이것입니다.
+기본값은 C 바인딩이고, C 바인딩은 정확히 musl 정적 빌드를 깨뜨립니다.
+
 > [!NOTE]
-> 미리 빌드된 바이너리와 AUR 패키지는 계획에 있지만 아직 배포하지 않았습니다.
+> 아직 릴리스가 없습니다 — 워크플로는 있고 첫 태그를 아직 붙이지 않았습니다.
+> 그때까지는 소스에서 빌드하세요.
 
 ## 설치 확인
 
diff --git a/docs/content/start/installation.md b/docs/content/start/installation.md
@@ -25,8 +25,26 @@ The binary lands at `target/release/sqzass`.
 | Rust | 1.97 or newer |
 | Everything else | — |
 
+## Prebuilt binaries
+
+Attached to each [release](https://github.com/sqzer-x/sqzass/releases): a
+statically linked `x86_64-unknown-linux-musl` build and an
+`aarch64-apple-darwin` one, each with a `.sha256` beside it.
+
+```bash
+curl -LO https://github.com/sqzer-x/sqzass/releases/latest/download/sqzass-v0.1.0-x86_64-unknown-linux-musl.tar.gz
+tar xzf sqzass-*.tar.gz
+sudo install -m755 sqzass-*/sqzass /usr/local/bin/
+```
+
+The Linux build is static, so it has no glibc requirement and runs on
+distributions older than the one it was built on. That is the reason this
+project pins syntect to its pure-Rust regex engine: the default one is a C
+binding, and a C binding is exactly what breaks a musl static build.
+
 > [!NOTE]
-> Prebuilt binaries and an AUR package are planned but not published yet.
+> There is no release yet — the workflow exists and the first tag has not been
+> cut. Build from source until then.
 
 ## Checking the install
 
diff --git a/packaging/aur/PKGBUILD b/packaging/aur/PKGBUILD
@@ -0,0 +1,38 @@
+# Maintainer: sqzer <110329244+sqzer-x@users.noreply.github.com>
+pkgname=sqzass
+pkgver=0.1.0
+pkgrel=1
+pkgdesc='A static site generator written in Rust'
+arch=('x86_64' 'aarch64')
+url='https://github.com/sqzer-x/sqzass'
+license=('MIT')
+depends=('gcc-libs')
+makedepends=('cargo')
+source=("$pkgname-$pkgver.tar.gz::$url/archive/refs/tags/v$pkgver.tar.gz")
+sha256sums=('SKIP')  # 태그를 붙인 뒤 `updpkgsums`로 채운다
+
+prepare() {
+  cd "$pkgname-$pkgver"
+  export RUSTUP_TOOLCHAIN=stable
+  cargo fetch --locked --target "$(rustc -vV | sed -n 's/host: //p')"
+}
+
+build() {
+  cd "$pkgname-$pkgver"
+  export RUSTUP_TOOLCHAIN=stable
+  export CARGO_TARGET_DIR=target
+  cargo build --frozen --release --all-features
+}
+
+check() {
+  cd "$pkgname-$pkgver"
+  export RUSTUP_TOOLCHAIN=stable
+  cargo test --frozen --all-features
+}
+
+package() {
+  cd "$pkgname-$pkgver"
+  install -Dm0755 -t "$pkgdir/usr/bin/" "target/release/$pkgname"
+  install -Dm0644 -t "$pkgdir/usr/share/licenses/$pkgname/" LICENSE
+  install -Dm0644 -t "$pkgdir/usr/share/doc/$pkgname/" README.md
+}
diff --git a/packaging/aur/README.md b/packaging/aur/README.md
@@ -0,0 +1,39 @@
+# AUR
+
+소스에서 빌드하는 `sqzass` 패키지다. 미리 빌드된 바이너리를 받는 `sqzass-bin`은
+만들지 않았다 — Rust 도구를 소스에서 빌드하는 건 Arch에서 정상이고, 패키지가 둘이면
+어느 쪽이 최신인지 묻는 이슈가 생긴다.
+
+`sha256sums`가 `SKIP`으로 되어 있다. **이대로 올리지 말 것.** 태그가 존재해야
+체크섬을 계산할 수 있으므로 순서가 이렇다:
+
+```bash
+# 1. 릴리스 태그를 먼저 만든다
+git tag v0.1.0 && git push origin v0.1.0
+
+# 2. 체크섬을 채운다 (pacman-contrib)
+cd packaging/aur && updpkgsums
+
+# 3. 로컬에서 실제로 빌드되는지 본다
+makepkg -si
+
+# 4. .SRCINFO를 만든다. AUR은 이 파일로 패키지를 읽는다
+makepkg --printsrcinfo > .SRCINFO
+```
+
+## 처음 올릴 때
+
+AUR 계정과 SSH 키가 필요하다. 웹에서 해야 하는 일이라 여기 자동화하지 않았다.
+
+```bash
+ssh-keygen -t ed25519 -f ~/.ssh/aur          # 키를 만들고
+# aur.archlinux.org 계정 설정에 공개키를 등록한 뒤
+git clone ssh://aur@aur.archlinux.org/sqzass.git aur-sqzass
+cp PKGBUILD .SRCINFO aur-sqzass/
+cd aur-sqzass && git add -A && git commit -m 'Initial import' && git push
+```
+
+## 버전을 올릴 때
+
+`pkgver`를 고치고 `pkgrel=1`로 되돌린 뒤 2~4를 반복한다. `pkgrel`은 같은 버전의
+패키징만 고쳤을 때 올린다.