on master help sign in
Merge remote-tracking branch 'origin/master' into head-static-only-render
commitbd3275839376c086bcec52fd6b28692d15598c5a authorLuigy Leon <luichi.19@gmail.com> dateThu 25 Oct 2018, 09:25 (8 years ago) parent84090c5d8aaa2f
19 files changed+122−304
README.md+7−23 default.nix+10−9 dep/reflex-platform/github.json+2−2 lib/backend/obelisk-backend.cabal−1 lib/backend/src/Obelisk/Backend.hs+1 lib/command/src/Obelisk/Command.hs+1−9 lib/command/src/Obelisk/Command/Deploy.hs+1 lib/command/src/Obelisk/Command/Nix.hs+2−37 lib/command/src/Obelisk/Command/Project.hs+1−2 lib/command/src/Obelisk/Command/Thunk.hs+6−8 lib/command/src/Obelisk/Command/Upgrade/Hash.hs+1−2 lib/command/src/Obelisk/Command/VmBuilder.hs+1−2 lib/frontend/src/Obelisk/Frontend.hs+5−4 lib/route/src/Obelisk/Route.hs+8−1 lib/route/src/Obelisk/Route/Frontend.hs+18−3 lib/selftest/src/Obelisk/SelfTest.hs+58−61 lib/snap/obelisk-snap.cabal−40 lib/snap/src/Obelisk/Snap.hs−99 release.nix−1
README.md+7−23
@@ -57,29 +57,13 @@ nix-shell -A obelisk.obelisk-command.env --run "cd lib/command && ghcid -c 'caba
5757```
5858
5959### Accessing private repositories
60−To allow the Nix builder to access private git repositories, follow these steps:
61−
62−1. [Get set up to connect to GitHub with SSH](https://help.github.com/articles/connecting-to-github-with-ssh/)
63−1. [Create a GitHub personal access token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/)
64−1. Set environment variables:
65− * NixOS: Add this to `/etc/nixos/configuration.nix`:
66− ```
67− nix.envVars = {
68− NIX_GITHUB_PRIVATE_USERNAME = "your-github-username";
69− NIX_GITHUB_PRIVATE_PASSWORD = "your-github-personal-access-token";
70− };
71− ```
72− * MacOS:
73− ```
74− sudo launchctl setenv NIX_GITHUB_PRIVATE_USERNAME "your-github-username"
75− sudo launchctl setenv NIX_GITHUB_PRIVATE_PASSWORD "your-github-personal-access"
76− sudo launchctl stop org.nixos.nix-daemon
77− sudo launchctl start org.nixos.nix-daemon
78− ```
79−1. `nix-env -i hub` OR `nix-env -iA nixos.gitAndTools.hub`
80−1. `hub clone yourusername/yourproject`
81− * NOTE: you must authenticate with hub at least once, because the `ob` command uses `hub` for authentication
82− #TODO: Make ob do this itself (either invoke hub automatically or not depend on hub)
60+
61+To allow the Nix builder to access private git repositories, you must be set up
62+to access them via SSH. Follow these steps depending on the platform you need
63+access to:
64+
65+- [GitHub](https://help.github.com/articles/connecting-to-github-with-ssh/)
66+- [GitLab](https://docs.gitlab.com/ee/gitlab-basics/create-your-ssh-keys.html)
8367
8468## Developing an Obelisk project
8569
default.nix+10−9
@@ -61,7 +61,6 @@ let
6161 obelisk-run = self.callCabal2nix "obelisk-run" (cleanSource ./lib/run) {};
6262 obelisk-route = self.callCabal2nix "obelisk-route" (cleanSource ./lib/route) {};
6363 obelisk-selftest = self.callCabal2nix "obelisk-selftest" (cleanSource ./lib/selftest) {};
64− obelisk-snap = self.callCabal2nix "obelisk-snap" (cleanSource ./lib/snap) {};
6564 obelisk-snap-extras = self.callCabal2nix "obelisk-snap-extras" (cleanSource ./lib/snap-extras) {};
6665 })
6766
@@ -135,7 +134,7 @@ in rec {
135134
136135 PATH="${command}/bin:$PATH"
137136 export OBELISK_IMPL="${hackGet ./.}"
138− "${ghcObelisk.obelisk-selftest}/bin/obelisk-selftest" "$@"
137+ "${ghcObelisk.obelisk-selftest}/bin/obelisk-selftest" +RTS -N -RTS "$@"
139138 '';
140139 #TODO: Why can't I build ./skeleton directly as a derivation? `nix-build -E ./.` doesn't work
141140 skeleton = pkgs.runCommand "skeleton" {
@@ -154,14 +153,15 @@ in rec {
154153 } ''
155154 set -euo pipefail
156155 touch "$out"
156+ mkdir -p "$symlinked"
157157 obelisk-asset-manifest-generate "$src" "$haskellManifest" ${packageName} ${moduleName} "$symlinked"
158158 '';
159159
160− compressedJs = frontend: pkgs.runCommand "compressedJs" { buildInputs = [ pkgs.closurecompiler ]; } ''
160+ compressedJs = frontend: optimizationLevel: pkgs.runCommand "compressedJs" { buildInputs = [ pkgs.closurecompiler ]; } ''
161161 mkdir $out
162162 cd $out
163163 ln -s "${haskellLib.justStaticExecutables frontend}/bin/frontend.jsexe/all.js" all.unminified.js
164− closure-compiler --externs "${reflex-platform.ghcjsExternsJs}" -O ADVANCED --jscomp_warning=checkVars --create_source_map="all.js.map" --source_map_format=V3 --js_output_file="all.js" all.unminified.js
164+ closure-compiler --externs "${reflex-platform.ghcjsExternsJs}" -O ${optimizationLevel} --jscomp_warning=checkVars --create_source_map="all.js.map" --source_map_format=V3 --js_output_file="all.js" all.unminified.js
165165 echo "//# sourceMappingURL=all.js.map" >> all.js
166166 '';
167167
@@ -232,14 +232,14 @@ in rec {
232232 };
233233 };
234234
235− serverExe = backend: frontend: assets: config:
235+ serverExe = backend: frontend: assets: config: optimizationLevel:
236236 pkgs.runCommand "serverExe" {} ''
237237 mkdir $out
238238 set -eux
239239 ln -s "${haskellLib.justStaticExecutables backend}"/bin/* $out/
240240 ln -s "${mkAssets assets}" $out/static.assets
241241 cp -r ${config} $out/config
242− ln -s ${mkAssets (compressedJs frontend)} $out/frontend.jsexe.assets
242+ ln -s ${mkAssets (compressedJs frontend optimizationLevel)} $out/frontend.jsexe.assets
243243 '';
244244
245245 server = { exe, hostName, adminEmail, routeHost, enableHttps, config }@args:
@@ -268,6 +268,7 @@ in rec {
268268 , tools ? _: []
269269 , shellToolOverrides ? _: _: {}
270270 , withHoogle ? false # Setting this to `true` makes shell reloading far slower
271+ , __closureCompilerOptimizationLevel ? "ADVANCED"
271272 }:
272273 let frontendName = "frontend";
273274 backendName = "backend";
@@ -296,7 +297,7 @@ in rec {
296297 overrides = totalOverrides;
297298 packages = combinedPackages;
298299 shells = {
299− ghcSavedSplices = (lib.filter (x: lib.hasAttr x combinedPackages) [
300+ ${if android == null && ios == null then null else "ghcSavedSplices"} = (lib.filter (x: lib.hasAttr x combinedPackages) [
300301 commonName
301302 frontendName
302303 ]);
@@ -324,10 +325,10 @@ in rec {
324325 nixpkgs.obeliskExecutableConfig.platforms.ios.inject injectableConfig processedStatic.symlinked;
325326 } // ios;
326327 };
327− passthru = { inherit android ios packages overrides tools shellToolOverrides withHoogle staticFiles; };
328+ passthru = { inherit android ios packages overrides tools shellToolOverrides withHoogle staticFiles __closureCompilerOptimizationLevel; };
328329 };
329330 in mkProject (projectDefinition args));
330− serverOn = sys: config: serverExe (projectOut sys).ghc.backend (projectOut system).ghcjs.frontend (projectOut sys).passthru.staticFiles config;
331+ serverOn = sys: config: serverExe (projectOut sys).ghc.backend (projectOut system).ghcjs.frontend (projectOut sys).passthru.staticFiles config (projectOut sys).passthru.__closureCompilerOptimizationLevel;
331332 linuxExe = serverOn "x86_64-linux";
332333 in projectOut system // {
333334 linuxExeConfigurable = linuxExe;
dep/reflex-platform/github.json+2−2
@@ -2,6 +2,6 @@
22 "owner": "reflex-frp",
33 "repo": "reflex-platform",
44 "branch": "develop",
5− "rev": "0c022883469da05987810557b61d37b7ae3e922e",
6− "sha256": "0aissxk9fpzymvr4qqnwkfvr5jya2h7qrxbx5byg0dv627six8yw"
5+ "rev": "018576456043fe8372a0332c93245a4a620c1fb7",
6+ "sha256": "0qhnyyf96iajz97pbqfsw2l1kkbr1yq3bvw3qhjk031vdrkv7b7y"
77}
lib/backend/obelisk-backend.cabal−1
@@ -17,7 +17,6 @@ library
1717 obelisk-executable-config-inject,
1818 obelisk-frontend,
1919 obelisk-route,
20− obelisk-snap,
2120 reflex-dom,
2221 snap,
2322 snap-server,
lib/backend/src/Obelisk/Backend.hs+1
@@ -29,6 +29,7 @@ import Data.Default (Default (..))
2929import Data.Dependent.Sum
3030import Data.Functor.Sum
3131import Data.Functor.Identity
32+import Data.Monoid ((<>))
3233import Data.Text (Text)
3334import qualified Data.Text as T
3435import Data.Text.Encoding
lib/command/src/Obelisk/Command.hs+1−9
@@ -238,18 +238,10 @@ mkObeliskConfig = do
238238 -- This function should not use argument parser (full argument parsing happens post handoff)
239239 let logLevel = toLogLevel $ "-v" `elem` cliArgs
240240 notInteractive <- not <$> isInteractiveTerm
241−-- instance MonadIO m => MonadError (Either Text ProcessFailed) (DieT m) where
242−-- throwError = \case
243−-- Left s -> do
244−-- putLog Alert s
245−-- liftIO $ exitWith $ ExitFailure 2
246−-- Right (ProcessFailed p code) -> do
247−-- putLog Alert $ "Process exited with code " <> T.pack (show code) <> "; " <> T.pack (show p)
248−-- liftIO $ exitWith $ ExitFailure 2
249241 cliConf <- newCliConfig logLevel notInteractive notInteractive $ \case
250242 ObeliskError_ProcessError (ProcessFailure p code) ann ->
251243 ( "Process exited with code " <> T.pack (show code) <> "; " <> reconstructCommand p
252− <> maybe "" ("\n\n" <>) ann
244+ <> maybe "" ("\n" <>) ann
253245 , 2
254246 )
255247 ObeliskError_Unstructured msg -> (msg, 2)
lib/command/src/Obelisk/Command/Deploy.hs+1
@@ -177,6 +177,7 @@ verifyHostKey knownHostsPath keyPath hostName =
177177 callProcessAndLogOutput (Notice, Warning) $ proc "ssh" $
178178 sshArgs knownHostsPath keyPath True <>
179179 [ "root@" <> hostName
180+ , "-o", "NumberOfPasswordPrompts=0"
180181 , "exit"
181182 ]
182183
lib/command/src/Obelisk/Command/Nix.hs+2−37
@@ -9,7 +9,6 @@
99{-# LANGUAGE TypeApplications #-}
1010module Obelisk.Command.Nix
1111 ( nixBuild
12− , withNixRemoteCheck
1312 , NixBuildConfig (..)
1413 , Target (..)
1514 , OutLink (..)
@@ -19,20 +18,14 @@ module Obelisk.Command.Nix
1918 , strArg
2019 ) where
2120
22−--import Control.Monad.Catch (catch, throwM)
23−import Control.Lens
24−import Control.Monad.Except (ExceptT, runExceptT, throwError)
25−import Control.Monad.IO.Class (liftIO)
2621import Data.Bool (bool)
2722import Data.Default
2823import Data.List (intercalate)
2924import Data.Monoid ((<>))
3025import qualified Data.Text as T
31−import System.Directory
32−import System.Environment
3326import System.Process (proc)
3427
35−import Obelisk.App (MonadInfallibleObelisk, MonadObelisk, ObeliskError (..))
28+import Obelisk.App (MonadObelisk)
3629import Obelisk.CliApp
3730
3831-- | Where to put nix-build output
@@ -87,7 +80,7 @@ instance Default NixBuildConfig where
8780
8881nixBuild :: MonadObelisk m => NixBuildConfig -> m FilePath
8982nixBuild cfg = withSpinner' ("Running nix-build on " <> desc) (Just $ const $ "Built " <> desc) $ do
90− output <- withNixRemoteCheck $ readProcessAndLogStderr Debug $ proc "nix-build" $ mconcat
83+ output <- readProcessAndLogStderr Debug $ proc "nix-build" $ mconcat
9184 [[path], attrArg, args, outLink, buildersArg]
9285 -- Remove final newline that Nix appends
9386 Just (outPath, '\n') <- pure $ T.unsnoc output
@@ -108,31 +101,3 @@ nixBuild cfg = withSpinner' ("Running nix-build on " <> desc) (Just $ const $ "B
108101 [] -> []
109102 builders -> ["--builders", intercalate ";" builders]
110103
111−-- | If a nix command fails, and this may be related to the NIX_REMOTE issue,
112−-- tell the user what to do.
113−--
114−-- Added: June, 2018. Consider removing this eventually.
115−withNixRemoteCheck
116− :: MonadObelisk m
117− => (forall m'. MonadInfallibleObelisk m' => ExceptT ObeliskError m' a)
118− -> m a
119−withNixRemoteCheck f = do
120− status <- runExceptT f
121− case status of
122− Right a -> pure a
123− Left e -> throwError =<< case matching asProcessFailure e of
124− Left _ -> pure e
125− Right pf -> liftIO (lookupEnv "NIX_REMOTE") >>= \case
126− Just _ -> pure e
127− Nothing -> liftIO (writable <$> getPermissions "/nix/var/nix/db") >>= \case
128− True -> pure e
129− False -> pure $ ObeliskError_ProcessError pf $ Just $ T.unlines
130− [ "!!! "
131− , "!!! A nix command failed to run. You might need to set the NIX_REMOTE environment variable"
132− , "!!! to `daemon`. To do this, run the following before running obelisk:"
133− , "!!! "
134− , "!!! export NIX_REMOTE=daemon"
135− , "!!! "
136− , "!!! For details, see https://github.com/NixOS/nixpkgs/issues/5713"
137− , "!!! "
138− ]
lib/command/src/Obelisk/Command/Project.hs+1−2
@@ -34,7 +34,6 @@ import GitHub.Data.Name (Name)
3434import Obelisk.App (MonadObelisk)
3535import Obelisk.CliApp
3636import Obelisk.Command.Thunk
37−import Obelisk.Command.Nix (withNixRemoteCheck)
3837--TODO: Make this module resilient to random exceptions
3938
4039--TODO: Don't hardcode this
@@ -204,7 +203,7 @@ inImpureProjectShell shellName command = withProjectRoot "." $ \root ->
204203 projectShell root False shellName command
205204
206205projectShell :: MonadObelisk m => FilePath -> Bool -> String -> String -> m ()
207−projectShell root isPure shellName command = withNixRemoteCheck $ do
206+projectShell root isPure shellName command = do
208207 (_, _, _, ph) <- createProcess_ "runNixShellAttr" $ setCwd (Just root) $ proc "nix-shell" $
209208 [ "--pure" | isPure ] <>
210209 [ "-A"
lib/command/src/Obelisk/Command/Thunk.hs+6−8
@@ -73,7 +73,6 @@ import Text.URI
7373
7474import Obelisk.App (MonadObelisk)
7575import Obelisk.CliApp
76−import Obelisk.Command.Nix (withNixRemoteCheck)
7776import Obelisk.Command.Utils
7877
7978--TODO: Support symlinked thunk data
@@ -160,14 +159,14 @@ getNixSha256ForUriUnpacked
160159 -> m NixSha256
161160getNixSha256ForUriUnpacked uri =
162161 withExitFailMessage ("nix-prefetch-url: Failed to determine sha256 hash of URL " <> render uri) $ do
163− [hash] <- fmap T.lines $ withNixRemoteCheck $ readProcessAndLogOutput (Debug, Debug) $
162+ [hash] <- fmap T.lines $ readProcessAndLogOutput (Debug, Debug) $
164163 proc "nix-prefetch-url" ["--unpack" , "--type" , "sha256" , T.unpack $ render uri]
165164 pure hash
166165
167166nixPrefetchGit :: MonadObelisk m => URI -> Text -> Bool -> m NixSha256
168167nixPrefetchGit uri rev fetchSubmodules =
169168 withExitFailMessage ("nix-prefetch-git: Failed to determine sha256 hash of Git repo " <> render uri <> " at " <> rev) $ do
170− out <- withNixRemoteCheck $ readProcessAndLogStderr Debug $
169+ out <- readProcessAndLogStderr Debug $
171170 proc "nix-prefetch-git" $ filter (/="")
172171 [ "--url", T.unpack $ render uri
173172 , "--rev", T.unpack rev
@@ -582,12 +581,11 @@ unpackThunk' noTrail thunkDir = readThunk thunkDir >>= \case
582581 git $ [ "clone" ]
583582 ++ ("--recursive" <$ guard (_gitSource_fetchSubmodules s))
584583 ++ [ T.unpack $ render $ _gitSource_url s ]
584+ ++ do branch <- maybeToList $ _gitSource_branch s
585+ [ "--branch", T.unpack $ untagName branch ]
585586 git ["reset", "--hard", Ref.toHexString $ _thunkRev_commit $ _thunkPtr_rev tptr]
586587 when (_gitSource_fetchSubmodules s) $
587588 git ["submodule", "update", "--recursive", "--init"]
588− case _gitSource_branch s of
589− Just b -> git ["branch", "-u", "origin/" <> T.unpack (untagName $ b)]
590− Nothing -> pure ()
591589
592590 liftIO $ createDirectory obGitDir
593591 callProcessAndLogOutput (Notice, Error) $
@@ -663,7 +661,7 @@ getThunkPtr' checkClean thunkDir = do
663661 let untrackedBranches = Map.keys errorMap
664662 when (not $ L.null untrackedBranches) $ failWith $ T.unlines $
665663 [ "thunk pack: Certain branches in the thunk have no upstream branch \
666− \set. This means don't know to check whether all your work is \
664+ \set. This means we don't know to check whether all your work is \
667665 \saved. The offending branches are:"
668666 , ""
669667 , T.unwords untrackedBranches
@@ -752,7 +750,7 @@ uriThunkPtr uri mbranch = do
752750 Left e -> do
753751 putLog Warning $ "\
754752\Failed to fetch archive from GitHub. This is probably a private repo. \
755−\Falling back on normal fetchgit. Original failure:\n\n"
753+\Falling back on normal fetchgit. Original failure:"
756754 errorToWarning e
757755 let s' = forgetGithub True s
758756 (,) (ThunkSource_Git s') <$> gitThunkRev s' commit
lib/command/src/Obelisk/Command/Upgrade/Hash.hs+1−2
@@ -23,7 +23,6 @@ import System.Process (cwd, proc)
2323
2424import Obelisk.App (MonadObelisk)
2525import Obelisk.CliApp
26−import Obelisk.Command.Nix (withNixRemoteCheck)
2726import Obelisk.Command.Utils
2827
2928import Obelisk.Migration
@@ -88,7 +87,7 @@ getHashAtGitRevision revs excludes dir = withSystemTempDirectory "obelisk-hashre
8887 return result
8988
9089nixHash :: MonadObelisk m => FilePath -> m Hash
91−nixHash dir = withSpinnerNoTrail "Running `nix-hash`" $ withNixRemoteCheck $
90+nixHash dir = withSpinnerNoTrail "Running `nix-hash`" $
9291 readProc $ proc "nix-hash" [dir]
9392
9493-- | Clean up the following files in the git working copy
lib/command/src/Obelisk/Command/VmBuilder.hs+1−2
@@ -22,7 +22,6 @@ import System.Process (proc)
2222
2323import Obelisk.App (MonadObelisk, getObeliskUserStateDir)
2424import Obelisk.CliApp
25−import Obelisk.Command.Nix (withNixRemoteCheck)
2625
2726-- | Generate the `--builders` argument string to enable the VM builder after ensuring it is available.
2827getNixBuildersArg :: MonadObelisk m => m String
@@ -155,7 +154,7 @@ getDockerBuilderStateDir = liftA2 (</>) getObeliskUserStateDir (pure "nix-docker
155154testLinuxBuild :: MonadObelisk m => FilePath -> m ()
156155testLinuxBuild stateDir
157156 | System.Info.os == "linux" = failWith "Using the docker builder is not necessary on linux."
158− | otherwise = withNixRemoteCheck $ do
157+ | otherwise = do
159158 (exitCode, _stdout, stderr) <- readCreateProcessWithExitCode $ proc "nix-build"
160159 [ "-E", "(import <nixpkgs> { system = \"x86_64-linux\"; }).writeText \"test\" builtins.currentTime"
161160 , "--builders", nixBuildersArgString stateDir
lib/frontend/src/Obelisk/Frontend.hs+5−4
@@ -32,6 +32,7 @@ import Data.ByteString (ByteString)
3232import Data.Dependent.Sum (DSum (..))
3333import Data.Functor.Sum
3434import Data.IORef
35+import Data.Monoid ((<>))
3536import GHCJS.DOM hiding (bracket, catch)
3637import GHCJS.DOM.Document
3738import GHCJS.DOM.Node
@@ -84,7 +85,7 @@ type DomCoreWidget x = PostBuildT DomTimeline (WithJSContextSingleton x (Perform
8485--TODO: Rename
8586{-# INLINABLE attachWidget''' #-}
8687attachWidget'''
87− :: (EventChannel -> PerformEventT DomTimeline (SpiderHost Global) (IORef (Maybe (EventTrigger DomTimeline ()))))
88+ :: (EventChannel -> PerformEventT DomTimeline DomHost (IORef (Maybe (EventTrigger DomTimeline ()))))
8889 -> IO ()
8990attachWidget''' w = runDomHost $ do
9091 events <- liftIO newChan
@@ -156,10 +157,10 @@ runFrontend validFullEncoder frontend = do
156157 mapRoutedT (mapSetRouteT appendBody) $ _frontend_body frontend
157158
158159renderFrontendHtml
159− :: (t ~ SpiderTimeline Global)
160+ :: (t ~ DomTimeline)
160161 => r
161− -> RoutedT t r (SetRouteT t r' (PostBuildT Spider (StaticDomBuilderT Spider (PerformEventT Spider (SpiderHost Global))))) ()
162− -> RoutedT t r (SetRouteT t r' (PostBuildT Spider (StaticDomBuilderT Spider (PerformEventT Spider (SpiderHost Global))))) ()
162+ -> RoutedT t r (SetRouteT t r' (PostBuildT DomTimeline (StaticDomBuilderT DomTimeline (PerformEventT DomTimeline DomHost)))) ()
163+ -> RoutedT t r (SetRouteT t r' (PostBuildT DomTimeline (StaticDomBuilderT DomTimeline (PerformEventT DomTimeline DomHost)))) ()
163164 -> IO ByteString
164165renderFrontendHtml route headWidget bodyWidget = do
165166 --TODO: We should probably have a "NullEventWriterT" or a frozen reflex timeline
lib/route/src/Obelisk/Route.hs+8−1
@@ -103,6 +103,7 @@ import Data.List.NonEmpty (NonEmpty (..))
103103import Data.Map (Map)
104104import qualified Data.Map as Map
105105import Data.Maybe
106+import Data.Monoid ((<>))
106107import Data.Set (Set)
107108import qualified Data.Set as Set
108109import Data.Some (Some)
@@ -528,6 +529,12 @@ unitEncoder expected = unsafeMkEncoder $ EncoderImpl
528529singlePathSegmentEncoder :: (Applicative check, MonadError Text parse) => Encoder check parse Text PageName
529530singlePathSegmentEncoder = pathOnlyEncoder . singletonListEncoder
530531
532+pathOnlyEncoderIgnoringQuery :: (Applicative check, MonadError Text parse) => Encoder check parse [Text] PageName
533+pathOnlyEncoderIgnoringQuery = unsafeMkEncoder $ EncoderImpl
534+ { _encoderImpl_decode = \(path, _query) -> pure path
535+ , _encoderImpl_encode = \path -> (path, mempty)
536+ }
537+
531538pathOnlyEncoder :: (Applicative check, MonadError Text parse) => Encoder check parse [Text] PageName
532539pathOnlyEncoder = unsafeMkEncoder $ EncoderImpl
533540 { _encoderImpl_decode = \(path, query) ->
@@ -747,7 +754,7 @@ obeliskRouteSegment r appRouteSegment = case r of
747754-- be combined with other such segment encoders before 'pathComponentEncoder' turns it into a proper 'Encoder'.
748755resourceRouteSegment :: (MonadError Text check, MonadError Text parse) => ResourceRoute a -> SegmentResult check parse a
749756resourceRouteSegment = \case
750− ResourceRoute_Static -> PathSegment "static" $ pathOnlyEncoder
757+ ResourceRoute_Static -> PathSegment "static" $ pathOnlyEncoderIgnoringQuery
751758 ResourceRoute_Ghcjs -> PathSegment "ghcjs" $ pathOnlyEncoder
752759 ResourceRoute_JSaddleWarp -> PathSegment "jsaddle" $ jsaddleWarpRouteEncoder
753760
lib/route/src/Obelisk/Route/Frontend.hs+18−3
@@ -49,7 +49,7 @@ import Obelisk.Route
4949
5050import Control.Category (Category (..), (.))
5151import Control.Category.Cartesian
52−import Control.Lens hiding (Bifunctor, bimap, universe)
52+import Control.Lens hiding (Bifunctor, bimap, universe, element)
5353import Control.Monad.Fix
5454import Control.Monad.Primitive
5555import Control.Monad.Ref
@@ -95,7 +95,11 @@ instance Monad m => Routed t r (RoutedT t r m) where
9595 askRoute = RoutedT ask
9696
9797newtype RoutedT t r m a = RoutedT { unRoutedT :: ReaderT (Dynamic t r) m a }
98− deriving (Functor, Applicative, Monad, MonadFix, MonadTrans, NotReady t, MonadHold t, MonadSample t, PostBuild t, TriggerEvent t, HasJSContext, MonadIO, MonadReflexCreateTrigger t, HasDocument)
98+ deriving (Functor, Applicative, Monad, MonadFix, MonadTrans, NotReady t, MonadHold t, MonadSample t, PostBuild t, TriggerEvent t, MonadIO, MonadReflexCreateTrigger t, HasDocument)
99+
100+instance HasJSContext m => HasJSContext (RoutedT t r m) where
101+ type JSContextPhantom (RoutedT t r m) = JSContextPhantom m
102+ askJSContext = lift askJSContext
99103
100104instance Prerender js m => Prerender js (RoutedT t r m) where
101105 prerenderClientDict = fmap (\Dict -> Dict) (prerenderClientDict :: Maybe (Dict (PrerenderClientConstraint js m)))
@@ -213,7 +217,18 @@ strictDynWidget_ f = RoutedT $ ReaderT $ \r -> do
213217 pure ()
214218
215219newtype SetRouteT t r m a = SetRouteT { unSetRouteT :: EventWriterT t (Endo r) m a }
216− deriving (Functor, Applicative, Monad, MonadFix, MonadTrans, NotReady t, MonadHold t, MonadSample t, PostBuild t, TriggerEvent t, HasJSContext, MonadIO, MonadReflexCreateTrigger t, HasDocument, DomBuilder t)
220+ deriving (Functor, Applicative, Monad, MonadFix, MonadTrans, MonadIO, NotReady t, MonadHold t, MonadSample t, PostBuild t, TriggerEvent t, MonadReflexCreateTrigger t, HasDocument)
221+
222+instance (MonadFix m, MonadHold t m, DomBuilder t m) => DomBuilder t (SetRouteT t r m) where
223+ type DomBuilderSpace (SetRouteT t r m) = DomBuilderSpace m
224+ element t cfg child = SetRouteT $ element t cfg $ unSetRouteT child
225+ inputElement = lift . inputElement
226+ textAreaElement = lift . textAreaElement
227+ selectElement cfg child = SetRouteT $ selectElement cfg $ unSetRouteT child
228+
229+instance HasJSContext m => HasJSContext (SetRouteT t r m) where
230+ type JSContextPhantom (SetRouteT t r m) = JSContextPhantom m
231+ askJSContext = lift askJSContext
217232
218233mapSetRouteT :: (forall x. m x -> n x) -> SetRouteT t r m a -> SetRouteT t r n a
219234mapSetRouteT f (SetRouteT x) = SetRouteT (mapEventWriterT f x)
lib/selftest/src/Obelisk/SelfTest.hs+58−61
@@ -64,22 +64,8 @@ tshow :: Show a => a -> Text
6464tshow = T.pack . show
6565
6666-- | Like `shelly` but used when running `ob` commands
67−--
68−-- This tests the NIX_REMOTE workaround, whether that gets printed
69−-- on failure, and whether retrying with the suggested workaround
70−-- leads to the test succeeding.
7167shellyOb :: MonadIO m => (Sh a -> Sh a) -> Sh a -> m a
72−shellyOb f obTest = shelly $ f $ do
73− obTest `catch_sh` \(e :: RunFailed) -> do
74− obStderr <- lastStderr
75− if T.isInfixOf "export NIX_REMOTE" obStderr
76− then do
77− runCli $ do
78− putLog Notice "Detected NIX_REMOTE suggestion in stderr of failed ob"
79− putLog Warning "Retrying this test with NIX_REMOTE=daemon"
80− setenv "NIX_REMOTE" "daemon"
81− f obTest
82− else throw e
68+shellyOb f obTest = shelly $ f obTest
8369
8470main :: IO ()
8571main = do
@@ -90,15 +76,21 @@ main = do
9076 let verbosity = bool silently verbosely isVerbose
9177 obeliskImpl <- fromString <$> getEnv "OBELISK_IMPL"
9278 httpManager <- HTTP.newManager HTTP.defaultManagerSettings
93− withSystemTempDirectory "blank-project" $ \blankProject ->
94− hspec $ do
79+ [p0, p1, p2, p3] <- liftIO $ getFreePorts 4
80+ withSystemTempDirectory "initCache" $ \initCache -> do
81+ -- Setup the ob init cache
82+ void . shellyOb verbosity $ chdir (fromString initCache) $ do
83+ run_ "ob" ["init"]
84+ run_ "git" ["init"]
85+ hspec $ parallel $ do
9586 let shelly_ = void . shellyOb verbosity
9687
97− inProj :: Sh a -> IO ()
98− inProj = shelly_ . chdir (fromString blankProject)
99−
10088 inTmp :: (Shelly.FilePath -> Sh a) -> IO ()
101− inTmp f = shelly_ . withSystemTempDirectory "ob-init" $ (chdir <*> f) . fromString
89+ inTmp f = shelly_ . withSystemTempDirectory "test" $ (chdir <*> f) . fromString
90+
91+ inTmpObInit f = inTmp $ \dir -> do
92+ run_ "cp" ["-a", fromString $ initCache <> "/.", toTextIgnore dir]
93+ f dir
10294
10395 assertRevEQ a b = liftIO . assertEqual "" "" =<< diff a b
10496 assertRevNE a b = liftIO . assertBool "" . (/= "") =<< diff a b
@@ -117,7 +109,7 @@ main = do
117109
118110 diff a b = run "git" ["diff", a, b]
119111
120− describe "ob init" $ do
112+ describe "ob init" $ parallel $ do
121113 it "works with default impl" $ inTmp $ \_ -> run "ob" ["init"]
122114 it "works with master branch impl" $ inTmp $ \_ -> run "ob" ["init", "--branch", "master"]
123115 it "works with symlink" $ inTmp $ \_ -> run "ob" ["init", "--symlink", obeliskImpl]
@@ -132,17 +124,16 @@ main = do
132124 void $ errExit False $ run "ob" ["init", "--symlink", "/dev/null"]
133125 ls tmp >>= liftIO . assertEqual "" []
134126
135− it "produces a valid route config" $ inTmp $ \tmp -> do
136− run_ "ob" ["init"]
127+ it "produces a valid route config" $ inTmpObInit $ \tmp -> do
137128 liftIO $ withCurrentDirectory (T.unpack $ toTextIgnore tmp) $ getConfigRoute `shouldNotReturn` Nothing
138129
139− describe "ob run" $ do
140− it "works in root directory" $ inTmp $ \_ -> do
141− run_ "ob" ["init"]
142− testObRunInDir Nothing httpManager
143− it "works in sub directory" $ inTmp $ \_ -> do
144− run_ "ob" ["init"]
145− testObRunInDir (Just "frontend") httpManager
130+ -- These tests fail with "Could not find module 'Obelisk.Generated.Static'"
131+ -- when not run by 'nix-build --attr selftest'
132+ describe "ob run" $ parallel $ do
133+ it "works in root directory" $ inTmpObInit $ \_ -> do
134+ testObRunInDir p0 p1 Nothing httpManager
135+ it "works in sub directory" $ inTmpObInit $ \_ -> do
136+ testObRunInDir p2 p3 (Just "frontend") httpManager
146137
147138 describe "obelisk project" $ parallel $ do
148139 it "can build obelisk command" $ shelly_ $ run "nix-build" ["-A", "command" , obeliskImpl]
@@ -151,38 +142,35 @@ main = do
151142 -- See https://github.com/obsidiansystems/obelisk/issues/101
152143 -- it "can build everything" $ shelly_ $ run "nix-build" [obeliskImpl]
153144
154− describe "blank initialized project" $ do
155− it "can be created" $ inProj $ do
156− run_ "ob" ["init"]
157− run_ "git" ["init"]
158− commitAll
145+ describe "blank initialized project" $ parallel $ do
159146
160− it "can build ghc.backend" $ inProj $ do
147+ it "can build ghc.backend" $ inTmpObInit $ \_ -> do
161148 run "nix-build" ["--no-out-link", "-A", "ghc.backend"]
162− it "can build ghcjs.frontend" $ inProj $ do
149+ it "can build ghcjs.frontend" $ inTmpObInit $ \_ -> do
163150 run "nix-build" ["--no-out-link", "-A", "ghcjs.frontend"]
164151
165152 if os == "darwin"
166− then it "can build ios" $ inProj $ run "nix-build" ["--no-out-link", "-A", "ios.frontend" ]
167− else it "can build android" $ inProj $ run "nix-build" ["--no-out-link", "-A", "android.frontend"]
153+ then it "can build ios" $ inTmpObInit $ \_ -> run "nix-build" ["--no-out-link", "-A", "ios.frontend" ]
154+ else it "can build android" $ inTmpObInit $ \_ -> run "nix-build" ["--no-out-link", "-A", "android.frontend"]
168155
169156 forM_ ["ghc", "ghcjs"] $ \compiler -> do
170157 let
171158 shell = "shells." <> compiler
172159 inShell cmd' = run "nix-shell" ["-A", fromString shell, "--run", cmd']
173− it ("can enter " <> shell) $ inProj $ inShell "exit"
174− it ("can build in " <> shell) $ inProj $ inShell $ "cabal new-build --" <> fromString compiler <> " all"
160+ it ("can enter " <> shell) $ inTmpObInit $ \_ -> inShell "exit"
161+ it ("can build in " <> shell) $ inTmpObInit $ \_ -> inShell $ "cabal new-build --" <> fromString compiler <> " all"
175162
176− it "can build reflex project" $ inProj $ do
163+ it "can build reflex project" $ inTmpObInit $ \_ -> do
177164 run "nix-build" []
178165
179− it "has idempotent thunk update" $ inProj $ do
166+ it "has idempotent thunk update" $ inTmpObInit $ \_ -> do
180167 u <- update
181168 uu <- update
182169 assertRevEQ u uu
183170
184− describe "ob thunk pack/unpack" $ do
185− it "has thunk pack and unpack inverses" $ inProj $ do
171+ describe "ob thunk pack/unpack" $ parallel $ do
172+ it "has thunk pack and unpack inverses" $ inTmpObInit $ \_ -> do
173+
186174 e <- commitAll
187175 eu <- unpack
188176 eup <- pack
@@ -193,6 +181,14 @@ main = do
193181 assertRevEQ eu eupu
194182 assertRevNE e eu
195183
184+ it "unpacks the correct branch" $ inTmp $ \dir -> do
185+ let branch = "master"
186+ run_ "git" ["clone", "https://github.com/reflex-frp/reflex.git", toTextIgnore dir, "--branch", branch]
187+ run_ "ob" ["thunk", "pack", toTextIgnore dir]
188+ run_ "ob" ["thunk", "unpack", toTextIgnore dir]
189+ branch' <- run "git" ["rev-parse", "--abbrev-ref", "HEAD"]
190+ liftIO $ assertEqual "" branch (T.strip branch')
191+
196192 it "can pack and unpack plain git repos" $ do
197193 shelly_ $ withSystemTempDirectory "git-repo" $ \dir -> do
198194 let repo = toTextIgnore $ dir </> ("repo" :: String)
@@ -211,19 +207,18 @@ main = do
211207
212208 testThunkPack $ fromText repo
213209
214− it "aborts thunk pack when there are uncommitted files" $ inProj $ do
210+ it "aborts thunk pack when there are uncommitted files" $ inTmpObInit $ \dir -> do
215211 void $ unpack
216− testThunkPack (blankProject </> thunk)
212+ testThunkPack (dir </> thunk)
217213
218214-- | Run `ob run` in the given directory (maximum of one level deep)
219−testObRunInDir :: Maybe Shelly.FilePath -> HTTP.Manager -> Sh ()
220−testObRunInDir mdir httpManager = handle_sh (\case ExitSuccess -> pure (); e -> throw e) $ do
215+testObRunInDir :: Socket.PortNumber -> Socket.PortNumber -> Maybe Shelly.FilePath -> HTTP.Manager -> Sh ()
216+testObRunInDir p0 p1 mdir httpManager = handle_sh (\case ExitSuccess -> pure (); e -> throw e) $ do
221217 let uri p = "http://localhost:" <> T.pack (show p) <> "/" -- trailing slash required for comparison
222− writefile "config/common/route" . uri =<< liftIO getFreePort -- Use a free port initially
218+ writefile "config/common/route" $ uri p0
223219 maybe id chdir mdir $ runHandle "ob" ["run"] $ \stdout -> do
224− freePort <- liftIO getFreePort
225220 firstUri <- handleObRunStdout httpManager stdout
226− let newUri = uri freePort
221+ let newUri = uri p1
227222 when (firstUri == newUri) $ errorExit $
228223 "Startup URI (" <> firstUri <> ") is the same as test URI (" <> newUri <> ")"
229224 maybe id (\_ -> chdir "..") mdir $ alterRouteTo newUri stdout
@@ -248,7 +243,7 @@ testThunkPack path' = withTempFile (T.unpack $ toTextIgnore path') "test-file" $
248243 ensureThunkPackFails "unsaved"
249244 chdir path' $ commit "test commit"
250245 -- Non-pushed commits in any branch
251− ensureThunkPackFails "not been pushed"
246+ ensureThunkPackFails "not yet pushed"
252247 -- Uncommitted files (unstaged)
253248 liftIO $ T.hPutStrLn handle "test file" >> hClose handle
254249 ensureThunkPackFails "modified"
@@ -275,12 +270,12 @@ alterRouteTo uri stdout = do
275270
276271-- | Handle stdout of `ob run`: check that the frontend and backend servers are started correctly
277272handleObRunStdout :: HTTP.Manager -> Handle -> Sh Text
278−handleObRunStdout httpManager stdout = flip fix ObRunState_Init $ \loop state -> do
273+handleObRunStdout httpManager stdout = flip fix (ObRunState_Init, []) $ \loop (state, msgs) -> do
279274 liftIO (T.hGetLine stdout) >>= \t -> case state of
280275 ObRunState_Init
281− | "Running test..." `T.isPrefixOf` t -> loop ObRunState_BackendStarted
276+ | "Running test..." `T.isPrefixOf` t -> loop (ObRunState_BackendStarted, msgs)
282277 ObRunState_Startup
283− | t == "backend stopped; make a change to your code to reload" -> loop ObRunState_Startup
278+ | t == "backend stopped; make a change to your code to reload" -> loop (ObRunState_Startup, msgs)
284279 -- | Just port <- "Backend running on port " `T.stripPrefix` t -> loop $ ObRunState_BackendStarted port
285280 | not (T.null t) -> errorExit $ "Startup: " <> t -- If theres any other output here, startup failed
286281 ObRunState_BackendStarted
@@ -288,7 +283,8 @@ handleObRunStdout httpManager stdout = flip fix ObRunState_Init $ \loop state ->
288283 obRunCheck httpManager stdout uri
289284 pure uri
290285 | not (T.null t) -> errorExit $ "Started: " <> t -- If theres any other output here, startup failed
291− _ -> loop state
286+ _ | "Failed" `T.isPrefixOf` t -> errorExit $ "ob run failed: " <> T.unlines (reverse $ t : msgs)
287+ | otherwise -> loop (state, t : msgs)
292288
293289-- | Make requests to frontend/backend servers to check they are working properly
294290obRunCheck :: HTTP.Manager -> Handle -> Text -> Sh ()
@@ -297,10 +293,11 @@ obRunCheck httpManager _stdout frontendUri = do
297293 req frontendUri >>= \r -> when (HTTP.responseStatus r /= HTTP.ok200) $ errorExit $
298294 "Request to frontend server failed: " <> T.pack (show r)
299295
300−getFreePort :: IO Socket.PortNumber
301−getFreePort = Socket.withSocketsDo $ do
296+getFreePorts :: Int -> IO [Socket.PortNumber]
297+getFreePorts 0 = pure []
298+getFreePorts n = Socket.withSocketsDo $ do
302299 addr:_ <- Socket.getAddrInfo (Just Socket.defaultHints) (Just "127.0.0.1") (Just "0")
303− bracket (open addr) Socket.close Socket.socketPort
300+ bracket (open addr) Socket.close $ \s -> (:) <$> Socket.socketPort s <*> getFreePorts (n - 1)
304301 where
305302 open addr = do
306303 sock <- Socket.socket (Socket.addrFamily addr) (Socket.addrSocketType addr) (Socket.addrProtocol addr)
lib/snap/obelisk-snap.cabaldeleted−40
@@ -1,40 +0,0 @@
1−Name: obelisk-snap
2−Version: 0.1
3−Synopsis: Project Synopsis Here
4−Description: Project Description Here
5−License: BSD3
6−Author: Obsidian Systems
7−Maintainer: maintainer@obsidian.systems
8−Stability: Experimental
9−Category: Web
10−Build-type: Simple
11−Cabal-version: >=1.2
12−
13−library
14− hs-source-dirs: src
15−
16− build-depends:
17− base,
18− bytestring,
19− data-default,
20− diagrams-lib,
21− diagrams-svg,
22− filepath,
23− lens,
24− lucid,
25− monad-control,
26− obelisk-asset-serve-snap,
27− obelisk-snap-extras,
28− semigroups,
29− raw-strings-qq,
30− snap,
31− snap-core,
32− svg-builder,
33− text,
34− transformers-base
35−
36− exposed-modules:
37− Obelisk.Snap
38−
39− other-extensions: TemplateHaskell
40− ghc-options: -threaded -Wall -fno-warn-unused-do-bind -fwarn-tabs -funbox-strict-fields -O2 -fprof-auto-calls -rtsopts
lib/snap/src/Obelisk/Snap.hsdeleted−99
@@ -1,99 +0,0 @@
1−{-# LANGUAGE TemplateHaskell #-}
2−{-# LANGUAGE QuasiQuotes #-}
3−{-# LANGUAGE OverloadedStrings #-}
4−
5−module Obelisk.Snap where
6−
7−import Obelisk.Asset.Serve.Snap
8−import Obelisk.Snap.Extras
9−
10−import Snap
11−
12−import Control.Lens
13−import Data.ByteString (ByteString)
14−import Data.Default
15−import Data.Monoid
16−import qualified Data.Text as T
17−import Lucid
18−import System.FilePath
19−import Control.Monad.IO.Class
20−
21−-- Data type for web app configuration
22−data AppConfig m
23− = AppConfig { _appConfig_initialBody :: m ByteString
24− , _appConfig_initialHead :: ByteString
25− , _appConfig_serveJsexe :: Bool
26− , _appConfig_jsexe :: FilePath
27− }
28−
29−-- Default instance for app configuration
30−instance Monad m => Default (AppConfig m) where
31− def = AppConfig { _appConfig_initialBody = return mempty
32− , _appConfig_initialHead = mempty
33− , _appConfig_serveJsexe = True
34− , _appConfig_jsexe = "frontend.jsexe"
35− }
36−
37−{- | Takes a location, app, and an app configuration as arguements and
38− routes the request to the correct handler. If "appConfig_serveJsexe"
39− returns "True", additional js paths and asset paths will be appended to
40− handler. Otherwise, it will return "404 Not Found" -}
41−serveAppAt :: MonadSnap m => ByteString -> FilePath -> AppConfig m -> m ()
42−serveAppAt loc app cfg = do
43− route $ [ (loc, ifTop $ serveStaticIndex cfg)
44− , (loc, serveAssets (app </> "static.assets") (app </> "static"))
45− , (loc <> "/version", doNotCache >> serveFileIfExistsAs "text/plain" (app </> "version"))
46− ]
47− ++ if _appConfig_serveJsexe cfg
48− then [(loc, serveAssets (app </> frontendJsAssetsPath cfg) (app </> frontendJsPath cfg))]
49− else []
50− ++ [ (loc, doNotCache >> error404) ]
51−
52−-- Helper function used in 'serveAppAt', writes a 404 error message in ByteString
53−error404 :: MonadSnap m => m ()
54−error404 = do
55− modifyResponse $ setResponseCode 404
56− writeBS "404 Not Found"
57−
58−-- Partially applied 'serveAppAt', specialized to the root directory
59−serveApp :: MonadSnap m => FilePath -> AppConfig m -> m ()
60−serveApp = serveAppAt ""
61−
62−-- Helper function used in 'serveAppAt', returns a frontendJs/... filepath
63−frontendJsPath :: AppConfig m -> FilePath
64−frontendJsPath (AppConfig { _appConfig_jsexe = jsexe }) = "frontendJs" </> jsexe
65−
66−-- Helper function used in 'serveAppAt', returns a frontendJs.assets/... filepath
67−frontendJsAssetsPath :: AppConfig m -> FilePath
68−frontendJsAssetsPath (AppConfig { _appConfig_jsexe = jsexe }) = "frontendJs.assets" </> jsexe
69−
70−-- Helper funtion used in 'serveAppAt', writes a lazy bytestring to the body of Http response
71−serveStaticIndex :: MonadSnap m => AppConfig m -> m ()
72−serveStaticIndex cfg = do
73− -- Decode, pack, and append asset target to file path
74− appJsPath <- liftIO $ getAssetPath (frontendJsAssetsPath cfg) "/all.js"
75− initialBody <- _appConfig_initialBody cfg
76− -- Render and write html head and body to response
77− writeLBS $ renderBS $ doctypehtml_ $ do
78− head_ $ do
79− meta_ [charset_ "utf-8"] -- meta-data charset description
80− meta_ [name_ "viewport", content_ "width=device-width, initial-scale=1.0"] -- meta-data viewport description
81− toHtmlRaw $ _appConfig_initialHead cfg
82− body_ $ do -- <body>
83− toHtmlRaw initialBody -- HtmlT monad wrapper
84− script_ [type_ "application/javascript", src_ (maybe "/all.js" T.pack appJsPath), defer_ "defer"] ("" :: String)
85−
86−{- | Takes an AppConfig monad and uses it to generate & write Lazy ByteString
87− to the body of the https response. This one comes with hard coded values
88− for the logo -}
89−serveIndex :: MonadSnap m => AppConfig m -> m ()
90−serveIndex cfg = do
91− appJsPath <- liftIO $ getAssetPath (frontendJsAssetsPath cfg) "all.js"
92− writeLBS $ renderBS $ doctypehtml_ $ do
93− head_ $ do
94− meta_ [charset_ "utf-8"]
95− meta_ [name_ "viewport", content_ "width=device-width, initial-scale=1.0"]
96− body_ $ do
97− script_ [type_ "application/javascript", src_ (maybe "/all.js" T.pack appJsPath), defer_ "defer"] ("" :: String)
98−
99−makeLenses ''AppConfig
release.nix−1
@@ -23,7 +23,6 @@ let
2323 "obelisk-run"
2424 "obelisk-route"
2525 "obelisk-selftest"
26− "obelisk-snap"
2726 "obelisk-snap-extras"
2827 ];
2928