From 9ed051d2801ca1c6ebf0418bc99d2402df64dad0 Mon Sep 17 00:00:00 2001 From: Jason Pochanke Date: Mon, 31 Aug 2026 19:45:57 +0200 Subject: [PATCH] Chore(ci) --- .gitea/workflows/build.yml | 66 ++++++++++++++++++++++----------- .gitignore | 3 ++ README.md | 66 ++++++++++++++++++++++++++++++++- scripts/build-linux-binaries.sh | 64 ++++++++++++++++++++++++++++++++ 4 files changed, 175 insertions(+), 24 deletions(-) create mode 100755 scripts/build-linux-binaries.sh diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index df4940b..a585a19 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -18,28 +18,50 @@ jobs: - name: Checking the format run: gleam format --check - #build-and-bundle: - # runs-on: ubuntu-latest - # container: - # # Replace this with the path to the image you built and pushed - # image: jaypoch/lumabuilder:latest + build-linux-binaries: + needs: format + runs-on: ubuntu-latest + container: + image: jaypoch/lumabuilder:latest - # steps: - # - name: Checkout code - # uses: actions/checkout@v4 + steps: + - name: Checkout code + uses: actions/checkout@v4 - # - name: Build Gleam to JavaScript - # run: gleam build --target javascript + - name: Install native packaging tools + run: | + set -eu + export PATH="$HOME/.cargo/bin:$PATH" - # - name: Build bundle - # run: | - # esbuild build/dev/javascript/luma/luma.mjs \ - # --bundle \ - # --outfile=parser.js \ - # --format=iife \ - # --global-name=App - # - name: Upload parser.js - # uses: https://gitea.com/actions/upload-artifact@v3 - # with: - # name: parser-js - # path: parser.js + if ! command -v rustup >/dev/null 2>&1; then + curl --fail --silent --show-error https://sh.rustup.rs \ + | sh -s -- -y --profile minimal + fi + export PATH="$HOME/.cargo/bin:$PATH" + rustup default stable + + if ! command -v zig >/dev/null 2>&1; then + curl --fail --silent --show-error --location \ + https://ziglang.org/download/0.16.0/zig-x86_64-linux-0.16.0.tar.xz \ + --output /tmp/zig.tar.xz + tar -xJf /tmp/zig.tar.xz -C /tmp + ln -s /tmp/zig-x86_64-linux-0.16.0/zig "$HOME/.cargo/bin/zig" + fi + + cargo install --locked queso + cargo install cargo-zigbuild + + - name: Build x86_64 and ARM64 binaries + run: ./scripts/build-linux-binaries.sh + + - name: Upload x86_64 binary + uses: https://gitea.com/actions/upload-artifact@v3 + with: + name: luma_markdown-linux-x86_64 + path: luma_markdown-linux-x86_64 + + - name: Upload ARM64 binary + uses: https://gitea.com/actions/upload-artifact@v3 + with: + name: luma_markdown-linux-arm64 + path: luma_markdown-linux-arm64 diff --git a/.gitignore b/.gitignore index 4ae96a6..07fee4f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,7 @@ erl_crash.dump .DS_Store *.swp *.swo +/luma_markdown +/luma_markdown-linux-x86_64 +/luma_markdown-linux-arm64 luma_markdown diff --git a/README.md b/README.md index 1502f6c..fe53400 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,9 @@ all of its input from standard input. ## Standalone executable Gleam can package the Erlang version of the program as an escript. An escript -is one executable file containing the compiled program and its dependencies -(it does require Erlang to be installed on the machine where it is run). +is one executable file containing the compiled program and its dependencies. +The target system must still have a compatible Erlang/OTP installation with +`escript` on `PATH`. From the project directory, run: @@ -50,6 +51,67 @@ printf '%s\n' '!4 emphasized text' | ./luma_markdown With no arguments, the executable reads until standard input reaches EOF and writes the resulting Markdown to standard output. +### ARM builds + +The escript contains portable BEAM bytecode, so the same file can work on +x86, ARM64, and ARM32. It is not, however, a self-contained native executable: +the target still needs Erlang/OTP. If the ARM system cannot have anything +installed, this built-in export is not sufficient. + +```sh +gleam export escript +./luma_markdown input.luma +``` + +For a no-install distribution, produce one bundled native artifact per +architecture (for example, an x86_64 Linux artifact and an ARM64 Linux +artifact). A native executable cannot run on both instruction sets. This +repository uses [Queso](https://github.com/jtdowney/queso) to bundle the +Erlang runtime and cross-compile the native launcher. + +Install Queso and Zig once on the build machine: + +```sh +cargo install --locked queso +cargo install cargo-zigbuild +# Install Zig from https://ziglang.org/download/ +# Install Rust through https://rustup.rs (the build script adds the targets) +``` + +On Arch Linux, the Rust toolchain manager can be installed with: + +```sh +sudo pacman -S rustup +rustup default stable +``` + +Then build both self-contained binaries with one command: + +```sh +./scripts/build-linux-binaries.sh +``` + +The results are: + +```text +./luma_markdown-linux-x86_64 +./luma_markdown-linux-arm64 +``` + +Copy the matching file to a Linux system and run it directly. The destination +does not need Erlang, Gleam, Zig, or Queso. + +### CI artifacts + +Every push runs the native build in Gitea Actions after formatting succeeds. +The workflow uploads two downloadable artifacts: + +- `luma_markdown-linux-x86_64` +- `luma_markdown-linux-arm64` + +Download the artifact matching the destination machine, make it executable if +necessary, and run it directly. + ## Neovim preview The working preview configuration is also included in diff --git a/scripts/build-linux-binaries.sh b/scripts/build-linux-binaries.sh new file mode 100755 index 0000000..0c088f3 --- /dev/null +++ b/scripts/build-linux-binaries.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Queso bundles the BEAM runtime and uses Zig to cross-compile its native +# launcher. Install both tools before running this script. +export PATH="$HOME/.cargo/bin:$PATH" +QUESO="${QUESO:-$(command -v queso || true)}" +if [ -z "$QUESO" ] && [ -x "$HOME/.cargo/bin/queso" ]; then + QUESO="$HOME/.cargo/bin/queso" +fi +if [ -z "$QUESO" ]; then + echo "queso is required: cargo install --locked queso" >&2 + exit 1 +fi +if ! command -v cargo-zigbuild >/dev/null 2>&1; then + echo "cargo-zigbuild is required: cargo install cargo-zigbuild" >&2 + exit 1 +fi +if ! command -v zig >/dev/null 2>&1; then + echo "zig is required: https://ziglang.org/download/" >&2 + exit 1 +fi +if ! command -v rustup >/dev/null 2>&1; then + echo "rustup is required to install the Linux cross-compilation targets" >&2 + exit 1 +fi + +# Queso uses cargo-zigbuild for the native launcher. Keep this setup in the +# build command so a fresh build machine does not fail halfway through. +rustup target add x86_64-unknown-linux-musl aarch64-unknown-linux-musl + +rm -rf build/queso +# zstd (used by Queso's launcher) uses cc-rs directly. Point it at Zig so the +# build does not require separate *-linux-musl-gcc packages on the host. +mkdir -p build/luma-cross +cat > build/luma-cross/x86_64-cc <<'EOF' +#!/usr/bin/env bash +args=() +for arg in "$@"; do + [[ "$arg" == --target=* ]] || args+=("$arg") +done +exec zig cc -target x86_64-linux-musl "${args[@]}" +EOF +cat > build/luma-cross/aarch64-cc <<'EOF' +#!/usr/bin/env bash +args=() +for arg in "$@"; do + [[ "$arg" == --target=* ]] || args+=("$arg") +done +exec zig cc -target aarch64-linux-musl "${args[@]}" +EOF +chmod +x build/luma-cross/*-cc +export CC_x86_64_unknown_linux_musl="$PWD/build/luma-cross/x86_64-cc" +export CC_aarch64_unknown_linux_musl="$PWD/build/luma-cross/aarch64-cc" + +"$QUESO" build \ + --target x86_64-linux-static \ + --target aarch64-linux-static + +find build/queso -type f -name '*x86_64*' -perm -u+x -exec cp {} luma_markdown-linux-x86_64 \; +find build/queso -type f -name '*aarch64*' -perm -u+x -exec cp {} luma_markdown-linux-arm64 \; + +chmod +x luma_markdown-linux-x86_64 luma_markdown-linux-arm64 +echo "Built luma_markdown-linux-x86_64 and luma_markdown-linux-arm64"