1{-# LANGUAGE NoImplicitPrelude #-}
2{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE RecordWildCards #-}
4module Stackage.CompleteBuild
13import Control.Concurrent (threadDelay)
14import Control.Concurrent.Async (withAsync)
15import Data.Default.Class (def)
16import Data.Semigroup (Max (..), Option (..))
17import Data.Text.Read (decimal)
19import Data.Yaml (decodeFileEither, encodeFile)
20import Network.HTTP.Client
21import Network.HTTP.Client.TLS (tlsManagerSettings)
22import Stackage.BuildConstraints
23import Stackage.BuildPlan
24import Stackage.CheckBuildPlan
25import Stackage.PerformBuild
26import Stackage.Prelude
27import Stackage.ServerBundle
28import Stackage.UpdateBuildPlan
30import System.Environment (lookupEnv)
31import System.IO (BufferMode (LineBuffering), hSetBuffering)
33-- | Flags passed in from the command line.
34data BuildFlags = BuildFlags
35 { bfEnableTests :: !Bool
36 , bfEnableHaddock :: !Bool
38 , bfEnableLibProfile :: !Bool
40 , bfSkipCheck :: !Bool
43data BuildType = Nightly | LTS BumpType
44 deriving (Show, Read, Eq, Ord)
46data BumpType = Major | Minor
47 deriving (Show, Read, Eq, Ord)
49data Settings = Settings
51 , planFile :: FilePath
52 , buildDir :: FilePath
54 , title :: Text -> Text -- ^ GHC version -> title
56 , setArgs :: Text -> UploadBundle -> UploadBundle
58 , distroName :: Text -- ^ distro name on Hackage
61nightlyPlanFile :: Text -- ^ day
63nightlyPlanFile day = fpFromText ("nightly-" ++ day) <.> "yaml"
65nightlySettings :: Text -- ^ day
68nightlySettings day plan' = Settings
69 { planFile = nightlyPlanFile day
70 , buildDir = fpFromText $ "builds/stackage-nightly-" ++ day
71 , logDir = fpFromText $ "logs/stackage-nightly-" ++ day
72 , title = \ghcVer -> concat
79 , setArgs = \ghcVer ub -> ub { ubNightly = Just ghcVer }
81 , postBuild = return ()
82 , distroName = "Stackage"
85 slug' = "nightly-" ++ day
87getSettings :: Manager -> BuildType -> IO Settings
88getSettings man Nightly = do
89 day <- tshow . utctDay <$> getCurrentTime
90 bc <- defaultBuildConstraints man
91 pkgs <- getLatestAllowedPlans bc
92 plan' <- newBuildPlan pkgs bc
93 return $ nightlySettings day plan'
94getSettings man (LTS bumpType) = do
95 Option mlts <- fmap (fmap getMax) $ runResourceT
97 $$ foldMapC (Option . fmap Max . parseLTSVer . filename)
99 (new, plan') <- case bumpType of
103 Nothing -> LTSVer 0 0
104 Just (LTSVer x _) -> LTSVer (x + 1) 0
105 bc <- defaultBuildConstraints man
106 pkgs <- getLatestAllowedPlans bc
107 plan' <- newBuildPlan pkgs bc
110 old <- maybe (error "No LTS plans found in current directory") return mlts
111 oldplan <- decodeFileEither (fpToString $ renderLTSVer old)
112 >>= either throwM return
113 let new = incrLTSVer old
114 let bc = updateBuildConstraints oldplan
115 pkgs <- getLatestAllowedPlans bc
116 plan' <- newBuildPlan pkgs bc
119 let newfile = renderLTSVer new
123 , buildDir = fpFromText $ "builds/stackage-lts-" ++ tshow new
124 , logDir = fpFromText $ "logs/stackage-lts-" ++ tshow new
125 , title = \ghcVer -> concat
131 , slug = "lts-" ++ tshow new
132 , setArgs = \_ ub -> ub { ubLTS = Just $ tshow new }
135 let git args = withCheckedProcess
136 (proc "git" args) $ \ClosedStream Inherited Inherited ->
138 putStrLn "Committing new LTS file to Git"
139 git ["add", fpToString newfile]
140 git ["commit", "-m", "Added new LTS release: " ++ show new]
141 putStrLn "Pushing to Git repository"
143 , distroName = "LTSHaskell"
146data LTSVer = LTSVer !Int !Int
148instance Show LTSVer where
149 show (LTSVer x y) = concat [show x, ".", show y]
150incrLTSVer :: LTSVer -> LTSVer
151incrLTSVer (LTSVer x y) = LTSVer x (y + 1)
153parseLTSVer :: FilePath -> Maybe LTSVer
155 w <- stripPrefix "lts-" $ fpToText fp
156 x <- stripSuffix ".yaml" w
157 Right (major, y) <- Just $ decimal x
158 z <- stripPrefix "." y
159 Right (minor, "") <- Just $ decimal z
160 return $ LTSVer major minor
161renderLTSVer :: LTSVer -> FilePath
162renderLTSVer lts = fpFromText $ concat
168-- | Just print a message saying "still alive" every minute, to appease Travis.
169stillAlive :: IO () -> IO ()
171 withAsync (printer 1) $ const inner
173 printer i = forever $ do
175 putStrLn $ "Still alive: " ++ tshow i
178-- | Generate and check a new build plan, but do not execute it.
182justCheck = stillAlive $ withManager tlsManagerSettings $ \man -> do
183 putStrLn "Loading build constraints"
184 bc <- defaultBuildConstraints man
186 putStrLn "Creating build plan"
187 plans <- getLatestAllowedPlans bc
188 plan <- newBuildPlan plans bc
190 putStrLn $ "Writing build plan to check-plan.yaml"
191 encodeFile "check-plan.yaml" plan
193 putStrLn "Checking plan"
196 putStrLn "Plan seems valid!"
198getPerformBuild :: BuildFlags -> Settings -> PerformBuild
199getPerformBuild buildFlags Settings {..} = PerformBuild
201 , pbInstallDest = buildDir
203 , pbLog = hPut stdout
205 , pbGlobalInstall = False
206 , pbEnableTests = bfEnableTests buildFlags
207 , pbEnableHaddock = bfEnableHaddock buildFlags
208 , pbEnableLibProfiling = bfEnableLibProfile buildFlags
209 , pbVerbose = bfVerbose buildFlags
210 , pbAllowNewer = bfSkipCheck buildFlags
213-- | Make a complete plan, build, test and upload bundle, docs and
215completeBuild :: BuildType -> BuildFlags -> IO ()
216completeBuild buildType buildFlags = withManager tlsManagerSettings $ \man -> do
217 hSetBuffering stdout LineBuffering
219 putStrLn $ "Loading settings for: " ++ tshow buildType
220 settings@Settings {..} <- getSettings man buildType
222 putStrLn $ "Writing build plan to: " ++ fpToText planFile
223 encodeFile (fpToString planFile) plan
225 if bfSkipCheck buildFlags
226 then putStrLn "Skipping build plan check"
228 putStrLn "Checking build plan"
231 putStrLn "Performing build"
232 performBuild (getPerformBuild buildFlags settings) >>= mapM_ putStrLn
234 when (bfDoUpload buildFlags) $
235 finallyUpload settings man
238 :: Text -- ^ nightly date
240justUploadNightly day = do
241 plan <- decodeFileEither (fpToString $ nightlyPlanFile day)
242 >>= either throwM return
243 withManager tlsManagerSettings $ finallyUpload $ nightlySettings day plan
245-- | The final part of the complete build process: uploading a bundle,
246-- docs and a distro to hackage.
247finallyUpload :: Settings -> Manager -> IO ()
248finallyUpload settings@Settings{..} man = do
249 putStrLn "Uploading bundle to Stackage Server"
251 mtoken <- lookupEnv "STACKAGE_AUTH_TOKEN"
254 Nothing -> decodeUtf8 <$> readFile "/auth-token"
255 Just token -> return $ pack token
258 let ghcVer = display $ siGhcVersion $ bpSystemInfo plan
259 (ident, mloc) <- flip uploadBundle man $ setArgs ghcVer def
260 { ubContents = serverBundle now (title ghcVer) slug plan
261 , ubAuthToken = token
263 putStrLn $ "New ident: " ++ unSnapshotIdent ident
265 putStrLn $ "Track progress at: " ++ loc
267 postBuild `catchAny` print
269 putStrLn "Uploading docs to Stackage Server"
270 res1 <- uploadDocs UploadDocs
272 , udAuthToken = token
273 , udDocs = pbDocDir pb
276 putStrLn $ "Doc upload response: " ++ tshow res1
278 ecreds <- tryIO $ readFile "/hackage-creds"
279 case map encodeUtf8 $ words $ decodeUtf8 $ either (const "") id ecreds of
280 [username, password] -> do
281 putStrLn "Uploading as Hackage distro"
282 res2 <- uploadHackageDistroNamed distroName plan username password man
283 putStrLn $ "Distro upload response: " ++ tshow res2
284 _ -> putStrLn "No creds found, skipping Hackage distro upload"
286 putStrLn "Uploading doc map"
287 uploadDocMap UploadDocMap
289 , udmAuthToken = token
290 , udmSnapshot = ident
291 , udmDocDir = pbDocDir pb
295 pb = getPerformBuild (error "finallyUpload.buildFlags") settings
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list