on master help sign in
Merge branch 'qa' into use-posix-escape
commit28cc8927e9e74eee961c6515217dd1bfd6e2819b authorElliot Cameron <eacameron@gmail.com> dateFri 3 Jan 2020, 11:04 (7 years ago) parent9aebf990665ab2
23 files changed+412−238
.gitignore+1 ChangeLog.md+11−11 default.nix+2−2 lib/cliapp/obelisk-cliapp.cabal+1 lib/cliapp/src/Obelisk/CliApp.hs+13−6 lib/cliapp/src/Obelisk/CliApp/Demo.hs−1 lib/cliapp/src/Obelisk/CliApp/Process.hs+81−34 lib/command/obelisk-command.cabal+1−1 lib/command/src/Obelisk/Command.hs+28−13 lib/command/src/Obelisk/Command/Deploy.hs+10−11 lib/command/src/Obelisk/Command/Nix.hs+1−2 lib/command/src/Obelisk/Command/Project.hs+6−11 lib/command/src/Obelisk/Command/Run.hs+5−4 lib/command/src/Obelisk/Command/Thunk.hs+177−99 lib/command/src/Obelisk/Command/Utils.hs+22−13 lib/command/src/Obelisk/Command/VmBuilder.hs−1 lib/run/src/Obelisk/Run.hs+1−1 lib/selftest/src/Obelisk/SelfTest.hs+38−22 skeleton/.gitignore+1 skeleton/common/common.cabal−3 skeleton/common/src/Common/Route.hs+1 skeleton/frontend/src/Frontend.hs+9−3 skeleton/static/main.css+3
.gitignore+1
@@ -2,6 +2,7 @@
22**/.cabal-sandbox
33**/cabal.sandbox.config
44result
5+result-*
56*.o
67*.hi
78TAGS
ChangeLog.md+11−11
@@ -2,23 +2,23 @@
22
33This 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, and the date should reflect that release.
44
5−## Unreleased
6−
7−* Generalised pathSegmentEncoder, added pathFieldEncoder.
8−* Added some Prisms to the encoder library for manipulating DSums (perhaps they should get moved to dependent-sum before release?)
9−* Add "ob doc" command, which lists paths to haddock documentation for specified packages
10−* Bump reflex-platform so that obelisk now uses GHC 8.6.5 and the nixos-19.03 nixpkgs set
11−* Add support in obelisk-route for single parameters in URL paths
12−* Bump reflex-platform so that obelisk now uses reflex-dom 0.5.2.0
5+## v0.3.0.0 - 2019-12-20
6+
7+* Generalised `pathSegmentEncoder`, added `pathFieldEncoder`.
8+* Added some `Prism`s to the encoder library for manipulating `DSum`s.
9+* Add `ob doc` command, which lists paths to haddock documentation for specified packages.
10+* Bump reflex-platform so that obelisk now uses GHC 8.6.5 and the nixos-19.03 nixpkgs set.
11+* Add support in `obelisk-route` for single parameters in URL paths.
12+* Bump reflex-platform so that obelisk now uses reflex-dom 0.5.2.0.
1313* Use a `--pure` nix shell in `ob run` for parity with `ob repl` and more resilience against "works on my machine".
1414* Pin usages of `<nixpkgs>` in obelisk thunks, etc. to the nixpkgs used by the project's obelisk impl.
1515* Backport ACMEv2 support in obelisk server to regain LetsEncrypt account creation.
16−* Enable https in `ob run`
17−* `ob run` now handles ghci errors better, and includes a custom ghcid
16+* Enable HTTPS in `ob run`.
17+* `ob run` now handles `ghci` errors better, and includes a custom `ghcid`
1818 version. As a result, you no longer need to have ghcid installed to
1919 use `ob run`, as we provide one for you.
2020* `ob` commands now complain less on systems with umasks other than `0022`.
21−* Ignore package environment files in `ob run` and `ob repl`
21+* Ignore package environment files in `ob run` and `ob repl`.
2222* Add `Obelisk.Route.Frontend.routeLinkDynAttr`.
2323
2424## v0.2.0.0 - 2019-8-17
default.nix+2−2
@@ -125,10 +125,10 @@ in rec {
125125 set -euo pipefail
126126
127127 PATH="${command}/bin:$PATH"
128− export OBELISK_IMPL="${hackGet ./.}"
128+ export OBELISK_IMPL="$(mktemp -d)"
129+ git clone file://${pathGit} $OBELISK_IMPL
129130 "${ghcObelisk.obelisk-selftest}/bin/obelisk-selftest" +RTS -N -RTS "$@"
130131 '';
131− #TODO: Why can't I build ./skeleton directly as a derivation? `nix-build -E ./.` doesn't work
132132 skeleton = pkgs.runCommand "skeleton" {
133133 dir = builtins.filterSource (path: type: builtins.trace path (baseNameOf path != ".obelisk")) ./skeleton;
134134 } ''
lib/cliapp/obelisk-cliapp.cabal+1
@@ -10,6 +10,7 @@ library
1010 ansi-terminal
1111 , base
1212 , bytestring
13+ , containers
1314 , exceptions
1415 , io-streams
1516 , lens
lib/cliapp/src/Obelisk/CliApp.hs+13−6
@@ -33,16 +33,23 @@ module Obelisk.CliApp
3333 , Severity (..)
3434
3535 -- .Process
36− , ProcessFailure (..)
3736 , AsProcessFailure (..)
38− , readProcessAndLogStderr
39− , readProcessAndLogOutput
40− , readCreateProcessWithExitCode
37+ , ProcessFailure (..)
38+ , ProcessSpec (..)
39+ , callCommand
40+ , callProcess
4141 , callProcessAndLogOutput
4242 , createProcess_
43− , callProcess
44− , callCommand
43+ , overCreateProcess
44+ , proc
45+ , readCreateProcessWithExitCode
46+ , readProcessAndLogOutput
47+ , readProcessAndLogStderr
4548 , reconstructCommand
49+ , setCwd
50+ , setDelegateCtlc
51+ , setEnvOverride
52+ , shell
4653 ) where
4754
4855import Control.Monad.Log (Severity (..))
lib/cliapp/src/Obelisk/CliApp/Demo.hs−1
@@ -8,7 +8,6 @@ import Control.Monad.Fail
88import Control.Monad.IO.Class
99import Data.Semigroup ((<>))
1010import qualified Data.Text as T
11−import System.Process (proc)
1211
1312import Control.Monad.Catch (MonadMask)
1413
lib/cliapp/src/Obelisk/CliApp/Process.hs+81−34
@@ -9,17 +9,24 @@
99-- | An extension of `System.Process` that integrates with logging (`Obelisk.CLI.Logging`)
1010-- and is thus spinner friendly.
1111module Obelisk.CliApp.Process
12− ( ProcessFailure (..)
13− , AsProcessFailure (..)
14− , readProcessAndLogStderr
15− , readProcessAndLogOutput
16− , readCreateProcessWithExitCode
12+ ( AsProcessFailure (..)
13+ , ProcessFailure (..)
14+ , ProcessSpec (..)
15+ , callCommand
16+ , callProcess
1717 , callProcessAndLogOutput
1818 , createProcess
1919 , createProcess_
20− , callProcess
21− , callCommand
20+ , overCreateProcess
21+ , proc
22+ , readCreateProcessWithExitCode
23+ , readProcessAndLogOutput
24+ , readProcessAndLogStderr
2225 , reconstructCommand
26+ , setCwd
27+ , setDelegateCtlc
28+ , setEnvOverride
29+ , shell
2330 ) where
2431
2532import Control.Monad ((<=<), join, void)
@@ -31,18 +38,20 @@ import qualified Data.ByteString as BS
3138import qualified Data.ByteString.Char8 as BSC
3239import qualified Data.ByteString.UTF8 as BSU
3340import Data.Function (fix)
41+import Data.Map (Map)
42+import qualified Data.Map as Map
3443import Data.Monoid ((<>))
3544import Data.Text (Text)
3645import qualified Data.Text as T
3746import qualified Data.Text.Encoding as T
3847import Data.Text.Encoding.Error (lenientDecode)
48+import System.Environment (getEnvironment)
3949import System.Exit (ExitCode (..))
4050import System.IO (Handle)
4151import System.IO.Streams (InputStream, handleToInputStream)
4252import qualified System.IO.Streams as Streams
4353import System.IO.Streams.Concurrent (concurrentMerge)
44−import System.Process (CreateProcess, ProcessHandle, StdStream (CreatePipe), cmdspec, std_err, std_out,
45− waitForProcess)
54+import System.Process (CreateProcess, ProcessHandle, StdStream (CreatePipe), std_err, std_out, waitForProcess)
4655import qualified System.Process as Process
4756import Text.ShellEscape (bytes, bash)
4857
@@ -50,6 +59,30 @@ import Control.Monad.Log (Severity (..))
5059import Obelisk.CliApp.Logging (putLog, putLogRaw)
5160import Obelisk.CliApp.Types (CliLog, CliThrow)
5261
62+data ProcessSpec = ProcessSpec
63+ { _processSpec_createProcess :: !CreateProcess
64+ , _processSpec_overrideEnv :: !(Maybe (Map String String -> Map String String))
65+ }
66+
67+proc :: FilePath -> [String] -> ProcessSpec
68+proc cmd args = ProcessSpec (Process.proc cmd args) Nothing
69+
70+shell :: String -> ProcessSpec
71+shell cmd = ProcessSpec (Process.shell cmd) Nothing
72+
73+setEnvOverride :: (Map String String -> Map String String) -> ProcessSpec -> ProcessSpec
74+setEnvOverride f p = p { _processSpec_overrideEnv = Just f }
75+
76+overCreateProcess :: (CreateProcess -> CreateProcess) -> ProcessSpec -> ProcessSpec
77+overCreateProcess f (ProcessSpec p x) = ProcessSpec (f p) x
78+
79+setDelegateCtlc :: Bool -> ProcessSpec -> ProcessSpec
80+setDelegateCtlc b = overCreateProcess (\p -> p { Process.delegate_ctlc = b })
81+
82+setCwd :: Maybe FilePath -> ProcessSpec -> ProcessSpec
83+setCwd fp = overCreateProcess (\p -> p { Process.cwd = fp })
84+
85+
5386-- TODO put back in `Obelisk.CliApp.Process` and use prisms for extensible exceptions
5487data ProcessFailure = ProcessFailure Process.CmdSpec Int -- exit code
5588 deriving Show
@@ -64,7 +97,7 @@ instance AsProcessFailure ProcessFailure where
6497
6598readProcessAndLogStderr
6699 :: (MonadIO m, CliLog m, CliThrow e m, AsProcessFailure e, MonadFail m)
67− => Severity -> CreateProcess -> m Text
100+ => Severity -> ProcessSpec -> m Text
68101readProcessAndLogStderr sev process = do
69102 (out, _err) <- withProcess process $ \_out err -> do
70103 streamToLog =<< liftIO (streamHandle sev err)
@@ -72,9 +105,10 @@ readProcessAndLogStderr sev process = do
72105
73106readCreateProcessWithExitCode
74107 :: (MonadIO m, CliLog m, CliThrow e m, AsProcessFailure e)
75− => CreateProcess -> m (ExitCode, String, String)
76−readCreateProcessWithExitCode process = do
77− putLog Debug $ "Creating process: " <> reconstructCommand (cmdspec process)
108+ => ProcessSpec -> m (ExitCode, String, String)
109+readCreateProcessWithExitCode procSpec = do
110+ process <- mkCreateProcess procSpec
111+ putLog Debug $ "Creating process: " <> reconstructProcSpec procSpec
78112 liftIO $ Process.readCreateProcessWithExitCode process ""
79113
80114-- | Like `System.Process.readProcess` but logs the combined output (stdout and stderr)
@@ -86,10 +120,10 @@ readCreateProcessWithExitCode process = do
86120-- `callProcessAndLogOutput (Debug, Debug)`.
87121readProcessAndLogOutput
88122 :: (MonadIO m, CliLog m, CliThrow e m, AsProcessFailure e, MonadFail m)
89− => (Severity, Severity) -> CreateProcess -> m Text
123+ => (Severity, Severity) -> ProcessSpec -> m Text
90124readProcessAndLogOutput (sev_out, sev_err) process = do
91− (_, Just out, Just err, p) <- createProcess $ process
92− { std_out = CreatePipe , std_err = CreatePipe }
125+ (_, Just out, Just err, p) <- createProcess $ overCreateProcess
126+ (\p -> p { std_out = CreatePipe , std_err = CreatePipe }) process
93127
94128 -- TODO interleave stdout and stderr in log correctly
95129 streamToLog =<< liftIO (streamHandle sev_err err)
@@ -98,9 +132,9 @@ readProcessAndLogOutput (sev_out, sev_err) process = do
98132
99133 liftIO (waitForProcess p) >>= \case
100134 ExitSuccess -> pure outText
101− ExitFailure code -> throwError $ review asProcessFailure $ ProcessFailure (cmdspec process) code
135+ ExitFailure code -> throwError $ review asProcessFailure $ ProcessFailure (Process.cmdspec $ _processSpec_createProcess process) code
102136
103−-- | Like `System.Process.callProcess` but logs the combined output (stdout and stderr)
137+-- | Like 'System.Process.callProcess' but logs the combined output (stdout and stderr)
104138-- with the corresponding severity.
105139--
106140-- Usually this function is called as `callProcessAndLogOutput (Debug, Error)`. However
@@ -108,9 +142,8 @@ readProcessAndLogOutput (sev_out, sev_err) process = do
108142-- which case it is advisable to call it with a non-Error severity for stderr, like
109143-- `callProcessAndLogOutput (Debug, Debug)`.
110144callProcessAndLogOutput
111−
112145 :: (MonadIO m, CliLog m, CliThrow e m, AsProcessFailure e, MonadFail m)
113− => (Severity, Severity) -> CreateProcess -> m ()
146+ => (Severity, Severity) -> ProcessSpec -> m ()
114147callProcessAndLogOutput (sev_out, sev_err) process =
115148 void $ withProcess process $ \out err -> do
116149 stream <- liftIO $ join $ combineStream
@@ -120,21 +153,31 @@ callProcessAndLogOutput (sev_out, sev_err) process =
120153 where
121154 combineStream s1 s2 = concurrentMerge [s1, s2]
122155
123−-- | Like `System.Process.createProcess` but also logs (debug) the process being run
156+-- | Like 'System.Process.createProcess' but also logs (debug) the process being run
124157createProcess
125158 :: (MonadIO m, CliLog m)
126− => CreateProcess -> m (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
127−createProcess p = do
128− putLog Debug $ "Creating process: " <> reconstructCommand (cmdspec p)
159+ => ProcessSpec -> m (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
160+createProcess procSpec = do
161+ p <- mkCreateProcess procSpec
162+ putLog Debug $ "Creating process: " <> reconstructProcSpec procSpec
129163 liftIO $ Process.createProcess p
130164
131165-- | Like `System.Process.createProcess_` but also logs (debug) the process being run
132166createProcess_
133167 :: (MonadIO m, CliLog m)
134− => String -> CreateProcess -> m (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
135−createProcess_ name p = do
136− putLog Debug $ "Creating process " <> T.pack name <> ": " <> reconstructCommand (cmdspec p)
137− liftIO $ Process.createProcess p
168+ => String -> ProcessSpec -> m (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
169+createProcess_ name procSpec = do
170+ p <- mkCreateProcess procSpec
171+ putLog Debug $ "Creating process " <> T.pack name <> ": " <> reconstructProcSpec procSpec
172+ liftIO $ Process.createProcess_ name p
173+
174+mkCreateProcess :: MonadIO m => ProcessSpec -> m Process.CreateProcess
175+mkCreateProcess (ProcessSpec p override') = do
176+ case override' of
177+ Nothing -> pure p
178+ Just override -> do
179+ procEnv <- Map.fromList <$> maybe (liftIO getEnvironment) pure (Process.env p)
180+ pure $ p { Process.env = Just $ Map.toAscList (override procEnv) }
138181
139182-- | Like `System.Process.callProcess` but also logs (debug) the process being run
140183callProcess
@@ -154,17 +197,17 @@ callCommand cmd = do
154197
155198withProcess
156199 :: (MonadIO m, CliLog m, CliThrow e m, AsProcessFailure e, MonadFail m)
157− => CreateProcess -> (Handle -> Handle -> m ()) -> m (Handle, Handle)
200+ => ProcessSpec -> (Handle -> Handle -> m ()) -> m (Handle, Handle)
158201withProcess process f = do -- TODO: Use bracket.
159202 -- FIXME: Using `withCreateProcess` here leads to something operating illegally on closed handles.
160− (_, Just out, Just err, p) <- createProcess $ process
161− { std_out = CreatePipe , std_err = CreatePipe }
203+ (_, Just out, Just err, p) <- createProcess $ overCreateProcess
204+ (\x -> x { std_out = CreatePipe , std_err = CreatePipe }) process
162205
163206 f out err -- Pass the handles to the passed function
164207
165208 liftIO (waitForProcess p) >>= \case
166209 ExitSuccess -> return (out, err)
167− ExitFailure code -> throwError $ review asProcessFailure $ ProcessFailure (cmdspec process) code
210+ ExitFailure code -> throwError $ review asProcessFailure $ ProcessFailure (Process.cmdspec $ _processSpec_createProcess process) code
168211
169212-- Create an input stream from the file handle, associating each item with the given severity.
170213streamHandle :: Severity -> Handle -> IO (InputStream (Severity, BSC.ByteString))
@@ -181,8 +224,12 @@ streamToLog stream = fix $ \loop -> do
181224
182225-- | Pretty print a 'CmdSpec'
183226reconstructCommand :: Process.CmdSpec -> Text
184−reconstructCommand (Process.ShellCommand str) = T.pack str
185−reconstructCommand (Process.RawCommand c as) = processToShellString c as
227+reconstructCommand p = case p of
228+ Process.ShellCommand str -> T.pack str
229+ Process.RawCommand c as -> processToShellString c as
186230 where
187231 processToShellString cmd args = T.pack $ unwords $
188232 map (BSU.toString . bytes . bash . BSU.fromString) (cmd : args)
233+
234+reconstructProcSpec :: ProcessSpec -> Text
235+reconstructProcSpec = reconstructCommand . Process.cmdspec . _processSpec_createProcess
lib/command/obelisk-command.cabal+1−1
@@ -1,5 +1,5 @@
11name: obelisk-command
2−version: 0.1
2+version: 0.3
33cabal-version: >= 1.8
44build-type: Simple
55
lib/command/src/Obelisk/Command.hs+28−13
@@ -14,6 +14,7 @@ import Data.Bool (bool)
1414import qualified Data.ByteString.Base16 as Base16
1515import qualified Data.ByteString.Lazy as LBS
1616import qualified Data.ByteString.UTF8 as BSU
17+import Data.Foldable (for_)
1718import Data.List
1819import Data.Maybe (catMaybes)
1920import qualified Data.Text as T
@@ -222,20 +223,35 @@ thunkDirectoryParser = fmap (dropTrailingPathSeparator . normalise) . strArgumen
222223 , help "Path to directory containing thunk data"
223224 ]
224225
226+thunkConfig :: Parser ThunkConfig
227+thunkConfig = ThunkConfig
228+ <$>
229+ ( flag' (Just True) (long "private" <> help "Mark thunks as pointing to a private repository")
230+ <|> flag' (Just False) (long "public" <> help "Mark thunks as pointing to a public repository")
231+ <|> pure Nothing
232+ )
233+
234+thunkUpdateConfig :: Parser ThunkUpdateConfig
235+thunkUpdateConfig = ThunkUpdateConfig
236+ <$> optional (strOption (long "branch" <> metavar "BRANCH" <> help "Use the given branch when looking for the latest revision"))
237+ <*> thunkConfig
238+
239+thunkPackConfig :: Parser ThunkPackConfig
240+thunkPackConfig = ThunkPackConfig
241+ <$> switch (long "force" <> short 'f' <> help "Force packing thunks even if there are branches not pushed upstream, uncommitted changes, stashes. This will cause changes that have not been pushed upstream to be lost; use with care.")
242+ <*> thunkConfig
243+
225244data ThunkCommand
226− = ThunkCommand_Update [FilePath] (Maybe String)
245+ = ThunkCommand_Update [FilePath] ThunkUpdateConfig
227246 | ThunkCommand_Unpack [FilePath]
228− | ThunkCommand_Pack [FilePath] Bool
247+ | ThunkCommand_Pack [FilePath] ThunkPackConfig
229248 deriving Show
230249
231−forceFlag :: Parser Bool
232−forceFlag = switch $ long "force" <> short 'f' <> help "Force packing thunks even if there are branches not pushed upstream, uncommitted changes, stashes. This will cause changes that have not been pushed upstream to be lost; use with care."
233−
234250thunkCommand :: Parser ThunkCommand
235251thunkCommand = hsubparser $ mconcat
236− [ command "update" $ info (ThunkCommand_Update <$> some thunkDirectoryParser <*> optional (strOption (long "branch" <> metavar "BRANCH"))) $ progDesc "Update thunk to latest revision available"
252+ [ command "update" $ info (ThunkCommand_Update <$> some thunkDirectoryParser <*> thunkUpdateConfig) $ progDesc "Update thunk to latest revision available"
237253 , command "unpack" $ info (ThunkCommand_Unpack <$> some thunkDirectoryParser) $ progDesc "Unpack thunk into git checkout of revision it points to"
238− , command "pack" $ info (ThunkCommand_Pack <$> some thunkDirectoryParser <*> forceFlag) $ progDesc "Pack git checkout into thunk that points at the current branch's upstream"
254+ , command "pack" $ info (ThunkCommand_Pack <$> some thunkDirectoryParser <*> thunkPackConfig) $ progDesc "Pack git checkout into thunk that points at the current branch's upstream"
239255 ]
240256
241257data ShellOpts
@@ -376,17 +392,16 @@ ob = \case
376392 DeployCommand_Update -> deployUpdate "."
377393 DeployCommand_Test (platform, extraArgs) -> deployMobile platform extraArgs
378394 ObCommand_Run -> inNixShell' $ static run
379− -- inNixShell ($(mkClosure 'ghcidAction) ())
380395 ObCommand_Thunk tc -> case tc of
381− ThunkCommand_Update thunks mBranch -> mapM_ ((flip updateThunkToLatest) mBranch) thunks
382− ThunkCommand_Unpack thunks -> mapM_ unpackThunk thunks
383− ThunkCommand_Pack thunks force -> forM_ thunks (packThunk force)
396+ ThunkCommand_Update thunks config -> for_ thunks (updateThunkToLatest config)
397+ ThunkCommand_Unpack thunks -> for_ thunks unpackThunk
398+ ThunkCommand_Pack thunks config -> for_ thunks (packThunk config)
384399 ObCommand_Repl -> runRepl
385400 ObCommand_Watch -> inNixShell' $ static runWatch
386401 ObCommand_Shell so -> withProjectRoot "." $ \root ->
387402 projectProc root False (_shellOpts_shell so) (pure . bash . BSU.fromString <$> _shellOpts_command so)
388− ObCommand_Doc shell pkgs -> withProjectRoot "." $ \root ->
389− projectShell root False shell (Just $ haddockCommand pkgs)
403+ ObCommand_Doc shell' pkgs -> withProjectRoot "." $ \root ->
404+ projectShell root False shell' (Just $ haddockCommand pkgs)
390405 ObCommand_Internal icmd -> case icmd of
391406 ObInternal_RunStaticIO k -> liftIO (unsafeLookupStaticPtr @(ObeliskT IO ()) k) >>= \case
392407 Nothing -> failWith $ "ObInternal_RunStaticIO: no such StaticKey: " <> T.pack (show k)
lib/command/src/Obelisk/Command/Deploy.hs+10−11
@@ -25,17 +25,17 @@ import Nix.Pretty
2525import Nix.String (principledMakeNixStringWithoutContext)
2626import Nix.Value
2727import System.Directory
28−import System.Environment (getEnvironment)
2928import System.FilePath
3029import System.IO
3130import System.Posix.Files
32−import System.Process (delegate_ctlc, env, proc, cwd)
3331import Text.URI (URI)
3432import qualified Text.URI as URI
3533import Text.URI.Lens
3634
3735import Obelisk.App (MonadObelisk)
38−import Obelisk.CliApp (Severity (..), callProcessAndLogOutput, failWith, putLog, withSpinner)
36+import Obelisk.CliApp (
37+ Severity (..), callProcessAndLogOutput, failWith, proc, putLog,
38+ setCwd, setDelegateCtlc, setEnvOverride, withSpinner)
3939import Obelisk.Command.Nix
4040import Obelisk.Command.Project
4141import Obelisk.Command.Thunk
@@ -82,7 +82,7 @@ deployInit thunkPtr deployDir sshKeyPath hostnames route adminEmail enableHttps
8282 writeDeployConfig deployDir "backend_hosts" $ unlines hostnames
8383 writeDeployConfig deployDir "enable_https" $ show enableHttps
8484 writeDeployConfig deployDir "admin_email" adminEmail
85− writeDeployConfig deployDir ("config" </> "common" </> "route") $ route
85+ writeDeployConfig deployDir ("config" </> "common" </> "route") route
8686 withSpinner "Creating source thunk (./src)" $ liftIO $ do
8787 createThunk (deployDir </> "src") thunkPtr
8888 setupObeliskImpl deployDir
@@ -107,7 +107,7 @@ deployPush deployPath getNixBuilders = do
107107 Right (ThunkData_Packed ptr) -> return ptr
108108 Right (ThunkData_Checkout _) -> do
109109 checkGitCleanStatus srcPath True >>= \case
110− True -> packThunk False srcPath
110+ True -> packThunk (ThunkPackConfig False (ThunkConfig Nothing)) srcPath
111111 False -> failWith $ T.pack $ "ob deploy push: ensure " <> srcPath <> " has no pending changes and latest is pushed upstream."
112112 Left err -> failWith $ "ob deploy push: couldn't read src thunk: " <> T.pack (show err)
113113 let version = show . _thunkRev_commit $ _thunkPtr_rev thunkPtr
@@ -157,7 +157,7 @@ deployPush deployPath getNixBuilders = do
157157 ]
158158 isClean <- checkGitCleanStatus deployPath True
159159 when (not isClean) $ do
160− withSpinner "Commiting changes to Git" $ do
160+ withSpinner "Committing changes to Git" $ do
161161 callProcessAndLogOutput (Debug, Error) $ proc "git"
162162 ["-C", deployPath, "add", "."]
163163 callProcessAndLogOutput (Debug, Error) $ proc "git"
@@ -165,12 +165,11 @@ deployPush deployPath getNixBuilders = do
165165 putLog Notice $ "Deployed => " <> T.pack route
166166 where
167167 callProcess' envMap cmd args = do
168− processEnv <- Map.toList . (envMap <>) . Map.fromList <$> liftIO getEnvironment
169− let p = (proc cmd args) { delegate_ctlc = True, env = Just processEnv }
168+ let p = setEnvOverride (envMap <>) $ setDelegateCtlc True $ proc cmd args
170169 callProcessAndLogOutput (Notice, Notice) p
171170
172171deployUpdate :: MonadObelisk m => FilePath -> m ()
173−deployUpdate deployPath = updateThunkToLatest (deployPath </> "src") Nothing
172+deployUpdate deployPath = updateThunkToLatest (ThunkUpdateConfig Nothing (ThunkConfig Nothing)) (deployPath </> "src")
174173
175174keytoolToAndroidConfig :: KeytoolConfig -> HM.HashMap Text (NValue t Identity m)
176175keytoolToAndroidConfig conf =
@@ -219,7 +218,7 @@ deployMobile platform mobileArgs = withProjectRoot "." $ \root -> do
219218 , _keytoolConfig_storepass = keyStorePassword
220219 , _keytoolConfig_keypass = keyStorePassword
221220 }
222− createKeystore root $ keyToolConf
221+ createKeystore root keyToolConf
223222 liftIO $ BSL.writeFile keytoolConfPath $ encode keyToolConf
224223 checkKeytoolConfExist <- liftIO $ doesFileExist keytoolConfPath
225224 unless checkKeytoolConfExist $ failWith "Missing android KeytoolConfig"
@@ -275,7 +274,7 @@ instance ToJSON KeytoolConfig
275274createKeystore :: MonadObelisk m => FilePath -> KeytoolConfig -> m ()
276275createKeystore root config = do
277276 let expr = "with (import " <> toImplDir root <> ").reflex-platform.nixpkgs; pkgs.mkShell { buildInputs = [ pkgs.jdk ]; }"
278− callProcessAndLogOutput (Notice,Notice) $ (proc "nix-shell" ["default.nix", "-E" , expr, "--run" , keytoolCmd]) { cwd = Just root }
277+ callProcessAndLogOutput (Notice,Notice) $ setCwd (Just root) $ proc "nix-shell" ["default.nix", "-E" , expr, "--run" , keytoolCmd]
279278 where
280279 keytoolCmd = processToShellString "keytool"
281280 [ "-genkeypair", "-noprompt"
lib/command/src/Obelisk/Command/Nix.hs+1−2
@@ -37,7 +37,6 @@ import Data.List (intercalate)
3737import Data.Maybe
3838import Data.Monoid ((<>))
3939import qualified Data.Text as T
40−import System.Process (proc)
4140
4241import Obelisk.App (MonadObelisk)
4342import Obelisk.CliApp
@@ -160,7 +159,7 @@ instance Default NixCmd where
160159
161160nixCmd :: MonadObelisk m => NixCmd -> m FilePath
162161nixCmd cmdCfg = withSpinner' ("Running " <> cmd <> desc) (Just $ const $ "Built " <> desc) $ do
163− output <- readProcessAndLogStderr Debug $ proc (T.unpack cmd) $ options
162+ output <- readProcessAndLogStderr Debug $ proc (T.unpack cmd) options
164163 -- Remove final newline that Nix appends
165164 Just (outPath, '\n') <- pure $ T.unsnoc output
166165 pure $ T.unpack outPath
lib/command/src/Obelisk/Command/Project.hs+6−11
@@ -33,9 +33,9 @@ import System.IO.Temp
3333import System.IO.Unsafe (unsafePerformIO)
3434import System.Posix (FileStatus, FileMode, CMode (..), UserID, deviceID, fileID, fileMode, fileOwner, getFileStatus, getRealUserID)
3535import System.Posix.Files
36−import System.Process (CreateProcess, cwd, proc, waitForProcess, delegate_ctlc)
37−import Text.ShellEscape (Bash, bytes)
36+import System.Process (waitForProcess)
3837import System.Which (staticWhich)
38+import Text.ShellEscape (Bash, bytes)
3939
4040import GitHub.Data.GitData (Branch)
4141import GitHub.Data.Name (Name)
@@ -57,6 +57,7 @@ obeliskSourceWithBranch branch = ThunkSource_GitHub $ GitHubSource
5757 { _gitHubSource_owner = "obsidiansystems"
5858 , _gitHubSource_repo = "obelisk"
5959 , _gitHubSource_branch = Just branch
60+ , _gitHubSource_private = False
6061 }
6162
6263data InitSource
@@ -172,7 +173,7 @@ findProjectRoot :: MonadObelisk m => FilePath -> m (Maybe FilePath)
172173findProjectRoot target = do
173174 myUid <- liftIO getRealUserID
174175 targetStat <- liftIO $ getFileStatus target
175− umask <- liftIO $ getUmask
176+ umask <- liftIO getUmask
176177 (result, _) <- liftIO $ runStateT (walkToProjectRoot target targetStat umask myUid) []
177178 return result
178179
@@ -259,9 +260,9 @@ projectProc root isPure shellName command =
259260projectShell :: MonadObelisk m => FilePath -> Bool -> String -> Maybe String -> m ()
260261projectShell root isPure shellName command = do
261262 let nixPath = $(staticWhich "nix")
262− nixpkgsPath <- fmap T.strip $ readProcessAndLogStderr Debug $ proc nixPath ["eval", "(import .obelisk/impl {}).nixpkgs.path"]
263+ nixpkgsPath <- fmap T.strip $ readProcessAndLogStderr Debug $ setCwd (Just root) $ proc nixPath ["eval", "(import .obelisk/impl {}).nixpkgs.path"]
263264 nixRemote <- liftIO $ lookupEnv "NIX_REMOTE"
264− (_, _, _, ph) <- createProcess_ "runNixShellAttr" $ setCtlc $ setCwd (Just root) $ proc "nix-shell" $
265+ (_, _, _, ph) <- createProcess_ "runNixShellAttr" $ setDelegateCtlc True $ setCwd (Just root) $ proc "nix-shell" $
265266 [ "default.nix"] <>
266267 ["--pure" | isPure] <>
267268 [ "-A"
@@ -276,9 +277,3 @@ projectShell root isPure shellName command = do
276277 c
277278 ]
278279 void $ liftIO $ waitForProcess ph
279−
280−setCtlc :: CreateProcess -> CreateProcess
281−setCtlc cfg = cfg { delegate_ctlc = True }
282−
283−setCwd :: Maybe FilePath -> CreateProcess -> CreateProcess
284−setCwd fp cfg = cfg { cwd = fp }
lib/command/src/Obelisk/Command/Run.hs+5−4
@@ -33,7 +33,6 @@ import Language.Haskell.Extension
3333import Network.Socket hiding (Debug)
3434import System.Directory
3535import System.FilePath
36−import System.Process (proc)
3736import System.IO.Temp (withSystemTempDirectory)
3837import System.Which (staticWhich)
3938import Text.ShellEscape (bash, bytes)
@@ -41,7 +40,7 @@ import Text.ShellEscape (bash, bytes)
4140import Obelisk.App (MonadObelisk, ObeliskT)
4241import Obelisk.CliApp
4342 ( CliT (..), HasCliConfig, Severity (..)
44− , callCommand, failWith, getCliConfig, putLog
43+ , callCommand, failWith, getCliConfig, putLog, proc
4544 , readProcessAndLogStderr, runCli)
4645import Obelisk.Command.Project (inProjectProc, withProjectRoot)
4746
@@ -134,7 +133,9 @@ parseCabalPackage dir = do
134133 return Nothing
135134 Right (DecodeResult hpackPackage _ _ _) -> do
136135 return $ Just $ renderPackage [] hpackPackage
137− else return Nothing
136+ else do
137+ putLog Error $ T.pack $ "Found neither cabal nor hpack file"
138+ return Nothing
138139
139140 fmap join $ forM mCabalContents $ \cabalContents -> do
140141 let (warnings, result) = runParseResult $ parseGenericPackageDescription $
@@ -168,7 +169,7 @@ withUTF8FileContentsM fp f = do
168169withGhciScript
169170 :: MonadObelisk m
170171 => [FilePath] -- ^ List of packages to load into ghci
171− -> (FilePath -> m ()) -- ^ Action to run with the path to generated temporory .ghci
172+ -> (FilePath -> m ()) -- ^ Action to run with the path to generated temporary .ghci
172173 -> m ()
173174withGhciScript pkgs f = do
174175 (pkgDirErrs, packageInfos) <- fmap partitionEithers $ forM pkgs $ \pkg -> do
lib/command/src/Obelisk/Command/Thunk.hs+177−99
@@ -8,25 +8,30 @@
88{-# LANGUAGE ScopedTypeVariables #-}
99{-# LANGUAGE TypeApplications #-}
1010module Obelisk.Command.Thunk
11− ( ThunkPtr (..)
11+ ( GitHubSource (..)
12+ , GitUri (..)
13+ , ReadThunkError (..)
14+ , ThunkConfig (..)
15+ , ThunkData (..)
16+ , ThunkPackConfig (..)
17+ , ThunkPtr (..)
1218 , ThunkRev (..)
1319 , ThunkSource (..)
14− , ThunkData (..)
15− , ReadThunkError (..)
16− , GitHubSource (..)
17− , getThunkGitBranch
18− , getLatestRev
19− , updateThunkToLatest
20+ , ThunkUpdateConfig (..)
2021 , createThunk
2122 , createThunkWithLatest
23+ , getLatestRev
24+ , getThunkGitBranch
25+ , getThunkPtr
26+ , gitUriToText
2227 , nixBuildAttrWithCache
2328 , nixBuildThunkAttrWithCache
24− , unpackThunk
2529 , packThunk
30+ , parseGitUri
2631 , readThunk
32+ , unpackThunk
2733 , updateThunk
28− , getThunkPtr
29− , parseGitUri
34+ , updateThunkToLatest
3035 , uriThunkPtr
3136 ) where
3237
@@ -68,8 +73,7 @@ import System.FilePath
6873import System.IO.Error
6974import System.IO.Temp
7075import System.Posix (getSymbolicLinkStatus, modificationTime)
71−import System.Process (proc)
72−import Text.URI
76+import qualified Text.URI as URI
7377
7478import Obelisk.App (MonadObelisk)
7579import Obelisk.CliApp
@@ -111,37 +115,63 @@ data GitHubSource = GitHubSource
111115 { _gitHubSource_owner :: Name Owner
112116 , _gitHubSource_repo :: Name Repo
113117 , _gitHubSource_branch :: Maybe (Name Branch)
118+ , _gitHubSource_private :: Bool
114119 }
115120 deriving (Show, Eq, Ord)
116121
122+newtype GitUri = GitUri URI.URI deriving (Eq, Ord, Show)
123+
124+gitUriToText :: GitUri -> Text
125+gitUriToText (GitUri uri)
126+ | (T.toLower . URI.unRText <$> URI.uriScheme uri) == Just "file"
127+ , Just (_, path) <- URI.uriPath uri
128+ = "/" <> T.intercalate "/" (map URI.unRText $ NonEmpty.toList path)
129+ | otherwise = URI.render uri
130+
117131data GitSource = GitSource
118− { _gitSource_url :: URI
132+ { _gitSource_url :: GitUri
119133 , _gitSource_branch :: Maybe (Name Branch)
120134 , _gitSource_fetchSubmodules :: Bool
135+ , _gitSource_private :: Bool
121136 }
122137 deriving (Show, Eq, Ord)
123138
139+newtype ThunkConfig = ThunkConfig
140+ { _thunkConfig_private :: Maybe Bool
141+ } deriving Show
142+
143+data ThunkUpdateConfig = ThunkUpdateConfig
144+ { _thunkUpdateConfig_branch :: Maybe String
145+ , _thunkUpdateConfig_config :: ThunkConfig
146+ } deriving Show
147+
148+data ThunkPackConfig = ThunkPackConfig
149+ { _thunkPackConfig_force :: Bool
150+ , _thunkPackConfig_config :: ThunkConfig
151+ } deriving Show
152+
124153-- | Convert a GitHub source to a regular Git source. Assumes no submodules.
125154forgetGithub :: Bool -> GitHubSource -> GitSource
126155forgetGithub useSsh s = GitSource
127− { _gitSource_url = URI
128− { uriScheme = Just $ fromRight' $ mkScheme $ if useSsh then "ssh" else "https"
129− , uriAuthority = Right $ Authority
130− { authUserInfo = UserInfo (fromRight' $ mkUsername "git") Nothing
156+ { _gitSource_url = GitUri $ URI.URI
157+ { URI.uriScheme = Just $ fromRight' $ URI.mkScheme $ if useSsh then "ssh" else "https"
158+ , URI.uriAuthority = Right $ URI.Authority
159+ { URI.authUserInfo = URI.UserInfo (fromRight' $ URI.mkUsername "git") Nothing
131160 <$ guard useSsh
132− , authHost = fromRight' $ mkHost "github.com"
133− , authPort = Nothing
161+ , URI.authHost = fromRight' $ URI.mkHost "github.com"
162+ , URI.authPort = Nothing
134163 }
135− , uriPath = Just ( False
136− , fromRight' . mkPathPiece <$>
164+ , URI.uriPath = Just ( False
165+ , fromRight' . URI.mkPathPiece <$>
137166 untagName (_gitHubSource_owner s)
138167 :| [ untagName (_gitHubSource_repo s) <> ".git" ]
139168 )
140− , uriQuery = []
141− , uriFragment = Nothing
169+ , URI.uriQuery = []
170+ , URI.uriFragment = Nothing
142171 }
143172 , _gitSource_branch = _gitHubSource_branch s
144173 , _gitSource_fetchSubmodules = False
174+ , _gitSource_private = _gitHubSource_private s
145175 }
146176
147177getThunkGitBranch :: ThunkPtr -> Maybe Text
@@ -155,20 +185,20 @@ commitNameToRef (N c) = Ref.fromHex $ encodeUtf8 c
155185-- TODO: Use spinner here.
156186getNixSha256ForUriUnpacked
157187 :: MonadObelisk m
158− => URI
188+ => GitUri
159189 -> m NixSha256
160190getNixSha256ForUriUnpacked uri =
161− withExitFailMessage ("nix-prefetch-url: Failed to determine sha256 hash of URL " <> render uri) $ do
191+ withExitFailMessage ("nix-prefetch-url: Failed to determine sha256 hash of URL " <> gitUriToText uri) $ do
162192 [hash] <- fmap T.lines $ readProcessAndLogOutput (Debug, Debug) $
163− proc "nix-prefetch-url" ["--unpack" , "--type" , "sha256" , T.unpack $ render uri]
193+ proc "nix-prefetch-url" ["--unpack", "--type", "sha256", T.unpack $ gitUriToText uri]
164194 pure hash
165195
166−nixPrefetchGit :: MonadObelisk m => URI -> Text -> Bool -> m NixSha256
196+nixPrefetchGit :: MonadObelisk m => GitUri -> Text -> Bool -> m NixSha256
167197nixPrefetchGit uri rev fetchSubmodules =
168− withExitFailMessage ("nix-prefetch-git: Failed to determine sha256 hash of Git repo " <> render uri <> " at " <> rev) $ do
198+ withExitFailMessage ("nix-prefetch-git: Failed to determine sha256 hash of Git repo " <> gitUriToText uri <> " at " <> rev) $ do
169199 out <- readProcessAndLogStderr Debug $
170200 proc "nix-prefetch-git" $ filter (/="")
171− [ "--url", T.unpack $ render uri
201+ [ "--url", T.unpack $ gitUriToText uri
172202 , "--rev", T.unpack rev
173203 , if fetchSubmodules then "--fetch-submodules" else ""
174204 , "--quiet"
@@ -286,10 +316,12 @@ parseGitHubSource v = do
286316 owner <- v Aeson..: "owner"
287317 repo <- v Aeson..: "repo"
288318 branch <- v Aeson..:! "branch"
319+ private <- v Aeson..:? "private"
289320 pure $ GitHubSource
290321 { _gitHubSource_owner = owner
291322 , _gitHubSource_repo = repo
292323 , _gitHubSource_branch = branch
324+ , _gitHubSource_private = fromMaybe False private
293325 }
294326
295327parseGitSource :: Aeson.Object -> Aeson.Parser GitSource
@@ -297,10 +329,12 @@ parseGitSource v = do
297329 Just url <- parseGitUri <$> v Aeson..: "url"
298330 branch <- v Aeson..:! "branch"
299331 fetchSubmodules <- v Aeson..:! "fetchSubmodules"
332+ private <- v Aeson..:? "private"
300333 pure $ GitSource
301334 { _gitSource_url = url
302335 , _gitSource_branch = branch
303336 , _gitSource_fetchSubmodules = fromMaybe False fetchSubmodules
337+ , _gitSource_private = fromMaybe False private
304338 }
305339
306340overwriteThunk :: MonadObelisk m => FilePath -> ThunkPtr -> m ()
@@ -327,13 +361,15 @@ encodeThunkPtrData (ThunkPtr rev src) = case src of
327361 , ("branch" .=) <$> _gitHubSource_branch s
328362 , Just $ "rev" .= Ref.toHexString (_thunkRev_commit rev)
329363 , Just $ "sha256" .= _thunkRev_nixSha256 rev
364+ , Just $ "private" .= _gitHubSource_private s
330365 ]
331366 ThunkSource_Git s -> encodePretty' plainGitCfg $ Aeson.object $ catMaybes
332− [ Just $ "url" .= render (_gitSource_url s)
367+ [ Just $ "url" .= gitUriToText (_gitSource_url s)
333368 , Just $ "rev" .= Ref.toHexString (_thunkRev_commit rev)
334369 , ("branch" .=) <$> _gitSource_branch s
335370 , Just $ "sha256" .= _thunkRev_nixSha256 rev
336371 , Just $ "fetchSubmodules" .= _gitSource_fetchSubmodules s
372+ , Just $ "private" .= _gitSource_private s
337373 ]
338374 where
339375 githubCfg = defConfig
@@ -354,6 +390,7 @@ encodeThunkPtrData (ThunkPtr rev src) = case src of
354390 [ "url"
355391 , "rev"
356392 , "sha256"
393+ , "private"
357394 , "fetchSubmodules"
358395 ] <> compare
359396 , confTrailingNewline = True
@@ -377,8 +414,8 @@ createThunkWithLatest target s = do
377414 , _thunkPtr_rev = rev
378415 }
379416
380−updateThunkToLatest :: MonadObelisk m => FilePath -> Maybe String -> m ()
381−updateThunkToLatest target mBranch = withSpinner' ("Updating thunk " <> T.pack target <> " to latest") (pure $ const $ "Thunk " <> T.pack target <> " updated to latest") $ do
417+updateThunkToLatest :: MonadObelisk m => ThunkUpdateConfig -> FilePath -> m ()
418+updateThunkToLatest (ThunkUpdateConfig mBranch thunkConfig) target = withSpinner' ("Updating thunk " <> T.pack target <> " to latest") (pure $ const $ "Thunk " <> T.pack target <> " updated to latest") $ do
382419 checkThunkDirectory "ob thunk update directory cannot be '.'" target
383420 -- check to see if thunk should be updated to a specific branch or just update it's current branch
384421 case mBranch of
@@ -390,7 +427,7 @@ updateThunkToLatest target mBranch = withSpinner' ("Updating thunk " <> T.pack t
390427 ThunkData_Checkout _ -> failWith "cannot update an unpacked thunk"
391428 let src = _thunkPtr_source ptr
392429 rev <- getLatestRev src
393− overwriteThunk overwrite $ ThunkPtr
430+ overwriteThunk overwrite $ modifyThunkPtrByConfig thunkConfig $ ThunkPtr
394431 { _thunkPtr_source = src
395432 , _thunkPtr_rev = rev
396433 }
@@ -398,24 +435,25 @@ updateThunkToLatest target mBranch = withSpinner' ("Updating thunk " <> T.pack t
398435 Left err -> failWith $ T.pack $ "thunk update: " <> show err
399436 Right c -> case c of
400437 ThunkData_Packed t -> case _thunkPtr_source t of
401− ThunkSource_Git tsg -> setThunk target tsg branch
438+ ThunkSource_Git tsg -> setThunk thunkConfig target tsg branch
402439 ThunkSource_GitHub tsgh -> do
403440 let tsg = forgetGithub False tsgh
404− setThunk target tsg branch
405− ThunkData_Checkout _ -> failWith $ T.pack $ "thunk located at " <> (show target) <> " is unpacked. Use ob thunk pack on the desired directory and then try ob thunk update again."
441+ setThunk thunkConfig target tsg branch
442+ ThunkData_Checkout _ -> failWith $ T.pack $ "thunk located at " <> show target <> " is unpacked. Use ob thunk pack on the desired directory and then try ob thunk update again."
406443
407−setThunk :: MonadObelisk m => FilePath -> GitSource -> String -> m ()
408−setThunk target gs branch = do
444+setThunk :: MonadObelisk m => ThunkConfig -> FilePath -> GitSource -> String -> m ()
445+setThunk thunkConfig target gs branch = do
409446 newThunkPtr <- uriThunkPtr (_gitSource_url gs) (Just $ T.pack branch) Nothing
410447 overwriteThunk target newThunkPtr
411− updateThunkToLatest target Nothing
448+ updateThunkToLatest (ThunkUpdateConfig Nothing thunkConfig) target
412449
413450-- | All recognized github standalone loaders, ordered from newest to oldest.
414451-- This tool will only ever produce the newest one when it writes a thunk.
415452gitHubStandaloneLoaders :: NonEmpty Text
416453gitHubStandaloneLoaders =
417− gitHubStandaloneLoaderV2 :|
454+ gitHubStandaloneLoaderV4 :|
418455 [ gitHubStandaloneLoaderV3
456+ , gitHubStandaloneLoaderV2
419457 , gitHubStandaloneLoaderV1
420458 ]
421459
@@ -455,10 +493,23 @@ gitHubStandaloneLoaderV3 = T.unlines
455493 , "in import (fetch (builtins.fromJSON (builtins.readFile ./github.json)))"
456494 ]
457495
496+gitHubStandaloneLoaderV4 :: Text
497+gitHubStandaloneLoaderV4 = T.unlines
498+ [ "# DO NOT HAND-EDIT THIS FILE"
499+ , "let fetch = { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }:"
500+ , " if !fetchSubmodules && !private then builtins.fetchTarball {"
501+ , " url = \"https://github.com/${owner}/${repo}/archive/${rev}.tar.gz\"; inherit sha256;"
502+ , " } else (import <nixpkgs> {}).fetchFromGitHub {"
503+ , " inherit owner repo rev sha256 fetchSubmodules private;"
504+ , " };"
505+ , "in import (fetch (builtins.fromJSON (builtins.readFile ./github.json)))"
506+ ]
507+
458508plainGitStandaloneLoaders :: NonEmpty Text
459509plainGitStandaloneLoaders =
460− plainGitStandaloneLoaderV1 :|
461− [ plainGitStandaloneLoaderV2
510+ plainGitStandaloneLoaderV3 :|
511+ [ plainGitStandaloneLoaderV1
512+ , plainGitStandaloneLoaderV2
462513 ]
463514
464515plainGitStandaloneLoaderV1 :: Text
@@ -479,6 +530,22 @@ plainGitStandaloneLoaderV2 = T.unlines
479530 , "in import (fetchGit (builtins.fromJSON (builtins.readFile ./git.json)))"
480531 ]
481532
533+plainGitStandaloneLoaderV3 :: Text
534+plainGitStandaloneLoaderV3 = T.unlines
535+ [ "# DO NOT HAND-EDIT THIS FILE"
536+ , "let fetch = {url, rev, ref ? null, sha256 ? null, fetchSubmodules ? false, private ? false, ...}:"
537+ , " let realUrl = let firstChar = builtins.substring 0 1 url; in"
538+ , " if firstChar == \"/\" then /. + url"
539+ , " else if firstChar == \".\" then ./. + url"
540+ , " else url;"
541+ , " in if !fetchSubmodules && private then builtins.fetchGit {"
542+ , " url = realUrl; inherit rev;"
543+ , " } else (import <nixpkgs> {}).fetchgit {"
544+ , " url = realUrl; inherit rev sha256;"
545+ , " };"
546+ , "in import (fetch (builtins.fromJSON (builtins.readFile ./git.json)))"
547+ ]
548+
482549thunkFileNames :: [FilePath]
483550thunkFileNames = ["default.nix", "github.json", "git.json"]
484551
@@ -520,7 +587,7 @@ nixBuildThunkAttrWithCache thunkDir attr = do
520587 Just c -> return c
521588 Nothing -> do
522589 putLog Warning $ T.pack $ mconcat [thunkDir, ": ", attr, " not cached, building ..."]
523− _ <- nixCmd $ NixCmd_Build$ def
590+ _ <- nixCmd $ NixCmd_Build $ def
524591 Lens.& nixBuildConfig_outLink Lens..~ OutLink_IndirectRoot cachePath
525592 Lens.& nixCmdConfig_target Lens..~ Target
526593 { _target_path = Just thunkDir
@@ -570,9 +637,9 @@ updateThunk p f = withSystemTempDirectory "obelisk-thunkptr-" $ \tmpDir -> do
570637 callProcessAndLogOutput (Notice, Error) $
571638 proc "cp" ["-r", "-T", thunkDir, tmpThunk]
572639 return tmpThunk
573− Right _ -> failWith $ "Thunk is not packed"
640+ Right _ -> failWith "Thunk is not packed"
574641 updateThunkFromTmp p' = do
575− _ <- packThunk' True False p'
642+ _ <- packThunk' True (ThunkPackConfig False (ThunkConfig Nothing)) p'
576643 callProcessAndLogOutput (Notice, Error) $
577644 proc "cp" ["-r", "-T", p', p]
578645
@@ -606,7 +673,7 @@ unpackThunk' noTrail thunkDir = checkThunkDirectory "Can't pack/unpack from with
606673 let git = callProcessAndLogOutput (Notice, Notice) . gitProc tmpRepo
607674 git $ [ "clone" ]
608675 ++ ("--recursive" <$ guard (_gitSource_fetchSubmodules s))
609− ++ [ T.unpack $ render $ _gitSource_url s ]
676+ ++ [ T.unpack $ gitUriToText $ _gitSource_url s ]
610677 ++ do branch <- maybeToList $ _gitSource_branch s
611678 [ "--branch", T.unpack $ untagName branch ]
612679 git ["reset", "--hard", Ref.toHexString $ _thunkRev_commit $ _thunkPtr_rev tptr]
@@ -622,24 +689,32 @@ unpackThunk' noTrail thunkDir = checkThunkDirectory "Can't pack/unpack from with
622689 proc "mv" ["-T", tmpRepo, thunkDir]
623690
624691--TODO: add a rollback mode to pack to the original thunk
625−packThunk :: MonadObelisk m => Bool -> FilePath -> m ThunkPtr
692+packThunk :: MonadObelisk m => ThunkPackConfig -> FilePath -> m ThunkPtr
626693packThunk = packThunk' False
627694
628−packThunk' :: MonadObelisk m => Bool -> Bool -> FilePath -> m ThunkPtr
629−packThunk' noTrail force thunkDir = checkThunkDirectory "Can't pack/unpack from within the thunk directory" thunkDir >> readThunk thunkDir >>= \case
695+packThunk' :: MonadObelisk m => Bool -> ThunkPackConfig -> FilePath -> m ThunkPtr
696+packThunk' noTrail (ThunkPackConfig force thunkConfig) thunkDir = checkThunkDirectory "Can't pack/unpack from within the thunk directory" thunkDir >> readThunk thunkDir >>= \case
630697 Left err -> failWith $ T.pack $ "thunk pack: " <> show err
631698 Right (ThunkData_Packed _) -> failWith "pack: thunk is already packed"
632699 Right (ThunkData_Checkout _) -> do
633700 withSpinner' ("Packing thunk " <> T.pack thunkDir)
634701 (finalMsg noTrail $ const $ "Packed thunk " <> T.pack thunkDir) $ do
635− thunkPtr <- getThunkPtr (not force) thunkDir
702+ thunkPtr <- modifyThunkPtrByConfig thunkConfig <$> getThunkPtr (not force) thunkDir
636703 callProcessAndLogOutput (Debug, Error) $ proc "rm" ["-rf", thunkDir]
637704 liftIO $ createThunk thunkDir thunkPtr
638705 pure thunkPtr
639706
707+modifyThunkPtrByConfig :: ThunkConfig -> ThunkPtr -> ThunkPtr
708+modifyThunkPtrByConfig (ThunkConfig markPrivate') ptr = case markPrivate' of
709+ Nothing -> ptr
710+ Just markPrivate -> ptr { _thunkPtr_source = case _thunkPtr_source ptr of
711+ ThunkSource_Git s -> ThunkSource_Git $ s { _gitSource_private = markPrivate }
712+ ThunkSource_GitHub s -> ThunkSource_GitHub $ s { _gitHubSource_private = markPrivate }
713+ }
714+
640715getThunkPtr :: forall m. MonadObelisk m => Bool -> FilePath -> m ThunkPtr
641716getThunkPtr checkClean thunkDir = do
642− when checkClean $ ensureCleanGitRepo thunkDir True $
717+ when checkClean $ ensureCleanGitRepo thunkDir True
643718 "thunk pack: thunk checkout contains unsaved modifications"
644719
645720 -- Check whether there are any stashes
@@ -661,7 +736,7 @@ getThunkPtr checkClean thunkDir = do
661736 <$> T.lines
662737 <$> readGitProcess thunkDir ["rev-parse", "HEAD"]
663738 case b of
664− (Just "HEAD") -> failWith $ T.unlines $
739+ (Just "HEAD") -> failWith $ T.unlines
665740 [ "thunk pack: You are in 'detached HEAD' state."
666741 , "If you want to pack at the current ref \
667742 \then please create a new branch with 'git checkout -b <new-branch-name>' and push this upstream."
@@ -733,7 +808,7 @@ getThunkPtr checkClean thunkDir = do
733808 [ "thunk pack: Certain branches in the thunk have commits not yet pushed upstream:"
734809 , ""
735810 ] ++
736− (flip map (Map.toList nonGood) $ \(branch, (upstream, (ahead, behind))) -> mconcat
811+ flip map (Map.toList nonGood) (\(branch, (upstream, (ahead, behind))) -> mconcat
737812 [" ", branch, " ahead: ", T.pack (show ahead), " behind: ", T.pack (show behind), " remote branch ", upstream]) ++
738813 [ ""
739814 , "Please push these upstream and try again. (Or just fetch, if they are somehow \
@@ -741,7 +816,7 @@ getThunkPtr checkClean thunkDir = do
741816 ]
742817
743818 -- We assume it's safe to pack the thunk at this point
744− putLog Informational $ "All changes safe in git remotes. OK to pack thunk."
819+ putLog Informational "All changes safe in git remotes. OK to pack thunk."
745820
746821 let remote = maybe "origin" snd $ flip Map.lookup headUpstream =<< mCurrentBranch
747822
@@ -773,7 +848,7 @@ getLatestRev os = do
773848-- performance. If that doesn't work (e.g. authentication issue), we fall back
774849-- on just doing things the normal way for git repos in general, and save it as
775850-- a regular git thunk.
776−uriThunkPtr :: MonadObelisk m => URI -> Maybe Text -> Maybe Text -> m ThunkPtr
851+uriThunkPtr :: MonadObelisk m => GitUri -> Maybe Text -> Maybe Text -> m ThunkPtr
777852uriThunkPtr uri mbranch mcommit = do
778853 commit <- case mcommit of
779854 Nothing -> gitGetCommitBranch uri mbranch >>= return . snd
@@ -784,7 +859,7 @@ uriThunkPtr uri mbranch mcommit = do
784859 case rev of
785860 Right r -> pure (ThunkSource_GitHub s, r)
786861 Left e -> do
787− putLog Warning $ "\
862+ putLog Warning "\
788863\Failed to fetch archive from GitHub. This is probably a private repo. \
789864\Falling back on normal fetchgit. Original failure:"
790865 errorToWarning e
@@ -801,33 +876,33 @@ uriThunkPtr uri mbranch mcommit = do
801876-- If the thunk is a GitHub thunk and fails, we do *not* fall back like with
802877-- `uriThunkPtr`. Unlike a plain URL, a thunk src explicitly states which method
803878-- should be employed, and so we respect that.
804−uriToThunkSource :: URI -> Maybe Text -> ThunkSource
805−uriToThunkSource u
806− | Right uriAuth <- uriAuthority u
807− , Just scheme <- unRText <$> uriScheme u
879+uriToThunkSource :: GitUri -> Maybe Text -> ThunkSource
880+uriToThunkSource (GitUri u)
881+ | Right uriAuth <- URI.uriAuthority u
882+ , Just scheme <- URI.unRText <$> URI.uriScheme u
808883 , case scheme of
809− "ssh" -> uriAuth == Authority
810− { authUserInfo = Just $ UserInfo (fromRight' $ mkUsername "git") Nothing
811− , authHost = fromRight' $ mkHost "github.com"
812− , authPort = Nothing
884+ "ssh" -> uriAuth == URI.Authority
885+ { URI.authUserInfo = Just $ URI.UserInfo (fromRight' $ URI.mkUsername "git") Nothing
886+ , URI.authHost = fromRight' $ URI.mkHost "github.com"
887+ , URI.authPort = Nothing
813888 }
814889 s -> s `L.elem` [ "git", "https", "http" ] -- "http:" just redirects to "https:"
815− && unRText (authHost uriAuth) == "github.com"
816− , Just (_, owner :| [repoish]) <- uriPath u
890+ && URI.unRText (URI.authHost uriAuth) == "github.com"
891+ , Just (_, owner :| [repoish]) <- URI.uriPath u
817892 = \mbranch -> ThunkSource_GitHub $ GitHubSource
818− { _gitHubSource_owner = N $ unRText owner
893+ { _gitHubSource_owner = N $ URI.unRText owner
819894 , _gitHubSource_repo = N $ let
820− repoish' = unRText repoish
821− in case T.stripSuffix ".git" repoish' of
822− Just repo -> repo
823− Nothing -> repoish'
895+ repoish' = URI.unRText repoish
896+ in fromMaybe repoish' $ T.stripSuffix ".git" repoish'
824897 , _gitHubSource_branch = N <$> mbranch
898+ , _gitHubSource_private = False -- TODO: Can we try something to infer this?
825899 }
826900
827901 | otherwise = \mbranch -> ThunkSource_Git $ GitSource
828− { _gitSource_url = u
902+ { _gitSource_url = GitUri u
829903 , _gitSource_branch = N <$> mbranch
830904 , _gitSource_fetchSubmodules = False -- TODO: How do we determine if this should be true?
905+ , _gitSource_private = False -- TODO: Can we try something to infer this?
831906 }
832907
833908-- Funny signature indicates no effects depend on the optional branch name.
@@ -840,19 +915,19 @@ githubThunkRev
840915githubThunkRev s commit = do
841916 owner <- forcePP $ _gitHubSource_owner s
842917 repo <- forcePP $ _gitHubSource_repo s
843− revTarball <- mkPathPiece $ commit <> ".tar.gz"
844− let archiveUri = URI
845− { uriScheme = Just $ fromRight' $ mkScheme "https"
846− , uriAuthority = Right $ Authority
847− { authUserInfo = Nothing
848− , authHost = fromRight' $ mkHost "github.com"
849− , authPort = Nothing
918+ revTarball <- URI.mkPathPiece $ commit <> ".tar.gz"
919+ let archiveUri = GitUri $ URI.URI
920+ { URI.uriScheme = Just $ fromRight' $ URI.mkScheme "https"
921+ , URI.uriAuthority = Right $ URI.Authority
922+ { URI.authUserInfo = Nothing
923+ , URI.authHost = fromRight' $ URI.mkHost "github.com"
924+ , URI.authPort = Nothing
850925 }
851− , uriPath = Just ( False
852− , owner :| [ repo, fromRight' $ mkPathPiece "archive", revTarball ]
926+ , URI.uriPath = Just ( False
927+ , owner :| [ repo, fromRight' $ URI.mkPathPiece "archive", revTarball ]
853928 )
854− , uriQuery = []
855− , uriFragment = Nothing
929+ , URI.uriQuery = []
930+ , URI.uriFragment = Nothing
856931 }
857932 hash <- getNixSha256ForUriUnpacked archiveUri
858933 putLog Debug $ "Nix sha256 is " <> hash
@@ -861,8 +936,8 @@ githubThunkRev s commit = do
861936 , _thunkRev_nixSha256 = hash
862937 }
863938 where
864− forcePP :: Name entity -> m (RText 'PathPiece)
865− forcePP = mkPathPiece . untagName
939+ forcePP :: Name entity -> m (URI.RText 'URI.PathPiece)
940+ forcePP = URI.mkPathPiece . untagName
866941
867942gitThunkRev
868943 :: MonadObelisk m
@@ -871,11 +946,11 @@ gitThunkRev
871946 -> m ThunkRev
872947gitThunkRev s commit = do
873948 let u = _gitSource_url s
874− protocols = ["https", "ssh", "git"]
875− Just scheme <- pure $ unRText <$> uriScheme u
949+ protocols = ["file", "https", "ssh", "git"]
950+ scheme = maybe "file" URI.unRText $ URI.uriScheme $ (\(GitUri x) -> x) u
876951 unless (T.toLower scheme `elem` protocols) $
877952 failWith $ "obelisk currently only supports "
878− <> T.intercalate ", " protocols <> " protocols for non-GitHub remotes"
953+ <> T.intercalate ", " protocols <> " protocols for plain Git remotes"
879954 hash <- nixPrefetchGit u commit $ _gitSource_fetchSubmodules s
880955 putLog Informational $ "Nix sha256 is " <> hash
881956 pure $ ThunkRev
@@ -889,10 +964,10 @@ gitThunkRev s commit = do
889964-- If the branch name is passed in, it is returned exactly as-is. If it is not
890965-- passed it, the default branch of the repo is used instead.
891966gitGetCommitBranch
892− :: MonadObelisk m => URI -> Maybe Text -> m (Text, CommitId)
967+ :: MonadObelisk m => GitUri -> Maybe Text -> m (Text, CommitId)
893968gitGetCommitBranch uri mbranch = withExitFailMessage ("Failure for git remote " <> uriMsg) $ do
894969 (_, bothMaps) <- gitLsRemote
895− (T.unpack $ render uri)
970+ (T.unpack $ gitUriToText uri)
896971 (GitRef_Branch <$> mbranch)
897972 Nothing
898973 branch <- case mbranch of
@@ -908,18 +983,21 @@ gitGetCommitBranch uri mbranch = withExitFailMessage ("Failure for git remote "
908983 pure (branch, commit)
909984 where
910985 rethrowE = either failWith pure
911− uriMsg = render uri
986+ uriMsg = gitUriToText uri
987+
988+parseGitUri :: Text -> Maybe GitUri
989+parseGitUri x = GitUri <$> (parseFileURI x <|> parseAbsoluteURI x <|> parseSshShorthand x)
912990
913−parseGitUri :: Text -> Maybe URI
914−parseGitUri x = parseAbsoluteURI x <|> parseSshShorthand x
991+parseFileURI :: Text -> Maybe URI.URI
992+parseFileURI uri = if "/" `T.isPrefixOf` uri then parseAbsoluteURI ("file://" <> uri) else Nothing
915993
916−parseAbsoluteURI :: Text -> Maybe URI
994+parseAbsoluteURI :: Text -> Maybe URI.URI
917995parseAbsoluteURI uri = do
918− parsedUri <- mkURI uri
919− guard $ isPathAbsolute parsedUri
920− pure $ parsedUri
996+ parsedUri <- URI.mkURI uri
997+ guard $ URI.isPathAbsolute parsedUri
998+ pure parsedUri
921999
922−parseSshShorthand :: Text -> Maybe URI
1000+parseSshShorthand :: Text -> Maybe URI.URI
9231001parseSshShorthand uri = do
9241002 -- This is what git does to check that the remote
9251003 -- is not a local file path when parsing shorthand.
@@ -932,4 +1010,4 @@ parseSshShorthand uri = do
9321010 -- This check is used to disambiguate a filepath containing a colon from shorthand
9331011 guard $ isNothing (T.findIndex (=='/') authAndHostname)
9341012 && not (T.null colonAndPath)
935− mkURI properUri
1013+ URI.mkURI properUri
lib/command/src/Obelisk/Command/Utils.hs+22−13
@@ -23,7 +23,6 @@ import Data.Void (Void)
2323import System.Directory (canonicalizePath)
2424import System.Environment (getExecutablePath)
2525import System.Exit (ExitCode)
26−import qualified System.Process as P
2726import Text.Megaparsec as MP
2827import Text.Megaparsec.Char as MP
2928
@@ -61,20 +60,30 @@ initGit repo = do
6160 git ["add", "."]
6261 git ["commit", "-m", "Initial commit."]
6362
64−gitProcNoRepo :: [String] -> P.CreateProcess
65−gitProcNoRepo = P.proc "git"
63+gitProcNoRepo :: [String] -> ProcessSpec
64+gitProcNoRepo args = setEnvOverride (M.singleton "GIT_TERMINAL_PROMPT" "0" <>) $ proc "git" args
6665
67−gitProc :: FilePath -> [String] -> P.CreateProcess
66+gitProc :: FilePath -> [String] -> ProcessSpec
6867gitProc repo = gitProcNoRepo . runGitInDir
6968 where
7069 runGitInDir args' = case filter (not . null) args' of
7170 args@("clone":_) -> args <> [repo]
7271 args -> ["-C", repo] <> args
7372
74−-- | Recursively copy a directory using `cp -a`
75−copyDir :: FilePath -> FilePath -> P.CreateProcess
73+isolateGitProc :: ProcessSpec -> ProcessSpec
74+isolateGitProc = setEnvOverride (overrides <>)
75+ where
76+ overrides = M.fromList
77+ [ ("HOME", "/dev/null")
78+ , ("GIT_CONFIG_NOSYSTEM", "1")
79+ , ("GIT_TERMINAL_PROMPT", "0")
80+ , ("GIT_SSH_COMMAND", "ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no -o GSSAPIAuthentication=no")
81+ ]
82+
83+-- | Recursively copy a directory using `cp -a` -- TODO: Should use -rT instead of -a
84+copyDir :: FilePath -> FilePath -> ProcessSpec
7685copyDir src dest =
77− (P.proc "cp" ["-a", ".", dest]) { P.cwd = Just src }
86+ setCwd (Just src) $ proc "cp" ["-a", ".", dest] -- TODO: This will break if dest is relative since we change cwd
7887
7988readGitProcess :: MonadObelisk m => FilePath -> [String] -> m Text
8089readGitProcess repo = readProcessAndLogOutput (Debug, Notice) . gitProc repo
@@ -87,15 +96,15 @@ processToShellString cmd args = unwords $ map quoteAndEscape (cmd : args)
8796 where quoteAndEscape x = T.unpack $ "'" <> T.replace "'" "'\''" (T.pack x) <> "'"
8897
8998-- | A simpler wrapper for CliApp's most used process function with sensible defaults.
90−runProc :: MonadObelisk m => P.CreateProcess -> m ()
99+runProc :: MonadObelisk m => ProcessSpec -> m ()
91100runProc = callProcessAndLogOutput (Notice, Error)
92101
93102-- | Like runProc, but all output goes to Debug logging level
94−runProcSilently :: MonadObelisk m => P.CreateProcess -> m ()
103+runProcSilently :: MonadObelisk m => ProcessSpec -> m ()
95104runProcSilently = callProcessAndLogOutput (Debug, Debug)
96105
97106-- | A simpler wrapper for CliApp's readProcessAndLogStderr with sensible defaults.
98−readProc :: MonadObelisk m => P.CreateProcess -> m Text
107+readProc :: MonadObelisk m => ProcessSpec -> m Text
99108readProc = readProcessAndLogOutput (Debug, Error)
100109
101110tshow :: Show a => a -> Text
@@ -129,13 +138,13 @@ gitLsRemote repository mRef mBranch = do
129138 (exitCode, out, _err) <- case mBranch of
130139 Nothing -> readCreateProcessWithExitCode $ gitProcNoRepo $
131140 ["ls-remote", "--exit-code", "--symref", repository]
132− ++ (maybeToList $ T.unpack . showGitRef <$> mRef)
133− Just branchName -> readCreateProcessWithExitCode $ gitProcNoRepo $
141+ ++ maybeToList (T.unpack . showGitRef <$> mRef)
142+ Just branchName -> readCreateProcessWithExitCode $ gitProcNoRepo
134143 ["ls-remote", "--exit-code", repository, branchName]
135144 let t = T.pack out
136145 maps <- case MP.runParser parseLsRemote "" t of
137146 Left err -> failWith $ T.pack $ MP.errorBundlePretty err
138− Right table -> pure $ bimap M.fromList M.fromList $ partitionEithers $ table
147+ Right table -> pure $ bimap M.fromList M.fromList $ partitionEithers table
139148 putLog Debug $ "git ls-remote maps: " <> T.pack (show maps)
140149 pure (exitCode, maps)
141150
lib/command/src/Obelisk/Command/VmBuilder.hs−1
@@ -18,7 +18,6 @@ import System.Directory (createDirectoryIfMissing)
1818import System.Exit (ExitCode(..))
1919import System.FilePath ((<.>), (</>))
2020import qualified System.Info
21−import System.Process (proc)
2221
2322import Obelisk.App (MonadObelisk, getObeliskUserStateDir)
2423import Obelisk.CliApp
lib/run/src/Obelisk/Run.hs+1−1
@@ -129,7 +129,7 @@ runWidget conf configs frontend validFullEncoder = do
129129 putStrLn $ "Frontend running on " <> T.unpack (URI.render uri)
130130 settings = setBeforeMainLoop beforeMainLoop (setPort port (setTimeout 3600 defaultSettings))
131131 -- Providing TLS here will also incidentally provide it to proxied requests to the backend.
132− prepareRunner = case (uri ^? uriScheme . _Just . unRText) of
132+ prepareRunner = case uri ^? uriScheme . _Just . unRText of
133133 Just "https" -> do
134134 -- Generate a private key and self-signed certificate for TLS
135135 privateKey <- RSA.generateRSAKey' 2048 3
lib/selftest/src/Obelisk/SelfTest.hs+38−22
@@ -3,6 +3,7 @@
33{-# LANGUAGE OverloadedStrings #-}
44{-# LANGUAGE PatternGuards #-}
55{-# LANGUAGE ScopedTypeVariables #-}
6+{-# LANGUAGE ViewPatterns #-}
67module Obelisk.SelfTest where
78
89import Control.Exception (bracket, throw)
@@ -24,7 +25,7 @@ import Shelly
2425import System.Directory (withCurrentDirectory, getDirectoryContents)
2526import System.Environment
2627import System.Exit (ExitCode (..))
27−import System.Info
28+import qualified System.Info
2829import System.IO (Handle, hClose)
2930import System.IO.Temp
3031import System.Process (readProcessWithExitCode, CreateProcess(cwd), readCreateProcessWithExitCode, proc)
@@ -75,24 +76,38 @@ main = do
7576 putStrLn "Tests may take longer to run if there are unbuilt derivations: use -v for verbose output"
7677 let verbosity = bool silently verbosely isVerbose
7778 nixBuild args = run "nix-build" ("--no-out-link" : args)
78− obeliskImpl <- fromString <$> getEnv "OBELISK_IMPL"
79+ obeliskImplRaw <- fromString <$> getEnv "OBELISK_IMPL"
80+ let
81+ withObeliskImpl f =
82+ withSystemTempDirectory "obelisk-impl-copy" $ \(fromString -> obeliskImpl) -> do
83+ void . shellyOb verbosity $ chdir obeliskImpl $ do
84+ user <- T.strip <$> run "whoami" []
85+ run_ "cp" ["-rT", toTextIgnore obeliskImplRaw, toTextIgnore obeliskImpl]
86+ run_ "chown" ["-R", user, toTextIgnore obeliskImpl]
87+ run_ "chmod" ["-R", "g-rw,o-rw", toTextIgnore obeliskImpl]
88+
89+ withSystemTempDirectory "init Cache λ" $ \initCache -> do
90+ -- Setup the ob init cache
91+ void . shellyOb verbosity $ chdir (fromString initCache) $ do
92+ run_ "ob" ["init", "--symlink", toTextIgnore obeliskImpl]
93+ run_ "git" ["init"]
94+
95+ f obeliskImpl initCache
96+
7997 httpManager <- HTTP.newManager HTTP.defaultManagerSettings
8098 [p0, p1, p2, p3] <- liftIO $ getFreePorts 4
81− withSystemTempDirectory "initCache" $ \initCache -> do
82− -- Setup the ob init cache
83− void . shellyOb verbosity $ chdir (fromString initCache) $ do
84− run_ "ob" ["init"]
85− run_ "git" ["init"]
99+
100+ withObeliskImpl $ \obeliskImpl initCache ->
86101 hspec $ parallel $ do
87102 let shelly_ = void . shellyOb verbosity
88103
89104 inTmp :: (Shelly.FilePath -> Sh a) -> IO ()
90105 inTmp f = withTmp (chdir <*> f)
91106
92− withTmp f = shelly_ . withSystemTempDirectory "test" $ f . fromString
107+ withTmp f = shelly_ . withSystemTempDirectory "test λ" $ f . fromString
93108
94109 inTmpObInit f = inTmp $ \dir -> do
95− run_ "cp" ["-a", fromString $ initCache <> "/.", toTextIgnore dir]
110+ run_ "cp" ["-rT", fromString initCache, toTextIgnore dir]
96111 f dir
97112
98113 assertRevEQ a b = liftIO . assertEqual "" "" =<< diff a b
@@ -115,9 +130,9 @@ main = do
115130 describe "ob init" $ parallel $ do
116131 it "works with default impl" $ inTmp $ \_ -> run "ob" ["init"]
117132 it "works with master branch impl" $ inTmp $ \_ -> run "ob" ["init", "--branch", "master"]
118− it "works with symlink" $ inTmp $ \_ -> run "ob" ["init", "--symlink", obeliskImpl]
119− it "doesn't silently overwrite existing files" $ withSystemTempDirectory "ob-init" $ \dir -> do
120− let p force = (proc "ob" $ "--no-handoff" : "init" : ["--force"|force]) { cwd = Just dir }
133+ it "works with symlink" $ inTmp $ \_ -> run "ob" ["init", "--symlink", toTextIgnore obeliskImpl]
134+ it "doesn't silently overwrite existing files" $ withSystemTempDirectory "ob-init λ" $ \dir -> do
135+ let p force = (System.Process.proc "ob" $ "--no-handoff" : "init" : ["--force"|force]) { cwd = Just dir }
121136 (ExitSuccess, _, _) <- readCreateProcessWithExitCode (p False) ""
122137 (ExitFailure _, _, _) <- readCreateProcessWithExitCode (p False) ""
123138 (ExitSuccess, _, _) <- readCreateProcessWithExitCode (p True) ""
@@ -139,17 +154,17 @@ main = do
139154 it "works in sub directory" $ inTmpObInit $ \_ -> testObRunInDir p2 p3 (Just "frontend") httpManager
140155
141156 describe "obelisk project" $ parallel $ do
142− it "can build obelisk command" $ inTmpObInit $ \_ -> nixBuild ["-A", "command" , obeliskImpl]
143− it "can build obelisk skeleton" $ inTmpObInit $ \_ -> nixBuild ["-A", "skeleton", obeliskImpl]
144− it "can build obelisk shell" $ inTmpObInit $ \_ -> nixBuild ["-A", "shell", obeliskImpl]
145− it "can build everything" $ inTmpObInit $ \_ -> nixBuild [obeliskImpl]
157+ it "can build obelisk command" $ inTmpObInit $ \_ -> nixBuild ["-A", "command" , toTextIgnore obeliskImpl]
158+ it "can build obelisk skeleton" $ inTmpObInit $ \_ -> nixBuild ["-A", "skeleton", toTextIgnore obeliskImpl]
159+ it "can build obelisk shell" $ inTmpObInit $ \_ -> nixBuild ["-A", "shell", toTextIgnore obeliskImpl]
160+ it "can build everything" $ inTmpObInit $ \_ -> nixBuild [toTextIgnore obeliskImpl]
146161
147162 describe "blank initialized project" $ parallel $ do
148163
149164 it "can build ghc.backend" $ inTmpObInit $ \_ -> nixBuild ["-A", "ghc.backend"]
150165 it "can build ghcjs.frontend" $ inTmpObInit $ \_ -> nixBuild ["-A", "ghcjs.frontend"]
151166
152− if os == "darwin"
167+ if System.Info.os == "darwin"
153168 then it "can build ios" $ inTmpObInit $ \_ -> nixBuild ["-A", "ios.frontend"]
154169 else it "can build android after accepting license" $ inTmpObInit $ \dir -> do
155170 let defaultNixPath = dir </> ("default.nix" :: Shelly.FilePath)
@@ -162,12 +177,13 @@ main = do
162177
163178 forM_ ["ghc", "ghcjs"] $ \compiler -> do
164179 let
165− shell = "shells." <> compiler
166− inShell cmd' = run "nix-shell" ["default.nix", "-A", fromString shell, "--run", cmd']
167− it ("can enter " <> shell) $ inTmpObInit $ \_ -> inShell "exit"
168− it ("can build in " <> shell) $ inTmpObInit $ \_ -> inShell $ "cabal new-build --" <> fromString compiler <> " all"
180+ shellName = "shells." <> compiler
181+ inShell cmd' = run "nix-shell" ["default.nix", "-A", fromString shellName, "--run", cmd']
182+ it ("can enter " <> shellName) $ inTmpObInit $ \_ -> inShell "exit"
183+ it ("can build in " <> shellName) $ inTmpObInit $ \_ -> inShell $ "cabal new-build --" <> fromString compiler <> " all"
169184
170185 it "has idempotent thunk update" $ inTmpObInit $ \_ -> do
186+ _ <- pack
171187 u <- update
172188 uu <- update
173189 assertRevEQ u uu
@@ -175,6 +191,7 @@ main = do
175191 describe "ob thunk pack/unpack" $ parallel $ do
176192 it "has thunk pack and unpack inverses" $ inTmpObInit $ \_ -> do
177193
194+ _ <- pack
178195 e <- commitAll
179196 eu <- unpack
180197 eup <- pack
@@ -212,7 +229,6 @@ main = do
212229 testThunkPack $ fromText repo
213230
214231 it "aborts thunk pack when there are uncommitted files" $ inTmpObInit $ \dir -> do
215− void unpack
216232 testThunkPack (dir </> thunk)
217233
218234 describe "ob thunk update --branch" $ parallel $ do
skeleton/.gitignore+1
@@ -1,5 +1,6 @@
11dist-newstyle
22result
3+result-*
34result-android
45result-ios
56result-exe
skeleton/common/common.cabal−3
@@ -9,9 +9,6 @@ library
99 , obelisk-route
1010 , mtl
1111 , text
12− default-extensions:
13− TypeFamilies
14− PolyKinds
1512 exposed-modules:
1613 Common.Api
1714 Common.Route
skeleton/common/src/Common/Route.hs+1
@@ -8,6 +8,7 @@
88{-# LANGUAGE OverloadedStrings #-}
99{-# LANGUAGE RankNTypes #-}
1010{-# LANGUAGE TemplateHaskell #-}
11+{-# LANGUAGE TypeFamilies #-}
1112module Common.Route where
1213
1314{- -- You will probably want these imports for composing Encoders.
skeleton/frontend/src/Frontend.hs+9−3
@@ -1,24 +1,30 @@
11{-# LANGUAGE DataKinds #-}
22{-# LANGUAGE OverloadedStrings #-}
33{-# LANGUAGE TypeApplications #-}
4+
45module Frontend where
56
67import qualified Data.Text as T
78import qualified Data.Text.Encoding as T
9+
810import Obelisk.Frontend
911import Obelisk.Configs
1012import Obelisk.Route
13+import Obelisk.Generated.Static
14+
1115import Reflex.Dom.Core
1216
1317import Common.Api
1418import Common.Route
15−import Obelisk.Generated.Static
19+
1620
1721frontend :: Frontend (R FrontendRoute)
1822frontend = Frontend
19− { _frontend_head = el "title" $ text "Obelisk Minimal Example"
23+ { _frontend_head = do
24+ el "title" $ text "Obelisk Minimal Example"
25+ elAttr "link" ("href" =: static @"main.css" <> "type" =: "text/css" <> "rel" =: "stylesheet") blank
2026 , _frontend_body = do
21− text "Welcome to Obelisk!"
27+ el "h1" $ text "Welcome to Obelisk!"
2228 el "p" $ text $ T.pack commonStuff
2329 elAttr "img" ("src" =: static @"obelisk.jpg") blank
2430 el "div" $ do
skeleton/static/main.cssnew file+3
@@ -0,0 +1,3 @@
1+p {
2+ color: red;
3+}