on c8ebdad help sign in
.github/pull_request_template.md−2
since splitsnapshots
unifiedsplit
@@ -3,8 +3,6 @@
3I have:
3I have:
4
4
5 - [ ] Based work on latest `develop` branch
5 - [ ] Based work on latest `develop` branch
6− - [ ] Followed the [contribution guide](https://github.com/obsidiansystems/obelisk/blob/develop/CONTRIBUTING.md#submitting-changes)
7 - [ ] Looked for lint in my changes with `hlint .` (lint found code you did not write can be left alone)
6 - [ ] Looked for lint in my changes with `hlint .` (lint found code you did not write can be left alone)
8 - [ ] Run the test suite: `$(nix-build -A selftest --no-out-link)`
7 - [ ] Run the test suite: `$(nix-build -A selftest --no-out-link)`
9− - [ ] [Updated the changelog](https://github.com/obsidiansystems/obelisk/blob/develop/CONTRIBUTING.md#in-the-changelog)
10 - [ ] (Optional) Run CI tests locally: `nix-build release.nix -A build.x86_64-linux --no-out-link` (or `x86_64-darwin` on macOS)
8 - [ ] (Optional) Run CI tests locally: `nix-build release.nix -A build.x86_64-linux --no-out-link` (or `x86_64-darwin` on macOS)
ChangeLog.md+1−19
@@ -2,26 +2,8 @@
2
2
3This project's release branch is `master`. This log is written from the perspective of the release branch: when changes hit `master`, they are considered released.
3This project's release branch is `master`. This log is written from the perspective of the release branch: when changes hit `master`, they are considered released.
4
4
5−## v0.9.1.0
5+## Unreleased
6
6
7−* [#801](https://github.com/obsidiansystems/obelisk/pull/801): Remove errors and warning for local packages without a library component
8−* [#812](https://github.com/obsidiansystems/obelisk/pull/812): Add support for `NoImplicitPrelude` and other extensions disabled via `No`
9−* Pinned version bumps:
10− * reflex-platform [0.7.0.0](https://github.com/reflex-frp/reflex-platform/releases/tag/v0.7.0.0)
11− * hnix 0.8.0
12−* [#787](https://github.com/obsidiansystems/obelisk/pull/787): Set `immutable` cache control directive when serving content-addressed static assets
13−* Use iOS SDK 13.2
14−
15−
16−## v0.9.0.1
17−
18−* ([#810](https://github.com/obsidiansystems/obelisk/pull/810)) Fix loading of `all.js` in fully compiled web apps.
19−
20−## v0.9.0.0
21−
22−* **(Breaking change)** Backport nixpkgs upgrades to ACME/Let's Encrypt handling so that HTTPS deployments continue to work flawlessly. If your deployment is having trouble renewing [Let's Encrypt](https://letsencrypt.org/) certificates, upgrade to this version.
23− * **IMPORTANT:** In order to use [Let's Encrypt](https://letsencrypt.org/) you must now accept their [terms of service](https://letsencrypt.org/repository/). To do that, add `terms.security.acme.acceptTerms = true;` to the `import ./.obelisk/impl {` section in your `default.nix`. The new skeleton application may serve as an [example](https://github.com/obsidiansystems/obelisk/blob/4759342ab3570888c027d4c58cb5694cb832d624/skeleton/default.nix#L13).
24−* Update reflex-platform dependency to v0.6.0.0
25* ([#715](https://github.com/obsidiansystems/obelisk/pull/715)) In `Obelisk.Route` deprecate `isoEncoder` and `prismEncoder` in favor of more precisely named `viewEncoder` and `reviewEncoder` (respectively) and improve documentation regarding contravariance of `reviewEncoder`.
7* ([#715](https://github.com/obsidiansystems/obelisk/pull/715)) In `Obelisk.Route` deprecate `isoEncoder` and `prismEncoder` in favor of more precisely named `viewEncoder` and `reviewEncoder` (respectively) and improve documentation regarding contravariance of `reviewEncoder`.
26* ([#739](https://github.com/obsidiansystems/obelisk/pull/739)) Improve `ob shell` by allowing commands to be passed verbatim after a `--` argument. For example, `ob shell 'run command'` can now be written `ob shell -- run command`.
8* ([#739](https://github.com/obsidiansystems/obelisk/pull/739)) Improve `ob shell` by allowing commands to be passed verbatim after a `--` argument. For example, `ob shell 'run command'` can now be written `ob shell -- run command`.
27* ([#735](https://github.com/obsidiansystems/obelisk/pull/735)) Fix regression causing custom `Prelude`s to break `ob run`/`ob watch`/`ob repl`.
9* ([#735](https://github.com/obsidiansystems/obelisk/pull/735)) Fix regression causing custom `Prelude`s to break `ob run`/`ob watch`/`ob repl`.
README.md+3−6
@@ -66,10 +66,7 @@ Obelisk assumes basic knowledge of [Haskell](https://www.haskell.org/) and [Refl
66 sudo launchctl stop org.nixos.nix-daemon
66 sudo launchctl stop org.nixos.nix-daemon
67 sudo launchctl start org.nixos.nix-daemon
67 sudo launchctl start org.nixos.nix-daemon
68 ```
68 ```
69−1. Install obelisk:
70− ```bash
71− nix-env -f https://github.com/obsidiansystems/obelisk/archive/master.tar.gz -iA command
69+1. Install obelisk: `nix-env -f https://github.com/obsidiansystems/obelisk/archive/master.tar.gz -iA command`
72− ```
73
70
74### Accessing private repositories
71### Accessing private repositories
75
72
@@ -114,7 +111,7 @@ First create a new EC2 instance:
1141. Launch a NixOS 19.09 EC2 instance (we recommend [this AMI](https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#LaunchInstanceWizard:ami=ami-00a8eeaf232a74f84))
1111. Launch a NixOS 19.09 EC2 instance (we recommend [this AMI](https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#LaunchInstanceWizard:ami=ami-00a8eeaf232a74f84))
1151. In the instance configuration wizard ensure that your instance has at least 1GB RAM and 10GB disk space.
1121. In the instance configuration wizard ensure that your instance has at least 1GB RAM and 10GB disk space.
1161. When prompted save your AWS private key (`~/myaws.pem`) somewhere safe. We'll need it later during deployment.
1131. When prompted save your AWS private key (`~/myaws.pem`) somewhere safe. We'll need it later during deployment.
117−1. Go to "Security Groups", select your instance's security group and under "Inbound" tab add a new rule for HTTP port 80 and HTTPS port 443.
114+1. Go to "Security Groups", select your instance's security group and under "Inbound" tab add a new rule for HTTP port 80 and 443.
118
115
119At this stage your instance should be booting and become accessible shortly. Note down the hostname of your EC2 instance.
116At this stage your instance should be booting and become accessible shortly. Note down the hostname of your EC2 instance.
120
117
@@ -221,7 +218,7 @@ Your account must also belong to an Apple Developer Team, if you want to access
2211. Click accept to authorize on both the computer and the iPhone.
2181. Click accept to authorize on both the computer and the iPhone.
222
219
223##### Xcode
220##### Xcode
224−Install Xcode 11.2 (contains iOS SDK 13.2) and open it so that it runs its post install tool setup.
221+Install Xcode 8.2 (contains iOS SDK 10.2) and open it so that it runs its post install tool setup.
225
222
226These versions will work out of the box but iOS SDKs prior to 11.3 should also work. You can choose another installed version in `default.nix`
223These versions will work out of the box but iOS SDKs prior to 11.3 should also work. You can choose another installed version in `default.nix`
227
224
all-builds.nix+1−1
@@ -1,6 +1,6 @@
1{ self-args ? {
1{ self-args ? {
2 config.android_sdk.accept_license = true;
2 config.android_sdk.accept_license = true;
3− iosSdkVersion = "13.2";
3+ iosSdkVersion = "10.2";
4 }
4 }
5, local-self ? import ./. self-args
5, local-self ? import ./. self-args
6, supportedSystems ? [ builtins.currentSystem ]
6, supportedSystems ? [ builtins.currentSystem ]
default.nix+38−45
@@ -1,10 +1,7 @@
1{ system ? builtins.currentSystem
1{ system ? builtins.currentSystem
2, profiling ? false
2, profiling ? false
3−, iosSdkVersion ? "13.2"
3+, iosSdkVersion ? "10.2"
4, config ? {}
4, config ? {}
5−, terms ? { # Accepted terms, conditions, and licenses
6− security.acme.acceptTerms = false;
7− }
8, reflex-platform-func ? import ./dep/reflex-platform
5, reflex-platform-func ? import ./dep/reflex-platform
9}:
6}:
10let
7let
@@ -117,7 +114,7 @@ in rec {
117 "${routeHost}".email = adminEmail;
114 "${routeHost}".email = adminEmail;
118 } else {};
115 } else {};
119
116
120− security.acme.${if enableHttps && (terms.security.acme.acceptTerms or false) then "acceptTerms" else null} = true;
117+ security.acme.${if enableHttps then "acceptTerms" else null} = true;
121 };
118 };
122
119
123 mkObeliskApp =
120 mkObeliskApp =
@@ -141,9 +138,6 @@ in rec {
141 locations.${baseUrl} = {
138 locations.${baseUrl} = {
142 proxyPass = "http://127.0.0.1:" + toString internalPort;
139 proxyPass = "http://127.0.0.1:" + toString internalPort;
143 proxyWebsockets = true;
140 proxyWebsockets = true;
144− extraConfig = ''
145− access_log off;
146− '';
147 };
141 };
148 };
142 };
149 };
143 };
@@ -186,9 +180,7 @@ in rec {
186 set -eux
180 set -eux
187 ln -s '${if profiling then backend else haskellLib.justStaticExecutables backend}'/bin/* $out/
181 ln -s '${if profiling then backend else haskellLib.justStaticExecutables backend}'/bin/* $out/
188 ln -s '${mkAssets assets}' $out/static.assets
182 ln -s '${mkAssets assets}' $out/static.assets
189− for d in '${mkAssets (compressedJs frontend optimizationLevel)}'/*/; do
183+ ln -s '${mkAssets (compressedJs frontend optimizationLevel)}'/* $out
190− ln -s "$d" "$out"/"$(basename "$d").assets"
191− done
192 echo ${version} > $out/version
184 echo ${version} > $out/version
193 '';
185 '';
194
186
@@ -337,40 +329,39 @@ in rec {
337 });
329 });
338 in allConfig;
330 in allConfig;
339 in (mkProject (projectDefinition args)).projectConfig);
331 in (mkProject (projectDefinition args)).projectConfig);
340− mainProjectOut = projectOut { inherit system; };
332+ projectOutHost = projectOut { inherit system; };
341 serverOn = projectInst: version: serverExe
333 serverOn = projectInst: version: serverExe
342 projectInst.ghc.backend
334 projectInst.ghc.backend
343− mainProjectOut.ghcjs.frontend
335+ projectOutHost.ghcjs.frontend
344 projectInst.passthru.staticFiles
336 projectInst.passthru.staticFiles
345 projectInst.passthru.__closureCompilerOptimizationLevel
337 projectInst.passthru.__closureCompilerOptimizationLevel
346 version;
338 version;
347− linuxExe = serverOn (projectOut { system = "x86_64-linux"; });
348 dummyVersion = "Version number is only available for deployments";
339 dummyVersion = "Version number is only available for deployments";
349− in mainProjectOut // {
350− __unstable__.profiledObRun = let
340+ profiledObRun =
341+ let
351− profiled = projectOut { inherit system; enableLibraryProfiling = true; };
342+ profiled = projectOut { inherit system; enableLibraryProfiling = true; };
352− exeSource = builtins.toFile "ob-run.hs" ''
343+ exeSource = builtins.toFile "ob-run-profiled.hs" ''
353− {-# LANGUAGE NoImplicitPrelude #-}
344+ {-# LANGUAGE NoImplicitPrelude #-}
354− {-# LANGUAGE PackageImports #-}
345+ {-# LANGUAGE PackageImports #-}
355− module Main where
346+ module Main where
356−
347+
357− -- Explicitly import Prelude from base lest there be multiple modules called Prelude
348+ -- Explicitly import Prelude from base lest there be multiple modules called Prelude
358− import "base" Prelude (IO, (++), read)
349+ import "base" Prelude (IO, (++), read)
359−
350+
360− import "base" Control.Exception (finally)
351+ import "base" Control.Exception (finally)
361− import "reflex" Reflex.Profiled (writeProfilingData)
352+ import "reflex" Reflex.Profiled (writeProfilingData)
362− import "base" System.Environment (getArgs)
353+ import "base" System.Environment (getArgs)
363−
354+
364− import qualified "obelisk-run" Obelisk.Run
355+ import qualified "obelisk-run" Obelisk.Run
365− import qualified Frontend
356+ import qualified Frontend
366− import qualified Backend
357+ import qualified Backend
367−
358+
368− main :: IO ()
359+ main :: IO ()
369− main = do
360+ main = do
370− [portStr, assets, profFileName] <- getArgs
361+ [portStr, assets, profFileName] <- getArgs
371− Obelisk.Run.run (read portStr) (Obelisk.Run.runServeAsset assets) Backend.backend Frontend.frontend
362+ Obelisk.Run.run (read portStr) (Obelisk.Run.runServeAsset assets) Backend.backend Frontend.frontend
372− `finally` writeProfilingData (profFileName ++ ".rprof")
363+ `finally` writeProfilingData (profFileName ++ ".rprof")
373− '';
364+ '';
374 in nixpkgs.runCommand "ob-run" {
365 in nixpkgs.runCommand "ob-run" {
375 buildInputs = [ (profiled.ghc.ghcWithPackages (p: [p.backend p.frontend])) ];
366 buildInputs = [ (profiled.ghc.ghcWithPackages (p: [p.backend p.frontend])) ];
376 } ''
367 } ''
@@ -378,14 +369,16 @@ in rec {
378 mkdir -p $out/bin
369 mkdir -p $out/bin
379 ghc -x hs -prof -fno-prof-auto -threaded ob-run.hs -o $out/bin/ob-run
370 ghc -x hs -prof -fno-prof-auto -threaded ob-run.hs -o $out/bin/ob-run
380 '';
371 '';
381−
372+ in projectOutHost // lib.makeExtensible (self: {
382− linuxExeConfigurable = linuxExe;
373+ __unstable__.profiledObRun = profiledObRun;
383− linuxExe = linuxExe dummyVersion;
374+ exeConfigurable = { system, version }: serverOn (projectOut { inherit system; }) version;
384− exe = serverOn mainProjectOut dummyVersion;
375+ exe = self.exeConfigurable { inherit system; version = dummyVersion; };
376+ linuxExeConfigurable = version: self.exeConfigurable { system = "x86_64-linux"; inherit version; };
377+ linuxExe = self.linuxExeConfigurable dummyVersion;
385 server = args@{ hostName, adminEmail, routeHost, enableHttps, version, module ? serverModules.mkBaseEc2 }:
378 server = args@{ hostName, adminEmail, routeHost, enableHttps, version, module ? serverModules.mkBaseEc2 }:
386− server (args // { exe = linuxExe version; });
379+ server (args // { exe = self.linuxExeConfigurable version; });
387 obelisk = import (base' + "/.obelisk/impl") {};
380 obelisk = import (base' + "/.obelisk/impl") {};
388− };
381+ });
389 haskellPackageSets = {
382 haskellPackageSets = {
390 inherit (reflex-platform) ghc ghcjs;
383 inherit (reflex-platform) ghc ghcjs;
391 };
384 };
dep/reflex-platform/github.json+3−3
@@ -1,8 +1,8 @@
1{
1{
2 "owner": "reflex-frp",
2 "owner": "reflex-frp",
3 "repo": "reflex-platform",
3 "repo": "reflex-platform",
4− "branch": "release/0.7.0.0",
4+ "branch": "eac@tweaks-from-wrinkl",
5 "private": false,
5 "private": false,
6− "rev": "27a7851a780ff21472d85f9a02fb84635ef9027a",
6+ "rev": "1cca141c7a87e98e8e36860c326408942851bf61",
7− "sha256": "1m4zlw91dpjlbdchjyxhvhv3k7lilm0qa17dhhww868xxc27sann"
7+ "sha256": "0mlxn6zy4983rr3ahslm5yvyz6gkqs9m6by9xl9vm0dhm2cb36gj"
8}
8}
haskell-overlays/misc-deps.nix+2−8
@@ -9,14 +9,8 @@ let
9in
9in
10
10
11{
11{
12− regex-base = self.callHackage "regex-base" "0.94.0.0" {};
12+ hnix = haskellLib.dontCheck (haskellLib.doJailbreak (self.callCabal2nix "hnix" (hackGet ../dep/hnix) {}));
13− regex-posix = self.callHackage "regex-posix" "0.96.0.0" {};
14− regex-tdfa = self.callHackage "regex-tdfa" "1.3.1.0" {};
15− test-framework = haskellLib.dontCheck (self.callHackage "test-framework" "0.8.2.0" {});
16− hnix = haskellLib.dontCheck (self.callHackage "hnix" "0.8.0" {});
17− hnix-store-core = self.callHackage "hnix-store-core" "0.2.0.0" {};
13+ hnix-store-core = self.callHackage "hnix-store-core" "0.1.0.0" {};
18−
19− aeson-gadt-th = self.callHackage "aeson-gadt-th" "0.2.4" {};
20
14
21 ghcid = self.callCabal2nix "ghcid" (hackGet ../dep/ghcid) {};
15 ghcid = self.callCabal2nix "ghcid" (hackGet ../dep/ghcid) {};
22 # Exports more internals
16 # Exports more internals
haskell-overlays/obelisk.nix−4
@@ -19,12 +19,8 @@ in
19 obelisk-command = haskellLib.overrideCabal (self.callCabal2nix "obelisk-command" (obeliskCleanSource ../lib/command) {}) {
19 obelisk-command = haskellLib.overrideCabal (self.callCabal2nix "obelisk-command" (obeliskCleanSource ../lib/command) {}) {
20 librarySystemDepends = [
20 librarySystemDepends = [
21 pkgs.jre
21 pkgs.jre
22− pkgs.git
23 pkgs.nix
22 pkgs.nix
24 pkgs.nix-prefetch-git
23 pkgs.nix-prefetch-git
25− pkgs.openssh
26− pkgs.rsync
27− pkgs.which
28 (haskellLib.justStaticExecutables self.ghcid)
24 (haskellLib.justStaticExecutables self.ghcid)
29 ];
25 ];
30 };
26 };
lib/command/obelisk-command.cabal+1−1
@@ -1,5 +1,5 @@
1name: obelisk-command
1name: obelisk-command
2−version: 0.9.0.1
2+version: 0.8.0.0
3cabal-version: >= 1.8
3cabal-version: >= 1.8
4build-type: Simple
4build-type: Simple
5
5
lib/command/src/Obelisk/Command/Deploy.hs+27−24
@@ -17,6 +17,7 @@ import Data.Bits
17import qualified Data.ByteString.Lazy as BSL
17import qualified Data.ByteString.Lazy as BSL
18import Data.Default
18import Data.Default
19import qualified Data.Map as Map
19import qualified Data.Map as Map
20+import Data.Maybe (isJust)
20import qualified Data.Set as Set
21import qualified Data.Set as Set
21import Data.String.Here.Interpolated (i)
22import Data.String.Here.Interpolated (i)
22import qualified Data.Text as T
23import qualified Data.Text as T
@@ -85,12 +86,13 @@ deployInit' thunkPtr (DeployInitOpts deployDir sshKeyPath hostnames route adminE
85 --accidentally create a production deployment that uses development
86 --accidentally create a production deployment that uses development
86 --credentials to connect to some resources. This could result in, e.g.,
87 --credentials to connect to some resources. This could result in, e.g.,
87 --production data backed up to a dev environment.
88 --production data backed up to a dev environment.
88− withSpinner "Creating project configuration directories" $ liftIO $ do
89+ withSpinner "Creating project configuration directories" $ do
89− mapM_ (createDirectoryIfMissing True)
90+ callProcessAndLogOutput (Notice, Error) $
91+ proc "mkdir" [ "-p"
90− [ deployDir </> "config" </> "backend"
92+ , deployDir </> "config" </> "backend"
91− , deployDir </> "config" </> "common"
93+ , deployDir </> "config" </> "common"
92− , deployDir </> "config" </> "frontend"
94+ , deployDir </> "config" </> "frontend"
93− ]
95+ ]
94
96
95 let srcDir = deployDir </> "src"
97 let srcDir = deployDir </> "src"
96 withSpinner ("Creating source thunk (" <> T.pack (makeRelative deployDir srcDir) <> ")") $ do
98 withSpinner ("Creating source thunk (" <> T.pack (makeRelative deployDir srcDir) <> ")") $ do
@@ -124,14 +126,15 @@ deployPush deployPath getNixBuilders = do
124 route <- readDeployConfig deployPath $ "config" </> "common" </> "route"
126 route <- readDeployConfig deployPath $ "config" </> "common" </> "route"
125 routeHost <- getHostFromRoute enableHttps route
127 routeHost <- getHostFromRoute enableHttps route
126 let srcPath = deployPath </> "src"
128 let srcPath = deployPath </> "src"
127− thunkPtr <- readThunk srcPath >>= \case
129+ thunkPtr' <- readThunk srcPath >>= \case
128− Right (ThunkData_Packed _ ptr) -> return ptr
130+ Right (ThunkData_Packed _ ptr) -> return $ Just ptr
129 Right ThunkData_Checkout -> do
131 Right ThunkData_Checkout -> do
130 checkGitCleanStatus srcPath True >>= \case
132 checkGitCleanStatus srcPath True >>= \case
131− True -> packThunk (ThunkPackConfig False (ThunkConfig Nothing)) srcPath
133+ True -> Just <$> packThunk (ThunkPackConfig False (ThunkConfig Nothing)) srcPath
134+ False -> Nothing <$ putLog Warning "Skipping packing of src thunk; won't be able to automatically commit this deployment"
132− False -> failWith $ T.pack $ "ob deploy push: ensure " <> srcPath <> " has no pending changes and latest is pushed upstream."
135+ -- failWith $ T.pack $ "ob deploy push: ensure " <> srcPath <> " has no pending changes and latest is pushed upstream."
133 Left err -> failWith $ "ob deploy push: couldn't read src thunk: " <> T.pack (show err)
136 Left err -> failWith $ "ob deploy push: couldn't read src thunk: " <> T.pack (show err)
134− let version = show . _thunkRev_commit $ _thunkPtr_rev thunkPtr
137+ let version' = show . _thunkRev_commit . _thunkPtr_rev <$> thunkPtr'
135 builders <- getNixBuilders
138 builders <- getNixBuilders
136 let moduleFile = deployPath </> "module.nix"
139 let moduleFile = deployPath </> "module.nix"
137 moduleFileExists <- liftIO $ doesFileExist moduleFile
140 moduleFileExists <- liftIO $ doesFileExist moduleFile
@@ -148,7 +151,7 @@ deployPush deployPath getNixBuilders = do
148 [ strArg "hostName" host
151 [ strArg "hostName" host
149 , strArg "adminEmail" adminEmail
152 , strArg "adminEmail" adminEmail
150 , strArg "routeHost" routeHost
153 , strArg "routeHost" routeHost
151− , strArg "version" version
154+ , strArg "version" $ maybe "Deployment was done with a dirty source tree" id version'
152 , boolArg "enableHttps" enableHttps
155 , boolArg "enableHttps" enableHttps
153 ] <> [rawArg "module" ("import " <> toNixPath moduleFile) | moduleFileExists ])
156 ] <> [rawArg "module" ("import " <> toNixPath moduleFile) | moduleFileExists ])
154 & nixCmdConfig_builders .~ builders
157 & nixCmdConfig_builders .~ builders
@@ -161,8 +164,8 @@ deployPush deployPath getNixBuilders = do
161 "nix-copy-closure" ["-v", "--to", "--use-substitutes", "root@" <> host, "--gzip", outputPath]
164 "nix-copy-closure" ["-v", "--to", "--use-substitutes", "root@" <> host, "--gzip", outputPath]
162 withSpinner "Uploading config" $ ifor_ buildOutputByHost $ \host _ -> do
165 withSpinner "Uploading config" $ ifor_ buildOutputByHost $ \host _ -> do
163 callProcessAndLogOutput (Notice, Warning) $
166 callProcessAndLogOutput (Notice, Warning) $
164− proc rsyncPath
167+ proc "rsync"
165− [ "-e " <> sshPath <> " " <> unwords sshOpts
168+ [ "-e ssh " <> unwords sshOpts
166 , "-qarvz"
169 , "-qarvz"
167 , deployPath </> "config"
170 , deployPath </> "config"
168 , "root@" <> host <> ":/var/lib/backend"
171 , "root@" <> host <> ":/var/lib/backend"
@@ -170,22 +173,22 @@ deployPush deployPath getNixBuilders = do
170 --TODO: Create GC root so we're sure our closure won't go away during this time period
173 --TODO: Create GC root so we're sure our closure won't go away during this time period
171 withSpinner "Switching to new configuration" $ ifor_ buildOutputByHost $ \host outputPath -> do
174 withSpinner "Switching to new configuration" $ ifor_ buildOutputByHost $ \host outputPath -> do
172 callProcessAndLogOutput (Notice, Warning) $
175 callProcessAndLogOutput (Notice, Warning) $
173− proc sshPath $ sshOpts <>
176+ proc "ssh" $ sshOpts <>
174 [ "root@" <> host
177 [ "root@" <> host
175 , unwords
178 , unwords
176− -- Note that we don't want to $(staticWhich "nix-env") here, because this is executing on a remote machine
177 [ "nix-env -p /nix/var/nix/profiles/system --set " <> outputPath
179 [ "nix-env -p /nix/var/nix/profiles/system --set " <> outputPath
178 , "&&"
180 , "&&"
179 , "/nix/var/nix/profiles/system/bin/switch-to-configuration switch"
181 , "/nix/var/nix/profiles/system/bin/switch-to-configuration switch"
180 ]
182 ]
181 ]
183 ]
184+ when (isJust version') $ do
182− isClean <- checkGitCleanStatus deployPath True
185+ isClean <- checkGitCleanStatus deployPath True
183− when (not isClean) $ do
186+ when (not isClean) $ do
184− withSpinner "Committing changes to Git" $ do
187+ withSpinner "Committing changes to Git" $ do
185− callProcessAndLogOutput (Debug, Error) $
188+ callProcessAndLogOutput (Debug, Error) $ proc "git"
186− gitProc deployPath ["add", "."]
189+ ["-C", deployPath, "add", "."]
187− callProcessAndLogOutput (Debug, Error) $
190+ callProcessAndLogOutput (Debug, Error) $ proc "git"
188− gitProc deployPath ["commit", "-m", "New deployment"]
191+ ["-C", deployPath, "commit", "-m", "New deployment"]
189 putLog Notice $ "Deployed => " <> T.pack route
192 putLog Notice $ "Deployed => " <> T.pack route
190 where
193 where
191 callProcess' envMap cmd args = do
194 callProcess' envMap cmd args = do
@@ -315,7 +318,7 @@ readDeployConfig deployDir fname = liftIO $ do
315
318
316verifyHostKey :: MonadObelisk m => FilePath -> FilePath -> String -> m ()
319verifyHostKey :: MonadObelisk m => FilePath -> FilePath -> String -> m ()
317verifyHostKey knownHostsPath keyPath hostName =
320verifyHostKey knownHostsPath keyPath hostName =
318− callProcessAndLogOutput (Notice, Warning) $ proc sshPath $
321+ callProcessAndLogOutput (Notice, Warning) $ proc "ssh" $
319 sshArgs knownHostsPath keyPath True <>
322 sshArgs knownHostsPath keyPath True <>
320 [ "root@" <> hostName
323 [ "root@" <> hostName
321 , "-o", "NumberOfPasswordPrompts=0"
324 , "-o", "NumberOfPasswordPrompts=0"
lib/command/src/Obelisk/Command/Preprocessor.hs+2−3
@@ -47,8 +47,7 @@ applyPackages origPath inPath outPath packagePaths' = do
47 Left err -> do
47 Left err -> do
48 hPutStrLn stderr $ "Error: Unable to parse cabal package " <> packagePath <> "; Skipping preprocessor on " <> origPath <> ". Error: " <> show err
48 hPutStrLn stderr $ "Error: Unable to parse cabal package " <> packagePath <> "; Skipping preprocessor on " <> origPath <> ". Error: " <> show err
49 pure Nothing
49 pure Nothing
50− Right (Just (_, packageInfo)) -> pure $ Just packageInfo
50+ Right (_, packageInfo) -> pure $ Just packageInfo
51− Right Nothing -> pure Nothing
52
51
53 writeOutput packageInfo' inPath outPath
52 writeOutput packageInfo' inPath outPath
54
53
@@ -76,7 +75,7 @@ generateHeader origPath packageInfo =
76 ext -> (TL.fromString (show ext) :)
75 ext -> (TL.fromString (show ext) :)
77 showExt = \case
76 showExt = \case
78 EnableExtension ext -> [TL.fromString (show ext)]
77 EnableExtension ext -> [TL.fromString (show ext)]
79− DisableExtension ext -> ["No" <> TL.fromString (show ext)]
78+ DisableExtension _ -> []
80 UnknownExtension ext -> [TL.fromString ext]
79 UnknownExtension ext -> [TL.fromString ext]
81
80
82 ghcOptions =
81 ghcOptions =
lib/command/src/Obelisk/Command/Project.hs+4−10
@@ -51,7 +51,7 @@ import Obelisk.App (MonadObelisk)
51import Obelisk.CliApp
51import Obelisk.CliApp
52import Obelisk.Command.Nix
52import Obelisk.Command.Nix
53import Obelisk.Command.Thunk
53import Obelisk.Command.Thunk
54−import Obelisk.Command.Utils (nixBuildExePath, nixExePath, toNixPath, cp, nixShellPath)
54+import Obelisk.Command.Utils (nixBuildExePath, nixExePath, toNixPath)
55
55
56--TODO: Make this module resilient to random exceptions
56--TODO: Make this module resilient to random exceptions
57
57
@@ -116,7 +116,7 @@ initProject source force = withSystemTempDirectory "ob-init" $ \tmpDir -> do
116 skel <- nixBuildAttrWithCache implDir "skeleton" --TODO: I don't think there's actually any reason to cache this
116 skel <- nixBuildAttrWithCache implDir "skeleton" --TODO: I don't think there's actually any reason to cache this
117
117
118 callProcessAndLogOutput (Notice, Error) $
118 callProcessAndLogOutput (Notice, Error) $
119− proc cp
119+ proc "cp"
120 [ "-r"
120 [ "-r"
121 , "--preserve=links"
121 , "--preserve=links"
122 , obDir
122 , obDir
@@ -126,7 +126,7 @@ initProject source force = withSystemTempDirectory "ob-init" $ \tmpDir -> do
126
126
127 withSpinner "Copying project skeleton" $ do
127 withSpinner "Copying project skeleton" $ do
128 callProcessAndLogOutput (Notice, Error) $
128 callProcessAndLogOutput (Notice, Error) $
129− proc cp
129+ proc "cp"
130 [ "-r"
130 [ "-r"
131 , "--no-preserve=mode"
131 , "--no-preserve=mode"
132 , "-T"
132 , "-T"
@@ -137,12 +137,6 @@ initProject source force = withSystemTempDirectory "ob-init" $ \tmpDir -> do
137 let configDir = "config"
137 let configDir = "config"
138 createDirectoryIfMissing False configDir
138 createDirectoryIfMissing False configDir
139 mapM_ (createDirectoryIfMissing False . (configDir </>)) ["backend", "common", "frontend"]
139 mapM_ (createDirectoryIfMissing False . (configDir </>)) ["backend", "common", "frontend"]
140− putLog Notice $ T.intercalate "\n"
141− [ "An obelisk project has been successfully initialized. Next steps:"
142− , " 'ob run': Start a development server"
143− , " 'ob watch': Watch for changes without starting a server"
144− , " 'ob repl': Load your project into GHCi"
145− ]
146
140
147callHandoffOb
141callHandoffOb
148 :: MonadObelisk m
142 :: MonadObelisk m
@@ -298,7 +292,7 @@ bashEscape :: String -> String
298bashEscape = BSU.toString . bytes . bash . BSU.fromString
292bashEscape = BSU.toString . bytes . bash . BSU.fromString
299
293
300nixShellRunProc :: NixShellConfig -> ProcessSpec
294nixShellRunProc :: NixShellConfig -> ProcessSpec
301−nixShellRunProc cfg = setDelegateCtlc True $ proc nixShellPath $ runNixShellConfig cfg
295+nixShellRunProc cfg = setDelegateCtlc True $ proc "nix-shell" $ runNixShellConfig cfg
302
296
303nixShellWithoutPkgs
297nixShellWithoutPkgs
304 :: MonadObelisk m
298 :: MonadObelisk m
lib/command/src/Obelisk/Command/Run.hs+11−9
@@ -14,7 +14,7 @@ module Obelisk.Command.Run where
14import Control.Arrow ((&&&))
14import Control.Arrow ((&&&))
15import Control.Exception (Exception, bracket)
15import Control.Exception (Exception, bracket)
16import Control.Lens (ifor, (.~), (&))
16import Control.Lens (ifor, (.~), (&))
17−import Control.Monad (filterM)
17+import Control.Monad (filterM, unless)
18import Control.Monad.Except (runExceptT, throwError)
18import Control.Monad.Except (runExceptT, throwError)
19import Control.Monad.IO.Class (liftIO)
19import Control.Monad.IO.Class (liftIO)
20import Control.Monad.Reader (MonadIO)
20import Control.Monad.Reader (MonadIO)
@@ -304,16 +304,15 @@ parseCabalPackage
304 -> m (Maybe CabalPackageInfo)
304 -> m (Maybe CabalPackageInfo)
305parseCabalPackage dir = parseCabalPackage' dir >>= \case
305parseCabalPackage dir = parseCabalPackage' dir >>= \case
306 Left err -> Nothing <$ putLog Error err
306 Left err -> Nothing <$ putLog Error err
307− Right (Just (warnings, pkgInfo)) -> do
307+ Right (warnings, pkgInfo) -> do
308 for_ warnings $ putLog Warning . T.pack . show
308 for_ warnings $ putLog Warning . T.pack . show
309 pure $ Just pkgInfo
309 pure $ Just pkgInfo
310− Right Nothing -> pure Nothing
311
310
312-- | Like 'parseCabalPackage' but returns errors and warnings directly so as to avoid 'MonadObelisk'.
311-- | Like 'parseCabalPackage' but returns errors and warnings directly so as to avoid 'MonadObelisk'.
313parseCabalPackage'
312parseCabalPackage'
314 :: (MonadIO m)
313 :: (MonadIO m)
315 => FilePath -- ^ Package directory
314 => FilePath -- ^ Package directory
316− -> m (Either T.Text (Maybe ([Dist.PWarning], CabalPackageInfo)))
315+ -> m (Either T.Text ([Dist.PWarning], CabalPackageInfo))
317parseCabalPackage' pkg = runExceptT $ do
316parseCabalPackage' pkg = runExceptT $ do
318 (cabalContents, packageFile, packageName) <- guessCabalPackageFile pkg >>= \case
317 (cabalContents, packageFile, packageName) <- guessCabalPackageFile pkg >>= \case
319 Left GuessPackageFileError_NotFound -> throwError $ "No .cabal or package.yaml file found in " <> T.pack pkg
318 Left GuessPackageFileError_NotFound -> throwError $ "No .cabal or package.yaml file found in " <> T.pack pkg
@@ -341,7 +340,7 @@ parseCabalPackage' pkg = runExceptT $ do
341 case condLibrary <$> result of
340 case condLibrary <$> result of
342 Right (Just condLib) -> do
341 Right (Just condLib) -> do
343 let (_, lib) = simplifyCondTree evalConfVar condLib
342 let (_, lib) = simplifyCondTree evalConfVar condLib
344− pure $ Just $ (warnings,) $ CabalPackageInfo
343+ pure $ (warnings,) $ CabalPackageInfo
345 { _cabalPackageInfo_packageName = T.pack packageName
344 { _cabalPackageInfo_packageName = T.pack packageName
346 , _cabalPackageInfo_packageFile = packageFile
345 , _cabalPackageInfo_packageFile = packageFile
347 , _cabalPackageInfo_packageRoot = takeDirectory packageFile
346 , _cabalPackageInfo_packageRoot = takeDirectory packageFile
@@ -355,17 +354,17 @@ parseCabalPackage' pkg = runExceptT $ do
355 , _cabalPackageInfo_compilerOptions = options $ libBuildInfo lib
354 , _cabalPackageInfo_compilerOptions = options $ libBuildInfo lib
356 , _cabalPackageInfo_cppOptions = cppOptions $ libBuildInfo lib
355 , _cabalPackageInfo_cppOptions = cppOptions $ libBuildInfo lib
357 }
356 }
358− Right Nothing -> pure Nothing
357+ Right Nothing -> throwError "Haskell package has no library component"
359 Left (_, errors) ->
358 Left (_, errors) ->
360 throwError $ T.pack $ "Failed to parse " <> packageFile <> ":\n" <> unlines (map show errors)
359 throwError $ T.pack $ "Failed to parse " <> packageFile <> ":\n" <> unlines (map show errors)
361
360
362parsePackagesOrFail :: (MonadObelisk m, Foldable f) => f FilePath -> m (NE.NonEmpty CabalPackageInfo)
361parsePackagesOrFail :: (MonadObelisk m, Foldable f) => f FilePath -> m (NE.NonEmpty CabalPackageInfo)
363parsePackagesOrFail dirs' = do
362parsePackagesOrFail dirs' = do
364− packageInfos' <- fmap catMaybes $ for dirs $ \dir -> do
363+ (pkgDirErrs, packageInfos') <- fmap partitionEithers $ for dirs $ \dir -> do
365 flip fmap (parseCabalPackage dir) $ \case
364 flip fmap (parseCabalPackage dir) $ \case
366 Just packageInfo
365 Just packageInfo
367− | _cabalPackageInfo_buildable packageInfo -> Just packageInfo
366+ | _cabalPackageInfo_buildable packageInfo -> Right packageInfo
368− _ -> Nothing
367+ _ -> Left dir
369
368
370 -- Sort duplicate packages such that we prefer shorter paths, but fall back to alphabetical ordering.
369 -- Sort duplicate packages such that we prefer shorter paths, but fall back to alphabetical ordering.
371 let packagesByName = Map.map (NE.sortBy $ comparing $ \p -> let n = _cabalPackageInfo_packageFile p in (length n, n))
370 let packagesByName = Map.map (NE.sortBy $ comparing $ \p -> let n = _cabalPackageInfo_packageFile p in (length n, n))
@@ -387,6 +386,9 @@ parsePackagesOrFail dirs' = do
387 "No valid, buildable packages found" <> (if null dirs then "" else " in " <> intercalate ", " dirs)
386 "No valid, buildable packages found" <> (if null dirs then "" else " in " <> intercalate ", " dirs)
388 Just xs -> pure xs
387 Just xs -> pure xs
389
388
389+ unless (null pkgDirErrs) $
390+ putLog Warning $ T.pack $ "Failed to find buildable packages in " <> intercalate ", " pkgDirErrs
391+
390 pure packageInfos
392 pure packageInfos
391 where
393 where
392 dirs = toList dirs'
394 dirs = toList dirs'
lib/command/src/Obelisk/Command/Thunk.hs+34−42
@@ -26,9 +26,8 @@ import qualified Data.ByteString.Lazy as LBS
26import Data.Containers.ListUtils (nubOrd)
26import Data.Containers.ListUtils (nubOrd)
27import Data.Default
27import Data.Default
28import Data.Either.Combinators (fromRight', rightToMaybe)
28import Data.Either.Combinators (fromRight', rightToMaybe)
29−import Data.Foldable (toList)
29+import Data.Foldable (for_, toList)
30import Data.Function ((&))
30import Data.Function ((&))
31−import Data.Functor ((<&>))
32import Data.Git.Ref (Ref)
31import Data.Git.Ref (Ref)
33import qualified Data.Git.Ref as Ref
32import qualified Data.Git.Ref as Ref
34import qualified Data.List as L
33import qualified Data.List as L
@@ -238,35 +237,38 @@ matchThunkSpecToDir
238 -> Set FilePath -- ^ Set of file paths relative to the given directory
237 -> Set FilePath -- ^ Set of file paths relative to the given directory
239 -> m ThunkData
238 -> m ThunkData
240matchThunkSpecToDir thunkSpec dir dirFiles = do
239matchThunkSpecToDir thunkSpec dir dirFiles = do
240+ isCheckout <- fmap or $ flip Map.traverseWithKey (_thunkSpec_files thunkSpec) $ \expectedPath -> \case
241+ ThunkFileSpec_CheckoutIndicator -> liftIO (doesDirectoryExist (dir </> expectedPath))
242+ _ -> pure False
243+ case isCheckout of
244+ True -> pure ThunkData_Checkout
245+ False -> do
241− case nonEmpty (toList $ dirFiles `Set.difference` expectedPaths) of
246+ for_ (nonEmpty (toList $ dirFiles `Set.difference` expectedPaths)) $ \fs ->
242− Just fs -> throwError $ ReadThunkError_UnrecognizedPaths $ (dir </>) <$> fs
247+ throwError $ ReadThunkError_UnrecognizedPaths $ (dir </>) <$> fs
243− Nothing -> pure ()
244− case nonEmpty (toList $ requiredPaths `Set.difference` dirFiles) of
248+ for_ (nonEmpty (toList $ requiredPaths `Set.difference` dirFiles)) $ \fs ->
245− Just fs -> throwError $ ReadThunkError_MissingPaths $ (dir </>) <$> fs
249+ throwError $ ReadThunkError_MissingPaths $ (dir </>) <$> fs
246− Nothing -> pure ()
247− datas <- fmap toList $ flip Map.traverseMaybeWithKey (_thunkSpec_files thunkSpec) $ \expectedPath -> \case
250+ datas <- fmap toList $ flip Map.traverseMaybeWithKey (_thunkSpec_files thunkSpec) $ \expectedPath -> \case
248− ThunkFileSpec_AttrCache -> Nothing <$ dirMayExist expectedPath
251+ ThunkFileSpec_AttrCache -> Nothing <$ dirMayExist expectedPath
249− ThunkFileSpec_CheckoutIndicator -> liftIO (doesDirectoryExist (dir </> expectedPath)) <&> \case
252+ ThunkFileSpec_CheckoutIndicator -> pure Nothing -- Handled above
250− False -> Nothing
251− True -> Just ThunkData_Checkout
252− ThunkFileSpec_FileMatches expectedContents -> handle (\(e :: IOError) -> throwError $ ReadThunkError_FileError e) $ do
253+ ThunkFileSpec_FileMatches expectedContents -> handle (\(e :: IOError) -> throwError $ ReadThunkError_FileError e) $ do
253− actualContents <- liftIO (T.readFile $ dir </> expectedPath)
254+ actualContents <- liftIO (T.readFile $ dir </> expectedPath)
254− case T.strip expectedContents == T.strip actualContents of
255+ case T.strip expectedContents == T.strip actualContents of
255− True -> pure Nothing
256+ True -> pure Nothing
256− False -> throwError $ ReadThunkError_FileDoesNotMatch (dir </> expectedPath) expectedContents
257+ False -> throwError $ ReadThunkError_FileDoesNotMatch (dir </> expectedPath) expectedContents
257− ThunkFileSpec_Ptr parser -> handle (\(e :: IOError) -> throwError $ ReadThunkError_FileError e) $ do
258+ ThunkFileSpec_Ptr parser -> handle (\(e :: IOError) -> throwError $ ReadThunkError_FileError e) $ do
258− let path = dir </> expectedPath
259+ let path = dir </> expectedPath
259− liftIO (doesFileExist path) >>= \case
260+ liftIO (doesFileExist path) >>= \case
260− False -> pure Nothing
261+ False -> pure Nothing
261− True -> do
262+ True -> do
262− actualContents <- liftIO $ LBS.readFile path
263+ actualContents <- liftIO $ LBS.readFile path
263− case parser actualContents of
264+ case parser actualContents of
264− Right v -> pure $ Just (ThunkData_Packed thunkSpec v)
265+ Right v -> pure $ Just (thunkSpec, v)
265− Left e -> throwError $ ReadThunkError_UnparseablePtr (dir </> expectedPath) e
266+ Left e -> throwError $ ReadThunkError_UnparseablePtr (dir </> expectedPath) e
266−
267+
267− case nonEmpty datas of
268+ uncurry ThunkData_Packed <$> case nonEmpty datas of
268− Nothing -> throwError ReadThunkError_UnrecognizedThunk
269+ Nothing -> throwError ReadThunkError_UnrecognizedThunk
269− Just xs -> fold1WithM xs $ \a b -> either throwError pure (mergeThunkData a b)
270+ Just xs -> fold1WithM xs $ \a@(_, ptrA) (_, ptrB) ->
271+ if ptrA == ptrB then pure a else throwError $ ReadThunkError_AmbiguousPackedState ptrA ptrB
270 where
272 where
271 rootPathsOnly = Set.fromList . mapMaybe takeRootDir . Map.keys
273 rootPathsOnly = Set.fromList . mapMaybe takeRootDir . Map.keys
272 takeRootDir = fmap NonEmpty.head . nonEmpty . splitPath
274 takeRootDir = fmap NonEmpty.head . nonEmpty . splitPath
@@ -282,15 +284,6 @@ matchThunkSpecToDir thunkSpec dir dirFiles = do
282 True -> throwError $ ReadThunkError_UnrecognizedPaths $ expectedPath :| []
284 True -> throwError $ ReadThunkError_UnrecognizedPaths $ expectedPath :| []
283 False -> pure ()
285 False -> pure ()
284
286
285− -- Combine 'ThunkData' from different files, preferring "Checkout" over "Packed"
286− mergeThunkData ThunkData_Checkout ThunkData_Checkout = Right ThunkData_Checkout
287− mergeThunkData ThunkData_Checkout ThunkData_Packed{} = Left bothPackedAndUnpacked
288− mergeThunkData ThunkData_Packed{} ThunkData_Checkout = Left bothPackedAndUnpacked
289− mergeThunkData a@(ThunkData_Packed _ ptrA) (ThunkData_Packed _ ptrB) =
290− if ptrA == ptrB then Right a else Left $ ReadThunkError_AmbiguousPackedState ptrA ptrB
291−
292− bothPackedAndUnpacked = ReadThunkError_UnrecognizedState "Both packed data and checkout present"
293−
294 fold1WithM (x :| xs) f = foldM f x xs
287 fold1WithM (x :| xs) f = foldM f x xs
295
288
296readThunkWith
289readThunkWith
@@ -305,8 +298,7 @@ readThunkWith specTypes dir = do
305 Left e -> putLog Debug [i|Thunk specification ${_thunkSpec_name spec} did not match ${dir}: ${e}|] *> loop rest
298 Left e -> putLog Debug [i|Thunk specification ${_thunkSpec_name spec} did not match ${dir}: ${e}|] *> loop rest
306 x@(Right _) -> x <$ putLog Debug [i|Thunk specification ${_thunkSpec_name spec} matched ${dir}|]
299 x@(Right _) -> x <$ putLog Debug [i|Thunk specification ${_thunkSpec_name spec} matched ${dir}|]
307
300
308−-- | Read a thunk and validate that it is exactly a packed thunk.
301+-- | Read a packed or unpacked thunk based on predefined thunk specifications.
309−-- If additional data is present, fail.
310readThunk :: (MonadObelisk m) => FilePath -> m (Either ReadThunkError ThunkData)
302readThunk :: (MonadObelisk m) => FilePath -> m (Either ReadThunkError ThunkData)
311readThunk = readThunkWith thunkSpecTypes
303readThunk = readThunkWith thunkSpecTypes
312
304
lib/command/src/Obelisk/Command/Utils.hs+1−27
@@ -60,32 +60,6 @@ nixPrefetchGitPath = $(staticWhich "nix-prefetch-git")
60nixPrefetchUrlPath :: FilePath
60nixPrefetchUrlPath :: FilePath
61nixPrefetchUrlPath = $(staticWhich "nix-prefetch-url")
61nixPrefetchUrlPath = $(staticWhich "nix-prefetch-url")
62
62
63−nixShellPath :: FilePath
64−nixShellPath = $(staticWhich "nix-shell")
65−
66−rsyncPath :: FilePath
67−rsyncPath = $(staticWhich "rsync")
68−
69−sshPath :: FilePath
70−sshPath = $(staticWhich "ssh")
71−
72−gitPath :: FilePath
73−gitPath = $(staticWhich "git")
74−
75−whichPath :: FilePath
76−whichPath = $(staticWhich "which")
77−
78−sshKeygenPath :: FilePath
79−sshKeygenPath = $(staticWhich "ssh-keygen")
80−
81−-- $(staticWhich "docker") was intentionally omitted, at least for now
82−-- One concern is that I don't know how particular docker is about having the
83−-- CLI exe match the version of the docker daemon, which is largely outside of
84−-- the control of obelisk-command.
85−-- TODO: Investigate the tradeoffs associated with this choice
86−dockerPath :: FilePath
87−dockerPath = "docker"
88−
89-- | Nix syntax requires relative paths to be prefixed by @./@ or
63-- | Nix syntax requires relative paths to be prefixed by @./@ or
90-- @../@. This will make a 'FilePath' that can be embedded in a Nix
64-- @../@. This will make a 'FilePath' that can be embedded in a Nix
91-- expression.
65-- expression.
@@ -123,7 +97,7 @@ initGit repo = do
123 git ["commit", "-m", "Initial commit."]
97 git ["commit", "-m", "Initial commit."]
124
98
125gitProcNoRepo :: [String] -> ProcessSpec
99gitProcNoRepo :: [String] -> ProcessSpec
126−gitProcNoRepo args = setEnvOverride (M.singleton "GIT_TERMINAL_PROMPT" "0" <>) $ proc gitPath args
100+gitProcNoRepo args = setEnvOverride (M.singleton "GIT_TERMINAL_PROMPT" "0" <>) $ proc "git" args
127
101
128gitProc :: FilePath -> [String] -> ProcessSpec
102gitProc :: FilePath -> [String] -> ProcessSpec
129gitProc repo = gitProcNoRepo . runGitInDir
103gitProc repo = gitProcNoRepo . runGitInDir
lib/command/src/Obelisk/Command/VmBuilder.hs+8−8
@@ -21,7 +21,7 @@ import qualified System.Info
21
21
22import Obelisk.App (MonadObelisk, getObeliskUserStateDir)
22import Obelisk.App (MonadObelisk, getObeliskUserStateDir)
23import Obelisk.CliApp
23import Obelisk.CliApp
24−import Obelisk.Command.Utils (rmPath, whichPath, sshKeygenPath, nixBuildExePath, dockerPath)
24+import Obelisk.Command.Utils (rmPath)
25
25
26-- | Generate the `--builders` argument string to enable the VM builder after ensuring it is available.
26-- | Generate the `--builders` argument string to enable the VM builder after ensuring it is available.
27getNixBuildersArg :: MonadObelisk m => m String
27getNixBuildersArg :: MonadObelisk m => m String
@@ -35,7 +35,7 @@ getNixBuildersArg = do
35
35
36checkForNixDarwin :: MonadObelisk m => String -> m ()
36checkForNixDarwin :: MonadObelisk m => String -> m ()
37checkForNixDarwin sshIdFile = do
37checkForNixDarwin sshIdFile = do
38− (exitCode, _, _) <- readCreateProcessWithExitCode $ proc whichPath ["darwin-rebuild"]
38+ (exitCode, _, _) <- readCreateProcessWithExitCode $ proc "which" ["darwin-rebuild"]
39 unless (exitCode == ExitSuccess) $ failWith $ T.intercalate "\n"
39 unless (exitCode == ExitSuccess) $ failWith $ T.intercalate "\n"
40 [ "Deployments from macOS require nix-darwin to be installed."
40 [ "Deployments from macOS require nix-darwin to be installed."
41 , "Follow the installation instructions here: https://github.com/LnL7/nix-darwin"
41 , "Follow the installation instructions here: https://github.com/LnL7/nix-darwin"
@@ -67,7 +67,7 @@ containerExists :: MonadObelisk m => FilePath -> m Bool
67containerExists stateDir = handle (\(_ :: IOError) -> failWith needDockerMsg) $ do
67containerExists stateDir = handle (\(_ :: IOError) -> failWith needDockerMsg) $ do
68 containerNames <- fmap (map T.strip . T.lines) $
68 containerNames <- fmap (map T.strip . T.lines) $
69 readProcessAndLogStderr Error $
69 readProcessAndLogStderr Error $
70− proc dockerPath ["container", "list", "--all", "--format", "{{.Names}}"]
70+ proc "docker" ["container", "list", "--all", "--format", "{{.Names}}"]
71 let exists = containerName `elem` containerNames
71 let exists = containerName `elem` containerNames
72 when exists $ testLinuxBuild stateDir
72 when exists $ testLinuxBuild stateDir
73 pure exists
73 pure exists
@@ -85,7 +85,7 @@ containerSshPort = 2222
85startContainer :: MonadObelisk m => m ()
85startContainer :: MonadObelisk m => m ()
86startContainer = withSpinner "Starting VM builder" $
86startContainer = withSpinner "Starting VM builder" $
87 callProcessAndLogOutput (Debug, Debug) $
87 callProcessAndLogOutput (Debug, Debug) $
88− proc dockerPath ["start", containerName]
88+ proc "docker" ["start", containerName]
89
89
90-- | Creates the Docker container; assumes it does not exist.
90-- | Creates the Docker container; assumes it does not exist.
91setupNixDocker :: MonadObelisk m => FilePath -> m ()
91setupNixDocker :: MonadObelisk m => FilePath -> m ()
@@ -98,12 +98,12 @@ setupNixDocker stateDir = withSpinner ("Creating Docker container named " <> con
98 callProcessAndLogOutput (Debug, Error) $
98 callProcessAndLogOutput (Debug, Error) $
99 proc rmPath ["-f", stateDir </> sshKeyFileName, stateDir </> sshKeyFileName <.> "pub"]
99 proc rmPath ["-f", stateDir </> sshKeyFileName, stateDir </> sshKeyFileName <.> "pub"]
100 callProcessAndLogOutput (Debug, Error) $
100 callProcessAndLogOutput (Debug, Error) $
101− proc sshKeygenPath ["-t", "ed25519", "-f", stateDir </> sshKeyFileName, "-P", ""]
101+ proc "ssh-keygen" ["-t", "ed25519", "-f", stateDir </> sshKeyFileName, "-P", ""]
102
102
103 -- Build the docker container (which uses the SSH keys in the 'ssh' folder)
103 -- Build the docker container (which uses the SSH keys in the 'ssh' folder)
104 containerId <- fmap T.strip $ readProcessAndLogStderr Error $
104 containerId <- fmap T.strip $ readProcessAndLogStderr Error $
105− proc dockerPath ["build", stateDir, "--quiet"]
105+ proc "docker" ["build", stateDir, "--quiet"]
106− callProcessAndLogOutput (Debug, Error) $ proc dockerPath
106+ callProcessAndLogOutput (Debug, Error) $ proc "docker"
107 [ "run"
107 [ "run"
108 , "--restart", "always"
108 , "--restart", "always"
109 , "--detach"
109 , "--detach"
@@ -155,7 +155,7 @@ testLinuxBuild :: MonadObelisk m => FilePath -> m ()
155testLinuxBuild stateDir
155testLinuxBuild stateDir
156 | System.Info.os == "linux" = failWith "Using the docker builder is not necessary on linux."
156 | System.Info.os == "linux" = failWith "Using the docker builder is not necessary on linux."
157 | otherwise = do
157 | otherwise = do
158− (exitCode, _stdout, stderr) <- readCreateProcessWithExitCode $ proc nixBuildExePath
158+ (exitCode, _stdout, stderr) <- readCreateProcessWithExitCode $ proc "nix-build"
159 [ "--no-out-link"
159 [ "--no-out-link"
160 , "-E", "(import <nixpkgs> { system = \"x86_64-linux\"; }).writeText \"test\" builtins.currentTime"
160 , "-E", "(import <nixpkgs> { system = \"x86_64-linux\"; }).writeText \"test\" builtins.currentTime"
161 , "--builders", nixBuildersArgString stateDir
161 , "--builders", nixBuildersArgString stateDir
lib/executable-config/default.nix+3−2
@@ -5,13 +5,14 @@
5{ lib
5{ lib
6, runCommand
6, runCommand
7, obeliskCleanSource
7, obeliskCleanSource
8+, staticDirName ? ""
8}:
9}:
9
10
10let
11let
11 injectConfig = config: assets: runCommand "inject-config" {} (''
12 injectConfig = config: assets: runCommand "inject-config" {} (''
12 set -x
13 set -x
13− mkdir -p $out
14+ mkdir -p "$out"/'${staticDirName}'
14− cp --no-preserve=mode -Lr "${assets}" $out/static
15+ cp --no-preserve=mode -Lr "${assets}"/. "$out"/'${staticDirName}'/
15 chmod +w "$out"
16 chmod +w "$out"
16 '' + lib.optionalString (!(builtins.isNull config)) ''
17 '' + lib.optionalString (!(builtins.isNull config)) ''
17 if ! mkdir $out/config; then
18 if ! mkdir $out/config; then
lib/executable-config/lookup/src/Obelisk/Configs.hs−2
@@ -22,7 +22,6 @@ import Control.Applicative (Alternative)
22import Control.Monad (MonadPlus)
22import Control.Monad (MonadPlus)
23import Control.Monad.Base (MonadBase)
23import Control.Monad.Base (MonadBase)
24import Control.Monad.Catch (MonadThrow)
24import Control.Monad.Catch (MonadThrow)
25−import Control.Monad.Fail (MonadFail)
26import Control.Monad.Fix (MonadFix)
25import Control.Monad.Fix (MonadFix)
27import Control.Monad.IO.Class (MonadIO)
26import Control.Monad.IO.Class (MonadIO)
28import Control.Monad.Morph (MFunctor)
27import Control.Monad.Morph (MFunctor)
@@ -88,7 +87,6 @@ newtype ConfigsT m a = ConfigsT { unConfigsT :: ReaderT (Map Text ByteString) m
88 , Monad
87 , Monad
89 , MonadPlus
88 , MonadPlus
90 , Alternative
89 , Alternative
91− , MonadFail
92 , MonadFix
90 , MonadFix
93 , MonadThrow
91 , MonadThrow
94 , MonadIO
92 , MonadIO
lib/route/src/Obelisk/Route.hs+2−3
@@ -170,7 +170,6 @@ import Data.Text (Text)
170import qualified Data.Text as T
170import qualified Data.Text as T
171import qualified Data.Text.Encoding as T
171import qualified Data.Text.Encoding as T
172import Data.Text.Lens (IsText, packed, unpacked)
172import Data.Text.Lens (IsText, packed, unpacked)
173−import Data.Type.Equality
174import Data.Universe
173import Data.Universe
175import Data.Universe.Some
174import Data.Universe.Some
176import Network.HTTP.Types.URI
175import Network.HTTP.Types.URI
@@ -1039,8 +1038,8 @@ someSumEncoder = Encoder $ pure $ EncoderImpl
1039
1038
1040data Void1 :: * -> * where {}
1039data Void1 :: * -> * where {}
1041
1040
1042−instance UniverseSome Void1 where
1041+instance Universe (Some Void1) where
1043− universeSome = []
1042+ universe = []
1044
1043
1045void1Encoder :: (Applicative check, MonadError Text parse) => Encoder check parse (Some Void1) a
1044void1Encoder :: (Applicative check, MonadError Text parse) => Encoder check parse (Some Void1) a
1046void1Encoder = Encoder $ pure $ EncoderImpl
1045void1Encoder = Encoder $ pure $ EncoderImpl
lib/run/src/Obelisk/Run.hs+3−3
@@ -144,9 +144,9 @@ runWidget conf configs frontend validFullEncoder = do
144
144
145 cert <- X509.newX509 >>= X509Request.makeX509FromReq certRequest
145 cert <- X509.newX509 >>= X509Request.makeX509FromReq certRequest
146 _ <- X509.setPublicKey cert privateKey
146 _ <- X509.setPublicKey cert privateKey
147− timenow <- getCurrentTime
147+ now <- getCurrentTime
148− _ <- X509.setNotBefore cert $ addUTCTime (-1) timenow
148+ _ <- X509.setNotBefore cert $ addUTCTime (-1) now
149− _ <- X509.setNotAfter cert $ addUTCTime (365 * 24 * 60 * 60) timenow
149+ _ <- X509.setNotAfter cert $ addUTCTime (365 * 24 * 60 * 60) now
150 _ <- X509.signX509 cert privateKey Nothing
150 _ <- X509.signX509 cert privateKey Nothing
151
151
152 certByteString <- BSUTF8.fromString <$> PEM.writeX509 cert
152 certByteString <- BSUTF8.fromString <$> PEM.writeX509 cert
lib/snap-extras/src/Obelisk/Snap/Extras.hs+1−1
@@ -17,7 +17,7 @@ import System.Directory
17-- | Set response header for "permanent" caching
17-- | Set response header for "permanent" caching
18cachePermanently :: MonadSnap m => m ()
18cachePermanently :: MonadSnap m => m ()
19cachePermanently = do
19cachePermanently = do
20− modifyResponse $ setHeader "Cache-Control" "public, max-age=315360000, immutable"
20+ modifyResponse $ setHeader "Cache-Control" "public, max-age=315360000"
21 modifyResponse $ setHeader "Expires" "Tue, 01 Feb 2050 00:00:00 GMT" --TODO: This should be set to "approximately one year from the time the response is sent"
21 modifyResponse $ setHeader "Expires" "Tue, 01 Feb 2050 00:00:00 GMT" --TODO: This should be set to "approximately one year from the time the response is sent"
22
22
23-- | Set response header to not cache
23-- | Set response header to not cache
skeleton/backend/backend.cabal+2−2
@@ -14,12 +14,12 @@ library
14 , obelisk-route
14 , obelisk-route
15 exposed-modules:
15 exposed-modules:
16 Backend
16 Backend
17− ghc-options: -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates -O -fno-show-valid-hole-fits
17+ ghc-options: -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates -O
18
18
19executable backend
19executable backend
20 main-is: main.hs
20 main-is: main.hs
21 hs-source-dirs: src-bin
21 hs-source-dirs: src-bin
22− ghc-options: -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates -O -threaded -fno-show-valid-hole-fits
22+ ghc-options: -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates -O -threaded
23 if impl(ghcjs)
23 if impl(ghcjs)
24 buildable: False
24 buildable: False
25 build-depends: base
25 build-depends: base
skeleton/cabal.project−1
@@ -1,3 +1,2 @@
1optional-packages:
1optional-packages:
2 *
2 *
3−write-ghc-environment-files: never
skeleton/common/common.cabal+1−1
@@ -12,4 +12,4 @@ library
12 exposed-modules:
12 exposed-modules:
13 Common.Api
13 Common.Api
14 Common.Route
14 Common.Route
15− ghc-options: -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates -O -fno-show-valid-hole-fits
15+ ghc-options: -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates -O
skeleton/default.nix+3−10
@@ -1,17 +1,10 @@
1−{ system ? builtins.currentSystem
2−, obelisk ? import ./.obelisk/impl {
1+{ obelisk ? import ./.obelisk/impl {
3− inherit system;
2+ system = builtins.currentSystem;
4− iosSdkVersion = "13.2";
3+ iosSdkVersion = "10.2";
5−
6 # You must accept the Android Software Development Kit License Agreement at
4 # You must accept the Android Software Development Kit License Agreement at
7 # https://developer.android.com/studio/terms in order to build Android apps.
5 # https://developer.android.com/studio/terms in order to build Android apps.
8 # Uncomment and set this to `true` to indicate your acceptance:
6 # Uncomment and set this to `true` to indicate your acceptance:
9 # config.android_sdk.accept_license = false;
7 # config.android_sdk.accept_license = false;
10−
11− # In order to use Let's Encrypt for HTTPS deployments you must accept
12− # their terms of service at https://letsencrypt.org/repository/.
13− # Uncomment and set this to `true` to indicate your acceptance:
14− # terms.security.acme.acceptTerms = false;
15 }
8 }
16}:
9}:
17with obelisk;
10with obelisk;
skeleton/frontend/frontend.cabal+2−2
@@ -16,7 +16,7 @@ library
16 , text
16 , text
17 exposed-modules:
17 exposed-modules:
18 Frontend
18 Frontend
19− ghc-options: -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates -O -fno-show-valid-hole-fits
19+ ghc-options: -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates -O
20
20
21executable frontend
21executable frontend
22 main-is: main.hs
22 main-is: main.hs
@@ -28,7 +28,7 @@ executable frontend
28 , reflex-dom
28 , reflex-dom
29 , obelisk-generated-static
29 , obelisk-generated-static
30 , frontend
30 , frontend
31− ghc-options: -threaded -O -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates -fno-show-valid-hole-fits
31+ ghc-options: -threaded -O -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates
32 if impl(ghcjs)
32 if impl(ghcjs)
33 ghc-options: -dedupe
33 ghc-options: -dedupe
34 cpp-options: -DGHCJS_BROWSER
34 cpp-options: -DGHCJS_BROWSER