Skip to content

Commit ad10749

Browse files
Modernize server build and CI (#322)
* Modernize server build and CI - Bump the Stackage resolver to lts-20.26 (GHC 9.2.8), the newest snapshot compatible with purescript-0.15.15. This requires protolude 0.3.3 (for lts-20.26's bytestring) and a happy-1.20.0 pin (purescript requires happy ==1.20.0 exactly). - Update the cabal file to cabal-version 2.4, build with -Wall -Werror, and remove unused dependencies; delete the redundant Setup.hs (build-type: Simple ignores it). - Clean up server/Main.hs for -Wall: remove dead imports, fix name shadowing, and exit with a usage message on bad arguments instead of a partial pattern match. - Remove the -j1 limit from CI server builds; runners have 4 vCPUs and 16GB of RAM. * Add cache headers to nginx config Browsers were heuristically caching the site and the compiled /output modules because no Cache-Control header was sent; with files dating from a 2023 deploy, cached clients could be considered fresh for months. no-cache makes browsers revalidate via ETag: unchanged files cost a 304, changed files are re-downloaded immediately. Matches the configuration applied to the live server on 2026-07-05. Also updates the changelog entries to reference this PR.
1 parent 41c6602 commit ad10749

8 files changed

Lines changed: 86 additions & 66 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
key: ${{ runner.os }}-stack-${{ hashFiles('stack.yaml.lock') }}-${{ hashFiles('trypurescript.cabal') }}
3636

3737
- name: Build server code
38-
run: stack --no-terminal -j1 build
38+
run: stack --no-terminal build
3939

4040
- name: Build server assets
4141
if: github.event_name == 'release'

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ New features:
1111
Bugfixes:
1212

1313
Other improvements:
14+
- Bump the Stackage resolver from `lts-20.9` to `lts-20.26` (GHC 9.2.5 → 9.2.8), the newest snapshot compatible with `purescript-0.15.15` (#322 by @thomashoneyman)
15+
- Modernize the server build: `cabal-version: 2.4`, build with `-Wall -Werror` (with attendant cleanup in `server/Main.hs`), remove unused dependencies and the redundant `Setup.hs` (#322 by @thomashoneyman)
16+
- Speed up CI server builds by removing the `-j1` limit; runners have had 4 vCPUs and 16GB of RAM for years (#322 by @thomashoneyman)
17+
- Serve the site and the compiled `/output` modules with `Cache-Control: no-cache` so browsers revalidate via ETag instead of heuristically caching a stale client for months after a deploy (#322 by @thomashoneyman)
1418

1519
## [v2026-07-05.1](https://github.com/purescript/trypurescript/releases/tag/v2026-07-05.1)
1620

Setup.hs

Lines changed: 0 additions & 2 deletions
This file was deleted.

deploy/nginx.conf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ server {
4444

4545
location / {
4646
root /var/www/trypurescript/public;
47+
# Always revalidate (cheap ETag 304s): the HTML references the bundle by
48+
# a fixed name, so without this browsers heuristically cache the site
49+
# and can serve a stale client for months after a deploy.
50+
add_header Cache-Control "no-cache";
4751
}
4852

4953
}
@@ -86,6 +90,10 @@ server {
8690
# match to ensure that we only serve JS files.
8791
location ~ ^/output/(.+\.js)$ {
8892
add_header Access-Control-Allow-Origin *;
93+
# Module URLs are generated by the compiler and change content when the
94+
# package set or compiler changes; revalidate so browsers never mix
95+
# modules from different deploys.
96+
add_header Cache-Control "no-cache";
8997
alias /var/www/trypurescript/staging/.psci_modules/$1;
9098
}
9199
}

server/Main.hs

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,22 @@
66

77
module Main (main) where
88

9-
import Control.Monad (unless, foldM)
10-
import Control.Monad.Error.Class (throwError)
9+
import Control.Monad (foldM)
1110
import Control.Monad.IO.Class (liftIO)
12-
import Control.Monad.Logger (runLogger')
1311
import qualified Control.Monad.State as State
1412
import Control.Monad.Trans (lift)
1513
import Control.Monad.Trans.Except (ExceptT(..), runExceptT)
16-
import Control.Monad.Trans.Reader (runReaderT)
1714
import Control.Monad.Writer.Strict (runWriterT)
1815
import qualified Data.Aeson as A
1916
import Data.Aeson ((.=))
20-
import Data.Bifunctor (first, second, bimap)
17+
import Data.Bifunctor (second, bimap)
2118
import qualified Data.ByteString.Lazy as BL
2219
import Data.Default (def)
2320
import Data.Function (on)
2421
import qualified Data.IORef as IORef
2522
import Data.List (nubBy)
2623
import qualified Data.List.NonEmpty as NE
2724
import qualified Data.Map as M
28-
import Data.Set (Set)
2925
import qualified Data.Set as Set
3026
import Data.Text (Text)
3127
import qualified Data.Text as T
@@ -50,6 +46,7 @@ import System.Environment (getArgs)
5046
import System.Exit (exitFailure)
5147
import System.FilePath.Glob (glob)
5248
import qualified System.IO as IO
49+
import Text.Read (readMaybe)
5350
import Web.Scotty
5451
import qualified Web.Scotty as Scotty
5552

@@ -204,9 +201,9 @@ lookupAllConstructors env = P.everywhereOnTypesM $ \case
204201
other -> pure other
205202
where
206203
lookupConstructor :: P.Environment -> P.ProperName 'P.TypeName -> [P.Qualified (P.ProperName 'P.TypeName)]
207-
lookupConstructor env nm =
204+
lookupConstructor env' nm =
208205
[ q
209-
| (q@(P.Qualified (N.ByModuleName _) thisNm), _) <- M.toList (P.types env)
206+
| (q@(P.Qualified (N.ByModuleName _) thisNm), _) <- M.toList (P.types env')
210207
, thisNm == nm
211208
]
212209

@@ -216,15 +213,15 @@ lookupAllConstructors env = P.everywhereOnTypesM $ \case
216213
replaceTypeVariablesAndDesugar :: (Text -> Int -> P.SourceType) -> P.SourceType -> P.SourceType
217214
replaceTypeVariablesAndDesugar f ty = State.evalState (P.everywhereOnTypesM go ty) (0, M.empty) where
218215
go = \case
219-
P.ParensInType _ ty -> pure ty
216+
P.ParensInType _ inner -> pure inner
220217
P.TypeVar _ s -> do
221-
(next, m) <- State.get
218+
(n, m) <- State.get
222219
case M.lookup s m of
223220
Nothing -> do
224-
let ty = f s next
225-
State.put (next + 1, M.insert s ty m)
226-
pure ty
227-
Just ty -> pure ty
221+
let unknown = f s n
222+
State.put (n + 1, M.insert s unknown m)
223+
pure unknown
224+
Just unknown -> pure unknown
228225
other -> pure other
229226

230227
tryParseType :: Text -> Maybe P.SourceType
@@ -242,16 +239,24 @@ main :: IO ()
242239
main = do
243240
-- Stop mangled "Compiling ModuleName" text
244241
IO.hSetBuffering IO.stderr IO.LineBuffering
245-
(portString : inputGlobs) <- getArgs
246-
let port = read portString
247-
inputFiles <- concat <$> traverse glob inputGlobs
248-
e <- runExceptT $ do
249-
modules <- ExceptT $ I.loadAllModules inputFiles
250-
(exts, env) <- ExceptT . I.runMake . I.make $ map (second CST.pureResult) modules
251-
namesEnv <- fmap fst . runWriterT $ foldM P.externsEnv P.primEnv exts
252-
let
253-
allModuleNames = fmap (P.getModuleName . snd) modules
254-
pure (allModuleNames, exts, namesEnv, env)
255-
case e of
256-
Left err -> print err >> exitFailure
257-
Right (allModuleNames, exts, namesEnv, env) -> server allModuleNames exts namesEnv env port
242+
args <- getArgs
243+
case args of
244+
portString : inputGlobs
245+
| Just port <- readMaybe portString -> run port inputGlobs
246+
_ -> do
247+
IO.hPutStrLn IO.stderr "Usage: trypurescript PORT INPUT_GLOB..."
248+
exitFailure
249+
where
250+
run :: Int -> [String] -> IO ()
251+
run port inputGlobs = do
252+
inputFiles <- concat <$> traverse glob inputGlobs
253+
e <- runExceptT $ do
254+
modules <- ExceptT $ I.loadAllModules inputFiles
255+
(exts, env) <- ExceptT . I.runMake . I.make $ map (second CST.pureResult) modules
256+
namesEnv <- fmap fst . runWriterT $ foldM P.externsEnv P.primEnv exts
257+
let
258+
allModuleNames = fmap (P.getModuleName . snd) modules
259+
pure (allModuleNames, exts, namesEnv, env)
260+
case e of
261+
Left err -> print err >> exitFailure
262+
Right (allModuleNames, exts, namesEnv, env) -> server allModuleNames exts namesEnv env port

stack.yaml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
resolver: lts-20.9
1+
resolver: lts-20.26
22
packages:
33
- "."
44

@@ -8,8 +8,11 @@ extra-deps:
88
- process-1.6.13.1
99
# The Cabal library is not in Stackage
1010
- Cabal-3.6.3.0
11-
# Protolude is not yet in resolver snapshot
12-
- protolude-0.3.1
11+
# Protolude is not in the resolver snapshot; 0.3.3 (not 0.3.1) is needed
12+
# for compatibility with the bytestring version in lts-20.26
13+
- protolude-0.3.3
14+
# purescript pins happy ==1.20.0, but lts-20.26 ships happy-1.20.1.1
15+
- happy-1.20.0
1316

1417
flags:
1518
these:

stack.yaml.lock

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# This file was autogenerated by Stack.
22
# You should not edit this file by hand.
33
# For more information, please see the documentation at:
4-
# https://docs.haskellstack.org/en/stable/lock_files
4+
# https://docs.haskellstack.org/en/stable/topics/lock_files
55

66
packages:
77
- completed:
@@ -33,15 +33,22 @@ packages:
3333
original:
3434
hackage: Cabal-3.6.3.0
3535
- completed:
36-
hackage: protolude-0.3.1@sha256:1cc9e5a5c26c33a43c52b554443dd9779fef13974eaa0beec7ca6d2551b400da,2647
36+
hackage: protolude-0.3.3@sha256:0106615f6fe08de16fe512852c728797db17374d36b4a4ec8187563e924b2439,2261
3737
pantry-tree:
38-
sha256: 6452a6ca8d395f7d810139779bb0fd16fc1dbb00f1862630bc08ef5a100430f9
39-
size: 1645
38+
sha256: 174c107f3c3af1dc23330ff4431445830aac4b4fdba326e3e654eb491d6e9ca6
39+
size: 1594
4040
original:
41-
hackage: protolude-0.3.1
41+
hackage: protolude-0.3.3
42+
- completed:
43+
hackage: happy-1.20.0@sha256:5d47dc221a9fe964e36aaaa2e1ab7e8f085a225fd6528d6eff310b92360bbe99,5732
44+
pantry-tree:
45+
sha256: ff2e7df7cfe8746eedc6f69c225fa8c899c26d28f1c4964eeb00a5d757d30351
46+
size: 8749
47+
original:
48+
hackage: happy-1.20.0
4249
snapshots:
4350
- completed:
44-
sha256: c11fcbeb1aa12761044755b1109d16952ede2cb6147ebde777dd5cb38f784501
45-
size: 649333
46-
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/9.yaml
47-
original: lts-20.9
51+
sha256: 5a59b2a405b3aba3c00188453be172b85893cab8ebc352b1ef58b0eae5d248a2
52+
size: 650475
53+
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/26.yaml
54+
original: lts-20.26

trypurescript.cabal

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,36 @@
1+
cabal-version: 2.4
12
name: trypurescript
23
version: 1.0.0
3-
cabal-version: >=1.8
44
build-type: Simple
5-
license: BSD3
5+
license: BSD-3-Clause
66
license-file: LICENSE
77
copyright: (c) Phil Freeman 2013
88
maintainer: paf31@cantab.net
99
synopsis: Interactive PureScript in the Browser
1010
description: A simple web app for trying out the PureScript compiler
1111
category: Web
1212
author: Phil Freeman
13-
data-dir: ""
1413

1514
executable trypurescript
1615
-- Since no one depends on this project as a library,
17-
-- packages below are `-any` because their versions are determined
18-
-- by the `stack.yml` file. Versions correspond to the ones
19-
-- specified in the resolver (i.e. the package set)
16+
-- packages below are unbounded because their versions are
17+
-- determined by the `stack.yaml` file. Versions correspond to the
18+
-- ones specified in the resolver (i.e. the package set)
2019
-- unless it is a versioned library added via `extra-deps` field.
21-
build-depends: base -any,
22-
aeson -any,
23-
bytestring -any,
24-
data-default -any,
25-
directory -any,
26-
filepath -any,
27-
Glob -any,
28-
scotty -any,
20+
build-depends: base,
21+
aeson,
22+
bytestring,
23+
containers,
24+
data-default,
25+
Glob,
26+
mtl,
2927
purescript,
30-
containers -any,
31-
http-types -any,
32-
transformers -any,
33-
mtl -any,
34-
text -any,
35-
time -any,
36-
warp -any
28+
scotty,
29+
text,
30+
time,
31+
transformers,
32+
warp
3733
hs-source-dirs: server
3834
main-is: Main.hs
39-
buildable: True
40-
other-modules: Main
41-
ghc-options: -Werror -O2 -threaded -rtsopts
35+
default-language: Haskell2010
36+
ghc-options: -Wall -Werror -O2 -threaded -rtsopts

0 commit comments

Comments
 (0)