Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
287 changes: 287 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ on:
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).
Expand Down Expand Up @@ -610,3 +614,286 @@ jobs:
# 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
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,34 @@ See the Plinth documentation:
- [Plutus / Plinth GitHub repository][plutus-repo]
- [Plinth project template][plinth-template]

Installing via ghcup
====================

The Plinth compiler `uplc-ghc` is distributed as a custom [ghcup][ghcup] tool
named `plinth`. It installs *alongside* your normal GHC (it never replaces the
`ghc` on your `PATH`). This needs **ghcup >= 0.2.1.0** (which added support for
third-party tools); upgrade with `ghcup upgrade` if needed.

Add the release channel once, then install and select a version:

$ ghcup config add-release-channel https://input-output-hk.github.io/ghc-plinth/ghcup-plinth.yaml
$ ghcup install plinth latest
$ ghcup set plinth latest # puts uplc-ghc on PATH

This provides `uplc-ghc`, both unversioned and version-suffixed
(`uplc-ghc-<version>`). The bindist's other tools (`ghc-pkg`, `haddock`, ...)
stay inside the installation directory rather than being linked onto your `PATH`,
so they cannot shadow the ones from your normal GHC. Point `cabal` at the
compiler to build a Plinth project:

$ cabal build -w uplc-ghc

`ghcup list -t plinth` shows the available versions. Prebuilt bindists are
published as GitHub Releases for x86_64/aarch64 Linux (glibc and musl), macOS
(Apple Silicon), and Windows; ghcup picks the right one for your platform.

[ghcup]: https://www.haskell.org/ghcup/ "ghcup"

The text below is the upstream GHC README.

This is the source tree for [GHC][1], a compiler and interactive
Expand Down
Loading
Loading