summaryrefslogtreecommitdiff
path: root/server/Worker.hs
blob: 7609d48c2eb9494b65a07900500112a6d8eb5fb7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
{-# LANGUAGE DataKinds         #-}
{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE LambdaCase        #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards   #-}

module Worker (linterThread, Job(..)) where

import           Universum

import           Bindings.Cli.Git              (gitProc)
import           CheckDir                      (recursiveCheckDir)
import           Cli.Extras                    (CliConfig, ProcessFailure,
                                                Severity (..),
                                                callProcessAndLogOutput,
                                                prettyProcessFailure, runCli)
import           Control.Concurrent.Async      (async, link)
import           Control.Concurrent.STM.TQueue
import qualified Data.Text                     as T
import qualified Data.UUID                     as UUID
import qualified Data.UUID.V4                  as UUID
import           Server                        (Config, JobStatus (..),
                                                Org (..),
                                                RemoteRef (reporef, repourl),
                                                ServerState, setJobStatus,
                                                tmpdir)
import           System.Directory              (doesDirectoryExist)
import           System.FilePath               ((</>))



data Job = Job
  { jobRef :: RemoteRef
  , jobOrg :: Org True
  }

linterThread :: Config True -> CliConfig -> TQueue Job -> MVar ServerState -> IO Void
linterThread config cliconfig queue done = forever $ do
  next <- atomically (readTQueue queue)
  -- TODO: this doesn't guard against two jobs running on the same repo!
  job <- async $ runJob config next cliconfig done
  link job -- TODO: is this a good idea? will crash the server if a job fails

-- | the actual check function. forks, calls out to git to update the
-- repository, create a new worktree, lints it, then tells git to
-- delete that tree again
runJob :: Config True -> Job -> CliConfig -> MVar ServerState -> IO (Either ProcessFailure ())
runJob config Job {..} cliconfig done = runCli cliconfig $ do
    res <- liftIO $ runCli cliconfig $ do
      ifM (liftIO $ doesDirectoryExist gitdir)
        -- TODO: these calls fail for dumb http, add some fallback!
        (callgit gitdir
         [ "fetch", "origin", toString ref, "--depth", "1" ])
        (callgit gitdir
         [ "clone", toString ref, "--bare"
         , "--depth", "1", "-b", toString ref])
      rand <- liftIO UUID.nextRandom
      let workdir = "/tmp" </> ("worktree-" <> UUID.toString rand)
      callgit gitdir [ "worktree", "add", workdir ]
      callgit workdir [ "checkout", toString ref ]
      res <- liftIO $ recursiveCheckDir (orgLintconfig jobOrg) workdir (orgEntrypoint jobOrg)
      callgit gitdir [ "worktree", "remove", "-f", "-f", workdir ]
      pure res
    liftIO $ setJobStatus done jobRef $ case res of
      Right res -> Linted res
      Left err  -> Failed (prettyProcessFailure err)
  where
    url = repourl jobRef
    ref = reporef jobRef
    callgit dir = callProcessAndLogOutput (Debug, Debug) . gitProc dir
    gitdir = view tmpdir config </> toString hashedname
    hashedname = T.map escapeSlash url
      where escapeSlash = \case { '/' -> '-'; a -> a }