on 3511812 help sign in
app/Main.hs+6−23
since splitsnapshots
unifiedsplit
@@ -1,30 +1,13 @@
1module Main (main) where
1module Main (main) where
2
2
3import qualified Data.ByteString.Char8 as BS
3import qualified Data.ByteString.Char8 as BS
4−import Data.Yaml (encode)
4+import Data.Yaml
5−import Options.Applicative
6import Server
5import Server
7import Server.Config
6import Server.Config
7+import System.Environment
8
8
9main :: IO ()
9main :: IO ()
10−main =
10+main = getArgs >>= \case
11− execParser (info (getOptions <**> helper) fullDesc) >>= \case
11+ [] -> runServer
12− PrintDefaultConfig -> putStrLn . BS.unpack . encode $ defaultConfig
12+ ["default"] -> putStrLn . BS.unpack . encode $ defaultConfig
13− RunServer config -> runServer config
13+ xs -> error . show $ xs
14−
15−data Run
16− = PrintDefaultConfig
17− | RunServer FilePath
18−
19−getOptions :: Parser Run
20−getOptions =
21− subparser
22− ( command "default" (info (pure PrintDefaultConfig <**> helper) fullDesc)
23− )
24− <|> ( RunServer
25− <$> strOption
26− ( long "config"
27− <> help "Path to yaml config file."
28− <> value "config.yaml"
29− )
30− )
package.yaml+1−1
@@ -53,6 +53,7 @@ library:
53 - cassava
53 - cassava
54 - http-media
54 - http-media
55 - utf8-string
55 - utf8-string
56+ - conduit-extra
56
57
57executables:
58executables:
58 sc-build-exe:
59 sc-build-exe:
@@ -66,7 +67,6 @@ executables:
66 - sc-build
67 - sc-build
67 - bytestring
68 - bytestring
68 - yaml
69 - yaml
69− - optparse-applicative
70 sc-build-migrations:
70 sc-build-migrations:
71 main: Main.hs
71 main: Main.hs
72 source-dirs: migrations
72 source-dirs: migrations
src/Control/HasGithubStatus.hs−1
@@ -11,4 +11,3 @@ class HasGithubStatus m where
11 schedulePendingStatus :: WithLog env Message m => Name Owner -> Name Repo -> Name Commit -> m ()
11 schedulePendingStatus :: WithLog env Message m => Name Owner -> Name Repo -> Name Commit -> m ()
12 scheduleTestedStatus :: WithLog env Message m => TestResult -> Name Owner -> Name Repo -> Name Commit -> m ()
12 scheduleTestedStatus :: WithLog env Message m => TestResult -> Name Owner -> Name Repo -> Name Commit -> m ()
13 scheduleFailedStatus :: WithLog env Message m => String -> Name Owner -> Name Repo -> Name Commit -> m ()
13 scheduleFailedStatus :: WithLog env Message m => String -> Name Owner -> Name Repo -> Name Commit -> m ()
14− deadlineHasPassedStatus :: WithLog env Message m => Name Owner -> Name Repo -> Name Commit -> m ()
src/Control/Task/Scheduler.hs+1−4
@@ -21,7 +21,6 @@ import Data.Word
21import GHC.TypeLits
21import GHC.TypeLits
22import Server.Schema
22import Server.Schema
23import Squeal.PostgreSQL
23import Squeal.PostgreSQL
24−import UnliftIO.Exception
25
24
26scheduleTask ::
25scheduleTask ::
27 (StaticPQ m, WithLog env Message m, Task t s, MonadUnliftIO m) =>
26 (StaticPQ m, WithLog env Message m, Task t s, MonadUnliftIO m) =>
@@ -67,9 +66,7 @@ instance
67 <> e
66 <> e
68 A.Success t -> do
67 A.Success t -> do
69 logInfo . T.pack $ "running task " <> task <> " with " <> show t <> " created at " <> show initialStartTime
68 logInfo . T.pack $ "running task " <> task <> " with " <> show t <> " created at " <> show initialStartTime
70− r <- performTask @t @ts @m t `catchAny` \e -> do
69+ r <- performTask @t @ts @m t
71− logError . T.pack . displayException $ e
72− return Failure
73 when (r == Failure) $ logWarning . T.pack $ "Task " <> task <> " failed"
70 when (r == Failure) $ logWarning . T.pack $ "Task " <> task <> " failed"
74 case r of
71 case r of
75 Success -> completeTaskQuery t
72 Success -> completeTaskQuery t
src/Control/Task/Scheduler/Query.hs+1−6
@@ -41,7 +41,6 @@ scheduleTaskQuery task = do
41 :* Set (param @2) `as` #payload
41 :* Set (param @2) `as` #payload
42 :* Default `as` #creation_time
42 :* Default `as` #creation_time
43 :* Default `as` #started
43 :* Default `as` #started
44− :* Default `as` #failures
45 )
44 )
46 ( OnConflict
45 ( OnConflict
47 (OnConstraint #pk_task_payload)
46 (OnConstraint #pk_task_payload)
@@ -88,7 +87,6 @@ rescheduleTaskQuery payload = do
88 #tasks
87 #tasks
89 ( Default `as` #creation_time
88 ( Default `as` #creation_time
90 :* Default `as` #started
89 :* Default `as` #started
91− :* Set (#failures + 1) `as` #failures
92 )
90 )
93 (param @1 .== #task .&& param @2 .== #payload)
91 (param @1 .== #task .&& param @2 .== #payload)
94 dbWrite (const $ return ()) $
92 dbWrite (const $ return ()) $
@@ -103,7 +101,7 @@ pickTasksQuery ::
103 Word64 ->
101 Word64 ->
104 m [PickedTask]
102 m [PickedTask]
105pickTasksQuery tasks limitCount = do
103pickTasksQuery tasks limitCount = do
106− logDebug $ "reading tasks " <> T.unwords (T.pack <$> tasks)
104+ logDebug "reading tasks"
107 let query :: Query_ Schema () PickedTask
105 let query :: Query_ Schema () PickedTask
108 query =
106 query =
109 select_
107 select_
@@ -114,10 +112,7 @@ pickTasksQuery tasks limitCount = do
114 ( from (table #tasks)
112 ( from (table #tasks)
115 & where_ (#task `in_` (literal <$> tasks))
113 & where_ (#task `in_` (literal <$> tasks))
116 & where_ (not_ . notNull $ #started)
114 & where_ (not_ . notNull $ #started)
117− & where_ (#failures .< 20)
118− & orderBy [#creation_time & Asc]
119 & limit limitCount
115 & limit limitCount
120− & orderBy [#creation_time & Desc]
121 )
116 )
122 startedMan :: Manipulation_ Schema (String, Jsonb Value) ()
117 startedMan :: Manipulation_ Schema (String, Jsonb Value) ()
123 startedMan =
118 startedMan =
src/Data/Submission.hs−1
@@ -29,7 +29,6 @@ data SubmissionStatus
29 = SubmissionRun TestResult
29 = SubmissionRun TestResult
30 | SubmissionFailed String
30 | SubmissionFailed String
31 | BuildScheduled
31 | BuildScheduled
32− | PastDeadline
33 deriving (Generic, FromJSON, ToJSON, Eq, Show)
32 deriving (Generic, FromJSON, ToJSON, Eq, Show)
34
33
35newtype TestResult
34newtype TestResult
src/Data/Tasks/Build.hs+5−2
@@ -17,6 +17,7 @@ import Control.Task
17import Data.Aeson hiding (Success)
17import Data.Aeson hiding (Success)
18import qualified Data.ByteString.Lazy.Char8 as BS
18import qualified Data.ByteString.Lazy.Char8 as BS
19import qualified Data.ByteString.Lazy.UTF8 as BS
19import qualified Data.ByteString.Lazy.UTF8 as BS
20+import qualified Data.Conduit.Binary as Conduit
20import Data.String
21import Data.String
21import Data.Submission
22import Data.Submission
22import Data.Submission.Query
23import Data.Submission.Query
@@ -106,7 +107,7 @@ instance Task Build "build-repo" where
106 buildCode <- ExceptT $ waitDockerForever cid
107 buildCode <- ExceptT $ waitDockerForever cid
107 buildOut <-
108 buildOut <-
108 ExceptT $
109 ExceptT $
109− D.getContainerLogs
110+ D.getContainerLogsStream
110 D.LogOpts
111 D.LogOpts
111 { stdout = True,
112 { stdout = True,
112 stderr = False,
113 stderr = False,
@@ -115,9 +116,10 @@ instance Task Build "build-repo" where
115 tail = D.All
116 tail = D.All
116 }
117 }
117 cid
118 cid
119+ Conduit.sinkLbs
118 buildErr <-
120 buildErr <-
119 ExceptT $
121 ExceptT $
120− D.getContainerLogs
122+ D.getContainerLogsStream
121 D.LogOpts
123 D.LogOpts
122 { stdout = True,
124 { stdout = True,
123 stderr = True,
125 stderr = True,
@@ -126,6 +128,7 @@ instance Task Build "build-repo" where
126 tail = D.All
128 tail = D.All
127 }
129 }
128 cid
130 cid
131+ Conduit.sinkLbs
129 return (buildCode, buildOut, buildErr)
132 return (buildCode, buildOut, buildErr)
130 case dockerRes of
133 case dockerRes of
131 (ExitFailure n, _, buildErr) -> do
134 (ExitFailure n, _, buildErr) -> do
src/Server.hs+25−55
@@ -30,7 +30,6 @@ import Data.Submission.Query
30import Data.Tasks.Build
30import Data.Tasks.Build
31import Data.Tasks.StatusUpdate
31import Data.Tasks.StatusUpdate
32import qualified Data.Text as T
32import qualified Data.Text as T
33−import Data.Time.Clock
34import Data.Yaml
33import Data.Yaml
35import GHC.Generics
34import GHC.Generics
36import GitHub hiding (Accept)
35import GitHub hiding (Accept)
@@ -50,6 +49,7 @@ import Server.Config
50import Server.Html
49import Server.Html
51import Server.Schema
50import Server.Schema
52import Squeal.PostgreSQL
51import Squeal.PostgreSQL
52+import System.IO
53import Text.Blaze.Html
53import Text.Blaze.Html
54import UnliftIO.Exception (catch, displayException, handleAny, throwIO, try)
54import UnliftIO.Exception (catch, displayException, handleAny, throwIO, try)
55
55
@@ -75,18 +75,14 @@ type API =
75 :> Post '[HTML] Markup
75 :> Post '[HTML] Markup
76 :<|> "results" :> Capture "task name" String :> Get '[CSV, HTML] [Score]
76 :<|> "results" :> Capture "task name" String :> Get '[CSV, HTML] [Score]
77
77
78−webhookInstallation :: WithLog env Message m => RepoWebhookEvent -> ((), InstallationEvent) -> m ()
78+webhookInstallation :: MonadIO m => RepoWebhookEvent -> ((), InstallationEvent) -> m ()
79webhookInstallation _ ((), ev) =
79webhookInstallation _ ((), ev) =
80− logInfo . T.pack . show $ ev
80+ liftIO $ do
81+ print ev
82+ hFlush stdout
81
83
82webhookPushEvent ::
84webhookPushEvent ::
83− ( StaticPQ m,
84− MonadReader (ServerData m) m,
85+ (StaticPQ m, MonadReader (ServerData m) m, MonadUnliftIO m, HasTasks m, MonadError ServerError m) =>
85− MonadUnliftIO m,
86− HasTasks m,
87− MonadError ServerError m,
88− WithLog env Message m
89− ) =>
90 RepoWebhookEvent ->
86 RepoWebhookEvent ->
91 ((), PushEvent) ->
87 ((), PushEvent) ->
92 m ()
88 m ()
@@ -104,28 +100,18 @@ webhookPushEvent _ ((), ev) = do
104 M.takeWhileAntitone (repoName >) tasks
100 M.takeWhileAntitone (repoName >) tasks
105 case task of
101 case task of
106 Just (name, TaskConfig {..}) | T.take (T.length name) repoName == name -> do
102 Just (name, TaskConfig {..}) | T.take (T.length name) repoName == name -> do
107− nowTime <- liftIO getCurrentTime
108− if nowTime > deadline
109− then do
110− deadlineHasPassedStatus (N owner) (N repoName) (N sha)
111− createSubmission $
112− Submission
113− user
114− (T.unpack fullRepoName)
115− (T.unpack name)
116− (T.unpack sha)
117− (Jsonb PastDeadline)
118− else do
119− schedulePendingStatus (N owner) (N repoName) (N sha)
103+ schedulePendingStatus (N owner) (N repoName) (N sha)
120− createSubmission $
104+ createSubmission $
121− Submission
105+ Submission
122− user
106+ user
123− (T.unpack fullRepoName)
107+ (T.unpack fullRepoName)
124− (T.unpack name)
108+ (T.unpack name)
125− (T.unpack sha)
109+ (T.unpack sha)
126− (Jsonb BuildScheduled)
110+ (Jsonb BuildScheduled)
127− scheduleTask $ Build prebuild dockerfile (N owner) (N repoName) (N sha) timeoutMinutes
111+ scheduleTask $ Build prebuild dockerfile (N owner) (N repoName) (N sha) timeoutMinutes
128− logInfo . T.pack . show $ ev
112+ liftIO $ do
113+ print ev
114+ hFlush stdout
129 _ -> return ()
115 _ -> return ()
130
116
131retestSubmission ::
117retestSubmission ::
@@ -175,9 +161,9 @@ instance MonadError ServerError (ServerM schema) where
175server :: ServerT API (ServerM Schema)
161server :: ServerT API (ServerM Schema)
176server = (webhookInstallation :<|> webhookPushEvent) :<|> getSubmissionR :<|> retestSubmission :<|> getResults
162server = (webhookInstallation :<|> webhookPushEvent) :<|> getSubmissionR :<|> retestSubmission :<|> getResults
177
163
178−runServer :: FilePath -> IO ()
164+runServer :: IO ()
179−runServer config = do
165+runServer = do
180− c@Config {..} <- getConfig config
166+ c@Config {..} <- getConfig
181 print c
167 print c
182 pem <- BS.readFile appPkPemPath
168 pem <- BS.readFile appPkPemPath
183 appPkPem <- case readRsaPem pem of
169 appPkPem <- case readRsaPem pem of
@@ -292,7 +278,7 @@ instance (MonadReader (ServerData m) m, StaticPQ m, MonadUnliftIO m) => HasGithu
292 ( NewStatus
278 ( NewStatus
293 StatusPending
279 StatusPending
294 (Just . URL $ h <> "/submission/" <> owner <> "/" <> repo <> "/" <> sha)
280 (Just . URL $ h <> "/submission/" <> owner <> "/" <> repo <> "/" <> sha)
295− (Just "⌛")
281+ Nothing
296 (Just c)
282 (Just c)
297 )
283 )
298 scheduleTestedStatus TestResult {..} nOwner@(N owner) nRepo@(N repo) nCommit@(N sha) = do
284 scheduleTestedStatus TestResult {..} nOwner@(N owner) nRepo@(N repo) nCommit@(N sha) = do
@@ -300,7 +286,7 @@ instance (MonadReader (ServerData m) m, StaticPQ m, MonadUnliftIO m) => HasGithu
300 c <- asks context
286 c <- asks context
301 let total = M.size tests
287 let total = M.size tests
302 passed = M.size . M.filter id $ tests
288 passed = M.size . M.filter id $ tests
303− description = T.pack $ show passed <> "/" <> show total <> if passed == total then " ✅" else " ❌"
289+ description = T.pack $ show passed <> "/" <> show total
304 status =
290 status =
305 StatusUpdate
291 StatusUpdate
306 nOwner
292 nOwner
@@ -325,22 +311,7 @@ instance (MonadReader (ServerData m) m, StaticPQ m, MonadUnliftIO m) => HasGithu
325 NewStatus
311 NewStatus
326 { newStatusState = StatusError,
312 { newStatusState = StatusError,
327 newStatusTargetUrl = Just . URL $ h <> "/submission/" <> owner <> "/" <> repo <> "/" <> sha,
313 newStatusTargetUrl = Just . URL $ h <> "/submission/" <> owner <> "/" <> repo <> "/" <> sha,
328− newStatusDescription = Just "Build failed ☠️",
314+ newStatusDescription = Just "Build failed",
329− newStatusContext = Just c
330− }
331− scheduleTask status
332− deadlineHasPassedStatus nOwner@(N owner) nRepo@(N repo) nCommit@(N sha) = do
333− h <- asks baseUrl
334− c <- asks context
335− let status =
336− StatusUpdate
337− nOwner
338− nRepo
339− nCommit
340− NewStatus
341− { newStatusState = StatusError,
342− newStatusTargetUrl = Just . URL $ h <> "/submission/" <> owner <> "/" <> repo <> "/" <> sha,
343− newStatusDescription = Just "The deadline has passed ⏰",
344 newStatusContext = Just c
315 newStatusContext = Just c
345 }
316 }
346 scheduleTask status
317 scheduleTask status
@@ -354,8 +325,7 @@ data TaskConfig
354 = TaskConfig
325 = TaskConfig
355 { prebuild :: String,
326 { prebuild :: String,
356 dockerfile :: String,
327 dockerfile :: String,
357− timeoutMinutes :: Int,
328+ timeoutMinutes :: Int
358− deadline :: UTCTime
359 }
329 }
360 deriving stock (Show, Eq, Generic)
330 deriving stock (Show, Eq, Generic)
361 deriving anyclass (FromJSON, ToJSON)
331 deriving anyclass (FromJSON, ToJSON)
src/Server/Config.hs+2−2
@@ -71,8 +71,8 @@ defaultConfig =
71 githubContext = "MY CI NAME"
71 githubContext = "MY CI NAME"
72 }
72 }
73
73
74−getConfig :: FilePath -> IO Config
74+getConfig :: IO Config
75−getConfig = decodeFileThrow
75+getConfig = decodeFileThrow "config.yaml"
76
76
77-- getConfig :: IO Config
77-- getConfig :: IO Config
78-- getConfig =
78-- getConfig =
src/Server/Html.hs+4−7
@@ -39,16 +39,15 @@ getSubmissionR user repo sha' = do
39 Nothing ->
39 Nothing ->
40 return . ("Not found",) $
40 return . ("Not found",) $
41 H.h1 "404. not found. go away."
41 H.h1 "404. not found. go away."
42− Just Submission {..} -> return . (problem,) $ do
42+ Just Submission {..} -> return . (repo,) $ do
43 H.h1 $ toHtml problem
43 H.h1 $ toHtml problem
44 H.h2 $ toHtml userName
44 H.h2 $ toHtml userName
45− H.h3 . H.code . toHtml . take 7 $ sha
46 case status of
45 case status of
47 Jsonb BuildScheduled -> do
46 Jsonb BuildScheduled -> do
48− H.h3 "Waiting to build ⌛"
47+ H.h3 "Waiting to build"
49 restartBuild
48 restartBuild
50 Jsonb (SubmissionFailed err) -> do
49 Jsonb (SubmissionFailed err) -> do
51− H.h3 "Failed to build ☠️"
50+ H.h3 "Failed to build"
52 restartBuild
51 restartBuild
53 H.code . H.pre $ toHtml err
52 H.code . H.pre $ toHtml err
54 Jsonb (SubmissionRun (TestResult tests)) -> do
53 Jsonb (SubmissionRun (TestResult tests)) -> do
@@ -56,13 +55,11 @@ getSubmissionR user repo sha' = do
56 passed = M.size . M.filter Prelude.id $ tests
55 passed = M.size . M.filter Prelude.id $ tests
57 H.h3 . toHtml $
56 H.h3 . toHtml $
58 "Tests have run: " <> show passed <> "/" <> show total
57 "Tests have run: " <> show passed <> "/" <> show total
59− <> if passed == total then " 🎉" else " 🍌"
58+ <> if passed == total then " ✅" else ""
60 restartBuild
59 restartBuild
61 H.table $ flip M.foldMapWithKey tests $ \testName testPassed -> H.tr $ do
60 H.table $ flip M.foldMapWithKey tests $ \testName testPassed -> H.tr $ do
62 (H.td H.! A.align "right") . H.p $ toHtml testName
61 (H.td H.! A.align "right") . H.p $ toHtml testName
63 H.td $ if testPassed then "✅" else "❌"
62 H.td $ if testPassed then "✅" else "❌"
64− Jsonb PastDeadline ->
65− H.h3 "This was submitted after the deadline ⏰"
66 return . docTypeHtml $ do
63 return . docTypeHtml $ do
67 H.head $ do
64 H.head $ do
68 H.title $ toHtml titleText
65 H.title $ toHtml titleText
src/Server/Schema.hs+1−3
@@ -6,15 +6,13 @@ module Server.Schema
6where
6where
7
7
8import Server.Schema.V1
8import Server.Schema.V1
9−import Server.Schema.V2
10import Squeal.PostgreSQL
9import Squeal.PostgreSQL
11
10
12type StaticPQ m = MonadPQ Schema m
11type StaticPQ m = MonadPQ Schema m
13
12
14−type Schema = SchemaV2
13+type Schema = SchemaV1
15
14
16migration :: AlignedList (Migration (Terminally PQ IO)) (Public '[]) Schema
15migration :: AlignedList (Migration (Terminally PQ IO)) (Public '[]) Schema
17migration =
16migration =
18 pureMigration schemaMigrationV1
17 pureMigration schemaMigrationV1
19− :>> pureMigration schemaMigrationV2
20 :>> Done
18 :>> Done
src/Server/Schema/V2.hsdeleted−46
@@ -1,46 +0,0 @@
1−module Server.Schema.V2
2− ( SchemaV2,
3− schemaMigrationV2,
4− )
5−where
6−
7−import Data.Aeson
8−import Data.Int
9−import Data.Submission
10−import Data.Text (Text)
11−import Data.Time
12−import Server.Schema.V1
13−import Squeal.PostgreSQL
14−
15−type SchemaV2 =
16− Public
17− '[ "tasks"
18− ::: 'Table
19− ( '[ "pk_task_payload" :=> 'PrimaryKey '["task", "payload"]
20− ]
21− :=> '[ "task" ::: 'NoDef :=> 'NotNull (PG Text),
22− "payload" ::: 'NoDef :=> 'NotNull (PG (Jsonb Value)),
23− "creation_time" ::: 'Def :=> 'NotNull (PG UTCTime),
24− "started" ::: 'Def :=> 'NotNull (PG Bool),
25− "failures" ::: 'Def :=> 'NotNull (PG Int32)
26− ]
27− ),
28− "submissions"
29− ::: 'Table
30− ( '["pk_submission_repo_full_name_sha" :=> 'PrimaryKey '["repo_full_name", "sha"]]
31− :=> '[ "user_name" ::: 'NoDef :=> 'NotNull (PG String),
32− "repo_full_name" ::: 'NoDef :=> 'NotNull (PG String),
33− "problem" ::: 'NoDef :=> 'NotNull (PG String),
34− "sha" ::: 'NoDef :=> 'NotNull (PG String),
35− "status" ::: 'NoDef :=> 'NotNull (PG (Jsonb SubmissionStatus))
36− ]
37− )
38− ]
39−
40−schemaMigrationV2 :: Migration Definition SchemaV1 SchemaV2
41−schemaMigrationV2 =
42− Migration
43− { name = "v2",
44− up = alterTable #tasks (addColumn #failures (default_ 0 $ notNullable int)),
45− down = alterTable #tasks (dropColumn #failures)
46− }
stack.yaml+1−2
@@ -46,9 +46,8 @@ extra-deps:
46- git: https://github.com/ilyakooo0/github-app
46- git: https://github.com/ilyakooo0/github-app
47 commit: 0bede85ab53eb8f953cf6f4e58a756a205e88e0b
47 commit: 0bede85ab53eb8f953cf6f4e58a756a205e88e0b
48- servant-github-webhook-0.4.2.0
48- servant-github-webhook-0.4.2.0
49+- docker-0.6.0.3
49- http-client-0.5.14
50- http-client-0.5.14
50−- git: https://github.com/ilyakooo0/docker-hs
51− commit: 5cfdc4d7facd7043f530bce4ce0f0c57c923b029
52
51
53# Override default flag values for local packages and extra-deps
52# Override default flag values for local packages and extra-deps
54# flags: {}
53# flags: {}