on master help sign in
ob deploy: HTTPs support
2a6a278Sridhar Ratnakumar
README.md+15−9
@@ -116,22 +116,28 @@ First create a new EC2 instance:
1161. Launch a NixOS 17.09 EC2 instance (we recommend [this AMI](https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#LaunchInstanceWizard:ami=ami-40bee63a))
1161. Launch a NixOS 17.09 EC2 instance (we recommend [this AMI](https://console.aws.amazon.com/ec2/v2/home?region=us-east-1#LaunchInstanceWizard:ami=ami-40bee63a))
1171. In the instance configuration wizard ensure that your instance has at least 1GB RAM and 10GB disk space.
1171. In the instance configuration wizard ensure that your instance has at least 1GB RAM and 10GB disk space.
1181. When prompted save your AWS private key (`~/myaws.pem`) somewhere safe. We'll need it later during deployment.
1181. When prompted save your AWS private key (`~/myaws.pem`) somewhere safe. We'll need it later during deployment.
119−1. Go to "Security Groups", select your instance's security group and under "Inbound" tab add a new rule for HTTP port 80.
119+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.
120
120
121−At this stage your instance should be booting and become accessible shortly. Note down the hostname of your instance. It should look like this:
121+At this stage your instance should be booting and become accessible shortly. Note down the hostname of your EC2 instance.
122−
123−```
124−INSTANCE_HOSTNAME=ec2-??-??-??-??.ca-central-1.compute.amazonaws.com
125−```
126
122
127Now go to your Obelisk project directory (`~/code/myapp`), and initialize a deployment config (`~/code/myapp-deploy`):
123Now go to your Obelisk project directory (`~/code/myapp`), and initialize a deployment config (`~/code/myapp-deploy`):
128Your project directory must be "thunkable", i.e. something on which `ob thunk pack` can be called. Usually it will be a git repository whose current revision has been pushed upstream.
124Your project directory must be "thunkable", i.e. something on which `ob thunk pack` can be called. Usually it will be a git repository whose current revision has been pushed upstream.
129
125
130```
126```
131cd ~/code/myapp
127cd ~/code/myapp
132−ob deploy init --ssh-key ~/myaws.pem --hostname ${INSTANCE_HOSTNAME} ~/code/myapp-deploy
128+SERVER=ec2-35-183-22-197.ca-central-1.compute.amazonaws.com
129+ROUTE=https://myapp.com # Publicly accessible route to your app
130+EMAIL=myname@myapp.com
131+ob deploy init \
132+ --ssh-key ~/myaws.pem \
133+ --hostname $SERVER \
134+ --route $ROUTE \
135+ --admin-email $EMAIL \
136+ ~/code/myapp-deploy
133```
137```
134
138
139+NOTE: HTTPS is enabled by default; to disable https, pass `--disable-https` to the `ob deploy init` command above.
140+
135Then go to that created deployment configuration directory, and initiate the deployment:
141Then go to that created deployment configuration directory, and initiate the deployment:
136
142
137```
143```
@@ -139,9 +145,9 @@ cd ~/code/myapp-deploy
139ob deploy push
145ob deploy push
140```
146```
141
147
142−`ob deploy push` will locally build your app and then transfer it, along with all the Nix package dependencies, via ssh to the EC2 instance. It will also configure Nginx so that the public port 80 proxies to the running app.
148+`ob deploy push` will locally build your app and then transfer it, along with all the Nix package dependencies, via ssh to the EC2 instance.
143
149
144−At this point you are done. Your app will be accessible at `http://${HOSTNAME}`!
150+At this point you are done. Your app will be accessible at `${ROUTE}`.
145
151
146### Deploying an updated version
152### Deploying an updated version
147
153
default.nix+22−13
@@ -192,25 +192,33 @@ rec {
192 ln -s ${compressedJs frontend} $out/frontend.jsexe
192 ln -s ${compressedJs frontend} $out/frontend.jsexe
193 ''; #TODO: run frontend.jsexe through the asset processing pipeline
193 ''; #TODO: run frontend.jsexe through the asset processing pipeline
194
194
195− server = exe: hostName:
195+ server = { exe, hostName, adminEmail, routeHost, enableHttps }:
196 let system = "x86_64-linux";
196 let system = "x86_64-linux";
197 nixos = import (pkgs.path + /nixos);
197 nixos = import (pkgs.path + /nixos);
198− https = (import lib/https {}).module {
199− backendPort = 8000; # TODO read from config
198+ backendPort = 8000;
200− # sslConfig = {
201− # hostName = "example.com";
202− # adminEmail = "webmaster@example.com";
203− # subdomains = [ "www" ];
204− # };
205− };
206 in nixos {
199 in nixos {
207 inherit system;
200 inherit system;
208 configuration = args: {
201 configuration = args: {
209 imports = [
202 imports = [
210 (pkgs.path + /nixos/modules/virtualisation/amazon-image.nix)
203 (pkgs.path + /nixos/modules/virtualisation/amazon-image.nix)
211− https
212 ];
204 ];
205+ networking = {
213− networking = { inherit hostName; };
206+ inherit hostName;
207+ firewall.allowedTCPPorts = if enableHttps then [ 80 443 ] else [ 80 ];
208+ };
209+ services.nginx = {
210+ enable = true;
211+ virtualHosts."${routeHost}" = {
212+ enableACME = enableHttps;
213+ forceSSL = enableHttps;
214+ locations."/" = {
215+ proxyPass = "http://localhost:" + toString backendPort;
216+ };
217+ };
218+ };
219+ security.acme.certs = if enableHttps then {
220+ "${routeHost}".email = adminEmail;
221+ } else { };
214 systemd.services.backend = {
222 systemd.services.backend = {
215 wantedBy = [ "multi-user.target" ];
223 wantedBy = [ "multi-user.target" ];
216 after = [ "network.target" ];
224 after = [ "network.target" ];
@@ -303,8 +311,9 @@ rec {
303 in projectOut system // {
311 in projectOut system // {
304 inherit linuxExe;
312 inherit linuxExe;
305 exe = serverOn system;
313 exe = serverOn system;
306− server = { hostName }: server linuxExe hostName;
314+ server = args@{ hostName, adminEmail, routeHost, enableHttps }:
315+ server (args // { exe = linuxExe;});
307− obelisk = import (base + /.obelisk/impl) {};
316+ obelisk = import (base + "/.obelisk/impl") {};
308 };
317 };
309 haskellPackageSets = {
318 haskellPackageSets = {
310 ghc = reflex-platform.ghc.override {
319 ghc = reflex-platform.ghc.override {
lib/command/obelisk-command.cabal+2
@@ -27,6 +27,8 @@ library
27 , io-streams
27 , io-streams
28 , exceptions
28 , exceptions
29 , logging-effect
29 , logging-effect
30+ , lens
31+ , modern-uri
30 , monad-loops
32 , monad-loops
31 , mtl
33 , mtl
32 , network
34 , network
lib/command/src/Obelisk/Command.hs+11−1
@@ -175,6 +175,9 @@ deployInitOpts = DeployInitOpts
175 <$> strArgument (action "directory" <> metavar "DEPLOYDIR" <> help "Path to a directory that it will create")
175 <$> strArgument (action "directory" <> metavar "DEPLOYDIR" <> help "Path to a directory that it will create")
176 <*> strOption (long "ssh-key" <> action "file" <> metavar "SSHKEY" <> help "Path to an ssh key that it will symlink to")
176 <*> strOption (long "ssh-key" <> action "file" <> metavar "SSHKEY" <> help "Path to an ssh key that it will symlink to")
177 <*> some (strOption (long "hostname" <> metavar "HOSTNAME" <> help "hostname of the deployment target"))
177 <*> some (strOption (long "hostname" <> metavar "HOSTNAME" <> help "hostname of the deployment target"))
178+ <*> strOption (long "route" <> metavar "PUBLICROUTE" <> help "Publicly accessible URL of your app")
179+ <*> strOption (long "admin-email" <> metavar "ADMINEMAIL" <> help "Email address where administrative alerts will be sent")
180+ <*> flag True False (long "disable-https" <> help "Disable automatic https configuration for the backend")
178 <*> strOption (long "upstream" <> value "origin" <> metavar "REMOTE" <> help "git remote to use for the src thunk" <> showDefault)
181 <*> strOption (long "upstream" <> value "origin" <> metavar "REMOTE" <> help "git remote to use for the src thunk" <> showDefault)
179
182
180type TeamID = String
183type TeamID = String
@@ -195,6 +198,9 @@ data DeployInitOpts = DeployInitOpts
195 { _deployInitOpts_outputDir :: FilePath
198 { _deployInitOpts_outputDir :: FilePath
196 , _deployInitOpts_sshKey :: FilePath
199 , _deployInitOpts_sshKey :: FilePath
197 , _deployInitOpts_hostname :: [String]
200 , _deployInitOpts_hostname :: [String]
201+ , _deployInitOpts_route :: String
202+ , _deployInitOpts_adminEmail :: String
203+ , _deployInitOpts_enableHttps :: Bool
198 , _deployInitOpts_remote :: String
204 , _deployInitOpts_remote :: String
199 }
205 }
200 deriving Show
206 deriving Show
@@ -376,7 +382,11 @@ ob = \case
376 getThunkPtr' False root (T.pack $ _deployInitOpts_remote deployOpts)
382 getThunkPtr' False root (T.pack $ _deployInitOpts_remote deployOpts)
377 let sshKeyPath = _deployInitOpts_sshKey deployOpts
383 let sshKeyPath = _deployInitOpts_sshKey deployOpts
378 hostname = _deployInitOpts_hostname deployOpts
384 hostname = _deployInitOpts_hostname deployOpts
385+ route = _deployInitOpts_route deployOpts
386+ adminEmail = _deployInitOpts_adminEmail deployOpts
387+ enableHttps = _deployInitOpts_enableHttps deployOpts
379− deployInit thunkPtr (root </> "config") deployDir sshKeyPath hostname
388+ deployInit thunkPtr (root </> "config") deployDir
389+ sshKeyPath hostname route adminEmail enableHttps
380 DeployCommand_Push remoteBuilder -> deployPush "." $ case remoteBuilder of
390 DeployCommand_Push remoteBuilder -> deployPush "." $ case remoteBuilder of
381 Nothing -> pure []
391 Nothing -> pure []
382 Just RemoteBuilder_ObeliskVM -> (:[]) <$> VmBuilder.getNixBuildersArg
392 Just RemoteBuilder_ObeliskVM -> (:[]) <$> VmBuilder.getNixBuildersArg
lib/command/src/Obelisk/Command/Deploy.hs+121−24
@@ -2,9 +2,12 @@
2{-# LANGUAGE LambdaCase #-}
2{-# LANGUAGE LambdaCase #-}
3{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE ScopedTypeVariables #-}
4{-# LANGUAGE ScopedTypeVariables #-}
5+{-# LANGUAGE ViewPatterns #-}
5module Obelisk.Command.Deploy where
6module Obelisk.Command.Deploy where
6
7
8+import Control.Lens
7import Control.Monad
9import Control.Monad
10+import Control.Monad.Catch (Exception (displayException), MonadThrow, throwM, try)
8import Control.Monad.IO.Class (liftIO)
11import Control.Monad.IO.Class (liftIO)
9import Data.Bits
12import Data.Bits
10import Data.Default
13import Data.Default
@@ -18,6 +21,9 @@ import System.Environment (getEnvironment)
18import System.FilePath
21import System.FilePath
19import System.Posix.Files
22import System.Posix.Files
20import System.Process (delegate_ctlc, env, proc)
23import System.Process (delegate_ctlc, env, proc)
24+import Text.URI (URI)
25+import qualified Text.URI as URI
26+import Text.URI.Lens
21
27
22import Obelisk.App (MonadObelisk)
28import Obelisk.App (MonadObelisk)
23import Obelisk.CliApp (Severity (..), callProcessAndLogOutput, failWith, putLog, withSpinner)
29import Obelisk.CliApp (Severity (..), callProcessAndLogOutput, failWith, putLog, withSpinner)
@@ -26,30 +32,48 @@ import Obelisk.Command.Project
26import Obelisk.Command.Thunk
32import Obelisk.Command.Thunk
27import Obelisk.Command.Utils
33import Obelisk.Command.Utils
28
34
29−deployInit :: MonadObelisk m => ThunkPtr -> FilePath -> FilePath -> FilePath -> [String] -> m ()
35+deployInit
36+ :: MonadObelisk m
37+ => ThunkPtr
38+ -> FilePath
39+ -> FilePath
40+ -> FilePath
41+ -> [String] -- ^ hostnames
42+ -> String -- ^ route
43+ -> String -- ^ admin email
44+ -> Bool -- ^ enable https
45+ -> m ()
30−deployInit thunkPtr configDir deployDir sshKeyPath hostnames = do
46+deployInit thunkPtr configDir deployDir sshKeyPath hostnames route adminEmail enableHttps = do
31− hasConfigDir <- liftIO $ do
47+ (hasConfigDir, localKey) <- withSpinner ("Preparing " <> T.pack deployDir) $ do
48+ hasConfigDir <- liftIO $ do
32− createDirectoryIfMissing True deployDir
49+ createDirectoryIfMissing True deployDir
33− doesDirectoryExist configDir
50+ doesDirectoryExist configDir
34− localKey <- liftIO (doesFileExist sshKeyPath) >>= \case
51+ localKey <- liftIO (doesFileExist sshKeyPath) >>= \case
35− False -> failWith $ T.pack $ "ob deploy init: file does not exist: " <> sshKeyPath
52+ False -> failWith $ T.pack $ "ob deploy init: file does not exist: " <> sshKeyPath
36− True -> pure $ deployDir </> "ssh_key"
53+ True -> pure $ deployDir </> "ssh_key"
37− callProcessAndLogOutput (Notice, Error) $ proc "cp" [sshKeyPath, localKey]
54+ callProcessAndLogOutput (Notice, Error) $
55+ proc "cp" [sshKeyPath, localKey]
38− liftIO $ setFileMode localKey $ ownerReadMode .|. ownerWriteMode
56+ liftIO $ setFileMode localKey $ ownerReadMode .|. ownerWriteMode
39− liftIO $ writeFile (deployDir </> "backend_hosts") $ unlines hostnames
57+ return $ (hasConfigDir, localKey)
58+ withSpinner "Validating configuration" $ do
59+ void $ getHostFromRoute enableHttps route -- make sure that hostname is present
40 forM_ hostnames $ \hostname -> do
60 forM_ hostnames $ \hostname -> do
41 putLog Notice $ "Verifying host keys (" <> T.pack hostname <> ")"
61 putLog Notice $ "Verifying host keys (" <> T.pack hostname <> ")"
62+ -- Note: we can't use a spinner here as this function will prompt the user.
42 verifyHostKey (deployDir </> "backend_known_hosts") localKey hostname
63 verifyHostKey (deployDir </> "backend_known_hosts") localKey hostname
43− when hasConfigDir $ do
64+ when hasConfigDir $ withSpinner "Importing project configuration" $ do
44− callProcessAndLogOutput (Notice, Error) $ proc "cp"
65+ callProcessAndLogOutput (Notice, Error) $
45− [ "-r"
46− , "-T"
47− , configDir
48− , deployDir </> "config"
66+ proc "cp" [ "-r" , "-T" , configDir , deployDir </> "config"]
49− ]
67+ withSpinner "Writing deployment configuration" $ do
68+ writeDeployConfig deployDir "backend_hosts" $ unlines hostnames
69+ writeDeployConfig deployDir "enable_https" $ show enableHttps
70+ writeDeployConfig deployDir "admin_email" adminEmail
71+ writeDeployConfig deployDir ("config" </> "common" </> "route") $ route
72+ withSpinner "Creating source thunk (./src)" $ liftIO $ do
50− liftIO $ createThunk (deployDir </> "src") thunkPtr
73+ createThunk (deployDir </> "src") thunkPtr
51− liftIO $ setupObeliskImpl deployDir
74+ setupObeliskImpl deployDir
75+ withSpinner ("Initializing git repository (" <> T.pack deployDir <> ")") $
52− initGit deployDir
76+ initGit deployDir
53
77
54setupObeliskImpl :: FilePath -> IO ()
78setupObeliskImpl :: FilePath -> IO ()
55setupObeliskImpl deployDir = do
79setupObeliskImpl deployDir = do
@@ -59,8 +83,11 @@ setupObeliskImpl deployDir = do
59
83
60deployPush :: MonadObelisk m => FilePath -> m [String] -> m ()
84deployPush :: MonadObelisk m => FilePath -> m [String] -> m ()
61deployPush deployPath getNixBuilders = do
85deployPush deployPath getNixBuilders = do
62− let backendHosts = deployPath </> "backend_hosts"
86+ host <- readDeployConfig deployPath "backend_hosts"
63− host <- liftIO $ fmap (T.unpack . T.strip) $ T.readFile backendHosts
87+ adminEmail <- readDeployConfig deployPath "admin_email"
88+ enableHttps <- read <$> readDeployConfig deployPath "enable_https"
89+ route <- readDeployConfig deployPath $ "config" </> "common" </> "route"
90+ routeHost <- getHostFromRoute enableHttps route
64 let srcPath = deployPath </> "src"
91 let srcPath = deployPath </> "src"
65 build = do
92 build = do
66 builders <- getNixBuilders
93 builders <- getNixBuilders
@@ -70,7 +97,12 @@ deployPush deployPath getNixBuilders = do
70 , _target_attr = Just "server"
97 , _target_attr = Just "server"
71 }
98 }
72 , _nixBuildConfig_outLink = OutLink_None
99 , _nixBuildConfig_outLink = OutLink_None
73− , _nixBuildConfig_args = pure $ Arg "hostName" host
100+ , _nixBuildConfig_args =
101+ [ strArg "hostName" host
102+ , strArg "adminEmail" adminEmail
103+ , strArg "routeHost" routeHost
104+ , boolArg "enableHttps" enableHttps
105+ ]
74 , _nixBuildConfig_builders = builders
106 , _nixBuildConfig_builders = builders
75 }
107 }
76 return $ listToMaybe $ lines buildOutput
108 return $ listToMaybe $ lines buildOutput
@@ -114,6 +146,7 @@ deployPush deployPath getNixBuilders = do
114 ["-C", deployPath, "add", "."]
146 ["-C", deployPath, "add", "."]
115 callProcessAndLogOutput (Debug, Error) $ proc "git"
147 callProcessAndLogOutput (Debug, Error) $ proc "git"
116 ["-C", deployPath, "commit", "-m", "New deployment"]
148 ["-C", deployPath, "commit", "-m", "New deployment"]
149+ putLog Notice $ "Deployed => " <> T.pack route
117 where
150 where
118 callProcess' envMap cmd args = do
151 callProcess' envMap cmd args = do
119 processEnv <- Map.toList . (envMap <>) . Map.fromList <$> liftIO getEnvironment
152 processEnv <- Map.toList . (envMap <>) . Map.fromList <$> liftIO getEnvironment
@@ -131,6 +164,14 @@ deployMobile platform mobileArgs = withProjectRoot "." $ \root -> do
131 result <- nixBuildAttrWithCache srcDir $ platform <> ".frontend"
164 result <- nixBuildAttrWithCache srcDir $ platform <> ".frontend"
132 callProcessAndLogOutput (Notice, Error) $ proc (result </> "bin" </> "deploy") mobileArgs
165 callProcessAndLogOutput (Notice, Error) $ proc (result </> "bin" </> "deploy") mobileArgs
133
166
167+-- | Simplified deployment configuration mechanism. At one point we may revisit this.
168+writeDeployConfig :: MonadObelisk m => FilePath -> FilePath -> String -> m ()
169+writeDeployConfig deployDir fname = liftIO . writeFile (deployDir </> fname)
170+
171+readDeployConfig :: MonadObelisk m => FilePath -> FilePath -> m String
172+readDeployConfig deployDir fname = liftIO $ do
173+ fmap (T.unpack . T.strip) $ T.readFile $ deployDir </> fname
174+
134verifyHostKey :: MonadObelisk m => FilePath -> FilePath -> String -> m ()
175verifyHostKey :: MonadObelisk m => FilePath -> FilePath -> String -> m ()
135verifyHostKey knownHostsPath keyPath hostName =
176verifyHostKey knownHostsPath keyPath hostName =
136 callProcessAndLogOutput (Notice, Warning) $ proc "ssh" $
177 callProcessAndLogOutput (Notice, Warning) $ proc "ssh" $
@@ -145,3 +186,59 @@ sshArgs knownHostsPath keyPath askHostKeyCheck =
145 , "-o", "StrictHostKeyChecking=" <> if askHostKeyCheck then "ask" else "yes"
186 , "-o", "StrictHostKeyChecking=" <> if askHostKeyCheck then "ask" else "yes"
146 , "-i", keyPath
187 , "-i", keyPath
147 ]
188 ]
189+
190+-- common/route validation
191+-- TODO: move these to executable-config once the typed-config stuff is done.
192+
193+data InvalidRoute
194+ = InvalidRoute_NotHttps URI
195+ | InvalidRoute_MissingScheme URI
196+ | InvalidRoute_MissingHost URI
197+ | InvalidRoute_HasPort URI
198+ | InvalidRoute_HasPath URI
199+ deriving Show
200+
201+instance Exception InvalidRoute where
202+ displayException = \case
203+ InvalidRoute_MissingScheme uri -> route uri "must have an URI scheme"
204+ InvalidRoute_NotHttps uri -> route uri "must be HTTPS"
205+ InvalidRoute_MissingHost uri -> route uri "must contain a hostname"
206+ InvalidRoute_HasPort uri -> route uri "cannot specify port"
207+ InvalidRoute_HasPath uri -> route uri "cannot contain path"
208+ where
209+ route uri err = T.unpack $ "Route (" <> URI.render uri <> ") " <> err
210+
211+-- | Get the hostname from a https route
212+--
213+-- Fail if the route is invalid (i.e, no host present or scheme is not https)
214+getHostFromRoute
215+ :: MonadObelisk m
216+ => Bool -- ^ Ensure https?
217+ -> String
218+ -> m String
219+getHostFromRoute mustBeHttps route = do
220+ result :: Either InvalidRoute String <- try $ do
221+ validateCommonRouteAndGetHost mustBeHttps =<< URI.mkURI (T.strip $ T.pack route)
222+ either (failWith . T.pack . displayException) pure result
223+
224+validateCommonRouteAndGetHost
225+ :: (MonadThrow m, MonadObelisk m)
226+ => Bool -- ^ Ensure https?
227+ -> URI
228+ -> m String
229+validateCommonRouteAndGetHost mustBeHttps uri = do
230+ case uri ^? uriScheme of
231+ Just (Just (URI.unRText -> s)) -> case (mustBeHttps, s) of
232+ (False, _) -> pure ()
233+ (True, "https") -> pure ()
234+ _ -> throwM $ InvalidRoute_NotHttps uri
235+ _ -> throwM $ InvalidRoute_MissingScheme uri
236+ case uri ^. uriPath of
237+ [] -> pure ()
238+ _path -> throwM $ InvalidRoute_HasPath uri
239+ case uri ^? uriAuthority . _Right . authPort of
240+ Just (Just _port) -> throwM $ InvalidRoute_HasPort uri
241+ _ -> pure ()
242+ case uri ^? uriAuthority . _Right . authHost of
243+ Nothing -> throwM $ InvalidRoute_MissingHost uri
244+ Just sslHost -> return $ T.unpack $ URI.unRText sslHost
lib/command/src/Obelisk/Command/Nix.hs+19−5
@@ -10,10 +10,13 @@ module Obelisk.Command.Nix
10 , Target (..)
10 , Target (..)
11 , OutLink (..)
11 , OutLink (..)
12 , Arg (..)
12 , Arg (..)
13+ , boolArg
14+ , strArg
13 ) where
15 ) where
14
16
15import Control.Monad.Catch (catch, throwM)
17import Control.Monad.Catch (catch, throwM)
16import Control.Monad.IO.Class (liftIO)
18import Control.Monad.IO.Class (liftIO)
19+import Data.Bool (bool)
17import Data.Default
20import Data.Default
18import Data.List (intercalate)
21import Data.List (intercalate)
19import Data.Monoid ((<>))
22import Data.Monoid ((<>))
@@ -46,10 +49,21 @@ instance Default Target where
46 , _target_attr = Nothing
49 , _target_attr = Nothing
47 }
50 }
48
51
49−data Arg = Arg
52+data Arg
50− { _arg_key :: String
53+ = Arg_Str String String
51− , _arg_value :: String
54+ | Arg_Expr String String
52− }
55+ deriving (Eq, Show)
56+
57+strArg :: String -> String -> Arg
58+strArg k = Arg_Str k
59+
60+boolArg :: String -> Bool -> Arg
61+boolArg k = Arg_Expr k . bool "false" "true"
62+
63+cliFromArgs :: [Arg] -> [String]
64+cliFromArgs = concatMap $ \case
65+ Arg_Str k v -> ["--argstr", k, v]
66+ Arg_Expr k v -> ["--arg", k, v]
53
67
54data NixBuildConfig = NixBuildConfig
68data NixBuildConfig = NixBuildConfig
55 { _nixBuildConfig_target :: Target
69 { _nixBuildConfig_target :: Target
@@ -71,7 +85,7 @@ nixBuild cfg = withSpinner' ("Running nix-build on " <> desc) (Just $ const $ "B
71 attrArg = case attr of
85 attrArg = case attr of
72 Nothing -> []
86 Nothing -> []
73 Just a -> ["-A", a]
87 Just a -> ["-A", a]
74− args = mconcat [["--argstr", k, v] | Arg k v <- _nixBuildConfig_args cfg]
88+ args = cliFromArgs $ _nixBuildConfig_args cfg
75 outLink = case _nixBuildConfig_outLink cfg of
89 outLink = case _nixBuildConfig_outLink cfg of
76 OutLink_Default -> []
90 OutLink_Default -> []
77 OutLink_None -> ["--no-out-link"]
91 OutLink_None -> ["--no-out-link"]
lib/https/default.nixdeleted−7
@@ -1,7 +0,0 @@
1−# DO NOT HAND-EDIT THIS FILE
2−import ((import <nixpkgs> {}).fetchFromGitHub (
3− let json = builtins.fromJSON (builtins.readFile ./github.json);
4− in { inherit (json) owner repo rev sha256;
5− private = json.private or false;
6− }
7−))
lib/https/github.jsondeleted−6
@@ -1,6 +0,0 @@
1−{
2− "owner": "obsidiansystems",
3− "repo": "obelisk-https",
4− "rev": "5bc5cd81b669a0ea1b4415a1be8fd8c5a0680f0b",
5− "sha256": "105n7pxykz02n8ilgjznssmxa8hszclg21nqiyqdr64bcrxjyxf4"
6−}
migration/6a18939264224d6ce24c7426249edfea-e66afc8befac4d76b5780a960f768c31/obelisk-handoffnew file

No changes to the content.

migration/6a18939264224d6ce24c7426249edfea-e66afc8befac4d76b5780a960f768c31/obelisk-upgradenew file+15
@@ -0,0 +1,15 @@
1+# ob deploy: HTTPs support
2+
3+Obelisk deployments now support HTTPs. You must upgrade the deployment repo
4+associated with the obelisk project as follows:
5+
6+```
7+cd /your/project's/deployment/repo
8+echo True > enable_https
9+echo admin@yoursite.com > admin_email
10+echo https://yoursite.com/ > config/common/route
11+git add ... && git commit ...
12+ob deploy push
13+```
14+
15+If you do not wish to enable HTTPs, write `False` to `enable_https` instead.