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
39 , bfEnableExecDyn :: !Bool
41 , bfSkipCheck :: !Bool
44data BuildType = Nightly | LTS BumpType
45 deriving (Show, Read, Eq, Ord)
47data BumpType = Major | Minor
48 deriving (Show, Read, Eq, Ord)
50data Settings = Settings
52 , planFile :: FilePath
53 , buildDir :: FilePath
55 , title :: Text -> Text -- ^ GHC version -> title
57 , setArgs :: Text -> UploadBundle -> UploadBundle
59 , distroName :: Text -- ^ distro name on Hackage
62nightlyPlanFile :: Text -- ^ day
64nightlyPlanFile day = fpFromText ("nightly-" ++ day) <.> "yaml"
66nightlySettings :: Text -- ^ day
69nightlySettings day plan' = Settings
70 { planFile = nightlyPlanFile day
71 , buildDir = fpFromText $ "builds/nightly"
72 , logDir = fpFromText $ "logs/stackage-nightly-" ++ day
73 , title = \ghcVer -> concat
80 , setArgs = \ghcVer ub -> ub { ubNightly = Just ghcVer }
82 , postBuild = return ()
83 , distroName = "Stackage"
86 slug' = "nightly-" ++ day
88getSettings :: Manager -> BuildType -> IO Settings
89getSettings man Nightly = do
90 day <- tshow . utctDay <$> getCurrentTime
91 bc <- defaultBuildConstraints man
92 pkgs <- getLatestAllowedPlans bc
93 plan' <- newBuildPlan pkgs bc
94 return $ nightlySettings day plan'
95getSettings man (LTS bumpType) = do
96 Option mlts <- fmap (fmap getMax) $ runResourceT
98 $$ foldMapC (Option . fmap Max . parseLTSVer . filename)
100 (new, plan') <- case bumpType of
104 Nothing -> LTSVer 0 0
105 Just (LTSVer x _) -> LTSVer (x + 1) 0
106 bc <- defaultBuildConstraints man
107 pkgs <- getLatestAllowedPlans bc
108 plan' <- newBuildPlan pkgs bc
111 old <- maybe (error "No LTS plans found in current directory") return mlts
112 oldplan <- decodeFileEither (fpToString $ renderLTSVer old)
113 >>= either throwM return
114 let new = incrLTSVer old
115 let bc = updateBuildConstraints oldplan
116 pkgs <- getLatestAllowedPlans bc
117 plan' <- newBuildPlan pkgs bc
120 let newfile = renderLTSVer new
124 , buildDir = fpFromText $ "builds/lts"
125 , logDir = fpFromText $ "logs/stackage-lts-" ++ tshow new
126 , title = \ghcVer -> concat
132 , slug = "lts-" ++ tshow new
133 , setArgs = \_ ub -> ub { ubLTS = Just $ tshow new }
136 let git args = withCheckedProcess
137 (proc "git" args) $ \ClosedStream Inherited Inherited ->
139 putStrLn "Committing new LTS file to Git"
140 git ["add", fpToString newfile]
141 git ["commit", "-m", "Added new LTS release: " ++ show new]
142 putStrLn "Pushing to Git repository"
144 , distroName = "LTSHaskell"
147data LTSVer = LTSVer !Int !Int
149instance Show LTSVer where
150 show (LTSVer x y) = concat [show x, ".", show y]
151incrLTSVer :: LTSVer -> LTSVer
152incrLTSVer (LTSVer x y) = LTSVer x (y + 1)
154parseLTSVer :: FilePath -> Maybe LTSVer
156 w <- stripPrefix "lts-" $ fpToText fp
157 x <- stripSuffix ".yaml" w
158 Right (major, y) <- Just $ decimal x
159 z <- stripPrefix "." y
160 Right (minor, "") <- Just $ decimal z
161 return $ LTSVer major minor
162renderLTSVer :: LTSVer -> FilePath
163renderLTSVer lts = fpFromText $ concat
169-- | Just print a message saying "still alive" every minute, to appease Travis.
170stillAlive :: IO () -> IO ()
172 withAsync (printer 1) $ const inner
174 printer i = forever $ do
176 putStrLn $ "Still alive: " ++ tshow i
179-- | Generate and check a new build plan, but do not execute it.
183justCheck = stillAlive $ withManager tlsManagerSettings $ \man -> do
184 putStrLn "Loading build constraints"
185 bc <- defaultBuildConstraints man
187 putStrLn "Creating build plan"
188 plans <- getLatestAllowedPlans bc
189 plan <- newBuildPlan plans bc
191 putStrLn $ "Writing build plan to check-plan.yaml"
192 encodeFile "check-plan.yaml" plan
194 putStrLn "Checking plan"
197 putStrLn "Plan seems valid!"
199getPerformBuild :: BuildFlags -> Settings -> PerformBuild
200getPerformBuild buildFlags Settings {..} = PerformBuild
202 , pbInstallDest = buildDir
204 , pbLog = hPut stdout
206 , pbGlobalInstall = False
207 , pbEnableTests = bfEnableTests buildFlags
208 , pbEnableHaddock = bfEnableHaddock buildFlags
209 , pbEnableLibProfiling = bfEnableLibProfile buildFlags
210 , pbEnableExecDyn = bfEnableExecDyn buildFlags
211 , pbVerbose = bfVerbose buildFlags
212 , pbAllowNewer = bfSkipCheck buildFlags
215-- | Make a complete plan, build, test and upload bundle, docs and
217completeBuild :: BuildType -> BuildFlags -> IO ()
218completeBuild buildType buildFlags = withManager tlsManagerSettings $ \man -> do
219 hSetBuffering stdout LineBuffering
221 putStrLn $ "Loading settings for: " ++ tshow buildType
222 settings@Settings {..} <- getSettings man buildType
224 putStrLn $ "Writing build plan to: " ++ fpToText planFile
225 encodeFile (fpToString planFile) plan
227 if bfSkipCheck buildFlags
228 then putStrLn "Skipping build plan check"
230 putStrLn "Checking build plan"
233 putStrLn "Performing build"
234 performBuild (getPerformBuild buildFlags settings) >>= mapM_ putStrLn
236 when (bfDoUpload buildFlags) $
237 finallyUpload settings man
240 :: Text -- ^ nightly date
242justUploadNightly day = do
243 plan <- decodeFileEither (fpToString $ nightlyPlanFile day)
244 >>= either throwM return
245 withManager tlsManagerSettings $ finallyUpload $ nightlySettings day plan
247-- | The final part of the complete build process: uploading a bundle,
248-- docs and a distro to hackage.
249finallyUpload :: Settings -> Manager -> IO ()
250finallyUpload settings@Settings{..} man = do
251 putStrLn "Uploading bundle to Stackage Server"
253 mtoken <- lookupEnv "STACKAGE_AUTH_TOKEN"
256 Nothing -> decodeUtf8 <$> readFile "/auth-token"
257 Just token -> return $ pack token
260 let ghcVer = display $ siGhcVersion $ bpSystemInfo plan
261 (ident, mloc) <- flip uploadBundle man $ setArgs ghcVer def
262 { ubContents = serverBundle now (title ghcVer) slug plan
263 , ubAuthToken = token
265 putStrLn $ "New ident: " ++ unSnapshotIdent ident
267 putStrLn $ "Track progress at: " ++ loc
269 postBuild `catchAny` print
271 putStrLn "Uploading docs to Stackage Server"
272 res1 <- uploadDocs UploadDocs
274 , udAuthToken = token
275 , udDocs = pbDocDir pb
278 putStrLn $ "Doc upload response: " ++ tshow res1
280 ecreds <- tryIO $ readFile "/hackage-creds"
281 case map encodeUtf8 $ words $ decodeUtf8 $ either (const "") id ecreds of
282 [username, password] -> do
283 putStrLn "Uploading as Hackage distro"
284 res2 <- uploadHackageDistroNamed distroName plan username password man
285 putStrLn $ "Distro upload response: " ++ tshow res2
286 _ -> putStrLn "No creds found, skipping Hackage distro upload"
288 putStrLn "Uploading doc map"
289 uploadDocMap UploadDocMap
291 , udmAuthToken = token
292 , udmSnapshot = ident
293 , udmDocDir = pbDocDir pb
297 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