Skip to content

Commit 0f520ce

Browse files
authored
Migrate to Spago version 1 (#64)
Migrate from Spago 0.21.0 (Dhall) to Spago 1.0.3 (YAML + Registry) Replace Dhall configuration with spago.yaml using registry-based package set 73.0.0. Update Docker image and test runner for Spago 1.0 compatibility. Key changes: - Dockerfile: node:22-bookworm-slim (Spago 1.0.3 needs node:sqlite), documented dependencies, npm cache cleanup - bin/run.sh: rewrite package name to match pre-compiled lockfile for --offline --pure mode, HOME=/tmp for read-only Docker, sanitize Spago 1.0 output (strip stack traces, new preamble format) - pre-compiled: spago.yaml replaces dhall files - Test examples: spago.yaml, regenerated expected_results.json [Companion PR in exercism/purescript](exercism/purescript#323).
1 parent 1f63885 commit 0f520ce

29 files changed

Lines changed: 1678 additions & 3276 deletions

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ tests/
1111
pre-compiled/.spago
1212
pre-compiled/node_modules
1313
pre-compiled/output
14+
pre-compiled/spago.lock
1415
.appends
1516
.gitignore
1617
.gitattributes

Dockerfile

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,33 @@
1-
# Use the latest LTS version of Node.js
2-
FROM node:20-bullseye-slim
1+
# Spago 1.0.3 requires Node.js >= 22.5.0 (uses node:sqlite built-in module)
2+
FROM node:22-bookworm-slim
33

4-
# Update package lists and install required dependencies
4+
# Install system dependencies:
5+
# - ca-certificates: HTTPS support for downloading packages from the registry
6+
# - git: required by Spago for registry index management
7+
# - jq: used by run.sh to generate results.json output
8+
# - libncurses5: runtime dependency for the PureScript compiler (purs)
59
RUN apt-get update && apt-get install -y --no-install-recommends \
610
ca-certificates \
711
git \
812
jq \
913
libncurses5 \
1014
&& rm -rf /var/lib/apt/lists/*
1115

12-
# Set up working directory
13-
WORKDIR /opt/test-runner/pre-compiled
16+
# Use a single WORKDIR for the test runner root
17+
WORKDIR /opt/test-runner
1418

15-
# Copy and install dependencies
16-
COPY pre-compiled .
17-
RUN npm install && npx spago install && npx spago build --deps-only
19+
# Install PureScript dependencies and pre-compile them.
20+
# The output/ and .spago/ directories are reused at runtime to avoid
21+
# recompiling 280+ modules for every student submission.
22+
COPY pre-compiled pre-compiled/
23+
RUN cd pre-compiled \
24+
&& npm install \
25+
&& npx spago install \
26+
&& npm cache clean --force \
27+
&& rm -rf /root/.npm
1828

19-
# Set up bin directory
20-
WORKDIR /opt/test-runner/bin
21-
COPY bin/run.sh bin/run-tests.sh ./
29+
# Copy runner scripts
30+
COPY bin/run.sh bin/run-tests.sh bin/
31+
RUN chmod +x bin/*.sh
2232

23-
# Ensure scripts have execution permissions
24-
RUN chmod +x /opt/test-runner/bin/*.sh
25-
26-
# Set the entry point
2733
ENTRYPOINT ["/opt/test-runner/bin/run.sh"]

bin/run-tests.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env bash
22

33
# Synopsis:
4-
# Test the test runner by running it against a predefined set of solutions
4+
# Test the test runner by running it against a predefined set of solutions
55
# with an expected output.
66

77
# Output:
@@ -19,7 +19,7 @@ exit_code=0
1919
base_dir=$(builtin cd "${BASH_SOURCE%/*}/.." || exit; pwd)
2020

2121
# Iterate over all test Spago projects
22-
for config in "${base_dir}"/tests/*/spago.dhall; do
22+
for config in "${base_dir}"/tests/*/spago.yaml; do
2323
exercise_dir=$(dirname "${config}")
2424
slug=$(basename "${exercise_dir}")
2525
expected_results_file="${exercise_dir}/expected_results.json"

bin/run.sh

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ results_file="${output_dir}/results.json"
3737
# - We can work with a write-able file-system
3838
# - We avoid copying files between the docker host and client giving a nice speedup.
3939
build_dir=/tmp/build
40-
cache_dir=${build_dir}/cache
4140

4241
if [ ! -d "${input_dir}" ]; then
4342
echo "No such directory: ${input_dir}"
@@ -55,17 +54,12 @@ fi
5554
mkdir -p ${build_dir}
5655
pushd "${build_dir}" > /dev/null || exit
5756

58-
# Put the basic spago project in place
59-
cp "${input_dir}"/*.dhall .
57+
# Put the spago project in place: copy and rewrite the package name to match
58+
# the pre-compiled lockfile so we can use --pure mode (no registry access).
59+
sed 's/^ name: .*/ name: pre-compiled/' "${input_dir}/spago.yaml" > spago.yaml
6060
ln -s "${input_dir}"/src .
6161
ln -s "${input_dir}"/test .
6262

63-
# Setup cache directory. We require a writable dhall cache because dhall will
64-
# attempt to fetch the upstream package-set definition.
65-
mkdir ${cache_dir}
66-
cp -R "${HOME}"/.cache/dhall ${cache_dir}
67-
cp -R "${HOME}"/.cache/dhall-haskell ${cache_dir}
68-
6963
# Setup our prepared node setup.
7064
ln -s "${base_dir}/pre-compiled/node_modules" .
7165

@@ -75,17 +69,15 @@ ln -s "${base_dir}/pre-compiled/node_modules" .
7569
# flag).
7670
cp -R -p "${base_dir}/pre-compiled/output" .
7771
cp -R "${base_dir}/pre-compiled/.spago" .
72+
cp "${base_dir}/pre-compiled/spago.lock" .
7873

7974
echo "Build and test ${slug} in ${build_dir}..."
8075

8176
# Run the tests for the provided implementation file and redirect stdout and
82-
# stderr to capture it. We do our best to minimize the output to emit and
83-
# compiler errors or unit test output as this scrubbed and presented to the
84-
# student. In addition spago will try to write to ~/cache/.spago and will fail
85-
# on a read-only mount and thus we skip the global cache and request to not
86-
# install packages.
87-
export XDG_CACHE_HOME=${cache_dir}
88-
spago_output=$(npx spago --global-cache skip --no-psa test --no-install 2>&1)
77+
# stderr to capture it.
78+
# --offline --pure: use cached packages and lockfile, no registry/network access
79+
# HOME=/tmp: Spago's SQLite cache needs a writable directory (Docker runs --read-only)
80+
spago_output=$(HOME=/tmp npx spago test --offline --pure 2>&1)
8981
exit_code=$?
9082

9183
popd > /dev/null || exit
@@ -95,9 +87,30 @@ popd > /dev/null || exit
9587
if [ $exit_code -eq 0 ]; then
9688
jq -n '{version: 1, status: "pass"}' > "${results_file}"
9789
else
98-
sanitized_spago_output=$(echo "${spago_output}" | sed -E \
99-
-e '/^Compiling/d' \
100-
-e '/at.*:[[:digit:]]+:[[:digit:]]+\)?/d')
90+
sanitized_spago_output=$(printf '%s\n' "${spago_output}" | awk '
91+
BEGIN { blanks = 2 }
92+
/^Reading Spago workspace/ || \
93+
/^✓ Selecting package/ || \
94+
/^Checking dependencies/ || \
95+
/^Downloading dependencies/ || \
96+
/^No lockfile found/ || \
97+
/^Lockfile written/ || \
98+
/^Building\.\.\./ || \
99+
/^\[[[:space:]]*[0-9]+ of [0-9]+\] Compiling / || \
100+
/^✓ Build succeeded/ || \
101+
/^Running tests for package/ || \
102+
/^✘ Tests failed/ || \
103+
/^✘ Failed to build/ || \
104+
/^[[:space:]]+Src[[:space:]]+Lib[[:space:]]+All/ || \
105+
/^Warnings[[:space:]]+[0-9]/ || \
106+
/^Errors[[:space:]]+[0-9]/ || \
107+
/^[[:space:]]+at .*(\.js|\.mjs|node:internal).*:[0-9]/ { next }
108+
/^\[WARNING / { warn = 1; next }
109+
warn && /^$/ { warn = 0; next }
110+
warn { next }
111+
NF { blanks = 0; print; next }
112+
blanks < 2 { blanks++; print }
113+
')
101114

102115
jq --null-input --arg output "${sanitized_spago_output}" '{version: 1, status: "fail", message: $output}' > "${results_file}"
103116
fi

bin/update-tests.sh

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
#!/usr/bin/env bash
22

3-
# This script will update spago.dhall and package.dhall of all exercises
4-
# using the master files from the project template (pre-compiled/).
3+
# This script will update the workspace section of all test examples'
4+
# spago.yaml to match the pre-compiled project's workspace configuration
5+
# (package set version, extra packages).
56

67
set -o pipefail
78
set -u
89

910
base_dir=$(builtin cd "${BASH_SOURCE%/*}/.." || exit; pwd)
10-
project_dir="${base_dir}/pre-compiled"
1111

12-
for config in ./tests/*/spago.dhall; do
12+
# Extract the workspace section from pre-compiled/spago.yaml
13+
workspace_section=$(sed -n '/^workspace:/,$p' "${base_dir}/pre-compiled/spago.yaml")
14+
15+
for config in "${base_dir}"/tests/*/spago.yaml; do
1316
exercise_dir=$(dirname "${config}")
14-
# slug=$(basename "${exercise_dir}")
17+
slug=$(basename "${exercise_dir}")
1518

1619
echo "Working in ${exercise_dir}..."
1720

18-
# sed -e "s/pre-compiled/${slug}/" < "${project_dir}/spago.dhall" > "${exercise_dir}/spago.dhall"
19-
cp "${project_dir}/packages.dhall" "${exercise_dir}/packages.dhall"
21+
# Replace the workspace section (everything from "workspace:" to EOF)
22+
package_section=$(sed -n '1,/^workspace:/{ /^workspace:/!p; }' "${config}")
23+
printf '%s\n%s\n' "${package_section}" "${workspace_section}" > "${config}"
2024
done

0 commit comments

Comments
 (0)