ci: test ghcup install of the bindist on every platform #140
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| pull_request: | |
| types: [opened, synchronize] | |
| push: | |
| branches: [master, ghc-9.6-plinth] | |
| # Pushing a v* tag builds every platform (as on a branch push) and then the | |
| # `release` job publishes a GitHub Release + updates the ghcup channel. See | |
| # Note [ghcup release channel] on the release job below. | |
| tags: ['v*'] | |
| workflow_dispatch: | |
| # Least-privilege GITHUB_TOKEN: jobs only check out code (contents: read). | |
| # Submodules use a dedicated SSH key and artifacts use the Actions runtime | |
| # token, so no write scopes are needed. | |
| permissions: | |
| contents: read | |
| jobs: | |
| plinth-build-linux: | |
| name: plinth-build / ${{ matrix.os }} / ghc ${{ matrix.ghc }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, ubuntu-24.04-arm] | |
| ghc: ['9.6.7'] | |
| steps: | |
| # checkout first: the local composite action (.github/actions/prepare) must | |
| # be on disk before the runner can resolve it. | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| - uses: ./.github/actions/prepare | |
| with: | |
| ghc-version: ${{ matrix.ghc }} | |
| # BINDIST=1 produces the fixed-up uplc-ghc bindist (+ .tar.xz archive) while | |
| # keeping the lean dev flavour. The plinth-test job consumes that archive. | |
| - name: Build uplc-ghc bindist | |
| run: BINDIST=1 ./plinth-build.sh | |
| - name: Upload uplc-ghc bindist | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| # key by platform + GHC so multiple build matrix legs (or another job) | |
| # never collide on the artifact name within a single run. See the | |
| # matching download in the plinth-test job, which shares this matrix. | |
| name: plinth-bindist-${{ matrix.os }}-${{ matrix.ghc }} | |
| path: _build/bindist/*.tar.xz | |
| retention-days: 1 | |
| # the archive is already xz-compressed; don't re-zip it | |
| compression-level: 0 | |
| # plinth-test runs in its own job for a separate status check and timeout | |
| # budget. It depends on `plinth-build-linux` (it can't run in parallel: it | |
| # needs the built compiler), downloading the bindist archive instead of | |
| # rebuilding GHC. Its matrix mirrors the build so the artifact-name key | |
| # matches per leg. | |
| plinth-test-linux: | |
| name: plinth-test / ${{ matrix.os }} / ghc ${{ matrix.ghc }} | |
| needs: plinth-build-linux | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, ubuntu-24.04-arm] | |
| ghc: ['9.6.7'] | |
| steps: | |
| # checkout first: the local composite action (.github/actions/prepare) must | |
| # be on disk before the runner can resolve it. The prepare action also | |
| # fetches submodules, which the test needs: plinth/test/cabal.project pulls | |
| # plutus-tx/plutus-core/plutus-ledger-api from the plutus submodule. | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| - uses: ./.github/actions/prepare | |
| with: | |
| ghc-version: ${{ matrix.ghc }} | |
| # A single Plinth plugin (Core -> PLC) compile of a makeLift-heavy module | |
| # (PlutusLedgerApi.V3.Contexts) spikes past the ~16 GB runner RAM and gets | |
| # OOM-killed even with JOBS=1. Add a 16 GB swapfile on the large /mnt disk | |
| # so the spike spills to swap and the compile completes. | |
| - name: Add 16 GB swap | |
| run: | | |
| sudo fallocate -l 16G /mnt/swapfile | |
| sudo chmod 600 /mnt/swapfile | |
| sudo mkswap /mnt/swapfile | |
| sudo swapon /mnt/swapfile | |
| swapon --show | |
| free -h | |
| - name: Download uplc-ghc bindist | |
| uses: actions/download-artifact@v4 | |
| with: | |
| # matches the plinth-build upload name; both jobs share the same matrix. | |
| name: plinth-bindist-${{ matrix.os }}-${{ matrix.ghc }} | |
| path: _build/artifact | |
| # Install the bindist with the usual configure + make install; the bindist's | |
| # wrappers/ dir (added by the BINDIST fixup) makes this produce bin/uplc-ghc. | |
| # Each step is guarded so a missing artifact or failed install fails loudly | |
| # here, not with a confusing error deep in the test. | |
| - name: Install uplc-ghc bindist | |
| run: | | |
| shopt -s nullglob | |
| archives=(_build/artifact/*.tar.xz) | |
| if [ ${#archives[@]} -ne 1 ]; then | |
| echo "error: expected exactly one bindist archive, found: ${archives[*]:-none}" | |
| exit 1 | |
| fi | |
| mkdir -p _build/bindist | |
| echo "extracting ${archives[0]}" | |
| tar --xz -xf "${archives[0]}" -C _build/bindist | |
| bindirs=(_build/bindist/ghc-*) | |
| if [ ${#bindirs[@]} -ne 1 ]; then | |
| echo "error: expected one extracted bindist dir, found: ${bindirs[*]:-none}" | |
| exit 1 | |
| fi | |
| ( cd "${bindirs[0]}" && ./configure --prefix="$GITHUB_WORKSPACE/_build/install" && make install ) | |
| uplc="$GITHUB_WORKSPACE/_build/install/bin/uplc-ghc" | |
| if [ ! -x "$uplc" ]; then | |
| echo "error: $uplc missing or not executable after make install" | |
| ls -l "$GITHUB_WORKSPACE/_build/install/bin" || true | |
| exit 1 | |
| fi | |
| rm -rf _build/artifact | |
| # Build packages serially (JOBS=1): compiling with the Plinth plugin | |
| # (Core -> PLC) is memory-heavy, and concurrent GHC processes OOM-kill the | |
| # runner (seen mid-compile on PlutusLedgerApi.V3). | |
| - name: Test uplc-ghc | |
| run: JOBS=1 GHC="$GITHUB_WORKSPACE/_build/install/bin/uplc-ghc" ./plinth-test.sh | |
| # musl (Alpine) build, mirroring plinth-build-linux. GitHub runners are glibc | |
| # and haskell-actions/setup only ships glibc GHC, so the toolchain + build run | |
| # inside an alpine:3.20 container via `docker run` (with a musl boot-GHC from | |
| # ghcup). The workspace is mounted in; host-level free-disk / submodule steps | |
| # still run on the runner first. `docker run` (not a job-level `container:`) | |
| # keeps those host steps working. | |
| plinth-build-linux-musl: | |
| name: plinth-build / ${{ matrix.os }} (musl) / ghc ${{ matrix.ghc }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, ubuntu-24.04-arm] | |
| ghc: ['9.6.7'] | |
| steps: | |
| - name: Free disk space | |
| uses: jlumbroso/free-disk-space@main | |
| with: | |
| tool-cache: false | |
| android: true | |
| dotnet: true | |
| haskell: false | |
| large-packages: false | |
| docker-images: true | |
| swap-storage: true | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| # The runner's workspace disk (/) is small; /mnt has ~70 GB. Bind-mount it as | |
| # _build so the GHC build has room. docker -v is recursive, so this nested | |
| # mount is visible inside the container at /workspace/_build. | |
| - name: Build on the large /mnt disk | |
| run: | | |
| sudo mkdir -p /mnt/_build | |
| mkdir -p "$GITHUB_WORKSPACE/_build" | |
| sudo mount --bind /mnt/_build "$GITHUB_WORKSPACE/_build" | |
| sudo chown "$USER" "$GITHUB_WORKSPACE/_build" | |
| # The static plugin baked into uplc-ghc is built from the plutus submodule. | |
| - name: Init and update submodules | |
| run: | | |
| git submodule sync | |
| git submodule update --init --recursive --depth 1 --jobs 4 | |
| # BINDIST=1 produces the fixed-up uplc-ghc bindist (+ .tar.xz). plinth-build.sh | |
| # appends a -musl suffix to the archive name when run on musl libc. | |
| - name: Build uplc-ghc bindist (Alpine/musl) | |
| run: | | |
| docker run --rm \ | |
| -v "$GITHUB_WORKSPACE:/workspace" -w /workspace \ | |
| -e BINDIST=1 -e LANG=C.UTF-8 -e LC_ALL=C.UTF-8 \ | |
| alpine:3.20 sh -c ' | |
| set -eux | |
| . ./.github/alpine-setup.sh | |
| ./plinth-build.sh | |
| ' | |
| - name: Upload uplc-ghc bindist (musl) | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: plinth-bindist-musl-${{ matrix.os }}-${{ matrix.ghc }} | |
| path: _build/bindist/*.tar.xz | |
| retention-days: 1 | |
| # the archive is already xz-compressed; don't re-zip it | |
| compression-level: 0 | |
| # musl test, mirroring plinth-test-linux: consume the musl bindist, install it | |
| # and run the test suite inside alpine:3.20. Depends on plinth-build-linux-musl. | |
| plinth-test-linux-musl: | |
| name: plinth-test / ${{ matrix.os }} (musl) / ghc ${{ matrix.ghc }} | |
| needs: plinth-build-linux-musl | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, ubuntu-24.04-arm] | |
| ghc: ['9.6.7'] | |
| steps: | |
| - name: Free disk space | |
| uses: jlumbroso/free-disk-space@main | |
| with: | |
| tool-cache: false | |
| android: true | |
| dotnet: true | |
| haskell: false | |
| large-packages: false | |
| docker-images: true | |
| swap-storage: true | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| # plinth/test/cabal.project pulls plutus-tx/plutus-core/plutus-ledger-api | |
| # from the plutus submodule. | |
| - name: Init and update submodules | |
| run: | | |
| git submodule sync | |
| git submodule update --init --recursive --depth 1 --jobs 4 | |
| # A single Plinth plugin compile of a makeLift-heavy module | |
| # (PlutusLedgerApi.V3.Contexts) spikes past the ~16 GB runner RAM and gets | |
| # OOM-killed even with JOBS=1. Add a 16 GB swapfile on the large /mnt disk | |
| # so the spike spills to swap; the container shares the host's memory+swap. | |
| - name: Add 16 GB swap | |
| run: | | |
| sudo fallocate -l 16G /mnt/swapfile | |
| sudo chmod 600 /mnt/swapfile | |
| sudo mkswap /mnt/swapfile | |
| sudo swapon /mnt/swapfile | |
| swapon --show | |
| free -h | |
| - name: Download uplc-ghc bindist (musl) | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: plinth-bindist-musl-${{ matrix.os }}-${{ matrix.ghc }} | |
| path: _build/artifact | |
| # Install the musl bindist (configure + make install) and run the test suite | |
| # inside Alpine. Mirrors the glibc test job's install guards (exactly one | |
| # archive, exactly one extracted dir, executable check) and JOBS=1. | |
| - name: Install and test uplc-ghc (Alpine/musl) | |
| run: | | |
| docker run --rm \ | |
| -v "$GITHUB_WORKSPACE:/workspace" -w /workspace \ | |
| -e LANG=C.UTF-8 -e LC_ALL=C.UTF-8 \ | |
| alpine:3.20 sh -c ' | |
| set -eux | |
| . ./.github/alpine-setup.sh | |
| archives=$(ls _build/artifact/*.tar.xz) | |
| n=$(echo "$archives" | wc -l) | |
| if [ "$n" -ne 1 ]; then | |
| echo "error: expected exactly one bindist archive, found: ${archives:-none}" | |
| exit 1 | |
| fi | |
| mkdir -p _build/bindist | |
| echo "extracting $archives" | |
| tar --xz -xf "$archives" -C _build/bindist | |
| bindirs=$(ls -d _build/bindist/ghc-*) | |
| if [ "$(echo "$bindirs" | wc -l)" -ne 1 ]; then | |
| echo "error: expected one extracted bindist dir, found: ${bindirs:-none}" | |
| exit 1 | |
| fi | |
| ( cd "$bindirs" && ./configure --prefix=/workspace/_build/install && make install ) | |
| uplc=/workspace/_build/install/bin/uplc-ghc | |
| if [ ! -x "$uplc" ]; then | |
| echo "error: $uplc missing or not executable after make install" | |
| ls -l /workspace/_build/install/bin || true | |
| exit 1 | |
| fi | |
| rm -rf _build/artifact | |
| JOBS=1 GHC="$uplc" ./plinth-test.sh | |
| ' | |
| plinth-build-windows: | |
| name: plinth-build / windows-latest / ghc ${{ matrix.ghc }} | |
| runs-on: windows-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| ghc: ['9.6.7'] | |
| defaults: | |
| run: | |
| shell: msys2 {0} | |
| steps: | |
| # Git on Windows defaults to not creating symlinks; the submodules and | |
| # bindist rely on them, so enable symlink support before checkout. | |
| - name: Configure git for Windows | |
| shell: bash | |
| run: git config --global core.symlinks true | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| # The pre-MSYS2 step runs in the default Git-for-Windows bash because the | |
| # MSYS2 environment is not set up yet. All submodules are public HTTPS, so | |
| # no credentials are needed. | |
| - name: Init and update submodules | |
| shell: bash | |
| run: | | |
| git submodule sync | |
| # Shallow (single-commit) submodule clones: the build never needs | |
| # submodule history, and these 29 submodules (notably plutus) carry a | |
| # lot of it. --jobs fetches them in parallel. GitHub allows fetching | |
| # the pinned SHA directly, so depth 1 works even when it isn't a | |
| # branch tip. | |
| git submodule update --init --recursive --depth 1 --jobs 4 | |
| # MINGW64 toolchain + the autotools/python/sphinx needed by the build. | |
| # path-type: inherit keeps the Windows PATH (incl. the GHC/cabal installed | |
| # below) visible inside the MSYS2 shell. | |
| - name: Setup MSYS2 | |
| uses: msys2/setup-msys2@v2 | |
| with: | |
| msystem: MINGW64 | |
| path-type: inherit | |
| update: false | |
| install: >- | |
| autoconf automake make tar xz patch curl | |
| mingw-w64-x86_64-python mingw-w64-x86_64-python-sphinx | |
| - uses: haskell-actions/setup@v2 | |
| id: setup | |
| name: Setup Haskell tools | |
| with: | |
| ghc-version: ${{ matrix.ghc }} | |
| cabal-version: "latest" | |
| cabal-update: true | |
| # BINDIST=1 produces the fixed-up uplc-ghc bindist (+ .tar.xz archive); the | |
| # plinth-test-windows job consumes that archive instead of rebuilding. | |
| - name: Build uplc-ghc bindist | |
| run: BINDIST=1 ./plinth-build.sh | |
| - name: Upload uplc-ghc bindist | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| # key by GHC so this never collides with another job's artifact within a | |
| # run. See the matching download in the plinth-test-windows job. | |
| name: plinth-bindist-windows-${{ matrix.ghc }} | |
| path: _build/bindist/*.tar.xz | |
| retention-days: 1 | |
| # the archive is already xz-compressed; don't re-zip it | |
| compression-level: 0 | |
| # plinth-test on Windows, mirroring plinth-test-linux: consume the bindist | |
| # produced by plinth-build-windows, install it (configure + make install under | |
| # MSYS2) and run the test suite. Depends on plinth-build-windows. | |
| plinth-test-windows: | |
| name: plinth-test / windows-latest / ghc ${{ matrix.ghc }} | |
| needs: plinth-build-windows | |
| runs-on: windows-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| ghc: ['9.6.7'] | |
| defaults: | |
| run: | |
| shell: msys2 {0} | |
| steps: | |
| # Git on Windows defaults to not creating symlinks; the submodules and the | |
| # bindist's wrappers/ dir rely on them, so enable symlink support first. | |
| - name: Configure git for Windows | |
| shell: bash | |
| run: git config --global core.symlinks true | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| # The pre-MSYS2 step runs in the default Git-for-Windows bash because the | |
| # MSYS2 environment is not set up yet. plinth/test/cabal.project pulls | |
| # plutus-tx/plutus-core/plutus-ledger-api from the plutus submodule. | |
| - name: Init and update submodules | |
| shell: bash | |
| run: | | |
| git submodule sync | |
| # Shallow (single-commit) submodule clones: the build never needs | |
| # submodule history. --jobs fetches them in parallel. | |
| git submodule update --init --recursive --depth 1 --jobs 4 | |
| # MINGW64 toolchain incl. tar/xz to extract the bindist. path-type: inherit | |
| # keeps the Windows PATH (incl. the cabal installed below) visible. | |
| - name: Setup MSYS2 | |
| uses: msys2/setup-msys2@v2 | |
| with: | |
| msystem: MINGW64 | |
| path-type: inherit | |
| update: false | |
| install: >- | |
| autoconf automake make tar xz patch curl | |
| mingw-w64-x86_64-python mingw-w64-x86_64-python-sphinx | |
| # plinth-test.sh invokes cabal, so cabal must be on PATH. (The boot GHC this | |
| # also installs is unused by the test.) | |
| - uses: haskell-actions/setup@v2 | |
| id: setup | |
| name: Setup Haskell tools | |
| with: | |
| ghc-version: ${{ matrix.ghc }} | |
| cabal-version: "latest" | |
| cabal-update: true | |
| - name: Download uplc-ghc bindist | |
| uses: actions/download-artifact@v4 | |
| with: | |
| # matches the plinth-build-windows upload name. | |
| name: plinth-bindist-windows-${{ matrix.ghc }} | |
| path: _build/artifact | |
| # Unlike Unix, the Windows GHC bindist has no configure/make install: it is | |
| # relocatable, so we just extract it and run bin/uplc-ghc.exe in place. | |
| - name: Extract uplc-ghc bindist | |
| run: | | |
| shopt -s nullglob | |
| archives=(_build/artifact/*.tar.xz) | |
| if [ ${#archives[@]} -ne 1 ]; then | |
| echo "error: expected exactly one bindist archive, found: ${archives[*]:-none}" | |
| exit 1 | |
| fi | |
| mkdir -p _build/install | |
| echo "extracting ${archives[0]}" | |
| tar --xz -xf "${archives[0]}" -C _build/install | |
| bindirs=(_build/install/ghc-*) | |
| if [ ${#bindirs[@]} -ne 1 ]; then | |
| echo "error: expected one extracted bindist dir, found: ${bindirs[*]:-none}" | |
| exit 1 | |
| fi | |
| uplc="${bindirs[0]}/bin/uplc-ghc.exe" | |
| if [ ! -x "$uplc" ]; then | |
| echo "error: $uplc missing or not executable in extracted bindist" | |
| ls -l "${bindirs[0]}/bin" || true | |
| exit 1 | |
| fi | |
| rm -rf _build/artifact | |
| # Build packages serially (JOBS=1): compiling with the Plinth plugin | |
| # (Core -> PLC) is memory-heavy. Pass GHC as an absolute path: plinth-test.sh | |
| # cd's into plinth/test before invoking cabal, so a repo-root-relative path | |
| # would be resolved against the wrong directory. | |
| - name: Test uplc-ghc | |
| run: | | |
| bindirs=(_build/install/ghc-*) | |
| JOBS=1 GHC="$(pwd)/${bindirs[0]}/bin/uplc-ghc.exe" ./plinth-test.sh | |
| # macOS build, mirroring plinth-build-linux. macOS uses the Unix bindist | |
| # (configure + make install), so the build is much like Linux; the | |
| # differences are: no /mnt large-disk mount (the runner's disk is large | |
| # enough for the lean flavour) and autoconf/automake come from Homebrew | |
| # rather than the base image. Runs on Apple Silicon (aarch64-darwin). | |
| plinth-build-macos: | |
| name: plinth-build / macos-15 / ghc ${{ matrix.ghc }} | |
| runs-on: macos-15 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| ghc: ['9.6.7'] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| # The static plugin baked into uplc-ghc is built from the plutus submodule. | |
| - name: Init and update submodules | |
| run: | | |
| git submodule sync | |
| # Shallow (single-commit) submodule clones: the build never needs | |
| # submodule history. --jobs fetches them in parallel. | |
| git submodule update --init --recursive --depth 1 --jobs 4 | |
| # The GHC source build's boot step runs autoreconf, which needs | |
| # autoconf/automake; they are not in the macOS runner image. The lean | |
| # BINDIST flavour builds with --docs=none, so no sphinx is needed. | |
| - name: Install autotools (macOS) | |
| run: brew install autoconf automake | |
| - uses: haskell-actions/setup@v2 | |
| id: setup | |
| name: Setup Haskell tools | |
| with: | |
| ghc-version: ${{ matrix.ghc }} | |
| cabal-version: "latest" | |
| cabal-update: true | |
| # BINDIST=1 produces the fixed-up uplc-ghc bindist (+ .tar.xz archive); the | |
| # plinth-test-macos job consumes that archive instead of rebuilding. | |
| - name: Build uplc-ghc bindist | |
| run: BINDIST=1 ./plinth-build.sh | |
| - name: Upload uplc-ghc bindist | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| # key by GHC so this never collides with another job's artifact within a | |
| # run. See the matching download in the plinth-test-macos job. | |
| name: plinth-bindist-macos-${{ matrix.ghc }} | |
| path: _build/bindist/*.tar.xz | |
| retention-days: 1 | |
| # the archive is already xz-compressed; don't re-zip it | |
| compression-level: 0 | |
| # plinth-test on macOS, mirroring plinth-test-linux: consume the bindist | |
| # produced by plinth-build-macos, install it (configure + make install) and | |
| # run the test suite. Depends on plinth-build-macos. | |
| plinth-test-macos: | |
| name: plinth-test / macos-15 / ghc ${{ matrix.ghc }} | |
| needs: plinth-build-macos | |
| runs-on: macos-15 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| ghc: ['9.6.7'] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| # plinth/test/cabal.project pulls plutus-tx/plutus-core/plutus-ledger-api | |
| # from the plutus submodule. | |
| - name: Init and update submodules | |
| run: | | |
| git submodule sync | |
| git submodule update --init --recursive --depth 1 --jobs 4 | |
| # plinth-test.sh invokes cabal, so cabal must be on PATH. (The boot GHC this | |
| # also installs is unused by the test.) | |
| - uses: haskell-actions/setup@v2 | |
| id: setup | |
| name: Setup Haskell tools | |
| with: | |
| ghc-version: ${{ matrix.ghc }} | |
| cabal-version: "latest" | |
| cabal-update: true | |
| - name: Download uplc-ghc bindist | |
| uses: actions/download-artifact@v4 | |
| with: | |
| # matches the plinth-build-macos upload name. | |
| name: plinth-bindist-macos-${{ matrix.ghc }} | |
| path: _build/artifact | |
| # Install the bindist with the usual configure + make install; the bindist's | |
| # wrappers/ dir (added by the BINDIST fixup) makes this produce bin/uplc-ghc. | |
| # Each step is guarded so a missing artifact or failed install fails loudly | |
| # here, not with a confusing error deep in the test. | |
| - name: Install uplc-ghc bindist | |
| run: | | |
| shopt -s nullglob | |
| archives=(_build/artifact/*.tar.xz) | |
| if [ ${#archives[@]} -ne 1 ]; then | |
| echo "error: expected exactly one bindist archive, found: ${archives[*]:-none}" | |
| exit 1 | |
| fi | |
| mkdir -p _build/bindist | |
| echo "extracting ${archives[0]}" | |
| tar --xz -xf "${archives[0]}" -C _build/bindist | |
| bindirs=(_build/bindist/ghc-*) | |
| if [ ${#bindirs[@]} -ne 1 ]; then | |
| echo "error: expected one extracted bindist dir, found: ${bindirs[*]:-none}" | |
| exit 1 | |
| fi | |
| ( cd "${bindirs[0]}" && ./configure --prefix="$GITHUB_WORKSPACE/_build/install" && make install ) | |
| uplc="$GITHUB_WORKSPACE/_build/install/bin/uplc-ghc" | |
| if [ ! -x "$uplc" ]; then | |
| echo "error: $uplc missing or not executable after make install" | |
| ls -l "$GITHUB_WORKSPACE/_build/install/bin" || true | |
| exit 1 | |
| fi | |
| rm -rf _build/artifact | |
| # Build packages serially (JOBS=1): compiling with the Plinth plugin | |
| # (Core -> PLC) is memory-heavy. A single makeLift-heavy compile | |
| # (PlutusLedgerApi.V3.Contexts) spikes well past the runner's RAM; unlike | |
| # the Linux job (explicit swapfile) macOS grows its swap dynamically, so the | |
| # spike spills to swap on its own and no swap-setup step is needed here. | |
| - name: Test uplc-ghc | |
| run: JOBS=1 GHC="$GITHUB_WORKSPACE/_build/install/bin/uplc-ghc" ./plinth-test.sh | |
| # ghcup install tests: verify each platform's bindist installs cleanly as the | |
| # custom `plinth` ghcup tool -- the same path end users take -- on every push, | |
| # not just on tags. Each mirrors its plinth-test-* sibling but installs via | |
| # ghcup + generate-ghcup-metadata.py (file:// metadata) instead of a bare | |
| # configure+make, so a broken dlInstallSpec / generator / metadata schema is | |
| # caught in the PR that introduces it. plinth-ghcup-test.sh does the work; it | |
| # needs only the bindist tarball + python3 + a ghcup >= 0.2.1.0 (no GHC | |
| # rebuild, no submodules -- the sample it compiles is plain Haskell). | |
| plinth-ghcup-test-linux: | |
| name: plinth-ghcup-test / ${{ matrix.os }} / ghc ${{ matrix.ghc }} | |
| needs: plinth-build-linux | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, ubuntu-24.04-arm] | |
| ghc: ['9.6.7'] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| - name: Download uplc-ghc bindist | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: plinth-bindist-${{ matrix.os }}-${{ matrix.ghc }} | |
| path: _build/artifact | |
| # A stock ghcup for this architecture; the installer DSL needs >= 0.2.1.0 | |
| # (plinth-ghcup-test.sh capability-gates on it). | |
| - name: Install stock ghcup | |
| run: | | |
| set -eux | |
| mkdir -p "$HOME/.local/bin" | |
| curl -fL "https://downloads.haskell.org/~ghcup/$(uname -m)-linux-ghcup" \ | |
| -o "$HOME/.local/bin/ghcup" | |
| chmod +x "$HOME/.local/bin/ghcup" | |
| echo "$HOME/.local/bin" >> "$GITHUB_PATH" | |
| - name: Install and test uplc-ghc via ghcup | |
| run: BINDIST_DIR=_build/artifact ./plinth-ghcup-test.sh | |
| plinth-ghcup-test-linux-musl: | |
| name: plinth-ghcup-test / ${{ matrix.os }} (musl) / ghc ${{ matrix.ghc }} | |
| needs: plinth-build-linux-musl | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, ubuntu-24.04-arm] | |
| ghc: ['9.6.7'] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| - name: Download uplc-ghc bindist (musl) | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: plinth-bindist-musl-${{ matrix.os }}-${{ matrix.ghc }} | |
| path: _build/artifact | |
| # Run inside Alpine so the musl uplc-ghc can execute. Unlike the build/test | |
| # jobs we do NOT source alpine-setup.sh: that bootstraps a full boot GHC + | |
| # cabal we would never use here. Instead install just the deps needed to run | |
| # uplc-ghc and link the sample, and download the ghcup binary directly -- | |
| # the same x86_64/aarch64-linux ghcup the official installer uses on Alpine. | |
| # | |
| # coreutils/findutils/grep are the GNU tools the bindist's `make install` | |
| # (which ghcup runs for us) is exercised with upstream; busybox's applets | |
| # differ in flag support, so don't rely on them for that path. | |
| # | |
| # EXPECT_MUSL=1 asserts the artifact really is the musl bindist: the ghcup | |
| # platform is derived from the bindist name, and a musl bindist mislabelled | |
| # as glibc installs fine here while being broken for users on Alpine. | |
| - name: Install and test uplc-ghc via ghcup (Alpine/musl) | |
| run: | | |
| docker run --rm \ | |
| -v "$GITHUB_WORKSPACE:/workspace" -w /workspace \ | |
| -e LANG=C.UTF-8 -e LC_ALL=C.UTF-8 \ | |
| alpine:3.20 sh -c ' | |
| set -eux | |
| apk add --no-cache bash build-base coreutils curl findutils grep \ | |
| gmp-dev ncurses-dev python3 tar xz | |
| mkdir -p "$HOME/.local/bin" | |
| curl -fL "https://downloads.haskell.org/~ghcup/$(uname -m)-linux-ghcup" \ | |
| -o "$HOME/.local/bin/ghcup" | |
| chmod +x "$HOME/.local/bin/ghcup" | |
| BINDIST_DIR=_build/artifact GHCUP_BIN="$HOME/.local/bin/ghcup" \ | |
| EXPECT_MUSL=1 bash ./plinth-ghcup-test.sh | |
| ' | |
| plinth-ghcup-test-macos: | |
| name: plinth-ghcup-test / macos-15 / ghc ${{ matrix.ghc }} | |
| needs: plinth-build-macos | |
| runs-on: macos-15 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| ghc: ['9.6.7'] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| - name: Download uplc-ghc bindist | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: plinth-bindist-macos-${{ matrix.ghc }} | |
| path: _build/artifact | |
| # macos-15 is Apple Silicon (aarch64-apple-darwin). | |
| - name: Install stock ghcup | |
| run: | | |
| set -eux | |
| mkdir -p "$HOME/.local/bin" | |
| curl -fL "https://downloads.haskell.org/~ghcup/aarch64-apple-darwin-ghcup" \ | |
| -o "$HOME/.local/bin/ghcup" | |
| chmod +x "$HOME/.local/bin/ghcup" | |
| echo "$HOME/.local/bin" >> "$GITHUB_PATH" | |
| - name: Install and test uplc-ghc via ghcup | |
| run: BINDIST_DIR=_build/artifact ./plinth-ghcup-test.sh | |
| plinth-ghcup-test-windows: | |
| name: plinth-ghcup-test / windows-latest / ghc ${{ matrix.ghc }} | |
| needs: plinth-build-windows | |
| runs-on: windows-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| ghc: ['9.6.7'] | |
| defaults: | |
| run: | |
| shell: msys2 {0} | |
| steps: | |
| # The bindist / ghcup use symlinks; enable them before checkout. | |
| - name: Configure git for Windows | |
| shell: bash | |
| run: git config --global core.symlinks true | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| # python3 (for generate-ghcup-metadata.py) + tar/xz/curl to fetch ghcup and | |
| # unpack. cygpath (used by plinth-ghcup-test.sh for native/MSYS path | |
| # translation) is part of the base MSYS2 install. | |
| - name: Setup MSYS2 | |
| uses: msys2/setup-msys2@v2 | |
| with: | |
| msystem: MINGW64 | |
| path-type: inherit | |
| update: false | |
| install: >- | |
| tar xz curl mingw-w64-x86_64-python | |
| - name: Download uplc-ghc bindist | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: plinth-bindist-windows-${{ matrix.ghc }} | |
| path: _build/artifact | |
| - name: Install stock ghcup | |
| run: | | |
| set -eux | |
| mkdir -p "$HOME/.local/bin" | |
| curl -fL "https://downloads.haskell.org/~ghcup/x86_64-mingw64-ghcup.exe" \ | |
| -o "$HOME/.local/bin/ghcup.exe" | |
| - name: Install and test uplc-ghc via ghcup | |
| run: BINDIST_DIR=_build/artifact GHCUP_BIN="$HOME/.local/bin/ghcup.exe" ./plinth-ghcup-test.sh | |
| # Note [ghcup release channel] | |
| # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| # On a v* tag, publish the per-platform bindists as a GitHub Release and | |
| # (re)generate the ghcup channel metadata on the gh-pages branch. uplc-ghc is | |
| # distributed as a custom third-party ghcup tool named `plinth`, installable | |
| # with a stock ghcup >= 0.2.1.0 (installer DSL). Users then run: | |
| # ghcup config add-release-channel \ | |
| # https://input-output-hk.github.io/ghc-plinth/ghcup-plinth.yaml | |
| # ghcup install plinth <version> | |
| # | |
| # The generator (generate-ghcup-metadata.py) accumulates every released | |
| # version into gh-pages/ghcup-plinth.versions.json and re-emits the channel | |
| # YAML, so old versions stay installable. Before publishing, we validate the | |
| # metadata end-to-end by installing plinth from a file:// copy of it (real | |
| # download + configure + make + symlink), decoupled from the not-yet-public | |
| # release URLs. | |
| release: | |
| name: release / ghcup channel | |
| if: startsWith(github.ref, 'refs/tags/') | |
| # Gate publishing on the per-platform ghcup install tests (which transitively | |
| # require the build jobs and their uploaded bindist artifacts). | |
| needs: | |
| - plinth-ghcup-test-linux | |
| - plinth-ghcup-test-linux-musl | |
| - plinth-ghcup-test-windows | |
| - plinth-ghcup-test-macos | |
| runs-on: ubuntu-latest | |
| # Overrides the top-level `contents: read`: this job creates a Release and | |
| # pushes the regenerated channel to gh-pages. | |
| permissions: | |
| contents: write | |
| env: | |
| TAG: ${{ github.ref_name }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: false | |
| # Collect every platform's bindist tarball into dist/. Each artifact holds a | |
| # single ghc-<ver>-<platform>[-musl].tar.xz with a distinct name, so | |
| # merge-multiple flattens them without collisions. | |
| - name: Download all bindists | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: plinth-bindist-* | |
| path: dist | |
| merge-multiple: true | |
| - name: List bindists and set version | |
| run: | | |
| set -eux | |
| ls -l dist/*.tar.xz | |
| # ghcup tool version: the tag without the leading 'v'. | |
| echo "VERSION=${TAG#v}" >> "$GITHUB_ENV" | |
| # The install is already validated end-to-end on every platform by the | |
| # plinth-ghcup-test-* jobs this job `needs`, so no re-validation here. | |
| # Publish the Release with all tarballs. Create it if missing, otherwise | |
| # (re-)upload the assets. | |
| - name: Create GitHub Release | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -eux | |
| if ! gh release view "$TAG" >/dev/null 2>&1; then | |
| gh release create "$TAG" \ | |
| --title "uplc-ghc $VERSION" \ | |
| --notes "Plinth standalone compiler (uplc-ghc) bindists for $VERSION. Install via ghcup: see README." \ | |
| dist/*.tar.xz | |
| else | |
| gh release upload "$TAG" dist/*.tar.xz --clobber | |
| fi | |
| # Regenerate the channel on gh-pages with the real Release download URLs and | |
| # push it. The generator merges into the existing versions DB so previously | |
| # released versions remain in the channel. | |
| - name: Check out gh-pages | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: gh-pages | |
| path: gh-pages-out | |
| - name: Regenerate and publish ghcup channel | |
| run: | | |
| set -eux | |
| python3 generate-ghcup-metadata.py \ | |
| --version "$VERSION" \ | |
| --base-url "https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}" \ | |
| --db gh-pages-out/ghcup-plinth.versions.json \ | |
| --output gh-pages-out/ghcup-plinth.yaml \ | |
| --release-day "$(date -u +%Y-%m-%d)" \ | |
| --set-latest \ | |
| dist/*.tar.xz | |
| cd gh-pages-out | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add ghcup-plinth.yaml ghcup-plinth.versions.json | |
| git commit -m "ghcup channel: publish plinth ${VERSION} (${TAG})" || echo "no changes to commit" | |
| git push |