@@ -23,6 +23,7 @@ import Options.Applicative
2323import System.Directory
2424import System.Environment
2525import System.FilePath
26+import qualified System.Info
2627import System.IO (hIsTerminalDevice, stdout)
2728import System.Posix.Process (executeFile)
2829
@@ -31,6 +32,7 @@ import Obelisk.CliApp (Severity (..), failWith, getLogLevel, newCliConfig, putLo
3132import Obelisk.CliApp.Demo (cliDemo)
3233import Obelisk.Command.Deploy
3334import Obelisk.Command.Project
35+import qualified Obelisk.Command.VmBuilder as VmBuilder
3436import Obelisk.Command.Run
3537import Obelisk.Command.Thunk
3638
@@ -45,8 +47,12 @@ data Args = Args
4547 }
4648 deriving Show
4749
48−args :: Parser Args
49−args = Args <$> noHandoff <*> verbose <*> obCommand
50+newtype ArgsConfig = ArgsConfig
51+ { _argsConfig_enableVmBuilderByDefault :: Bool
52+ }
53+
54+args :: ArgsConfig -> Parser Args
55+args cfg = Args <$> noHandoff <*> verbose <*> obCommand cfg
5056
5157noHandoff :: Parser Bool
5258noHandoff = flag False True $ mconcat
@@ -62,8 +68,8 @@ verbose = flag False True $ mconcat
6268 , help "Be more verbose"
6369 ]
6470
65−argsInfo :: ParserInfo Args
66−argsInfo = info (args <**> helper) $ mconcat
71+argsInfo :: ArgsConfig -> ParserInfo Args
72+argsInfo cfg = info (args cfg <**> helper) $ mconcat
6773 [ fullDesc
6874 , progDesc "Manage Obelisk projects"
6975 ]
@@ -95,7 +101,7 @@ inNixShell' p = withProjectRoot "." $ \root -> do
95101 projectShell root False "ghc" cmd
96102 where
97103 mkCmd = do
98− obArgs <- getObArgs
104+ obArgs <- getObArgs =<< getArgsConfig
99105 progName <- getExecutablePath
100106 return $ progName : catMaybes
101107 [ Just "--no-handoff"
@@ -105,11 +111,11 @@ inNixShell' p = withProjectRoot "." $ \root -> do
105111 , Just $ encodeStaticKey $ staticKey p
106112 ]
107113
108−obCommand :: Parser ObCommand
109−obCommand = hsubparser
114+obCommand :: ArgsConfig -> Parser ObCommand
115+obCommand cfg = hsubparser
110116 (mconcat
111117 [ command "init" $ info (ObCommand_Init <$> initSource) $ progDesc "Initialize an Obelisk project"
112− , command "deploy" $ info (ObCommand_Deploy <$> deployCommand) $ progDesc "Prepare a deployment for an Obelisk project"
118+ , command "deploy" $ info (ObCommand_Deploy <$> deployCommand cfg) $ progDesc "Prepare a deployment for an Obelisk project"
113119 , command "run" $ info (pure ObCommand_Run) $ progDesc "Run current project in development mode"
114120 , command "thunk" $ info (ObCommand_Thunk <$> thunkCommand) $ progDesc "Manipulate thunk directories"
115121 , command "repl" $ info (pure ObCommand_Repl) $ progDesc "Open an interactive interpreter"
@@ -120,10 +126,10 @@ obCommand = hsubparser
120126 , command "internal" (info (ObCommand_Internal <$> internalCommand) mempty)
121127 ])
122128
123−deployCommand :: Parser DeployCommand
124−deployCommand = hsubparser $ mconcat
125− [ command "init" $ info deployInitCommand $ progDesc "Initialize a deployment configuration directory"
126− , command "push" $ info (pure DeployCommand_Push) mempty
129+deployCommand :: ArgsConfig -> Parser DeployCommand
130+deployCommand cfg = hsubparser $ mconcat
131+ [ command "init" $ info (DeployCommand_Init <$> deployInitOpts) $ progDesc "Initialize a deployment configuration directory"
132+ , command "push" $ info (DeployCommand_Push <$> remoteBuilderParser) mempty
127133 , command "test" $ info (DeployCommand_Test <$> platformP) $ progDesc "Test your obelisk project from a mobile platform."
128134 , command "update" $ info (pure DeployCommand_Update) $ progDesc "Update the deployment's src thunk to latest"
129135 ]
@@ -133,8 +139,25 @@ deployCommand = hsubparser $ mconcat
133139 , command "ios" $ info (pure IOS <*> strArgument (metavar "TEAMID" <> help "Your Team ID - found in the Apple developer portal")) mempty
134140 ]
135141
136−deployInitCommand :: Parser DeployCommand
137−deployInitCommand = fmap DeployCommand_Init $ DeployInitOpts
142+ remoteBuilderParser :: Parser (Maybe RemoteBuilder)
143+ remoteBuilderParser =
144+ flag (if enabledByDefault then enabled else Nothing) enabled (mconcat
145+ [ long $ "enable-" <> flagBase
146+ , help $ "Enable " <> flagDesc <> (if enabledByDefault then " (default)" else "")
147+ ])
148+ <|> flag enabled Nothing (mconcat
149+ [ long $ "disable-" <> flagBase
150+ , help $ "Disable a " <> flagDesc <> (if not enabledByDefault then " (default)" else "")
151+ ])
152+ where
153+ enabledByDefault = _argsConfig_enableVmBuilderByDefault cfg
154+ enabled = Just RemoteBuilder_ObeliskVM
155+ flagBase = "vm-builder"
156+ flagDesc = "managed Linux virtual machine and allow builds to use it (requires Docker)"
157+
158+
159+deployInitOpts :: Parser DeployInitOpts
160+deployInitOpts = DeployInitOpts
138161 <$> strArgument (action "directory" <> metavar "DEPLOYDIR" <> help "Path to a directory that it will create")
139162 <*> strOption (long "ssh-key" <> action "file" <> metavar "SSHKEY" <> help "Path to an ssh key that it will symlink to")
140163 <*> some (strOption (long "hostname" <> metavar "HOSTNAME" <> help "hostname of the deployment target"))
@@ -144,9 +167,12 @@ type TeamID = String
144167data PlatformDeployment = Android | IOS TeamID
145168 deriving (Show)
146169
170+data RemoteBuilder = RemoteBuilder_ObeliskVM
171+ deriving (Eq, Show)
172+
147173data DeployCommand
148174 = DeployCommand_Init DeployInitOpts
149− | DeployCommand_Push
175+ | DeployCommand_Push (Maybe RemoteBuilder)
150176 | DeployCommand_Test PlatformDeployment
151177 | DeployCommand_Update
152178 deriving Show
@@ -214,25 +240,27 @@ isInteractiveTerm = do
214240 inShellCompletion <- liftIO $ isInfixOf "completion" . unwords <$> getArgs
215241 return $ isTerm && not inShellCompletion
216242
217−mkObeliskConfig :: IO Obelisk
218−mkObeliskConfig = do
243+mkObeliskConfig :: ArgsConfig -> IO Obelisk
244+mkObeliskConfig argsCfg = do
219245 notInteractive <- not <$> isInteractiveTerm
220− logLevel <- toLogLevel <$> getObArgs
246+ logLevel <- toLogLevel <$> getObArgs argsCfg
221247 cliConf <- newCliConfig logLevel notInteractive notInteractive
222248 return $ Obelisk cliConf
223249 where
224250 toLogLevel = bool Notice Debug . _args_verbose
225251
226−getObArgs :: IO Args
227−getObArgs = getArgs >>= handleParseResult . execParserPure parserPrefs argsInfo
252+getObArgs :: ArgsConfig -> IO Args
253+getObArgs cfg = getArgs >>= handleParseResult . execParserPure parserPrefs (argsInfo cfg)
228254
229255main :: IO ()
230−main = mkObeliskConfig >>= (`runObelisk` ObeliskT main')
256+main = do
257+ argsCfg <- getArgsConfig
258+ mkObeliskConfig argsCfg >>= (`runObelisk` ObeliskT (main' argsCfg))
231259
232−main' :: MonadObelisk m => m ()
233−main' = do
260+main' :: MonadObelisk m => ArgsConfig -> m ()
261+main' argsCfg = do
234262 obPath <- liftIO getExecutablePath
235− obArgs <- liftIO getObArgs
263+ obArgs <- liftIO $ getObArgs argsCfg
236264 logLevel <- getLogLevel
237265 putLog Debug $ T.pack $ unwords
238266 [ "Starting Obelisk <" <> obPath <> ">"
@@ -246,7 +274,7 @@ main' = do
246274 --with optparse-applicative until we've done the handoff.
247275 myArgs <- liftIO getArgs
248276 let go as = do
249− args' <- liftIO $ handleParseResult (execParserPure parserPrefs argsInfo as)
277+ args' <- liftIO $ handleParseResult (execParserPure parserPrefs (argsInfo argsCfg) as)
250278 case _args_noHandOffPassed args' of
251279 False -> return ()
252280 True -> putLog Warning "--no-handoff should only be passed once and as the first argument; ignoring"
@@ -289,7 +317,9 @@ ob = \case
289317 let sshKeyPath = _deployInitOpts_sshKey deployOpts
290318 hostname = _deployInitOpts_hostname deployOpts
291319 deployInit thunkPtr (root </> "config") deployDir sshKeyPath hostname
292− DeployCommand_Push -> deployPush "."
320+ DeployCommand_Push remoteBuilder -> deployPush "." $ case remoteBuilder of
321+ Nothing -> pure []
322+ Just RemoteBuilder_ObeliskVM -> (:[]) <$> VmBuilder.getNixBuildersArg
293323 DeployCommand_Update -> deployUpdate "."
294324 DeployCommand_Test Android -> deployMobile "android" []
295325 DeployCommand_Test (IOS teamID) -> deployMobile "ios" [teamID]
@@ -310,6 +340,9 @@ ob = \case
310340
311341--TODO: Clean up all the magic strings throughout this codebase
312342
343+getArgsConfig :: IO ArgsConfig
344+getArgsConfig = pure $ ArgsConfig { _argsConfig_enableVmBuilderByDefault = System.Info.os == "darwin" }
345+
313346encodeStaticKey :: StaticKey -> String
314347encodeStaticKey = T.unpack . decodeUtf8 . Base16.encode . LBS.toStrict . Binary.encode
315348