-
-
Notifications
You must be signed in to change notification settings - Fork 10
Migrate to Spago version 1 #64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,27 +1,33 @@ | ||
| # Use the latest LTS version of Node.js | ||
| FROM node:20-bullseye-slim | ||
| # Spago 1.0.3 requires Node.js >= 22.5.0 (uses node:sqlite built-in module) | ||
| FROM node:22-bookworm-slim | ||
|
|
||
| # Update package lists and install required dependencies | ||
| # Install system dependencies: | ||
| # - ca-certificates: HTTPS support for downloading packages from the registry | ||
| # - git: required by Spago for registry index management | ||
| # - jq: used by run.sh to generate results.json output | ||
| # - libncurses5: runtime dependency for the PureScript compiler (purs) | ||
| RUN apt-get update && apt-get install -y --no-install-recommends \ | ||
| ca-certificates \ | ||
| git \ | ||
| jq \ | ||
| libncurses5 \ | ||
| && rm -rf /var/lib/apt/lists/* | ||
|
|
||
| # Set up working directory | ||
| WORKDIR /opt/test-runner/pre-compiled | ||
| # Use a single WORKDIR for the test runner root | ||
| WORKDIR /opt/test-runner | ||
|
|
||
| # Copy and install dependencies | ||
| COPY pre-compiled . | ||
| RUN npm install && npx spago install && npx spago build --deps-only | ||
| # Install PureScript dependencies and pre-compile them. | ||
| # The output/ and .spago/ directories are reused at runtime to avoid | ||
| # recompiling 280+ modules for every student submission. | ||
| COPY pre-compiled pre-compiled/ | ||
| RUN cd pre-compiled \ | ||
| && npm install \ | ||
| && npx spago install \ | ||
| && npm cache clean --force \ | ||
| && rm -rf /root/.npm | ||
|
|
||
| # Set up bin directory | ||
| WORKDIR /opt/test-runner/bin | ||
| COPY bin/run.sh bin/run-tests.sh ./ | ||
| # Copy runner scripts | ||
| COPY bin/run.sh bin/run-tests.sh bin/ | ||
| RUN chmod +x bin/*.sh | ||
|
|
||
| # Ensure scripts have execution permissions | ||
| RUN chmod +x /opt/test-runner/bin/*.sh | ||
|
|
||
| # Set the entry point | ||
| ENTRYPOINT ["/opt/test-runner/bin/run.sh"] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,7 +37,6 @@ results_file="${output_dir}/results.json" | |
| # - We can work with a write-able file-system | ||
| # - We avoid copying files between the docker host and client giving a nice speedup. | ||
| build_dir=/tmp/build | ||
| cache_dir=${build_dir}/cache | ||
|
|
||
| if [ ! -d "${input_dir}" ]; then | ||
| echo "No such directory: ${input_dir}" | ||
|
|
@@ -55,17 +54,12 @@ fi | |
| mkdir -p ${build_dir} | ||
| pushd "${build_dir}" > /dev/null || exit | ||
|
|
||
| # Put the basic spago project in place | ||
| cp "${input_dir}"/*.dhall . | ||
| # Put the spago project in place: copy and rewrite the package name to match | ||
| # the pre-compiled lockfile so we can use --pure mode (no registry access). | ||
| sed 's/^ name: .*/ name: pre-compiled/' "${input_dir}/spago.yaml" > spago.yaml | ||
| ln -s "${input_dir}"/src . | ||
| ln -s "${input_dir}"/test . | ||
|
|
||
| # Setup cache directory. We require a writable dhall cache because dhall will | ||
| # attempt to fetch the upstream package-set definition. | ||
| mkdir ${cache_dir} | ||
| cp -R "${HOME}"/.cache/dhall ${cache_dir} | ||
| cp -R "${HOME}"/.cache/dhall-haskell ${cache_dir} | ||
|
|
||
| # Setup our prepared node setup. | ||
| ln -s "${base_dir}/pre-compiled/node_modules" . | ||
|
|
||
|
|
@@ -75,17 +69,15 @@ ln -s "${base_dir}/pre-compiled/node_modules" . | |
| # flag). | ||
| cp -R -p "${base_dir}/pre-compiled/output" . | ||
| cp -R "${base_dir}/pre-compiled/.spago" . | ||
| cp "${base_dir}/pre-compiled/spago.lock" . | ||
|
|
||
| echo "Build and test ${slug} in ${build_dir}..." | ||
|
|
||
| # Run the tests for the provided implementation file and redirect stdout and | ||
| # stderr to capture it. We do our best to minimize the output to emit and | ||
| # compiler errors or unit test output as this scrubbed and presented to the | ||
| # student. In addition spago will try to write to ~/cache/.spago and will fail | ||
| # on a read-only mount and thus we skip the global cache and request to not | ||
| # install packages. | ||
| export XDG_CACHE_HOME=${cache_dir} | ||
| spago_output=$(npx spago --global-cache skip --no-psa test --no-install 2>&1) | ||
| # stderr to capture it. | ||
| # --offline --pure: use cached packages and lockfile, no registry/network access | ||
| # HOME=/tmp: Spago's SQLite cache needs a writable directory (Docker runs --read-only) | ||
| spago_output=$(HOME=/tmp npx spago test --offline --pure 2>&1) | ||
| exit_code=$? | ||
|
|
||
| popd > /dev/null || exit | ||
|
|
@@ -95,9 +87,43 @@ popd > /dev/null || exit | |
| if [ $exit_code -eq 0 ]; then | ||
| jq -n '{version: 1, status: "pass"}' > "${results_file}" | ||
| else | ||
| # Sanitize output: remove spago/build preamble and noise, keep compiler | ||
| # errors and test failure output. Strip JS stack traces from test failures. | ||
| sanitized_spago_output=$(echo "${spago_output}" | sed -E \ | ||
| -e '/^Compiling/d' \ | ||
| -e '/at.*:[[:digit:]]+:[[:digit:]]+\)?/d') | ||
| -e '/^Reading Spago workspace/d' \ | ||
| -e '/^✓ Selecting package/d' \ | ||
| -e '/^Checking dependencies/d' \ | ||
| -e '/^Downloading dependencies/d' \ | ||
| -e '/^No lockfile found/d' \ | ||
| -e '/^Lockfile written/d' \ | ||
| -e '/^Building\.\.\./d' \ | ||
| -e '/^\[[[:space:]]*[0-9]+ of [0-9]+\] Compiling /d' \ | ||
| -e '/^✓ Build succeeded/d' \ | ||
| -e '/^Running tests for package/d' \ | ||
| -e '/^✘ Tests failed/d' \ | ||
| -e '/^✘ Failed to build/d' \ | ||
| -e '/^[[:space:]]+Src[[:space:]]+Lib[[:space:]]+All/d' \ | ||
| -e '/^Warnings[[:space:]]+[0-9]/d' \ | ||
| -e '/^Errors[[:space:]]+[0-9]/d' \ | ||
| -e '/^[[:space:]]+at .*(\.js|\.mjs|node:internal).*:[0-9]/d' \ | ||
| -e '/^\[WARNING /,/^$/d') | ||
|
|
||
| # Remove leading/trailing blank lines and collapse runs of 3+ blank lines | ||
| sanitized_spago_output=$(printf '%s\n' "${sanitized_spago_output}" | awk ' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Want to take a go at combining the sed and awk? The
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in c18f902 |
||
| { a[NR] = $0 } | ||
| END { | ||
| # Find first and last non-empty lines | ||
| s = 1; e = NR | ||
| while (s <= NR && a[s] == "") s++ | ||
| while (e >= s && a[e] == "") e-- | ||
| # Print, collapsing runs of 3+ blank lines to 2 | ||
| blank = 0 | ||
| for (i = s; i <= e; i++) { | ||
| if (a[i] == "") { blank++; if (blank <= 2) print a[i] } | ||
| else { blank = 0; print a[i] } | ||
| } | ||
| } | ||
| ') | ||
|
|
||
| jq --null-input --arg output "${sanitized_spago_output}" '{version: 1, status: "fail", message: $output}' > "${results_file}" | ||
| fi | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,20 +1,24 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| # This script will update spago.dhall and package.dhall of all exercises | ||
| # using the master files from the project template (pre-compiled/). | ||
| # This script will update the workspace section of all test examples' | ||
| # spago.yaml to match the pre-compiled project's workspace configuration | ||
| # (package set version, extra packages). | ||
|
|
||
| set -o pipefail | ||
| set -u | ||
|
|
||
| base_dir=$(builtin cd "${BASH_SOURCE%/*}/.." || exit; pwd) | ||
| project_dir="${base_dir}/pre-compiled" | ||
|
|
||
| for config in ./tests/*/spago.dhall; do | ||
| # Extract the workspace section from pre-compiled/spago.yaml | ||
| workspace_section=$(sed -n '/^workspace:/,$p' "${base_dir}/pre-compiled/spago.yaml") | ||
|
|
||
| for config in "${base_dir}"/tests/*/spago.yaml; do | ||
| exercise_dir=$(dirname "${config}") | ||
| # slug=$(basename "${exercise_dir}") | ||
| slug=$(basename "${exercise_dir}") | ||
|
|
||
| echo "Working in ${exercise_dir}..." | ||
|
|
||
| # sed -e "s/pre-compiled/${slug}/" < "${project_dir}/spago.dhall" > "${exercise_dir}/spago.dhall" | ||
| cp "${project_dir}/packages.dhall" "${exercise_dir}/packages.dhall" | ||
| # Replace the workspace section (everything from "workspace:" to EOF) | ||
| package_section=$(sed -n '1,/^workspace:/{ /^workspace:/!p; }' "${config}") | ||
| printf '%s\n%s\n' "${package_section}" "${workspace_section}" > "${config}" | ||
| done |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The image build no longer runs a Spago build step to generate compiled artifacts, but bin/run.sh expects pre-compiled/output to exist and be populated (it copies it to preserve purs cache timestamps). If
npx spago installdoesn’t produce output/, the runner will either fail the copy step or lose the intended precompilation speedup (and may time out). Consider adding an explicitnpx spago build --deps-only(or the Spago 1 equivalent) after install to ensure output/ is created during docker build.