From 1dc914a71c94e0395641565e5891a2dc33ba1b35 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Fri, 1 Apr 2016 21:20:13 -0400 Subject: separate propellor --init --- src/Propellor/DotDir.hs | 348 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 348 insertions(+) create mode 100644 src/Propellor/DotDir.hs (limited to 'src/Propellor/DotDir.hs') diff --git a/src/Propellor/DotDir.hs b/src/Propellor/DotDir.hs new file mode 100644 index 00000000..92c20654 --- /dev/null +++ b/src/Propellor/DotDir.hs @@ -0,0 +1,348 @@ +module Propellor.DotDir where + +import Propellor.Message +import Propellor.Bootstrap +import Propellor.Git +import Propellor.Gpg +import Utility.UserInfo +import Utility.Monad +import Utility.Process +import Utility.SafeCommand +import Utility.Exception +import Utility.Path + +import Data.Char +import Data.List +import Control.Monad +import Control.Monad.IfElse +import System.Directory +import System.FilePath +import System.Posix.Directory +import System.IO +import Control.Applicative +import Prelude + +distdir :: FilePath +distdir = "/usr/src/propellor" + +-- A distribution may include a bundle of propellor's git repository here. +-- If not, it will be pulled from the network when needed. +distrepo :: FilePath +distrepo = distdir "propellor.git" + +-- File containing the head rev of the distrepo. +disthead :: FilePath +disthead = distdir "head" + +upstreambranch :: String +upstreambranch = "upstream/master" + +-- Using the github mirror of the main propellor repo because +-- it is accessible over https for better security. +netrepo :: String +netrepo = "https://github.com/joeyh/propellor.git" + +dotPropellor :: IO FilePath +dotPropellor = do + home <- myHomeDir + return (home ".propellor") + +interactiveInit :: IO () +interactiveInit = ifM (doesDirectoryExist =<< dotPropellor) + ( error "~/.propellor/ already exists, not doing anything" + , do + welcomeBanner + setup + ) + +welcomeBanner :: IO () +welcomeBanner = putStr $ unlines $ map prettify + [ "" + , "" + , " _ ______`| ,-.__" + , " .--------------------------- / ~___-=O`/|O`/__| (____.'" + , " - Welcome to -- ~ / | / ) _.-'-._" + , " - Propellor! -- `/-==__ _/__|/__=-| ( ~_" + , " `--------------------------- * ~ | | '--------'" + , " (o) `" + , "" + , "" + ] + where + prettify = map (replace '~' '\\') + replace x y c + | c == x = y + | otherwise = c + +prompt :: String -> [(String, IO ())] -> IO () +prompt p cs = do + putStr (p ++ " [" ++ intercalate "|" (map fst cs) ++ "] ") + hFlush stdout + r <- map toLower <$> getLine + if null r + then snd (head cs) -- default to first choice on return + else case filter (\(s, _) -> map toLower s == r) cs of + [(_, a)] -> a + _ -> do + putStrLn "Not a valid choice, try again.. (Or ctrl-c to quit)" + prompt p cs + +section :: IO () +section = do + putStrLn "" + putStrLn "---------------------------------------------------------------------------------" + putStrLn "" + +setup :: IO () +setup = do + dotpropellor <- dotPropellor + putStrLn "Propellor's configuration file is ~/.propellor/config.hs" + putStrLn "" + putStrLn "Lets get you started with a simple config that you can adapt" + putStrLn "to your needs. You can start with:" + putStrLn " A: A clone of propellor's git repository (most flexible)" + putStrLn " B: The bare minimum files to use propellor (most simple)" + prompt "Which would you prefer?" + [ ("A", fullClone) + , ("B", minimalConfig) + ] + putStrLn "Ok, ~/.propellor/config.hs is set up!" + changeWorkingDirectory dotpropellor + + section + putStrLn "Let's try building the propellor configuration, to make sure it will work..." + buildPropellor Nothing + putStrLn "Great! Propellor is bootstrapped." + + section + putStrLn "Propellor uses gpg to encrypt private data about the systems it manages," + putStrLn "and to sign git commits." + gpg <- getGpgBin + ifM (inPath gpg) + ( setupGpgKey + , do + putStrLn "You don't seem to have gpg installed, so skipping setting it up." + explainManualSetupGpgKey + ) + + section + putStrLn "Everything is set up ..." + putStrLn "Your next step is to edit ~/.propellor/config.hs," + putStrLn "and run propellor again to try it out." + putStrLn "" + putStrLn "For docs, see https://propellor.branchable.com/" + putStrLn "Enjoy propellor!" + +explainManualSetupGpgKey :: IO () +explainManualSetupGpgKey = do + putStrLn "Propellor can still be used without gpg, but it won't be able to" + putStrLn "manage private data. You can set this up later:" + putStrLn " 1. gpg --gen-key" + putStrLn " 2. propellor --add-key (pass it the key ID generated in step 1)" + +setupGpgKey :: IO () +setupGpgKey = do + ks <- listSecretKeys + putStrLn "" + case ks of + [] -> makeGpgKey + [(k, _)] -> propellorAddKey k + _ -> do + let nks = zip ks (map show ([1..] :: [Integer])) + putStrLn "I see you have several gpg keys:" + forM_ nks $ \((k, d), n) -> + putStrLn $ " " ++ n ++ " " ++ d ++ " (keyid " ++ k ++ ")" + prompt "Which of your gpg keys should propellor use?" + (map (\((k, _), n) -> (n, propellorAddKey k)) nks) + +makeGpgKey :: IO () +makeGpgKey = do + putStrLn "You seem to not have any gpg secret keys." + prompt "Would you like to create one now?" + [("Y", rungpg), ("N", nope)] + where + nope = do + putStrLn "No problem." + explainManualSetupGpgKey + rungpg = do + putStrLn "Running gpg --gen-key ..." + gpg <- getGpgBin + void $ boolSystem gpg [Param "--gen-key"] + ks <- listSecretKeys + case ks of + [] -> do + putStrLn "Hmm, gpg seemed to not set up a secret key." + prompt "Want to try running gpg again?" + [("Y", rungpg), ("N", nope)] + ((k, _):_) -> propellorAddKey k + +propellorAddKey :: String -> IO () +propellorAddKey keyid = do + putStrLn "" + putStrLn $ "Telling propellor to use your gpg key by running: propellor --add-key " ++ keyid + d <- dotPropellor + unlessM (boolSystem (d "propellor") [Param "--add-key", Param keyid]) $ do + putStrLn "Oops, that didn't work! You can retry the same command later." + putStrLn "Continuing onward ..." + +minimalConfig :: IO () +minimalConfig = do + d <- dotPropellor + createDirectoryIfMissing True d + let cabalfile = d "config.cabal" + let configfile = d "config.hs" + writeFile cabalfile (unlines cabalcontent) + writeFile configfile (unlines configcontent) + changeWorkingDirectory d + void $ boolSystem "git" [Param "init"] + void $ boolSystem "git" [Param "add" , File cabalfile, File configfile] + where + cabalcontent = + [ "-- This is a cabal file to use to build your propellor configuration." + , "" + , "Name: config" + , "Cabal-Version: >= 1.6" + , "Build-Type: Simple" + , "Version: 0" + , "" + , "Executable propellor-config" + , " Main-Is: config.hs" + , " GHC-Options: -threaded -Wall -fno-warn-tabs -O0" + , " Extensions: TypeOperators" + , " Build-Depends: propellor >= 3.0, base >= 3" + ] + configcontent = + [ "-- This is the main configuration file for Propellor, and is used to build" + , "-- the propellor program." + , "" + , "import Propellor" + , "import qualified Propellor.Property.File as File" + , "import qualified Propellor.Property.Apt as Apt" + , "import qualified Propellor.Property.Cron as Cron" + , "import qualified Propellor.Property.User as User" + , "" + , "main :: IO ()" + , "main = defaultMain hosts" + , "" + , "-- The hosts propellor knows about." + , "hosts :: [Host]" + , "hosts =" + , " [ mybox" + , " ]" + , "" + , "-- An example host." + , "mybox :: Host" + , "mybox = host \"mybox.example.com\" $ props" + , " & osDebian Unstable \"amd64\"" + , " & Apt.stdSourcesList" + , " & Apt.unattendedUpgrades" + , " & Apt.installed [\"etckeeper\"]" + , " & Apt.installed [\"ssh\"]" + , " & User.hasSomePassword (User \"root\")" + , " & File.dirExists \"/var/www\"" + , " & Cron.runPropellor (Cron.Times \"30 * * * *\")" + , "" + ] + +fullClone :: IO () +fullClone = do + d <- dotPropellor + ifM (doesFileExist distrepo <||> doesDirectoryExist distrepo) + ( do + void $ boolSystem "git" [Param "clone", File distrepo, File d] + fetchUpstreamBranch distrepo + changeWorkingDirectory d + void $ boolSystem "git" [Param "remote", Param "rm", Param "origin"] + , do + void $ boolSystem "git" [Param "clone", Param netrepo, File d] + changeWorkingDirectory d + -- Rename origin to upstream and avoid + -- git push to that read-only repo. + void $ boolSystem "git" [Param "remote", Param "rename", Param "origin", Param "upstream"] + void $ boolSystem "git" [Param "config", Param "--unset", Param "branch.master.remote", Param "upstream"] + ) + +fetchUpstreamBranch :: FilePath -> IO () +fetchUpstreamBranch repo = do + changeWorkingDirectory =<< dotPropellor + void $ boolSystem "git" + [ Param "fetch" + , File repo + , Param ("+refs/heads/master:refs/remotes/" ++ upstreambranch) + , Param "--quiet" + ] + +checkRepoUpToDate :: IO () +checkRepoUpToDate = whenM (gitbundleavail <&&> dotpropellorpopulated) $ do + headrev <- takeWhile (/= '\n') <$> readFile disthead + changeWorkingDirectory =<< dotPropellor + headknown <- catchMaybeIO $ + withQuietOutput createProcessSuccess $ + proc "git" ["log", headrev] + if (headknown == Nothing) + then setupUpstreamMaster headrev + else do + theirhead <- getCurrentGitSha1 =<< getCurrentBranchRef + when (theirhead /= headrev) $ do + merged <- not . null <$> + readProcess "git" ["log", headrev ++ "..HEAD", "--ancestry-path"] + unless merged $ + warnoutofdate True + where + gitbundleavail = doesFileExist disthead + dotpropellorpopulated = do + d <- dotPropellor + doesFileExist (d "propellor.cabal") + +-- Passed the user's dotpropellor repository, makes upstream/master +-- be a usefully mergeable branch. +-- +-- We cannot just use origin/master, because in the case of a distrepo, +-- it only contains 1 commit. So, trying to merge with it will result +-- in lots of merge conflicts, since git cannot find a common parent +-- commit. +-- +-- Instead, the upstream/master branch is created by taking the +-- upstream/master branch (which must be an old version of propellor, +-- as distributed), and diffing from it to the current origin/master, +-- and committing the result. This is done in a temporary clone of the +-- repository, giving it a new master branch. That new branch is fetched +-- into the user's repository, as if fetching from a upstream remote, +-- yielding a new upstream/master branch. +setupUpstreamMaster :: String -> IO () +setupUpstreamMaster newref = do + changeWorkingDirectory =<< dotPropellor + go =<< catchMaybeIO getoldrev + where + go Nothing = warnoutofdate False + go (Just oldref) = do + let tmprepo = ".git/propellordisttmp" + let cleantmprepo = void $ catchMaybeIO $ removeDirectoryRecursive tmprepo + cleantmprepo + git ["clone", "--quiet", ".", tmprepo] + + changeWorkingDirectory tmprepo + git ["fetch", distrepo, "--quiet"] + git ["reset", "--hard", oldref, "--quiet"] + git ["merge", newref, "-s", "recursive", "-Xtheirs", "--quiet", "-m", "merging upstream version"] + + fetchUpstreamBranch tmprepo + cleantmprepo + warnoutofdate True + + getoldrev = takeWhile (/= '\n') + <$> readProcess "git" ["show-ref", upstreambranch, "--hash"] + + git = run "git" + run cmd ps = unlessM (boolSystem cmd (map Param ps)) $ + error $ "Failed to run " ++ cmd ++ " " ++ show ps + +warnoutofdate :: Bool -> IO () +warnoutofdate havebranch = do + warningMessage ("** Your ~/.propellor/ is out of date..") + let also s = hPutStrLn stderr (" " ++ s) + also ("A newer upstream version is available in " ++ distrepo) + if havebranch + then also ("To merge it, run: git merge " ++ upstreambranch) + else also ("To merge it, find the most recent commit in your repository's history that corresponds to an upstream release of propellor, and set refs/remotes/" ++ upstreambranch ++ " to it. Then run propellor again.") + also "" -- cgit v1.3-2-g0d8e From 71bc7071094ef56bca518f1eb4660718a0c9d0b0 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Fri, 1 Apr 2016 23:40:39 -0400 Subject: verify use of gpg key having a prompt here makes it clearer to the user why gpg is prompting for a passphrase. --- joeyconfig.hs | 2 +- src/Propellor/DotDir.hs | 20 +++++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) (limited to 'src/Propellor/DotDir.hs') diff --git a/joeyconfig.hs b/joeyconfig.hs index 489a0f58..20103e61 100644 --- a/joeyconfig.hs +++ b/joeyconfig.hs @@ -43,7 +43,7 @@ main = defaultMain hosts -- / \___-=O`/|O`/__| (____.' {- Propellor -- \ / | / ) _.-"-._ Deployed -} -- `/-==__ _/__|/__=-| ( \_ hosts :: [Host] -- * \ | | '--------' -hosts = -- (o) ` +hosts = -- (o) ` [ darkstar , gnu , clam diff --git a/src/Propellor/DotDir.hs b/src/Propellor/DotDir.hs index 92c20654..bf7550d5 100644 --- a/src/Propellor/DotDir.hs +++ b/src/Propellor/DotDir.hs @@ -64,7 +64,7 @@ welcomeBanner = putStr $ unlines $ map prettify , " - Welcome to -- ~ / | / ) _.-'-._" , " - Propellor! -- `/-==__ _/__|/__=-| ( ~_" , " `--------------------------- * ~ | | '--------'" - , " (o) `" + , " (o) `" , "" , "" ] @@ -90,7 +90,7 @@ prompt p cs = do section :: IO () section = do putStrLn "" - putStrLn "---------------------------------------------------------------------------------" + putStrLn "------------------------------------------------------------------------------" putStrLn "" setup :: IO () @@ -111,11 +111,13 @@ setup = do section putStrLn "Let's try building the propellor configuration, to make sure it will work..." + putStrLn "" buildPropellor Nothing + putStrLn "" putStrLn "Great! Propellor is bootstrapped." section - putStrLn "Propellor uses gpg to encrypt private data about the systems it manages," + putStrLn "Propellor can use gpg to encrypt private data about the systems it manages," putStrLn "and to sign git commits." gpg <- getGpgBin ifM (inPath gpg) @@ -146,14 +148,21 @@ setupGpgKey = do putStrLn "" case ks of [] -> makeGpgKey - [(k, _)] -> propellorAddKey k + [(k, d)] -> do + putStrLn $ "You have one gpg key: " ++ desckey k d + prompt "Should propellor use that key?" + [ ("Y", propellorAddKey k) + , ("N", putStrLn $ "Skipping gpg setup. If you change your mind, run: propellor --add-key " ++ k) + ] _ -> do let nks = zip ks (map show ([1..] :: [Integer])) putStrLn "I see you have several gpg keys:" forM_ nks $ \((k, d), n) -> - putStrLn $ " " ++ n ++ " " ++ d ++ " (keyid " ++ k ++ ")" + putStrLn $ " " ++ n ++ " " ++ desckey k d prompt "Which of your gpg keys should propellor use?" (map (\((k, _), n) -> (n, propellorAddKey k)) nks) + where + desckey k d = d ++ " (keyid " ++ k ++ ")" makeGpgKey :: IO () makeGpgKey = do @@ -199,6 +208,7 @@ minimalConfig = do where cabalcontent = [ "-- This is a cabal file to use to build your propellor configuration." + , "-- https://propellor.branchable.com/" , "" , "Name: config" , "Cabal-Version: >= 1.6" -- cgit v1.3-2-g0d8e From e8d767448a64b0ad529015c7125d97811f9cbbd7 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Fri, 1 Apr 2016 23:57:25 -0400 Subject: cosmetics --- src/Propellor/DotDir.hs | 56 +++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 27 deletions(-) (limited to 'src/Propellor/DotDir.hs') diff --git a/src/Propellor/DotDir.hs b/src/Propellor/DotDir.hs index bf7550d5..f0dace2f 100644 --- a/src/Propellor/DotDir.hs +++ b/src/Propellor/DotDir.hs @@ -4,6 +4,7 @@ import Propellor.Message import Propellor.Bootstrap import Propellor.Git import Propellor.Gpg +import Propellor.Types.Result import Utility.UserInfo import Utility.Monad import Utility.Process @@ -95,7 +96,6 @@ section = do setup :: IO () setup = do - dotpropellor <- dotPropellor putStrLn "Propellor's configuration file is ~/.propellor/config.hs" putStrLn "" putStrLn "Lets get you started with a simple config that you can adapt" @@ -103,11 +103,10 @@ setup = do putStrLn " A: A clone of propellor's git repository (most flexible)" putStrLn " B: The bare minimum files to use propellor (most simple)" prompt "Which would you prefer?" - [ ("A", fullClone) - , ("B", minimalConfig) + [ ("A", actionMessage "Cloning propellor's git repository" fullClone >> return ()) + , ("B", actionMessage "Creating minimal config" minimalConfig >> return ()) ] - putStrLn "Ok, ~/.propellor/config.hs is set up!" - changeWorkingDirectory dotpropellor + changeWorkingDirectory =<< dotPropellor section putStrLn "Let's try building the propellor configuration, to make sure it will work..." @@ -129,7 +128,7 @@ setup = do section putStrLn "Everything is set up ..." - putStrLn "Your next step is to edit ~/.propellor/config.hs," + putStrLn "Your next step is to edit ~/.propellor/config.hs" putStrLn "and run propellor again to try it out." putStrLn "" putStrLn "For docs, see https://propellor.branchable.com/" @@ -150,7 +149,7 @@ setupGpgKey = do [] -> makeGpgKey [(k, d)] -> do putStrLn $ "You have one gpg key: " ++ desckey k d - prompt "Should propellor use that key?" + prompt "Should propellor use that key?" [ ("Y", propellorAddKey k) , ("N", putStrLn $ "Skipping gpg setup. If you change your mind, run: propellor --add-key " ++ k) ] @@ -194,7 +193,7 @@ propellorAddKey keyid = do putStrLn "Oops, that didn't work! You can retry the same command later." putStrLn "Continuing onward ..." -minimalConfig :: IO () +minimalConfig :: IO Result minimalConfig = do d <- dotPropellor createDirectoryIfMissing True d @@ -205,10 +204,10 @@ minimalConfig = do changeWorkingDirectory d void $ boolSystem "git" [Param "init"] void $ boolSystem "git" [Param "add" , File cabalfile, File configfile] + return MadeChange where cabalcontent = [ "-- This is a cabal file to use to build your propellor configuration." - , "-- https://propellor.branchable.com/" , "" , "Name: config" , "Cabal-Version: >= 1.6" @@ -223,7 +222,7 @@ minimalConfig = do ] configcontent = [ "-- This is the main configuration file for Propellor, and is used to build" - , "-- the propellor program." + , "-- the propellor program. https://propellor.branchable.com/" , "" , "import Propellor" , "import qualified Propellor.Property.File as File" @@ -254,28 +253,32 @@ minimalConfig = do , "" ] -fullClone :: IO () +fullClone :: IO Result fullClone = do d <- dotPropellor - ifM (doesFileExist distrepo <||> doesDirectoryExist distrepo) - ( do - void $ boolSystem "git" [Param "clone", File distrepo, File d] - fetchUpstreamBranch distrepo - changeWorkingDirectory d - void $ boolSystem "git" [Param "remote", Param "rm", Param "origin"] - , do - void $ boolSystem "git" [Param "clone", Param netrepo, File d] - changeWorkingDirectory d + let enterdotpropellor = changeWorkingDirectory d >> return True + ok <- ifM (doesFileExist distrepo <||> doesDirectoryExist distrepo) + ( allM id + [ boolSystem "git" [Param "clone", File distrepo, File d] + , fetchUpstreamBranch distrepo + , enterdotpropellor + , boolSystem "git" [Param "remote", Param "rm", Param "origin"] + ] + , allM id + [ boolSystem "git" [Param "clone", Param netrepo, File d] + , enterdotpropellor -- Rename origin to upstream and avoid -- git push to that read-only repo. - void $ boolSystem "git" [Param "remote", Param "rename", Param "origin", Param "upstream"] - void $ boolSystem "git" [Param "config", Param "--unset", Param "branch.master.remote", Param "upstream"] + , boolSystem "git" [Param "remote", Param "rename", Param "origin", Param "upstream"] + , boolSystem "git" [Param "config", Param "--unset", Param "branch.master.remote", Param "upstream"] + ] ) + return (toResult ok) -fetchUpstreamBranch :: FilePath -> IO () +fetchUpstreamBranch :: FilePath -> IO Bool fetchUpstreamBranch repo = do changeWorkingDirectory =<< dotPropellor - void $ boolSystem "git" + boolSystem "git" [ Param "fetch" , File repo , Param ("+refs/heads/master:refs/remotes/" ++ upstreambranch) @@ -304,8 +307,7 @@ checkRepoUpToDate = whenM (gitbundleavail <&&> dotpropellorpopulated) $ do d <- dotPropellor doesFileExist (d "propellor.cabal") --- Passed the user's dotpropellor repository, makes upstream/master --- be a usefully mergeable branch. +-- Makes upstream/master in dotPropellor be a usefully mergeable branch. -- -- We cannot just use origin/master, because in the case of a distrepo, -- it only contains 1 commit. So, trying to merge with it will result @@ -336,7 +338,7 @@ setupUpstreamMaster newref = do git ["reset", "--hard", oldref, "--quiet"] git ["merge", newref, "-s", "recursive", "-Xtheirs", "--quiet", "-m", "merging upstream version"] - fetchUpstreamBranch tmprepo + void $ fetchUpstreamBranch tmprepo cleantmprepo warnoutofdate True -- cgit v1.3-2-g0d8e From e3920861ee444945e54fd42ce0f599d585155652 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sat, 2 Apr 2016 01:29:23 -0400 Subject: Stack support. * Stack support. "git config propellor.buildsystem stack" will make propellor build its config using stack. * When propellor is installed using stack, propellor --init will automatically set propellor.buildsystem=stack. --- Makefile | 1 + debian/changelog | 4 ++ ...use___96__GHC__95__PACKAGE__95__PATH__96__.mdwn | 6 +++ propellor.cabal | 6 +++ src/Propellor/Bootstrap.hs | 54 ++++++++++++++++++---- src/Propellor/DotDir.hs | 47 +++++++++++++++---- stack.yaml | 6 +++ 7 files changed, 107 insertions(+), 17 deletions(-) create mode 100644 stack.yaml (limited to 'src/Propellor/DotDir.hs') diff --git a/Makefile b/Makefile index a9ad2b84..5322d6c5 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ install: mkdir -p dist/gittmp $(CABAL) sdist cat dist/propellor-*.tar.gz | (cd dist/gittmp && tar zx --strip-components=1) + cp stack.yaml dist/gittmp # also include in bundle # cabal sdist does not preserve symlinks, so copy over file cd dist/gittmp && for f in $$(find -type f); do rm -f $$f; cp -a ../../$$f $$f; done # reset mtime on files in git bundle so bundle is reproducible diff --git a/debian/changelog b/debian/changelog index ae593902..aab077b0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -68,6 +68,10 @@ propellor (3.0.0) UNRELEASED; urgency=medium walk the user through setting up ~/.propellor, with a choice between a clone of propellor's git repository, or a minimal config, and will configure propellor to use a gpg key. + * Stack support. "git config propellor.buildsystem stack" will make + propellor build its config using stack. + * When propellor is installed using stack, propellor --init will + automatically set propellor.buildsystem=stack. -- Joey Hess Wed, 30 Mar 2016 15:45:08 -0400 diff --git a/doc/todo/detect_and_use___96__GHC__95__PACKAGE__95__PATH__96__.mdwn b/doc/todo/detect_and_use___96__GHC__95__PACKAGE__95__PATH__96__.mdwn index 2973e662..55c3ef7e 100644 --- a/doc/todo/detect_and_use___96__GHC__95__PACKAGE__95__PATH__96__.mdwn +++ b/doc/todo/detect_and_use___96__GHC__95__PACKAGE__95__PATH__96__.mdwn @@ -7,3 +7,9 @@ and run with stack exec -- propellor ... see [[https://github.com/yesodweb/yesod/issues/1018]] and [[https://github.com/yesodweb/yesod/commit/a7cccf2a7c5df8b26da9ea4fdcb6bac5ab3a3b75]] + +> I don't think `stack exec propellor` makes sense to use. +> Instead, `stack install propellor` and then put that in PATH. +> I've now made `propellor --init` know when it was built using stack, +> and it will set up propellor to continue to build itself using stack. +> [[done]] --[[Joey]] diff --git a/propellor.cabal b/propellor.cabal index d97d4096..3431d410 100644 --- a/propellor.cabal +++ b/propellor.cabal @@ -34,6 +34,10 @@ Description: . It is configured using haskell. +Flag UseStack + Description: Have propellor rebuild itself using Stack (default is Cabal) + Default: False + Executable propellor Main-Is: wrapper.hs GHC-Options: -threaded -Wall -fno-warn-tabs -O0 @@ -46,6 +50,8 @@ Executable propellor unix, unix-compat, ansi-terminal, containers (>= 0.5), network, async, time, mtl, transformers, exceptions (>= 0.6), stm, text, concurrent-output + if flag(UseStack) + CPP-Options: -DUSE_STACK Executable propellor-config Main-Is: config.hs diff --git a/src/Propellor/Bootstrap.hs b/src/Propellor/Bootstrap.hs index 969e1a42..300be156 100644 --- a/src/Propellor/Bootstrap.hs +++ b/src/Propellor/Bootstrap.hs @@ -7,6 +7,7 @@ module Propellor.Bootstrap ( import Propellor.Base import Propellor.Types.Info +import Propellor.Git.Config import System.Posix.Files import Data.List @@ -139,16 +140,22 @@ buildPropellor mh = unlessM (actionMessage "Propellor build" (build msys)) $ Just (InfoVal sys) -> Just sys _ -> Nothing --- Build propellor using cabal, and symlink propellor to where cabal --- leaves the built binary. --- +-- Build propellor using cabal or stack, and symlink propellor to the +-- built binary. +build :: Maybe System -> IO Bool +build msys = catchBoolIO $ do + bs <- getGitConfigValue "propellor.buildsystem" + case bs of + Just "stack" -> stackBuild msys + _ -> cabalBuild msys + -- For speed, only runs cabal configure when it's not been run before. -- If the build fails cabal may need to have configure re-run. -- -- If the cabal configure fails, and a System is provided, installs -- dependencies and retries. -build :: Maybe System -> IO Bool -build msys = catchBoolIO $ do +cabalBuild :: Maybe System -> IO Bool +cabalBuild msys = do make "dist/setup-config" ["propellor.cabal"] cabal_configure unlessM cabal_build $ unlessM (cabal_configure <&&> cabal_build) $ @@ -163,14 +170,11 @@ build msys = catchBoolIO $ do unlessM (boolSystem "cp" [Param "-af", Param cabalbuiltbin, Param (tmpfor safetycopy)]) $ error "cp of binary failed" rename (tmpfor safetycopy) safetycopy - createSymbolicLink safetycopy (tmpfor dest) - rename (tmpfor dest) dest + symlinkPropellorBin safetycopy return True where - dest = "propellor" cabalbuiltbin = "dist/build/propellor-config/propellor-config" safetycopy = cabalbuiltbin ++ ".built" - tmpfor f = f ++ ".propellortmp" cabal_configure = ifM (cabal ["configure"]) ( return True , case msys of @@ -181,6 +185,35 @@ build msys = catchBoolIO $ do ) cabal_build = cabal ["build", "propellor-config"] +stackBuild :: Maybe System -> IO Bool +stackBuild _msys = do + createDirectoryIfMissing True builddest + ifM (stack buildparams) + ( do + symlinkPropellorBin (builddest "propellor-config") + return True + , return False + ) + where + builddest = ".built" + buildparams = + [ "--local-bin-path", builddest + , "build" + , ":propellor-config" -- only build config program + , "--copy-bins" + ] + +-- Atomic symlink creation/update. +symlinkPropellorBin :: FilePath -> IO () +symlinkPropellorBin bin = do + createSymbolicLink bin (tmpfor dest) + rename (tmpfor dest) dest + where + dest = "propellor" + +tmpfor :: FilePath -> FilePath +tmpfor f = f ++ ".propellortmp" + make :: FilePath -> [FilePath] -> IO Bool -> IO () make dest srcs builder = do dt <- getmtime dest @@ -193,3 +226,6 @@ make dest srcs builder = do cabal :: [String] -> IO Bool cabal = boolSystem "cabal" . map Param + +stack :: [String] -> IO Bool +stack = boolSystem "stack" . map Param diff --git a/src/Propellor/DotDir.hs b/src/Propellor/DotDir.hs index f0dace2f..90147abe 100644 --- a/src/Propellor/DotDir.hs +++ b/src/Propellor/DotDir.hs @@ -1,3 +1,5 @@ +{-# LANGUAGE CPP #-} + module Propellor.DotDir where import Propellor.Message @@ -11,9 +13,12 @@ import Utility.Process import Utility.SafeCommand import Utility.Exception import Utility.Path +-- This module is autogenerated by the build system. +import qualified Paths_propellor as Package import Data.Char import Data.List +import Data.Version import Control.Monad import Control.Monad.IfElse import System.Directory @@ -48,6 +53,15 @@ dotPropellor = do home <- myHomeDir return (home ".propellor") +data InitCfg = UseCabal | UseStack + +initCfg :: InitCfg +#ifdef USE_STACK +initCfg = UseStack +#else +initCfg = UseCabal +#endif + interactiveInit :: IO () interactiveInit = ifM (doesDirectoryExist =<< dotPropellor) ( error "~/.propellor/ already exists, not doing anything" @@ -95,7 +109,7 @@ section = do putStrLn "" setup :: IO () -setup = do +setup initcfg = do putStrLn "Propellor's configuration file is ~/.propellor/config.hs" putStrLn "" putStrLn "Lets get you started with a simple config that you can adapt" @@ -103,14 +117,21 @@ setup = do putStrLn " A: A clone of propellor's git repository (most flexible)" putStrLn " B: The bare minimum files to use propellor (most simple)" prompt "Which would you prefer?" - [ ("A", actionMessage "Cloning propellor's git repository" fullClone >> return ()) - , ("B", actionMessage "Creating minimal config" minimalConfig >> return ()) + [ ("A", void $ actionMessage "Cloning propellor's git repository" fullClone) + , ("B", void $ actionMessage "Creating minimal config" minimalConfig) ] changeWorkingDirectory =<< dotPropellor section putStrLn "Let's try building the propellor configuration, to make sure it will work..." putStrLn "" + void $ boolSystem "git" + [ Param "config" + , Param "propellor.buildsystem" + , Param $ case initCfg of + UseCabal -> "cabal" + UseStack -> "stack" + ] buildPropellor Nothing putStrLn "" putStrLn "Great! Propellor is bootstrapped." @@ -197,15 +218,16 @@ minimalConfig :: IO Result minimalConfig = do d <- dotPropellor createDirectoryIfMissing True d - let cabalfile = d "config.cabal" - let configfile = d "config.hs" - writeFile cabalfile (unlines cabalcontent) - writeFile configfile (unlines configcontent) changeWorkingDirectory d void $ boolSystem "git" [Param "init"] - void $ boolSystem "git" [Param "add" , File cabalfile, File configfile] + addfile "config.cabal" cabalcontent + addfile "config.hs" configcontent + addfile "stack.yaml" stackcontent return MadeChange where + addfile f content = do + writeFile f (unlines content) + void $ boolSystem "git" [Param "add" , File f] cabalcontent = [ "-- This is a cabal file to use to build your propellor configuration." , "" @@ -252,6 +274,15 @@ minimalConfig = do , " & Cron.runPropellor (Cron.Times \"30 * * * *\")" , "" ] + stackcontent = + -- This should be the same resolver version in propellor's + -- own stack.yaml + [ "resolver: lts-5.10" + , "packages:" + , "- '.'" + , "extra-deps:" + , "- propellor-" ++ showVersion Package.version + ] fullClone :: IO Result fullClone = do diff --git a/stack.yaml b/stack.yaml new file mode 100644 index 00000000..6b5e859c --- /dev/null +++ b/stack.yaml @@ -0,0 +1,6 @@ +resolver: lts-5.10 +packages: +- '.' +flags: + propellor: + usestack: true -- cgit v1.3-2-g0d8e From bdffac1bfae1ec20ac20453b559addca2b98e1ff Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sat, 2 Apr 2016 01:50:06 -0400 Subject: typo --- src/Propellor/DotDir.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Propellor/DotDir.hs') diff --git a/src/Propellor/DotDir.hs b/src/Propellor/DotDir.hs index 90147abe..43067417 100644 --- a/src/Propellor/DotDir.hs +++ b/src/Propellor/DotDir.hs @@ -109,7 +109,7 @@ section = do putStrLn "" setup :: IO () -setup initcfg = do +setup = do putStrLn "Propellor's configuration file is ~/.propellor/config.hs" putStrLn "" putStrLn "Lets get you started with a simple config that you can adapt" -- cgit v1.3-2-g0d8e From ecf786ddab0161a4f5fa84e07cced60efb1595cd Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sat, 2 Apr 2016 12:05:07 -0400 Subject: got rid of build flag to detect stack --- propellor.cabal | 6 ------ src/Propellor/DotDir.hs | 20 +++++++------------- stack.yaml | 3 --- 3 files changed, 7 insertions(+), 22 deletions(-) (limited to 'src/Propellor/DotDir.hs') diff --git a/propellor.cabal b/propellor.cabal index 3431d410..d97d4096 100644 --- a/propellor.cabal +++ b/propellor.cabal @@ -34,10 +34,6 @@ Description: . It is configured using haskell. -Flag UseStack - Description: Have propellor rebuild itself using Stack (default is Cabal) - Default: False - Executable propellor Main-Is: wrapper.hs GHC-Options: -threaded -Wall -fno-warn-tabs -O0 @@ -50,8 +46,6 @@ Executable propellor unix, unix-compat, ansi-terminal, containers (>= 0.5), network, async, time, mtl, transformers, exceptions (>= 0.6), stm, text, concurrent-output - if flag(UseStack) - CPP-Options: -DUSE_STACK Executable propellor-config Main-Is: config.hs diff --git a/src/Propellor/DotDir.hs b/src/Propellor/DotDir.hs index 43067417..21479cb1 100644 --- a/src/Propellor/DotDir.hs +++ b/src/Propellor/DotDir.hs @@ -1,5 +1,3 @@ -{-# LANGUAGE CPP #-} - module Propellor.DotDir where import Propellor.Message @@ -53,14 +51,11 @@ dotPropellor = do home <- myHomeDir return (home ".propellor") -data InitCfg = UseCabal | UseStack - -initCfg :: InitCfg -#ifdef USE_STACK -initCfg = UseStack -#else -initCfg = UseCabal -#endif +-- Detect if propellor was built using stack. This is somewhat of a hack. +buildSystem :: IO String +buildSystem = do + d <- Package.getLibDir + return $ if "stack-work" `isInfixOf` d then "stack" else "cabal" interactiveInit :: IO () interactiveInit = ifM (doesDirectoryExist =<< dotPropellor) @@ -125,12 +120,11 @@ setup = do section putStrLn "Let's try building the propellor configuration, to make sure it will work..." putStrLn "" + b <- buildSystem void $ boolSystem "git" [ Param "config" , Param "propellor.buildsystem" - , Param $ case initCfg of - UseCabal -> "cabal" - UseStack -> "stack" + , Param b ] buildPropellor Nothing putStrLn "" diff --git a/stack.yaml b/stack.yaml index 6b5e859c..7b6bcef8 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,6 +1,3 @@ resolver: lts-5.10 packages: - '.' -flags: - propellor: - usestack: true -- cgit v1.3-2-g0d8e From d9e7191bb54d27c5680a98da448725e5314a3e23 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sat, 2 Apr 2016 12:16:43 -0400 Subject: use concurrent-output consistently --- src/Propellor/DotDir.hs | 91 ++++++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 42 deletions(-) (limited to 'src/Propellor/DotDir.hs') diff --git a/src/Propellor/DotDir.hs b/src/Propellor/DotDir.hs index 21479cb1..d8be3af9 100644 --- a/src/Propellor/DotDir.hs +++ b/src/Propellor/DotDir.hs @@ -23,6 +23,7 @@ import System.Directory import System.FilePath import System.Posix.Directory import System.IO +import System.Console.Concurrent import Control.Applicative import Prelude @@ -65,8 +66,14 @@ interactiveInit = ifM (doesDirectoryExist =<< dotPropellor) setup ) +say :: String -> IO () +say = outputConcurrent + +sayLn :: String -> IO () +sayLn s = say (s ++ "\n") + welcomeBanner :: IO () -welcomeBanner = putStr $ unlines $ map prettify +welcomeBanner = say $ unlines $ map prettify [ "" , "" , " _ ______`| ,-.__" @@ -86,7 +93,7 @@ welcomeBanner = putStr $ unlines $ map prettify prompt :: String -> [(String, IO ())] -> IO () prompt p cs = do - putStr (p ++ " [" ++ intercalate "|" (map fst cs) ++ "] ") + say (p ++ " [" ++ intercalate "|" (map fst cs) ++ "] ") hFlush stdout r <- map toLower <$> getLine if null r @@ -94,23 +101,23 @@ prompt p cs = do else case filter (\(s, _) -> map toLower s == r) cs of [(_, a)] -> a _ -> do - putStrLn "Not a valid choice, try again.. (Or ctrl-c to quit)" + sayLn "Not a valid choice, try again.. (Or ctrl-c to quit)" prompt p cs section :: IO () section = do - putStrLn "" - putStrLn "------------------------------------------------------------------------------" - putStrLn "" + sayLn "" + sayLn "------------------------------------------------------------------------------" + sayLn "" setup :: IO () setup = do - putStrLn "Propellor's configuration file is ~/.propellor/config.hs" - putStrLn "" - putStrLn "Lets get you started with a simple config that you can adapt" - putStrLn "to your needs. You can start with:" - putStrLn " A: A clone of propellor's git repository (most flexible)" - putStrLn " B: The bare minimum files to use propellor (most simple)" + sayLn "Propellor's configuration file is ~/.propellor/config.hs" + sayLn "" + sayLn "Lets get you started with a simple config that you can adapt" + sayLn "to your needs. You can start with:" + sayLn " A: A clone of propellor's git repository (most flexible)" + sayLn " B: The bare minimum files to use propellor (most simple)" prompt "Which would you prefer?" [ ("A", void $ actionMessage "Cloning propellor's git repository" fullClone) , ("B", void $ actionMessage "Creating minimal config" minimalConfig) @@ -118,8 +125,8 @@ setup = do changeWorkingDirectory =<< dotPropellor section - putStrLn "Let's try building the propellor configuration, to make sure it will work..." - putStrLn "" + sayLn "Let's try building the propellor configuration, to make sure it will work..." + sayLn "" b <- buildSystem void $ boolSystem "git" [ Param "config" @@ -127,52 +134,52 @@ setup = do , Param b ] buildPropellor Nothing - putStrLn "" - putStrLn "Great! Propellor is bootstrapped." + sayLn "" + sayLn "Great! Propellor is bootstrapped." section - putStrLn "Propellor can use gpg to encrypt private data about the systems it manages," - putStrLn "and to sign git commits." + sayLn "Propellor can use gpg to encrypt private data about the systems it manages," + sayLn "and to sign git commits." gpg <- getGpgBin ifM (inPath gpg) ( setupGpgKey , do - putStrLn "You don't seem to have gpg installed, so skipping setting it up." + sayLn "You don't seem to have gpg installed, so skipping setting it up." explainManualSetupGpgKey ) section - putStrLn "Everything is set up ..." - putStrLn "Your next step is to edit ~/.propellor/config.hs" - putStrLn "and run propellor again to try it out." - putStrLn "" - putStrLn "For docs, see https://propellor.branchable.com/" - putStrLn "Enjoy propellor!" + sayLn "Everything is set up ..." + sayLn "Your next step is to edit ~/.propellor/config.hs" + sayLn "and run propellor again to try it out." + sayLn "" + sayLn "For docs, see https://propellor.branchable.com/" + sayLn "Enjoy propellor!" explainManualSetupGpgKey :: IO () explainManualSetupGpgKey = do - putStrLn "Propellor can still be used without gpg, but it won't be able to" - putStrLn "manage private data. You can set this up later:" - putStrLn " 1. gpg --gen-key" - putStrLn " 2. propellor --add-key (pass it the key ID generated in step 1)" + sayLn "Propellor can still be used without gpg, but it won't be able to" + sayLn "manage private data. You can set this up later:" + sayLn " 1. gpg --gen-key" + sayLn " 2. propellor --add-key (pass it the key ID generated in step 1)" setupGpgKey :: IO () setupGpgKey = do ks <- listSecretKeys - putStrLn "" + sayLn "" case ks of [] -> makeGpgKey [(k, d)] -> do - putStrLn $ "You have one gpg key: " ++ desckey k d + sayLn $ "You have one gpg key: " ++ desckey k d prompt "Should propellor use that key?" [ ("Y", propellorAddKey k) - , ("N", putStrLn $ "Skipping gpg setup. If you change your mind, run: propellor --add-key " ++ k) + , ("N", sayLn $ "Skipping gpg setup. If you change your mind, run: propellor --add-key " ++ k) ] _ -> do let nks = zip ks (map show ([1..] :: [Integer])) - putStrLn "I see you have several gpg keys:" + sayLn "I see you have several gpg keys:" forM_ nks $ \((k, d), n) -> - putStrLn $ " " ++ n ++ " " ++ desckey k d + sayLn $ " " ++ n ++ " " ++ desckey k d prompt "Which of your gpg keys should propellor use?" (map (\((k, _), n) -> (n, propellorAddKey k)) nks) where @@ -180,33 +187,33 @@ setupGpgKey = do makeGpgKey :: IO () makeGpgKey = do - putStrLn "You seem to not have any gpg secret keys." + sayLn "You seem to not have any gpg secret keys." prompt "Would you like to create one now?" [("Y", rungpg), ("N", nope)] where nope = do - putStrLn "No problem." + sayLn "No problem." explainManualSetupGpgKey rungpg = do - putStrLn "Running gpg --gen-key ..." + sayLn "Running gpg --gen-key ..." gpg <- getGpgBin void $ boolSystem gpg [Param "--gen-key"] ks <- listSecretKeys case ks of [] -> do - putStrLn "Hmm, gpg seemed to not set up a secret key." + sayLn "Hmm, gpg seemed to not set up a secret key." prompt "Want to try running gpg again?" [("Y", rungpg), ("N", nope)] ((k, _):_) -> propellorAddKey k propellorAddKey :: String -> IO () propellorAddKey keyid = do - putStrLn "" - putStrLn $ "Telling propellor to use your gpg key by running: propellor --add-key " ++ keyid + sayLn "" + sayLn $ "Telling propellor to use your gpg key by running: propellor --add-key " ++ keyid d <- dotPropellor unlessM (boolSystem (d "propellor") [Param "--add-key", Param keyid]) $ do - putStrLn "Oops, that didn't work! You can retry the same command later." - putStrLn "Continuing onward ..." + sayLn "Oops, that didn't work! You can retry the same command later." + sayLn "Continuing onward ..." minimalConfig :: IO Result minimalConfig = do -- cgit v1.3-2-g0d8e From 2d046cad32f5950472b87bc8eb97686fbf2cdcb3 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sat, 2 Apr 2016 12:21:49 -0400 Subject: force flush on prompt --- src/Propellor/DotDir.hs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/Propellor/DotDir.hs') diff --git a/src/Propellor/DotDir.hs b/src/Propellor/DotDir.hs index d8be3af9..4de7b9c8 100644 --- a/src/Propellor/DotDir.hs +++ b/src/Propellor/DotDir.hs @@ -94,6 +94,7 @@ welcomeBanner = say $ unlines $ map prettify prompt :: String -> [(String, IO ())] -> IO () prompt p cs = do say (p ++ " [" ++ intercalate "|" (map fst cs) ++ "] ") + flushConcurrentOutput hFlush stdout r <- map toLower <$> getLine if null r -- cgit v1.3-2-g0d8e From 149bb5e170e81d818564a5c35bc3e59c8e074687 Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Sat, 2 Apr 2016 12:22:35 -0400 Subject: move clouds --- src/Propellor/DotDir.hs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/Propellor/DotDir.hs') diff --git a/src/Propellor/DotDir.hs b/src/Propellor/DotDir.hs index 4de7b9c8..4f27788d 100644 --- a/src/Propellor/DotDir.hs +++ b/src/Propellor/DotDir.hs @@ -76,11 +76,11 @@ welcomeBanner :: IO () welcomeBanner = say $ unlines $ map prettify [ "" , "" - , " _ ______`| ,-.__" - , " .--------------------------- / ~___-=O`/|O`/__| (____.'" - , " - Welcome to -- ~ / | / ) _.-'-._" - , " - Propellor! -- `/-==__ _/__|/__=-| ( ~_" - , " `--------------------------- * ~ | | '--------'" + , " _ ______`| ,-.__" + , " .--------------------------- / ~___-=O`/|O`/__| (____.'" + , " - Welcome to -- ~ / | / ) _.-'-._" + , " - Propellor! -- `/-==__ _/__|/__=-| ( ~_" + , " `--------------------------- * ~ | | '--------'" , " (o) `" , "" , "" -- cgit v1.3-2-g0d8e