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
37 , bfEnableLibProfile :: !Bool
39 , bfSkipCheck :: !Bool
42data BuildType = Nightly | LTS BumpType
43 deriving (Show, Read, Eq, Ord)
45data BumpType = Major | Minor
46 deriving (Show, Read, Eq, Ord)
48data Settings = Settings
50 , planFile :: FilePath
51 , buildDir :: FilePath
53 , title :: Text -> Text -- ^ GHC version -> title
55 , setArgs :: Text -> UploadBundle -> UploadBundle
59nightlyPlanFile :: Text -- ^ day
61nightlyPlanFile day = fpFromText ("nightly-" ++ day) <.> "yaml"
63nightlySettings :: Text -- ^ day
66nightlySettings day plan' = Settings
67 { planFile = nightlyPlanFile day
68 , buildDir = fpFromText $ "builds/stackage-nightly-" ++ day
69 , logDir = fpFromText $ "logs/stackage-nightly-" ++ day
70 , title = \ghcVer -> concat
77 , setArgs = \ghcVer ub -> ub { ubNightly = Just ghcVer }
79 , postBuild = return ()
82 slug' = "nightly-" ++ day
84getSettings :: Manager -> BuildType -> IO Settings
85getSettings man Nightly = do
86 day <- tshow . utctDay <$> getCurrentTime
87 bc <- defaultBuildConstraints man
88 pkgs <- getLatestAllowedPlans bc
89 plan' <- newBuildPlan pkgs bc
90 return $ nightlySettings day plan'
91getSettings man (LTS bumpType) = do
92 Option mlts <- fmap (fmap getMax) $ runResourceT
94 $$ foldMapC (Option . fmap Max . parseLTSVer . filename)
96 (new, plan') <- case bumpType of
100 Nothing -> LTSVer 0 0
101 Just (LTSVer x _) -> LTSVer (x + 1) 0
102 bc <- defaultBuildConstraints man
103 pkgs <- getLatestAllowedPlans bc
104 plan' <- newBuildPlan pkgs bc
107 old <- maybe (error "No LTS plans found in current directory") return mlts
108 oldplan <- decodeFileEither (fpToString $ renderLTSVer old)
109 >>= either throwM return
110 let new = incrLTSVer old
111 let bc = updateBuildConstraints oldplan
112 pkgs <- getLatestAllowedPlans bc
113 plan' <- newBuildPlan pkgs bc
116 let newfile = renderLTSVer new
120 , buildDir = fpFromText $ "builds/stackage-lts-" ++ tshow new
121 , logDir = fpFromText $ "logs/stackage-lts-" ++ tshow new
122 , title = \ghcVer -> concat
128 , slug = "lts-" ++ tshow new
129 , setArgs = \_ ub -> ub { ubLTS = Just $ tshow new }
132 let git args = withCheckedProcess
133 (proc "git" args) $ \ClosedStream Inherited Inherited ->
135 putStrLn "Committing new LTS file to Git"
136 git ["add", fpToString newfile]
137 git ["commit", "-m", "Added new LTS release: " ++ show new]
138 putStrLn "Pushing to Git repository"
142data LTSVer = LTSVer !Int !Int
144instance Show LTSVer where
145 show (LTSVer x y) = concat [show x, ".", show y]
146incrLTSVer :: LTSVer -> LTSVer
147incrLTSVer (LTSVer x y) = LTSVer x (y + 1)
149parseLTSVer :: FilePath -> Maybe LTSVer
151 w <- stripPrefix "lts-" $ fpToText fp
152 x <- stripSuffix ".yaml" w
153 Right (major, y) <- Just $ decimal x
154 z <- stripPrefix "." y
155 Right (minor, "") <- Just $ decimal z
156 return $ LTSVer major minor
157renderLTSVer :: LTSVer -> FilePath
158renderLTSVer lts = fpFromText $ concat
164-- | Just print a message saying "still alive" every minute, to appease Travis.
165stillAlive :: IO () -> IO ()
167 withAsync (printer 1) $ const inner
169 printer i = forever $ do
171 putStrLn $ "Still alive: " ++ tshow i
174-- | Generate and check a new build plan, but do not execute it.
178justCheck = stillAlive $ withManager tlsManagerSettings $ \man -> do
179 putStrLn "Loading build constraints"
180 bc <- defaultBuildConstraints man
182 putStrLn "Creating build plan"
183 plans <- getLatestAllowedPlans bc
184 plan <- newBuildPlan plans bc
186 putStrLn $ "Writing build plan to check-plan.yaml"
187 encodeFile "check-plan.yaml" plan
189 putStrLn "Checking plan"
192 putStrLn "Plan seems valid!"
194getPerformBuild :: BuildFlags -> Settings -> PerformBuild
195getPerformBuild buildFlags Settings {..} = PerformBuild
197 , pbInstallDest = buildDir
199 , pbLog = hPut stdout
201 , pbGlobalInstall = False
202 , pbEnableTests = bfEnableTests buildFlags
203 , pbEnableLibProfiling = bfEnableLibProfile buildFlags
204 , pbVerbose = bfVerbose buildFlags
205 , pbAllowNewer = bfSkipCheck buildFlags
208-- | Make a complete plan, build, test and upload bundle, docs and
210completeBuild :: BuildType -> BuildFlags -> IO ()
211completeBuild buildType buildFlags = withManager tlsManagerSettings $ \man -> do
212 hSetBuffering stdout LineBuffering
214 putStrLn $ "Loading settings for: " ++ tshow buildType
215 settings@Settings {..} <- getSettings man buildType
217 putStrLn $ "Writing build plan to: " ++ fpToText planFile
218 encodeFile (fpToString planFile) plan
220 if bfSkipCheck buildFlags
221 then putStrLn "Skipping build plan check"
223 putStrLn "Checking build plan"
226 putStrLn "Performing build"
227 performBuild (getPerformBuild buildFlags settings) >>= mapM_ putStrLn
229 when (bfDoUpload buildFlags) $
230 finallyUpload settings man
233 :: Text -- ^ nightly date
235justUploadNightly day = do
236 plan <- decodeFileEither (fpToString $ nightlyPlanFile day)
237 >>= either throwM return
238 withManager tlsManagerSettings $ finallyUpload $ nightlySettings day plan
240-- | The final part of the complete build process: uploading a bundle,
241-- docs and a distro to hackage.
242finallyUpload :: Settings -> Manager -> IO ()
243finallyUpload settings@Settings{..} man = do
244 putStrLn "Uploading bundle to Stackage Server"
246 mtoken <- lookupEnv "STACKAGE_AUTH_TOKEN"
249 Nothing -> decodeUtf8 <$> readFile "/auth-token"
250 Just token -> return $ pack token
253 let ghcVer = display $ siGhcVersion $ bpSystemInfo plan
254 (ident, mloc) <- flip uploadBundle man $ setArgs ghcVer def
255 { ubContents = serverBundle now (title ghcVer) slug plan
256 , ubAuthToken = token
258 putStrLn $ "New ident: " ++ unSnapshotIdent ident
260 putStrLn $ "Track progress at: " ++ loc
262 postBuild `catchAny` print
264 putStrLn "Uploading docs to Stackage Server"
265 res1 <- uploadDocs UploadDocs
267 , udAuthToken = token
268 , udDocs = pbDocDir pb
271 putStrLn $ "Doc upload response: " ++ tshow res1
273 ecreds <- tryIO $ readFile "/hackage-creds"
274 case map encodeUtf8 $ words $ decodeUtf8 $ either (const "") id ecreds of
275 [username, password] -> do
276 putStrLn "Uploading as Hackage distro"
277 res2 <- uploadHackageDistro plan username password man
278 putStrLn $ "Distro upload response: " ++ tshow res2
279 _ -> putStrLn "No creds found, skipping Hackage distro upload"
281 putStrLn "Uploading doc map"
282 uploadDocMap UploadDocMap
284 , udmAuthToken = token
285 , udmSnapshot = ident
286 , udmDocDir = pbDocDir pb
290 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