1{-# LANGUAGE TypeApplications #-}
2{-# OPTIONS_GHC -Wno-orphans #-}
11import Control.Concurrent
12import Control.GithubCloner
13import Control.HasGithubStatus
14import Control.Monad.Except
15import Control.Monad.IO.Unlift
17import Data.Aeson hiding (Success)
18import qualified Data.ByteString.Lazy.Char8 as BS
19import qualified Data.ByteString.Lazy.UTF8 as BS
22import Data.Submission.Query
23import qualified Data.Text as T
24import Data.Time.Clock.POSIX
25import qualified Docker.Client as D
29import System.Directory
31import System.Process.Typed
34import UnliftIO.Exception
38 { preProcessShell :: String,
41 repoName :: Name Repo,
45 deriving (Eq, Show, Generic, FromJSON, ToJSON)
47instance Task Build "build-repo" where
50 (MonadUnliftIO m, StaticPQ m, HasDockerInfo m, GithubCloner m, HasGithubStatus m)
52 performTask Build {..} =
53 bracket createDir (liftIO . removePathForcibly) $ \dir -> flip catchAny (const $ return Failure) $ do
54 cloneRepo fullRepoName sha' dir
55 (shellCode, _, shellErr) <- readProcess . setWorkingDir dir $ shell preProcessShell
58 let err = BS.toString shellErr
60 "preprocessing command for repo " <> show fullRepoName <> " at sha "
62 <> " exited with code "
66 scheduleFailedStatus err owner repoName sha
67 updateSubmissionStatus fullRepoName sha' (SubmissionFailed err)
69 h <- liftIO D.defaultHttpHandler
71 let opts = D.defaultCreateOpts imageName
75 WithLog env Message m,
80 D.DockerT IO (Either err a) ->
82 runDocker = runEither <=< liftIO . D.runDockerT (D.defaultClientOpts {D.baseUrl = dUrl}, h)
83 imageName = T.pack dir
84 liftIO $ writeFile (dir <> "/Dockerfile") dockerFile
85 absDir <- liftIO $ makeAbsolute dir
86 let waitDockerForever cid =
87 D.waitContainer cid >>= \case
88 Left D.GenericDockerError {} -> waitDockerForever cid
90 cid <- runDocker . runExceptT $ do
91 ExceptT $ D.buildImageFromDockerfile (D.defaultBuildOpts imageName) absDir
92 ExceptT $ D.createContainer opts Nothing
93 dockerRes <- fmap (either id id)
94 . race (liftIO (threadDelay (timeoutMinutes * 60000000)) >> return (ExitFailure 1, "Time out", "Time out"))
99 D.ContainerDeleteOpts {deleteVolumes = True, force = True}
105 ExceptT $ D.startContainer D.defaultStartOpts cid
106 buildCode <- ExceptT $ waitDockerForever cid
129 return (buildCode, buildOut, buildErr)
131 (ExitFailure n, _, buildErr) -> do
132 let err = BS.toString buildErr
134 "test command for repo " <> show fullRepoName <> " at sha "
136 <> " exited with code "
140 scheduleFailedStatus err owner repoName sha
141 updateSubmissionStatus fullRepoName sha' (SubmissionFailed err)
142 (ExitSuccess, buildOut, _) ->
143 case eitherDecode' (BS.dropWhile (/= '{') buildOut) of
145 let err = err' <> " " <> BS.toString buildOut
147 "could not decode test result from repo " <> show fullRepoName
152 scheduleFailedStatus err owner repoName sha
153 updateSubmissionStatus fullRepoName sha' (SubmissionFailed (err <> BS.toString buildOut))
154 Right testResult@TestResult {..} -> do
155 scheduleTestedStatus testResult owner repoName sha
156 updateSubmissionStatus fullRepoName sha' (SubmissionRun testResult)
159 fullRepoName = T.unpack $ untagName owner <> "/" <> untagName repoName
160 sha' = T.unpack $ untagName sha
163 WithLog env Message m,
170 runEither (Left err) = do
172 logError $ T.pack err'
173 scheduleFailedStatus err' owner repoName sha
174 updateSubmissionStatus fullRepoName sha' (SubmissionFailed err')
176 runEither (Right a) = return a
178createDir :: MonadIO m => m FilePath
180 dir <- ("tmp/" <>) <$> getTmpDirName
181 liftIO $ createDirectoryIfMissing True dir
184cloneRepo :: (MonadIO m, GithubCloner m) => String -> String -> FilePath -> m ()
185cloneRepo repoName sha path = do
186 InstallationToken token <- getGithubInstallationToken
187 runProcess_ . setWorkingDir path . fromString $
188 "git clone https://x-access-token:" <> token <> "@github.com/" <> repoName <> ".git ."
189 runProcess_ . setWorkingDir path . fromString $ "git checkout " <> sha
192getTmpDirName :: MonadIO m => m FilePath
193getTmpDirName = liftIO $ do
194 time :: Integer <- round <$> getPOSIXTime
196 return $ show time <> "-" <> show i
198class HasDockerInfo m where
199 getDockerUrl :: m T.Text
201instance Exception D.DockerError
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