Skip to content

Commit e096498

Browse files
matbrgzclaude
andcommitted
Add Alpine/musl release builds
The Linux artifacts are dynamically linked against glibc, so they do not run on musl systems: the loader is missing and the shell reports it as `sh: ./scratch-run: not found`. `gcompat` does not help either — it gets past the loader and then fails on `Error relocating: fcntl64`. @yao-pkg/pkg already ships prebuilt musl Node bases for the pinned node22.22.2, so `alpine-x64`/`alpine-arm64` build like any other target. The existing targets are untouched. The musl binary is cross-compiled on the glibc runner, which cannot exec it, so the test job runs the same suite inside an Alpine container and points it at the binary through SCRATCH_RUN. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 78622fe commit e096498

4 files changed

Lines changed: 25 additions & 2 deletions

File tree

‎.github/workflows/main.yml‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ jobs:
1616
include:
1717
- { os: ubuntu-24.04, target: linux-x64, outdir: linux-amd64 }
1818
- { os: ubuntu-24.04-arm, target: linux-arm64, outdir: linux-arm64 }
19+
- { os: ubuntu-24.04, target: alpine-x64, outdir: alpine-amd64, musl: true }
20+
- { os: ubuntu-24.04-arm, target: alpine-arm64, outdir: alpine-arm64, musl: true }
1921
- { os: macos-26-intel, target: macos-x64, outdir: macos-amd64 }
2022
- { os: macos-26, target: macos-arm64, outdir: macos-arm64 }
2123
- { os: windows-2025, target: win-x64, outdir: win-amd64, ext: .exe }
@@ -50,9 +52,20 @@ jobs:
5052
run: codesign --force --sign - "bin/${{ matrix.outdir }}/scratch-run"
5153

5254
- name: Test
55+
if: ${{ !matrix.musl }}
5356
shell: bash
5457
run: python tests/test.py
5558

59+
# The musl build is cross-compiled on a glibc runner, which cannot exec
60+
# it, so run the same suite inside Alpine.
61+
- name: Test (Alpine)
62+
if: ${{ matrix.musl }}
63+
env:
64+
OUT: ${{ matrix.outdir }}
65+
run: |
66+
docker run --rm -v "$PWD:/w" -w /w -e "SCRATCH_RUN=/w/bin/$OUT/scratch-run" \
67+
alpine:3.22 sh -c 'apk add --no-cache python3 >/dev/null && python3 tests/test.py'
68+
5669
- name: Package
5770
shell: bash
5871
run: |

‎README.md‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ scratch-run is a headless command-line interpreter for Scratch projects, built o
44

55
It was created to **judge solutions written in Scratch**: a project reads from standard input and writes to standard output, so it can be graded like any other program. scratch-run powers Scratch problems on our online judge [VNOJ](https://github.com/VNOI-Admin/OJ), but it works standalone too.
66

7-
- **No runtime dependencies** — a single self-contained executable, packed with [@yao-pkg/pkg](https://github.com/yao-pkg/pkg).
7+
- **No runtime dependencies** — a single self-contained executable, packed with [@yao-pkg/pkg](https://github.com/yao-pkg/pkg). Linux builds are linked against the system C library, so there is a glibc build and a musl one.
88
- **Fast** — runs in turbo mode through the scratch-vm compiler, with no rendering, audio, or extensions.
99
- **Supports `.sb`, `.sb2`, `.sb3`, and raw `project.json`.**
1010

1111
## Installation
1212

1313
Download a prebuilt binary for your platform from [Releases](https://github.com/VNOI-Admin/scratch-run/releases). Builds are provided for Linux, macOS, and Windows, on both x64 and arm64.
1414

15+
On a musl-based distribution — Alpine, and the `alpine` container images built on it — take the `alpine_*` artifact. The `linux_*` ones need glibc and fail there with `sh: ./scratch-run: not found`, which looks like a missing file rather than a missing loader.
16+
1517
## Usage
1618

1719
```bash

‎build.sh‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ set -e
33

44
# Build standalone executables with @yao-pkg/pkg (standard mode).
55
#
6-
# ./build.sh build all 6 targets (cross-compile) + zip
6+
# ./build.sh build all 8 targets (cross-compile) + zip
77
# ./build.sh <arch> <outdir> [ext] build a single target (binary only)
88
#
99
# CI builds one native target per runner (no cross-compile, native macOS
@@ -37,6 +37,8 @@ if [ "$#" -ge 2 ]; then
3737
else
3838
build linux-x64 linux-amd64; zip_target linux-amd64
3939
build linux-arm64 linux-arm64; zip_target linux-arm64
40+
build alpine-x64 alpine-amd64; zip_target alpine-amd64
41+
build alpine-arm64 alpine-arm64; zip_target alpine-arm64
4042
build macos-x64 macos-amd64; zip_target macos-amd64
4143
build macos-arm64 macos-arm64; zip_target macos-arm64
4244
build win-x64 win-amd64 .exe; zip_target win-amd64 .exe

‎tests/test.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88

99

1010
def get_executable():
11+
# SCRATCH_RUN points the suite at a specific binary. Used to test the musl
12+
# build, which is cross-compiled on a glibc runner and can only be run
13+
# inside an Alpine container.
14+
if os.environ.get('SCRATCH_RUN'):
15+
return os.environ['SCRATCH_RUN']
16+
1117
# platform.machine() reports the arch under different names per OS:
1218
# Linux aarch64, macOS arm64, Windows ARM64 — all mean arm64.
1319
machine = platform.machine().lower()

0 commit comments

Comments
 (0)