@@ -10,7 +10,6 @@ import Colog
10import Control.AppRequestable
10import Control.AppRequestable
11import Control.Concurrent
11import Control.Concurrent
12import Control.GithubCloner
12import Control.GithubCloner
13−import Control.HasGithubStatus
14import Control.Monad.Except
13import Control.Monad.Except
15import Control.Monad.IO.Unlift
14import Control.Monad.IO.Unlift
16import Control.Monad.Reader
15import Control.Monad.Reader
@@ -30,7 +29,6 @@ import Data.Submission.Query
30import Data.Tasks.Build
29import Data.Tasks.Build
31import Data.Tasks.StatusUpdate
30import Data.Tasks.StatusUpdate
32import qualified Data.Text as T
31import qualified Data.Text as T
33−import Data.Time.Clock
34import Data.Yaml
32import Data.Yaml
35import GHC.Generics
33import GHC.Generics
36import GitHub hiding (Accept)
34import GitHub hiding (Accept)
@@ -39,7 +37,7 @@ import GitHub.App.Request
39import GitHub.Auth (Auth (OAuth))
37import GitHub.Auth (Auth (OAuth))
40import GitHub.Data.Name
38import GitHub.Data.Name
41import GitHub.Data.Webhooks.Events
39import GitHub.Data.Webhooks.Events
42−import GitHub.Data.Webhooks.Payload hiding (URL (..))
40+import GitHub.Data.Webhooks.Payload
43import Network.HTTP.Client.TLS (newTlsManager)
41import Network.HTTP.Client.TLS (newTlsManager)
44import Network.HTTP.Media.MediaType
42import Network.HTTP.Media.MediaType
45import Network.Wai.Handler.Warp
43import Network.Wai.Handler.Warp
@@ -50,6 +48,7 @@ import Server.Config
50import Server.Html
48import Server.Html
51import Server.Schema
49import Server.Schema
52import Squeal.PostgreSQL
50import Squeal.PostgreSQL
53import Text.Blaze.Html
52import Text.Blaze.Html
54import UnliftIO.Exception (catch, displayException, handleAny, throwIO, try)
53import UnliftIO.Exception (catch, displayException, handleAny, throwIO, try)
@@ -75,18 +74,17 @@ type API =
75 :> Post '[HTML] Markup
74 :> Post '[HTML] Markup
76 :<|> "results" :> Capture "task name" String :> Get '[CSV, HTML] [Score]
75 :<|> "results" :> Capture "task name" String :> Get '[CSV, HTML] [Score]
78−webhookInstallation :: WithLog env Message m => RepoWebhookEvent -> ((), InstallationEvent) -> m ()
77+webhookInstallation :: MonadIO m => RepoWebhookEvent -> ((), InstallationEvent) -> m ()
79webhookInstallation _ ((), ev) =
78webhookInstallation _ ((), ev) =
80− logInfo . T.pack . show $ ev
79+ liftIO $ do
83+pendingStatus :: NewStatus
84+pendingStatus = NewStatus StatusPending Nothing Nothing Nothing
82webhookPushEvent ::
86webhookPushEvent ::
84− MonadReader (ServerData m) m,
87− MonadError ServerError m,
87+ (StaticPQ m, WithLog env Message m, MonadUnliftIO m, HasTasks m, MonadError ServerError m) =>
88− WithLog env Message m
90 RepoWebhookEvent ->
88 RepoWebhookEvent ->
91 ((), PushEvent) ->
89 ((), PushEvent) ->
@@ -104,32 +102,22 @@ webhookPushEvent _ ((), ev) = do
104 M.takeWhileAntitone (repoName >) tasks
102 M.takeWhileAntitone (repoName >) tasks
105 case task of
103 case task of
106 Just (name, TaskConfig {..}) | T.take (T.length name) repoName == name -> do
104 Just (name, TaskConfig {..}) | T.take (T.length name) repoName == name -> do
107− nowTime <- liftIO getCurrentTime
105+ scheduleTask $ StatusUpdate (N owner) (N repoName) (N sha) pendingStatus
108− if nowTime > deadline
110− deadlineHasPassedStatus (N owner) (N repoName) (N sha)
111− createSubmission $
106+ createSubmission $
112− Submission
107+ Submission
114− (T.unpack fullRepoName)
109+ (T.unpack fullRepoName)
115− (T.unpack name)
110+ (T.unpack name)
116− (T.unpack sha)
111+ (T.unpack sha)
117− (Jsonb PastDeadline)
119− schedulePendingStatus (N owner) (N repoName) (N sha)
123− (T.unpack fullRepoName)
126− (Jsonb BuildScheduled)
112+ (Jsonb BuildScheduled)
127− scheduleTask $ Build prebuild dockerfile (N owner) (N repoName) (N sha) timeoutMinutes
113+ scheduleTask $ Build prebuild dockerfile (N owner) (N repoName) (N sha) timeoutMinutes
128− logInfo . T.pack . show $ ev
114+ liftIO $ do
129 _ -> return ()
117 _ -> return ()
131retestSubmission ::
119retestSubmission ::
132− (StaticPQ m, MonadReader (ServerData m) m, MonadUnliftIO m, HasTasks m, MonadHasBaseUrl m) =>
120+ (StaticPQ m, WithLog env Message m, MonadUnliftIO m, HasTasks m, MonadHasBaseUrl m) =>
133 String ->
121 String ->
134 String ->
122 String ->
135 String ->
123 String ->
@@ -148,7 +136,7 @@ retestSubmission owner repoName sha = do
148 M.takeWhileAntitone (repoName' >) tasks
136 M.takeWhileAntitone (repoName' >) tasks
149 case task of
137 case task of
150 Just (name, TaskConfig {..}) | T.take (T.length name) repoName' == name -> do
138 Just (name, TaskConfig {..}) | T.take (T.length name) repoName' == name -> do
151− schedulePendingStatus (N owner') (N repoName') (N sha')
139+ scheduleTask $ StatusUpdate (N owner') (N repoName') (N sha') pendingStatus
152 updateSubmissionStatus fullRepoName sha BuildScheduled
140 updateSubmissionStatus fullRepoName sha BuildScheduled
153 scheduleTask $ Build prebuild dockerfile (N owner') (N repoName') (N sha') timeoutMinutes
141 scheduleTask $ Build prebuild dockerfile (N owner') (N repoName') (N sha') timeoutMinutes
154 _ -> return ()
142 _ -> return ()
@@ -175,9 +163,9 @@ instance MonadError ServerError (ServerM schema) where
175server :: ServerT API (ServerM Schema)
163server :: ServerT API (ServerM Schema)
176server = (webhookInstallation :<|> webhookPushEvent) :<|> getSubmissionR :<|> retestSubmission :<|> getResults
164server = (webhookInstallation :<|> webhookPushEvent) :<|> getSubmissionR :<|> retestSubmission :<|> getResults
178−runServer :: FilePath -> IO ()
166+runServer :: IO ()
179−runServer config = do
167+runServer = do
180− c@Config {..} <- getConfig config
168+ c@Config {..} <- getConfig
182 pem <- BS.readFile appPkPemPath
170 pem <- BS.readFile appPkPemPath
183 appPkPem <- case readRsaPem pem of
171 appPkPem <- case readRsaPem pem of
@@ -198,26 +186,20 @@ runServer config = do
198 tasks = ts,
186 tasks = ts,
199 logger = cfilter ((logSeverity <=) . msgSeverity) richMessageAction,
187 logger = cfilter ((logSeverity <=) . msgSeverity) richMessageAction,
200 baseUrl = baseSiteUrl,
188 baseUrl = baseSiteUrl,
201− dockerUrl = T.pack cfgDockerUrl,
189+ dockerUrl = T.pack cfgDockerUrl
202− context = githubContext
204 repeatIfNotEmpty n f = f >>= \m -> do
191 repeatIfNotEmpty n f = f >>= \m -> do
205 when (m == 0) $ liftIO $ threadDelay n
192 when (m == 0) $ liftIO $ threadDelay n
206 repeatIfNotEmpty n f
193 repeatIfNotEmpty n f
207 printingErrors =
194 printingErrors =
208 handleAny (logError . T.pack . displayException >=> const (return 0))
195 handleAny (logError . T.pack . displayException >=> const (return 0))
209− usingConnectionPool pool
196+ _ <-
210− . (`runReaderT` serverData)
212− . handleAny (error . displayException)
214− replicateM_ builderCount
215− $ forkIO . repeatIfNotEmpty (10 ^ (6 :: Int))
197+ forkIO . repeatIfNotEmpty (10 ^ (6 :: Int))
216 . usingConnectionPool pool
198 . usingConnectionPool pool
217 . (`runReaderT` serverData)
199 . (`runReaderT` serverData)
219 . printingErrors
201 . printingErrors
220− $ runTasks @'[Build] 1
202+ $ runTasks @'[Build] 1
222 forkIO . repeatIfNotEmpty (10 ^ (6 :: Int))
204 forkIO . repeatIfNotEmpty (10 ^ (6 :: Int))
223 . usingConnectionPool pool
205 . usingConnectionPool pool
@@ -243,9 +225,8 @@ data ServerData m
243 { githubAppAuth :: !InstallationAuth,
225 { githubAppAuth :: !InstallationAuth,
244 tasks :: IORef TasksConfig,
226 tasks :: IORef TasksConfig,
245 logger :: LogAction m Message,
227 logger :: LogAction m Message,
246− baseUrl :: T.Text,
228+ baseUrl :: String,
247− dockerUrl :: T.Text,
229+ dockerUrl :: T.Text
251newtype PolyGitHubKey = PolyGitHubKey (forall result. GitHubKey result)
232newtype PolyGitHubKey = PolyGitHubKey (forall result. GitHubKey result)
@@ -280,71 +261,6 @@ instance MonadReader (ServerData n) m => MonadHasBaseUrl m where
280instance (MonadIO m, MonadReader (ServerData n) m) => HasTasks m where
261instance (MonadIO m, MonadReader (ServerData n) m) => HasTasks m where
281 getTasks = asks tasks >>= liftIO . readIORef
262 getTasks = asks tasks >>= liftIO . readIORef
283−instance (MonadReader (ServerData m) m, StaticPQ m, MonadUnliftIO m) => HasGithubStatus m where
284− schedulePendingStatus nOwner@(N owner) nRepo@(N repo) nCommit@(N sha) = do
294− (Just . URL $ h <> "/submission/" <> owner <> "/" <> repo <> "/" <> sha)
298− scheduleTestedStatus TestResult {..} nOwner@(N owner) nRepo@(N repo) nCommit@(N sha) = do
301− let total = M.size tests
302− passed = M.size . M.filter id $ tests
303− description = T.pack $ show passed <> "/" <> show total <> if passed == total then " ✅" else " ❌"
311− if total == passed then StatusSuccess else StatusFailure,
312− newStatusTargetUrl = Just . URL $ h <> "/submission/" <> owner <> "/" <> repo <> "/" <> sha,
313− newStatusDescription = Just description,
314− newStatusContext = Just c
317− scheduleFailedStatus _ nOwner@(N owner) nRepo@(N repo) nCommit@(N sha) = do
326− { newStatusState = StatusError,
327− newStatusTargetUrl = Just . URL $ h <> "/submission/" <> owner <> "/" <> repo <> "/" <> sha,
328− newStatusDescription = Just "Build failed ☠️",
329− newStatusContext = Just c
332− deadlineHasPassedStatus nOwner@(N owner) nRepo@(N repo) nCommit@(N sha) = do
341− { newStatusState = StatusError,
342− newStatusTargetUrl = Just . URL $ h <> "/submission/" <> owner <> "/" <> repo <> "/" <> sha,
343− newStatusDescription = Just "The deadline has passed ⏰",
344− newStatusContext = Just c
348instance (MonadReader (ServerData n) m) => HasDockerInfo m where
264instance (MonadReader (ServerData n) m) => HasDockerInfo m where
349 getDockerUrl = asks dockerUrl
265 getDockerUrl = asks dockerUrl
@@ -354,8 +270,7 @@ data TaskConfig
354 = TaskConfig
270 = TaskConfig
355 { prebuild :: String,
271 { prebuild :: String,
356 dockerfile :: String,
272 dockerfile :: String,
357− timeoutMinutes :: Int,
273+ timeoutMinutes :: Int
360 deriving stock (Show, Eq, Generic)
275 deriving stock (Show, Eq, Generic)
361 deriving anyclass (FromJSON, ToJSON)
276 deriving anyclass (FromJSON, ToJSON)