From 6a5a4b1718f776ed63d21696d0ebce547acdef7b Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Wed, 6 Feb 2019 17:43:37 +0900 Subject: [PATCH 01/71] Update drone version to 1.0 in docker-compose --- drone/docker-compose.yml | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/drone/docker-compose.yml b/drone/docker-compose.yml index 0d745c0..541c99d 100644 --- a/drone/docker-compose.yml +++ b/drone/docker-compose.yml @@ -2,25 +2,26 @@ version: '2' services: drone-server: - image: drone/drone:0.8 - + image: drone/drone:1.0.0-rc.5 ports: - - 8000:8000 + - 8000:80 + - 443:443 - 9000 volumes: - - .:/var/lib/drone/ + - /var/run/docker.sock:/var/run/docker.sock + - .:/data restart: always environment: - - DRONE_OPEN=true - - DRONE_HOST=${DRONE_HOST} - - DRONE_GITHUB=true - - DRONE_GITHUB_CLIENT=${DRONE_GITHUB_CLIENT} - - DRONE_GITHUB_SECRET=${DRONE_GITHUB_SECRET} - - DRONE_SECRET=${DRONE_SECRET} + - DRONE_GITHUB_SERVER=https://github.com + - DRONE_GITHUB_CLIENT_ID=${DRONE_GITHUB_CLIENT} + - DRONE_GITHUB_CLIENT_SECRET=${DRONE_GITHUB_SECRET} + - DRONE_RPC_SECRET=${DRONE_SECRET} + - DRONE_SERVER_HOST=abcdef.ngrok.io + - DRONE_SERVER_PROTO=https + - DRONE_TLS_AUTOCERT=true drone-agent: - image: drone/agent:0.8 - + image: drone/agent:1.0.0-rc.5 command: agent restart: always depends_on: @@ -28,5 +29,6 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock environment: - - DRONE_SERVER=drone-server:9000 - - DRONE_SECRET=${DRONE_SECRET} + - DRONE_RPC_SERVER=drone-server:9000 + - DRONE_RPC_SECRET=${DRONE_SECRET} + - DRONE_RUNNER_CAPACITY=2 From 989f79a120ef040530776a71befd6a6c9d26ab58 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Wed, 6 Feb 2019 19:27:15 +0900 Subject: [PATCH 02/71] Update drone to 1.0 with drone-haskell --- app/Main.hs | 13 +++++++------ src/Git/Plantation/API/CRUD.hs | 4 ++-- src/Git/Plantation/Cmd.hs | 5 +++-- stack.yaml | 2 +- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/Main.hs b/app/Main.hs index 60fc795..ed17617 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -62,16 +62,17 @@ versionOpt = optFlag [] ["version"] "Show version" runServer :: Options -> Config -> IO () runServer opts config = do - logOpts <- logOptionsHandle stdout $ opts ^. #verbose - token <- liftIO $ fromString <$> getEnv "GH_TOKEN" - dHost <- liftIO $ fromString <$> getEnv "DRONE_HOST" - dToken <- liftIO $ fromString <$> getEnv "DRONE_TOKEN" - let client = Drone.HttpsClient (#host @= dHost <: #token @= dToken <: nil) + logOpts <- logOptionsHandle stdout $ opts ^. #verbose + token <- liftIO $ fromString <$> getEnv "GH_TOKEN" + dHost <- liftIO $ fromString <$> getEnv "DRONE_HOST" + dToken <- liftIO $ fromString <$> getEnv "DRONE_TOKEN" + dPort <- liftIO $ readMaybe <$> getEnv "DRONE_PORT" + let client = #host @= dHost <: #port @= dPort <: #token @= dToken <: nil withLogFunc logOpts $ \logger -> do let env = #config @= config <: #token @= token <: #work @= (opts ^. #work) - <: #client @= client + <: #client @= Drone.HttpsClient client <: #logger @= logger <: nil :: Env B.putStr $ "Listening on port " <> (fromString . show) (opts ^. #port) <> "\n" diff --git a/src/Git/Plantation/API/CRUD.hs b/src/Git/Plantation/API/CRUD.hs index 8ba000f..d71347d 100644 --- a/src/Git/Plantation/API/CRUD.hs +++ b/src/Git/Plantation/API/CRUD.hs @@ -53,7 +53,7 @@ getScores = do fetchBuilds :: Drone.Client c => c -> Problem -> Plant (Text, [Drone.Build]) fetchBuilds client problem = do let (owner, repo) = splitRepoName $ problem ^. #repo_name - builds <- responseBody <$> runReq def (Drone.getBuilds client owner repo) + builds <- responseBody <$> runReq def (Drone.getBuilds client owner repo Nothing) pure (problem ^. #problem_name, builds) mkScore :: [Problem] -> Map Text [Drone.Build] -> Team -> Score @@ -63,7 +63,7 @@ mkScore problems builds team <: #stats @= stats <: nil where - isTeamBuild b = b ^. #branch == team ^. #name + isTeamBuild b = b ^. #source == team ^. #name builds' = Map.filter (not . null) $ Map.map (filter isTeamBuild) builds stats = Map.elems $ Map.mapWithKey toStatus builds' diff --git a/src/Git/Plantation/Cmd.hs b/src/Git/Plantation/Cmd.hs index 629a6ef..6938d7f 100644 --- a/src/Git/Plantation/Cmd.hs +++ b/src/Git/Plantation/Cmd.hs @@ -24,10 +24,11 @@ run opts = do logOpts <- logOptionsHandle stdout (opts ^. #verbose) token <- liftIO $ fromString <$> getEnv "GH_TOKEN" withLogFunc logOpts $ \logger -> do - let env = #config @= config + let client = #host @= "" <: #port @= Nothing <: #token @= "" <: nil + env = #config @= config <: #token @= token <: #work @= opts ^. #work - <: #client @= Drone.HttpsClient (#host @= "" <: #token @= "" <: nil) + <: #client @= Drone.HttpsClient client <: #logger @= logger <: nil runRIO env $ matchField diff --git a/stack.yaml b/stack.yaml index 6ead436..3a6d9b5 100644 --- a/stack.yaml +++ b/stack.yaml @@ -6,7 +6,7 @@ extra-deps: - github-0.20 - servant-github-webhook-0.4.1.0 - github: matsubara0507/drone-haskell - commit: 6e1f61e625fe04d2d90554ea3fe93894708a4fcf + commit: aa6f5152dd9ea72cb48a32bdc58c91de2bdc21a9 - github: matsubara0507/elm-export commit: 7dc72b9eb34a4f126bb37e288534ec9ac7dc4bc3 docker: From c23ab738d353e522c45f392b136889bd9cf11965 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Mon, 25 Feb 2019 14:43:17 +0900 Subject: [PATCH 03/71] Feat: remove github org for team --- config/.git-plantation.yaml | 12 ++++- elm-src/Generated/API.elm | 57 +++++++++++++++++++++--- src/Git/Plantation.hs | 10 ++--- src/Git/Plantation/API/CRUD.hs | 17 ++++--- src/Git/Plantation/API/Webhook.hs | 10 ++--- src/Git/Plantation/Cmd/Repo.hs | 36 ++++++++------- src/Git/Plantation/Config.hs | 7 ++- src/Git/Plantation/Data.hs | 9 ++-- src/Git/Plantation/{ => Data}/Problem.hs | 6 +-- src/Git/Plantation/Data/Team.hs | 46 +++++++++++++++++++ src/Git/Plantation/Env.hs | 4 ++ src/Git/Plantation/Team.hs | 21 --------- src/Language/Elm.hs | 7 ++- stack.yaml | 2 +- test/GenerateElm.hs | 5 ++- 15 files changed, 167 insertions(+), 82 deletions(-) rename src/Git/Plantation/{ => Data}/Problem.hs (76%) create mode 100644 src/Git/Plantation/Data/Team.hs delete mode 100644 src/Git/Plantation/Team.hs diff --git a/config/.git-plantation.yaml b/config/.git-plantation.yaml index f6bf288..172c759 100644 --- a/config/.git-plantation.yaml +++ b/config/.git-plantation.yaml @@ -28,6 +28,14 @@ problems: teams: - name: sample - github: sample-hige + id: alpha member: - - matsubara0507 + - name: MATSUBARA Nobutada + github: matsubara0507 + problems: + - problem: matsubara0507/git-challenge-tutorial + github: sample-hige/git-challenge-tutorial + - problem: matsubara0507/git-challenge-is-order-an-adding + github: sample-hige/git-challenge-is-order-an-adding + - problem: matsubara0507/git-challenge-minesweeper + github: sample-hige/git-challenge-minesweeper diff --git a/elm-src/Generated/API.elm b/elm-src/Generated/API.elm index 7da1107..7d83a8b 100644 --- a/elm-src/Generated/API.elm +++ b/elm-src/Generated/API.elm @@ -1,4 +1,4 @@ -module Generated.API exposing (Config, Problem, Score, Status, Team, decodeConfig, decodeProblem, decodeScore, decodeStatus, decodeTeam, encodeConfig, encodeProblem, encodeScore, encodeStatus, encodeTeam, getApiProblems, getApiScores, getApiTeams) +module Generated.API exposing (Config, Problem, Repo, Score, Status, Team, User, decodeConfig, decodeProblem, decodeRepo, decodeScore, decodeStatus, decodeTeam, decodeUser, encodeConfig, encodeProblem, encodeRepo, encodeScore, encodeStatus, encodeTeam, encodeUser, getApiProblems, getApiScores, getApiTeams) import Http import Json.Decode exposing (..) @@ -9,8 +9,9 @@ import String type alias Team = { name : String - , github : String - , member : List String + , id : String + , repos : List Repo + , member : List User } @@ -18,16 +19,60 @@ decodeTeam : Decoder Team decodeTeam = Json.Decode.succeed Team |> required "name" string - |> required "github" string - |> required "member" (list string) + |> required "id" string + |> required "repos" (list decodeRepo) + |> required "member" (list decodeUser) encodeTeam : Team -> Json.Encode.Value encodeTeam x = Json.Encode.object [ ( "name", Json.Encode.string x.name ) + , ( "id", Json.Encode.string x.id ) + , ( "repos", Json.Encode.list encodeRepo x.repos ) + , ( "member", Json.Encode.list encodeUser x.member ) + ] + + +type alias User = + { name : String + , github : String + } + + +decodeUser : Decoder User +decodeUser = + Json.Decode.succeed User + |> required "name" string + |> required "github" string + + +encodeUser : User -> Json.Encode.Value +encodeUser x = + Json.Encode.object + [ ( "name", Json.Encode.string x.name ) + , ( "github", Json.Encode.string x.github ) + ] + + +type alias Repo = + { problem : String + , github : String + } + + +decodeRepo : Decoder Repo +decodeRepo = + Json.Decode.succeed Repo + |> required "problem" string + |> required "github" string + + +encodeRepo : Repo -> Json.Encode.Value +encodeRepo x = + Json.Encode.object + [ ( "problem", Json.Encode.string x.problem ) , ( "github", Json.Encode.string x.github ) - , ( "member", Json.Encode.list Json.Encode.string x.member ) ] diff --git a/src/Git/Plantation.hs b/src/Git/Plantation.hs index 7bee96f..8b3008f 100644 --- a/src/Git/Plantation.hs +++ b/src/Git/Plantation.hs @@ -2,9 +2,7 @@ module Git.Plantation ( module X ) where -import Git.Plantation.Config as X -import Git.Plantation.Data as X -import Git.Plantation.Env as X -import Git.Plantation.Problem as X -import Git.Plantation.Score as X -import Git.Plantation.Team as X +import Git.Plantation.Config as X +import Git.Plantation.Data as X +import Git.Plantation.Env as X +import Git.Plantation.Score as X diff --git a/src/Git/Plantation/API/CRUD.hs b/src/Git/Plantation/API/CRUD.hs index d71347d..0cc1160 100644 --- a/src/Git/Plantation/API/CRUD.hs +++ b/src/Git/Plantation/API/CRUD.hs @@ -5,18 +5,17 @@ module Git.Plantation.API.CRUD where import RIO -import qualified RIO.Map as Map +import qualified RIO.Map as Map import Data.Default.Class import Data.Extensible -import qualified Drone.Client as Drone -import qualified Drone.Endpoints as Drone -import qualified Drone.Types as Drone -import Git.Plantation.Cmd (splitRepoName) -import Git.Plantation.Env (Plant) -import Git.Plantation.Problem (Problem) -import Git.Plantation.Score (Score, Status) -import Git.Plantation.Team (Team) +import qualified Drone.Client as Drone +import qualified Drone.Endpoints as Drone +import qualified Drone.Types as Drone +import Git.Plantation (Problem, Team) +import Git.Plantation.Cmd (splitRepoName) +import Git.Plantation.Env (Plant) +import Git.Plantation.Score (Score, Status) import Network.HTTP.Req import Servant diff --git a/src/Git/Plantation/API/Webhook.hs b/src/Git/Plantation/API/Webhook.hs index eaf2d9f..40ee84b 100644 --- a/src/Git/Plantation/API/Webhook.hs +++ b/src/Git/Plantation/API/Webhook.hs @@ -8,9 +8,9 @@ import RIO import qualified RIO.List as L import Git.Plantation.Cmd.Repo +import Git.Plantation.Data (Problem, Team) +import qualified Git.Plantation.Data.Team as Team import Git.Plantation.Env (Plant) -import Git.Plantation.Problem (Problem) -import Git.Plantation.Team (Team) import GitHub.Data.Webhooks.Events import GitHub.Data.Webhooks.Payload import Servant @@ -41,9 +41,9 @@ pushWebhook _ (_, ev) = do _ -> logError "Team or Problem not found." findTeamByPushEvent :: PushEvent -> [Team] -> Maybe Team -findTeamByPushEvent ev ts = do - owner <- whOrgLogin <$> evPushOrganization ev - L.find (\t -> t ^. #github == owner) ts +findTeamByPushEvent ev = L.find (isJust . Team.lookupRepo' repoName) + where + repoName = whRepoFullName $ evPushRepository ev findProblemByPushEvent :: PushEvent -> [Problem] -> Maybe Problem findProblemByPushEvent ev = diff --git a/src/Git/Plantation/Cmd/Repo.hs b/src/Git/Plantation/Cmd/Repo.hs index 8529a07..aa65fcc 100644 --- a/src/Git/Plantation/Cmd/Repo.hs +++ b/src/Git/Plantation/Cmd/Repo.hs @@ -7,19 +7,19 @@ module Git.Plantation.Cmd.Repo where import RIO -import qualified RIO.List as L -import qualified RIO.Text as Text +import qualified RIO.List as L +import qualified RIO.Text as Text import Data.Extensible -import qualified Git.Cmd as Git -import Git.Plantation.Env (Plant) -import Git.Plantation.Problem (Problem) -import Git.Plantation.Team (Team) -import GitHub.Data.Name (mkName) -import GitHub.Data.Repos (newRepo) -import GitHub.Endpoints.Repos (Auth (..)) -import qualified GitHub.Endpoints.Repos as GitHub -import Shelly hiding (FilePath) +import qualified Git.Cmd as Git +import Git.Plantation.Data (Problem, Team) +import qualified Git.Plantation.Data.Team as Team +import Git.Plantation.Env (Plant, maybeWithLogError) +import GitHub.Data.Name (mkName) +import GitHub.Data.Repos (newRepo) +import GitHub.Endpoints.Repos (Auth (..)) +import qualified GitHub.Endpoints.Repos as GitHub +import Shelly hiding (FilePath) type NewRepoCmd = Record '[ "repo" >: Maybe Text @@ -40,8 +40,8 @@ createRepo team problem = do teamUrl = mconcat ["https://", token, "@github.com/", teamRepo, ".git"] problemUrl = mconcat ["https://", token, "@github.com/", owner, "/", repo, ".git"] - shelly $ chdir_p (workDir (team ^. #github)) (Git.cloneOrFetch teamUrl repo) - shelly $ chdir_p (workDir (team ^. #github) repo) $ do + shelly $ chdir_p (workDir (team ^. #name)) (Git.cloneOrFetch teamUrl repo) + shelly $ chdir_p (workDir (team ^. #name) repo) $ do Git.remote ["add", "problem", problemUrl] Git.fetch ["--all"] forM_ (problem ^. #challenge_branches) $ @@ -55,7 +55,7 @@ createRepo team problem = do Git.existBranch (team ^. #name) >>= \case False -> Git.checkout ["-b", team ^. #name] True -> Git.checkout [team ^. #name] - writefile ciFileName (team ^. #github <> "/" <> repo) + writefile ciFileName teamRepo Git.add [ciFileName] Git.commit ["-m", "[CI SKIP] Add ci branch"] Git.push ["-u", "origin", team ^. #name] @@ -72,15 +72,17 @@ createRepoByRepoName team repoName = do createRepoInGitHub :: Team -> Problem -> Plant Text createRepoInGitHub team problem = do token <- asks (view #token) - let (_, repo) = splitRepoName $ problem ^. #repo_name + (owner, repo) <- maybeWithLogError + ((splitRepoName . view #github) <$> Team.lookupRepo problem team) + (mconcat ["Error: undefined problem ", problem ^. #repo_name, " in ", team ^. #name]) logInfo $ "create repo in github: " <> displayShow (problem ^. #repo_name) resp <- liftIO $ GitHub.createOrganizationRepo' (OAuth token) - (mkName Proxy $ team ^. #github) + (mkName Proxy owner) (newRepo $ mkName Proxy repo) case resp of Left err -> logError "Error: create github repo" >> fail (show err) - Right _ -> pure (team ^. #github <> "/" <> repo) + Right _ -> pure (team ^. #name <> "/" <> repo) pushForCI :: Team -> Problem -> Plant () pushForCI team problem = do diff --git a/src/Git/Plantation/Config.hs b/src/Git/Plantation/Config.hs index 257ccfa..d8336d0 100644 --- a/src/Git/Plantation/Config.hs +++ b/src/Git/Plantation/Config.hs @@ -7,10 +7,9 @@ module Git.Plantation.Config where import RIO import Data.Extensible -import qualified Data.Yaml as Y -import Elm (ElmType (..)) -import Git.Plantation.Problem (Problem) -import Git.Plantation.Team (Team) +import qualified Data.Yaml as Y +import Elm (ElmType (..)) +import Git.Plantation.Data (Problem, Team) import Language.Elm type Config = Record diff --git a/src/Git/Plantation/Data.hs b/src/Git/Plantation/Data.hs index daa03c2..abce7a9 100644 --- a/src/Git/Plantation/Data.hs +++ b/src/Git/Plantation/Data.hs @@ -1,7 +1,4 @@ -module Git.Plantation.Data where +module Git.Plantation.Data (module X) where -import RIO - -type Branch = Text - -type User = Text +import Git.Plantation.Data.Problem as X +import Git.Plantation.Data.Team as X diff --git a/src/Git/Plantation/Problem.hs b/src/Git/Plantation/Data/Problem.hs similarity index 76% rename from src/Git/Plantation/Problem.hs rename to src/Git/Plantation/Data/Problem.hs index 9dd86b5..196037c 100644 --- a/src/Git/Plantation/Problem.hs +++ b/src/Git/Plantation/Data/Problem.hs @@ -2,13 +2,11 @@ {-# LANGUAGE TypeOperators #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -module Git.Plantation.Problem where +module Git.Plantation.Data.Problem where import RIO import Data.Extensible -import Elm (ElmType (..)) -import Git.Plantation.Data (Branch) import Language.Elm type Problem = Record @@ -21,3 +19,5 @@ type Problem = Record instance ElmType Problem where toElmType = toElmRecordType "Problem" + +type Branch = Text diff --git a/src/Git/Plantation/Data/Team.hs b/src/Git/Plantation/Data/Team.hs new file mode 100644 index 0000000..b5f9b70 --- /dev/null +++ b/src/Git/Plantation/Data/Team.hs @@ -0,0 +1,46 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE OverloadedLabels #-} +{-# LANGUAGE TypeOperators #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module Git.Plantation.Data.Team where + +import RIO +import qualified RIO.List as L + +import Data.Extensible +import Git.Plantation.Data.Problem +import Language.Elm + +type Team = Record + '[ "name" >: Text + , "id" >: Text + , "repos" >: [Repo] + , "member" >: [User] + ] + +instance ElmType Team where + toElmType = toElmRecordType "Team" + +type User = Record + '[ "name" >: Text + , "github" >: Text + ] + +instance ElmType User where + toElmType = toElmRecordType "User" + +type Repo = Record + '[ "problem" >: Text + , "github" >: Text + ] + +instance ElmType Repo where + toElmType = toElmRecordType "Repo" + +lookupRepo :: Problem -> Team -> Maybe Repo +lookupRepo problem = lookupRepo' (problem ^. #repo_name) + +lookupRepo' :: Text -> Team -> Maybe Repo +lookupRepo' repoName team = + L.find (\repo -> repoName == repo ^. #problem) (team ^. #repos) diff --git a/src/Git/Plantation/Env.hs b/src/Git/Plantation/Env.hs index 96810fd..b9ef44c 100644 --- a/src/Git/Plantation/Env.hs +++ b/src/Git/Plantation/Env.hs @@ -24,3 +24,7 @@ type Env = Record instance HasLogFunc Env where logFuncL = lens (view #logger) (\x y -> x & #logger `set` y) + +maybeWithLogError :: Maybe a -> Text -> Plant a +maybeWithLogError (Just x) _ = pure x +maybeWithLogError Nothing e = logError (display e) >> fail (show e) diff --git a/src/Git/Plantation/Team.hs b/src/Git/Plantation/Team.hs deleted file mode 100644 index 78417a2..0000000 --- a/src/Git/Plantation/Team.hs +++ /dev/null @@ -1,21 +0,0 @@ -{-# LANGUAGE DataKinds #-} -{-# LANGUAGE TypeOperators #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} - -module Git.Plantation.Team where - -import RIO - -import Data.Extensible -import Elm (ElmType (..)) -import Git.Plantation.Data (User) -import Language.Elm - -type Team = Record - '[ "name" >: Text - , "github" >: Text - , "member" >: [User] - ] - -instance ElmType Team where - toElmType = toElmRecordType "Team" diff --git a/src/Language/Elm.hs b/src/Language/Elm.hs index 94baa3a..f22e479 100644 --- a/src/Language/Elm.hs +++ b/src/Language/Elm.hs @@ -4,7 +4,12 @@ {-# LANGUAGE TypeOperators #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -module Language.Elm where +module Language.Elm + ( ElmType(..) + , ToElmValue (..) + , elmTypeToElmValue + , toElmRecordType + ) where import RIO diff --git a/stack.yaml b/stack.yaml index 3a6d9b5..60dc54d 100644 --- a/stack.yaml +++ b/stack.yaml @@ -8,7 +8,7 @@ extra-deps: - github: matsubara0507/drone-haskell commit: aa6f5152dd9ea72cb48a32bdc58c91de2bdc21a9 - github: matsubara0507/elm-export - commit: 7dc72b9eb34a4f126bb37e288534ec9ac7dc4bc3 + commit: 0a05fecf56f84aa2ad998200a03c7df2a1676c0c docker: repo: fpco/stack-build enable: false diff --git a/test/GenerateElm.hs b/test/GenerateElm.hs index 2939cda..9b33ca8 100644 --- a/test/GenerateElm.hs +++ b/test/GenerateElm.hs @@ -10,7 +10,8 @@ import Data.Proxy (Proxy (..)) import Elm (ElmType, Spec (Spec), specsToDir, toElmDecoderSource, toElmEncoderSource, toElmTypeSource) -import Git.Plantation (Config, Problem, Score, Status, Team) +import Git.Plantation (Config, Problem, Repo, Score, Status, + Team, User) import Git.Plantation.API.CRUD (CRUD) import Servant ((:>)) import Servant.Elm (defElmImports, generateElmForAPI) @@ -20,6 +21,8 @@ spec :: Spec spec = Spec ["Generated", "API"] $ concat [ [defElmImports] , toElmTypeAll (Proxy @ Team) + , toElmTypeAll (Proxy @ User) + , toElmTypeAll (Proxy @ Repo) , toElmTypeAll (Proxy @ Problem) , toElmTypeAll (Proxy @ Config) , toElmTypeAll (Proxy @ Score) From d13205200fa7d759d86c1dcaf3ecb53a826fdaa0 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Mon, 25 Feb 2019 15:31:21 +0900 Subject: [PATCH 04/71] Fix: configs --- .env.template | 1 + config/.git-plantation.yaml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.env.template b/.env.template index 787ab3c..a558b44 100644 --- a/.env.template +++ b/.env.template @@ -2,6 +2,7 @@ PORT= WORK= CONFIG= DRONE_HOST= +DRONE_PORT= DRONE_TOKEN= GH_TOKEN= GH_SECRET= diff --git a/config/.git-plantation.yaml b/config/.git-plantation.yaml index 172c759..d1e8547 100644 --- a/config/.git-plantation.yaml +++ b/config/.git-plantation.yaml @@ -32,7 +32,7 @@ teams: member: - name: MATSUBARA Nobutada github: matsubara0507 - problems: + repos: - problem: matsubara0507/git-challenge-tutorial github: sample-hige/git-challenge-tutorial - problem: matsubara0507/git-challenge-is-order-an-adding From ff69f089c4a0f945f1a7ab9c8eb4b4611ed7a094 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Mon, 25 Feb 2019 15:32:22 +0900 Subject: [PATCH 05/71] Feat: dotenv --- app/Main.hs | 4 +++- package.yaml | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/Main.hs b/app/Main.hs index ed17617..b6e6f47 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -11,6 +11,7 @@ import Paths_git_plantation (version) import RIO import qualified RIO.ByteString as B +import Configuration.Dotenv (defaultConfig, loadFile) import Data.Extensible import Data.Extensible.GetOpt import Data.Version (Version) @@ -25,7 +26,8 @@ import qualified Servant.GitHub.Webhook (GitHubKey, gitHubKey) import System.Environment (getEnv) main :: IO () -main = withGetOpt "[options] [config-file]" opts $ \r args -> +main = withGetOpt "[options] [config-file]" opts $ \r args -> do + _ <- loadFile defaultConfig case (r ^. #version, listToMaybe args) of (True, _) -> B.putStr $ fromString (showVersion version) <> "\n" (_, Nothing) -> error "please input config file path." diff --git a/package.yaml b/package.yaml index b2dd7c1..04b9416 100644 --- a/package.yaml +++ b/package.yaml @@ -39,6 +39,7 @@ dependencies: - aeson - blaze-html - data-default-class +- dotenv - drone - elm-export - extensible >= 0.4.9 From fb68bde48c5ab4bbeccdf77b79320f10ecf639ce Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Mon, 25 Feb 2019 15:55:26 +0900 Subject: [PATCH 06/71] Modify: error handling in fetch build --- src/Git/Plantation/API/CRUD.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Git/Plantation/API/CRUD.hs b/src/Git/Plantation/API/CRUD.hs index 0cc1160..c9180f9 100644 --- a/src/Git/Plantation/API/CRUD.hs +++ b/src/Git/Plantation/API/CRUD.hs @@ -1,4 +1,5 @@ {-# LANGUAGE DataKinds #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE TypeOperators #-} @@ -52,7 +53,9 @@ getScores = do fetchBuilds :: Drone.Client c => c -> Problem -> Plant (Text, [Drone.Build]) fetchBuilds client problem = do let (owner, repo) = splitRepoName $ problem ^. #repo_name - builds <- responseBody <$> runReq def (Drone.getBuilds client owner repo Nothing) + builds <- tryAny (runReq def $ Drone.getBuilds client owner repo Nothing) >>= \case + Left err -> logError (display err) >> pure [] + Right resp -> pure $ responseBody resp pure (problem ^. #problem_name, builds) mkScore :: [Problem] -> Map Text [Drone.Build] -> Team -> Score From fe66f50b4d7881fe94038f3450207a2a242a6f98 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Mon, 25 Feb 2019 19:35:53 +0900 Subject: [PATCH 07/71] Feat: add log in shelly --- src/Git/Plantation/Cmd/Repo.hs | 14 +++++++------- src/Git/Plantation/Env.hs | 9 +++++++++ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/Git/Plantation/Cmd/Repo.hs b/src/Git/Plantation/Cmd/Repo.hs index aa65fcc..ebf2c2f 100644 --- a/src/Git/Plantation/Cmd/Repo.hs +++ b/src/Git/Plantation/Cmd/Repo.hs @@ -14,7 +14,7 @@ import Data.Extensible import qualified Git.Cmd as Git import Git.Plantation.Data (Problem, Team) import qualified Git.Plantation.Data.Team as Team -import Git.Plantation.Env (Plant, maybeWithLogError) +import Git.Plantation.Env (Plant, maybeWithLogError, shelly') import GitHub.Data.Name (mkName) import GitHub.Data.Repos (newRepo) import GitHub.Endpoints.Repos (Auth (..)) @@ -40,8 +40,8 @@ createRepo team problem = do teamUrl = mconcat ["https://", token, "@github.com/", teamRepo, ".git"] problemUrl = mconcat ["https://", token, "@github.com/", owner, "/", repo, ".git"] - shelly $ chdir_p (workDir (team ^. #name)) (Git.cloneOrFetch teamUrl repo) - shelly $ chdir_p (workDir (team ^. #name) repo) $ do + shelly' $ chdir_p (workDir (team ^. #name)) (Git.cloneOrFetch teamUrl repo) + shelly' $ chdir_p (workDir (team ^. #name) repo) $ do Git.remote ["add", "problem", problemUrl] Git.fetch ["--all"] forM_ (problem ^. #challenge_branches) $ @@ -49,8 +49,8 @@ createRepo team problem = do Git.push $ "-u" : "origin" : problem ^. #challenge_branches logInfo $ "Success: create repo as " <> displayShow teamRepo - shelly $ chdir_p (workDir owner) (Git.cloneOrFetch problemUrl repo) - shelly $ chdir_p (workDir owner repo) $ do + shelly' $ chdir_p (workDir owner) (Git.cloneOrFetch problemUrl repo) + shelly' $ chdir_p (workDir owner repo) $ do Git.checkout [problem ^. #ci_branch] Git.existBranch (team ^. #name) >>= \case False -> Git.checkout ["-b", team ^. #name] @@ -90,8 +90,8 @@ pushForCI team problem = do workDir <- asks (view #work) let (owner, repo) = splitRepoName $ problem ^. #repo_name problemUrl = mconcat ["https://", token, "@github.com/", owner, "/", repo, ".git"] - shelly $ chdir_p (workDir owner) (Git.cloneOrFetch problemUrl repo) - shelly $ chdir_p (workDir owner repo) $ do + shelly' $ chdir_p (workDir owner) (Git.cloneOrFetch problemUrl repo) + shelly' $ chdir_p (workDir owner repo) $ do Git.fetch [] Git.checkout [team ^. #name] Git.commit ["--allow-empty", "-m", "Empty Commit!!"] diff --git a/src/Git/Plantation/Env.hs b/src/Git/Plantation/Env.hs index b9ef44c..2b091a0 100644 --- a/src/Git/Plantation/Env.hs +++ b/src/Git/Plantation/Env.hs @@ -11,6 +11,7 @@ import Data.Extensible import qualified Drone.Client as Drone import Git.Plantation.Config import qualified GitHub.Auth as GitHub +import Shelly hiding (FilePath) type Plant = RIO Env @@ -28,3 +29,11 @@ instance HasLogFunc Env where maybeWithLogError :: Maybe a -> Text -> Plant a maybeWithLogError (Just x) _ = pure x maybeWithLogError Nothing e = logError (display e) >> fail (show e) + +shelly' :: Sh a -> Plant a +shelly' sh = do + env <- ask + shelly + $ (log_stdout_with (runRIO env . logDebug . display)) + $ (log_stderr_with (runRIO env . logDebug . display)) + $ sh From 58f4863691f248f59cbecd5dd16c004e93dd6459 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Mon, 25 Feb 2019 19:36:37 +0900 Subject: [PATCH 08/71] Feat: dotenv for tool --- tool/Main.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tool/Main.hs b/tool/Main.hs index 8478e7f..5a29c85 100644 --- a/tool/Main.hs +++ b/tool/Main.hs @@ -13,6 +13,7 @@ module Main where import qualified Paths_git_plantation as Meta import RIO +import Configuration.Dotenv (defaultConfig, loadFile) import Data.Extensible import Data.Version (Version) import qualified Data.Version as Version @@ -22,7 +23,9 @@ import Git.Plantation.Cmd import Options.Applicative main :: IO () -main = run =<< execParser opts +main = do + _ <- loadFile defaultConfig + run =<< execParser opts where opts = info (options <**> version Meta.version <**> helper) $ fullDesc From 93c4d128932ce45827b9585f12a1f09ef19cf498 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Mon, 25 Feb 2019 19:37:21 +0900 Subject: [PATCH 09/71] Fix: github name in create repo tool --- src/Git/Plantation/Cmd/Repo.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Git/Plantation/Cmd/Repo.hs b/src/Git/Plantation/Cmd/Repo.hs index ebf2c2f..ecd4da9 100644 --- a/src/Git/Plantation/Cmd/Repo.hs +++ b/src/Git/Plantation/Cmd/Repo.hs @@ -75,14 +75,14 @@ createRepoInGitHub team problem = do (owner, repo) <- maybeWithLogError ((splitRepoName . view #github) <$> Team.lookupRepo problem team) (mconcat ["Error: undefined problem ", problem ^. #repo_name, " in ", team ^. #name]) - logInfo $ "create repo in github: " <> displayShow (problem ^. #repo_name) + logInfo $ "create repo in github: " <> displayShow (owner <> "/" <> repo) resp <- liftIO $ GitHub.createOrganizationRepo' (OAuth token) (mkName Proxy owner) (newRepo $ mkName Proxy repo) case resp of Left err -> logError "Error: create github repo" >> fail (show err) - Right _ -> pure (team ^. #name <> "/" <> repo) + Right _ -> pure (owner <> "/" <> repo) pushForCI :: Team -> Problem -> Plant () pushForCI team problem = do From 26ecc35aab98df6fa78a099f5f0db8df0fe0e84c Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Mon, 25 Feb 2019 21:00:37 +0900 Subject: [PATCH 10/71] Doc: update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 323d7a0..95548d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ # Changelog for git-plantation ## Unreleased changes + +* org アカウント以外で config 設定 (#10) +* dotenv ファイルが使えるようになる (#10) +* `/score` エンドポイントで drone から取得できなくても空リストを返す (#10) From f1b778e8c3f89c0018f5339f427717a6e7f48630 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Tue, 26 Feb 2019 14:11:25 +0900 Subject: [PATCH 11/71] Feat: use exception --- src/Git/Plantation/Cmd/Options.hs | 3 ++- src/Git/Plantation/Cmd/Repo.hs | 10 +++++----- src/Git/Plantation/Env.hs | 32 ++++++++++++++++++++++++++++--- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/Git/Plantation/Cmd/Options.hs b/src/Git/Plantation/Cmd/Options.hs index cc4816c..370d2da 100644 --- a/src/Git/Plantation/Cmd/Options.hs +++ b/src/Git/Plantation/Cmd/Options.hs @@ -11,6 +11,7 @@ import qualified RIO.List as L import Data.Extensible import Git.Plantation.Cmd.Repo import Git.Plantation.Cmd.Run +import Git.Plantation.Env type Options = Record '[ "verbose" >: Bool @@ -32,4 +33,4 @@ instance Run ("new_repo" >: NewRepoCmd) where case (team, args ^. #repo) of (Nothing, _) -> logError $ "team is not found: " <> display (args ^. #team) (Just team', Just name) -> createRepoByRepoName team' name - (Just team', _) -> forM_ (conf ^. #problems) $ createRepo team' + (Just team', _) -> forM_ (conf ^. #problems) (tryAnyWithLogError . createRepo team') diff --git a/src/Git/Plantation/Cmd/Repo.hs b/src/Git/Plantation/Cmd/Repo.hs index ecd4da9..d77bf7f 100644 --- a/src/Git/Plantation/Cmd/Repo.hs +++ b/src/Git/Plantation/Cmd/Repo.hs @@ -14,7 +14,7 @@ import Data.Extensible import qualified Git.Cmd as Git import Git.Plantation.Data (Problem, Team) import qualified Git.Plantation.Data.Team as Team -import Git.Plantation.Env (Plant, maybeWithLogError, shelly') +import Git.Plantation.Env import GitHub.Data.Name (mkName) import GitHub.Data.Repos (newRepo) import GitHub.Endpoints.Repos (Auth (..)) @@ -67,21 +67,21 @@ createRepoByRepoName team repoName = do let problem = L.find (\p -> p ^. #repo_name == repoName) $ conf ^. #problems case problem of Nothing -> logError $ "repo is not found: " <> display repoName - Just problem' -> createRepo team problem' + Just problem' -> tryAnyWithLogError $ createRepo team problem' createRepoInGitHub :: Team -> Problem -> Plant Text createRepoInGitHub team problem = do token <- asks (view #token) - (owner, repo) <- maybeWithLogError + (owner, repo) <- fromJustWithThrow ((splitRepoName . view #github) <$> Team.lookupRepo problem team) - (mconcat ["Error: undefined problem ", problem ^. #repo_name, " in ", team ^. #name]) + (UndefinedTeamProblem team problem) logInfo $ "create repo in github: " <> displayShow (owner <> "/" <> repo) resp <- liftIO $ GitHub.createOrganizationRepo' (OAuth token) (mkName Proxy owner) (newRepo $ mkName Proxy repo) case resp of - Left err -> logError "Error: create github repo" >> fail (show err) + Left err -> throwIO $ CreateRepoError err team problem Right _ -> pure (owner <> "/" <> repo) pushForCI :: Team -> Problem -> Plant () diff --git a/src/Git/Plantation/Env.hs b/src/Git/Plantation/Env.hs index 2b091a0..b2132c1 100644 --- a/src/Git/Plantation/Env.hs +++ b/src/Git/Plantation/Env.hs @@ -1,4 +1,6 @@ {-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE TypeOperators #-} {-# OPTIONS_GHC -fno-warn-orphans #-} @@ -10,7 +12,9 @@ import RIO import Data.Extensible import qualified Drone.Client as Drone import Git.Plantation.Config +import Git.Plantation.Data (Problem, Team) import qualified GitHub.Auth as GitHub +import qualified GitHub.Data as GitHub import Shelly hiding (FilePath) type Plant = RIO Env @@ -26,9 +30,14 @@ type Env = Record instance HasLogFunc Env where logFuncL = lens (view #logger) (\x y -> x & #logger `set` y) -maybeWithLogError :: Maybe a -> Text -> Plant a -maybeWithLogError (Just x) _ = pure x -maybeWithLogError Nothing e = logError (display e) >> fail (show e) +fromJustWithThrow :: Exception e => Maybe a -> e -> Plant a +fromJustWithThrow (Just x) _ = pure x +fromJustWithThrow Nothing e = throwIO e + +tryAnyWithLogError :: Plant () -> Plant () +tryAnyWithLogError act = tryAny act >>= \case + Left e -> logError $ display e + Right _ -> pure () shelly' :: Sh a -> Plant a shelly' sh = do @@ -37,3 +46,20 @@ shelly' sh = do $ (log_stdout_with (runRIO env . logDebug . display)) $ (log_stderr_with (runRIO env . logDebug . display)) $ sh + +mkLogMessage :: Text -> Record xs -> Record ("message" >: Text ': xs) +mkLogMessage message r = #message @= message <: r + +data GitPlantException + = UndefinedTeamProblem Team Problem + | CreateRepoError GitHub.Error Team Problem + deriving (Typeable) + +instance Exception GitPlantException + +instance Show GitPlantException where + show = \case + UndefinedTeamProblem team problem -> + show $ mkLogMessage "undefined team repo" (#team @= team <: #problem @= problem <: nil) + CreateRepoError err team problem -> + show $ mkLogMessage "can't create repository" (#team @= team <: #problem @= problem <: #github_error @= err <: nil) From 066e2528f8fbc4ac68ccabd0371641e04b5a2a10 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Tue, 26 Feb 2019 23:13:53 +0900 Subject: [PATCH 12/71] Modify: use json message --- config/.git-plantation.yaml | 8 ++++---- src/Git/Plantation/Cmd/Repo.hs | 2 +- src/Git/Plantation/Env.hs | 23 ++++++++++++++++++----- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/config/.git-plantation.yaml b/config/.git-plantation.yaml index d1e8547..159f993 100644 --- a/config/.git-plantation.yaml +++ b/config/.git-plantation.yaml @@ -35,7 +35,7 @@ teams: repos: - problem: matsubara0507/git-challenge-tutorial github: sample-hige/git-challenge-tutorial - - problem: matsubara0507/git-challenge-is-order-an-adding - github: sample-hige/git-challenge-is-order-an-adding - - problem: matsubara0507/git-challenge-minesweeper - github: sample-hige/git-challenge-minesweeper + # - problem: matsubara0507/git-challenge-is-order-an-adding + # github: sample-hige/git-challenge-is-order-an-adding + # - problem: matsubara0507/git-challenge-minesweeper + # github: sample-hige/git-challenge-minesweeper diff --git a/src/Git/Plantation/Cmd/Repo.hs b/src/Git/Plantation/Cmd/Repo.hs index d77bf7f..c989f4a 100644 --- a/src/Git/Plantation/Cmd/Repo.hs +++ b/src/Git/Plantation/Cmd/Repo.hs @@ -81,7 +81,7 @@ createRepoInGitHub team problem = do (mkName Proxy owner) (newRepo $ mkName Proxy repo) case resp of - Left err -> throwIO $ CreateRepoError err team problem + Left err -> logDebug (displayShow err) >> throwIO (CreateRepoError err team problem) Right _ -> pure (owner <> "/" <> repo) pushForCI :: Team -> Problem -> Plant () diff --git a/src/Git/Plantation/Env.hs b/src/Git/Plantation/Env.hs index b2132c1..a4b8b72 100644 --- a/src/Git/Plantation/Env.hs +++ b/src/Git/Plantation/Env.hs @@ -9,12 +9,15 @@ module Git.Plantation.Env where import RIO +import Data.Aeson (ToJSON) +import qualified Data.Aeson.Text as Json import Data.Extensible import qualified Drone.Client as Drone import Git.Plantation.Config import Git.Plantation.Data (Problem, Team) import qualified GitHub.Auth as GitHub import qualified GitHub.Data as GitHub +import qualified RIO.Text.Lazy as TL import Shelly hiding (FilePath) type Plant = RIO Env @@ -47,8 +50,14 @@ shelly' sh = do $ (log_stderr_with (runRIO env . logDebug . display)) $ sh -mkLogMessage :: Text -> Record xs -> Record ("message" >: Text ': xs) -mkLogMessage message r = #message @= message <: r +mkLogMessage :: Text -> Record xs -> Record ("error_message" >: Text ': xs) +mkLogMessage message r = #error_message @= message <: r + +mkLogMessage' :: + Forall (KeyValue KnownSymbol (Instance1 ToJSON Identity)) xs + => Text -> Record xs -> String +mkLogMessage' message = + TL.unpack . Json.encodeToLazyText . mkLogMessage message data GitPlantException = UndefinedTeamProblem Team Problem @@ -60,6 +69,10 @@ instance Exception GitPlantException instance Show GitPlantException where show = \case UndefinedTeamProblem team problem -> - show $ mkLogMessage "undefined team repo" (#team @= team <: #problem @= problem <: nil) - CreateRepoError err team problem -> - show $ mkLogMessage "can't create repository" (#team @= team <: #problem @= problem <: #github_error @= err <: nil) + mkLogMessage' + "undefined team repo" + (#team @= team <: #problem @= problem <: nil) + CreateRepoError _err team problem -> + mkLogMessage' + "can't create repository" + (#team @= team <: #problem @= problem <: nil) From a14254b75c3754cae8963463bb05b5d5255fee98 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Thu, 28 Feb 2019 22:55:16 +0900 Subject: [PATCH 13/71] Modify: config example for test --- config/.git-plantation.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/.git-plantation.yaml b/config/.git-plantation.yaml index 159f993..d1e8547 100644 --- a/config/.git-plantation.yaml +++ b/config/.git-plantation.yaml @@ -35,7 +35,7 @@ teams: repos: - problem: matsubara0507/git-challenge-tutorial github: sample-hige/git-challenge-tutorial - # - problem: matsubara0507/git-challenge-is-order-an-adding - # github: sample-hige/git-challenge-is-order-an-adding - # - problem: matsubara0507/git-challenge-minesweeper - # github: sample-hige/git-challenge-minesweeper + - problem: matsubara0507/git-challenge-is-order-an-adding + github: sample-hige/git-challenge-is-order-an-adding + - problem: matsubara0507/git-challenge-minesweeper + github: sample-hige/git-challenge-minesweeper From 781fa10f2651e1eca051a426d9782ac055c24a92 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Thu, 28 Feb 2019 22:56:34 +0900 Subject: [PATCH 14/71] Doc: update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95548d9..3b6c680 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,3 +5,5 @@ * org アカウント以外で config 設定 (#10) * dotenv ファイルが使えるようになる (#10) * `/score` エンドポイントで drone から取得できなくても空リストを返す (#10) +* `fail` の代わりに例外処理を追加 (#11) +* JSON 形式のログを追加(#11) From b78a88505c4b0f142bebb867281f5172083b15c1 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Thu, 28 Feb 2019 23:00:59 +0900 Subject: [PATCH 15/71] CI: add travis config --- .travis.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..bd09d30 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,20 @@ +sudo: required +services: + - docker +language: generic +cache: + timeout: 360 + directories: + - "$HOME/.stack/" + - "$HOME/.local/bin/" + - ".stack-work/" +install: +- mkdir -p ~/.local/bin +- export PATH=$HOME/.local/bin:$PATH +- travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack' +jobs: + include: + - stage: build dependencies + script: stack --no-terminal --install-ghc test --bench --only-dependencies + - stage: run test + script: stack --no-terminal test --bench --no-run-benchmarks --no-haddock-deps --pedantic From 873348b2139ae988f5670599f0e286e1091eb698 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Thu, 28 Feb 2019 23:22:07 +0900 Subject: [PATCH 16/71] CI: elm language --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bd09d30..e9395c1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ sudo: required services: - docker -language: generic +language: elm cache: timeout: 360 directories: From fe415102983797d7198259ba913bb67e7b6447d6 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Fri, 1 Mar 2019 00:15:33 +0900 Subject: [PATCH 17/71] Modify: warning --- src/Git/Plantation/Cmd.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Git/Plantation/Cmd.hs b/src/Git/Plantation/Cmd.hs index 6938d7f..e4e9c17 100644 --- a/src/Git/Plantation/Cmd.hs +++ b/src/Git/Plantation/Cmd.hs @@ -18,7 +18,7 @@ import Git.Plantation.Cmd.Run as X import Git.Plantation.Config (readConfig) import System.Environment (getEnv) -run :: (MonadUnliftIO m, MonadThrow m) => Options -> m () +run :: MonadUnliftIO m => Options -> m () run opts = do config <- readConfig (opts ^. #config) logOpts <- logOptionsHandle stdout (opts ^. #verbose) From 74a14c82c902b9371062eb5d8a50b9fd65abda9d Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Thu, 28 Feb 2019 23:38:30 +0900 Subject: [PATCH 18/71] Refactor: generic repo cmd --- src/Git/Plantation/Cmd/Options.hs | 18 +++++++++++------- src/Git/Plantation/Cmd/Repo.hs | 20 +++++++++++--------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/Git/Plantation/Cmd/Options.hs b/src/Git/Plantation/Cmd/Options.hs index 370d2da..ab87057 100644 --- a/src/Git/Plantation/Cmd/Options.hs +++ b/src/Git/Plantation/Cmd/Options.hs @@ -11,6 +11,7 @@ import qualified RIO.List as L import Data.Extensible import Git.Plantation.Cmd.Repo import Git.Plantation.Cmd.Run +import Git.Plantation.Data (Problem, Team) import Git.Plantation.Env type Options = Record @@ -27,10 +28,13 @@ type SubCmdFields = ] instance Run ("new_repo" >: NewRepoCmd) where - run' _ args = do - conf <- asks (view #config) - let team = L.find (\t -> t ^. #name == args ^. #team) $ conf ^. #teams - case (team, args ^. #repo) of - (Nothing, _) -> logError $ "team is not found: " <> display (args ^. #team) - (Just team', Just name) -> createRepoByRepoName team' name - (Just team', _) -> forM_ (conf ^. #problems) (tryAnyWithLogError . createRepo team') + run' _ = runRepoCmd createRepo + +runRepoCmd :: (Team -> Problem -> Plant ()) -> Record RepoFields -> Plant () +runRepoCmd act args = do + conf <- asks (view #config) + let team = L.find (\t -> t ^. #name == args ^. #team) $ conf ^. #teams + case (team, args ^. #repo) of + (Nothing, _) -> logError $ "team is not found: " <> display (args ^. #team) + (Just team', Just name) -> actByRepoName act team' name + (Just team', _) -> forM_ (conf ^. #problems) (tryAnyWithLogError . act team') diff --git a/src/Git/Plantation/Cmd/Repo.hs b/src/Git/Plantation/Cmd/Repo.hs index c989f4a..9c5dba0 100644 --- a/src/Git/Plantation/Cmd/Repo.hs +++ b/src/Git/Plantation/Cmd/Repo.hs @@ -21,11 +21,21 @@ import GitHub.Endpoints.Repos (Auth (..)) import qualified GitHub.Endpoints.Repos as GitHub import Shelly hiding (FilePath) -type NewRepoCmd = Record +type RepoFields = '[ "repo" >: Maybe Text , "team" >: Text ] +type NewRepoCmd = Record RepoFields + +actByRepoName :: (Team -> Problem -> Plant ()) -> Team -> Text -> Plant () +actByRepoName act team repoName = do + conf <- asks (view #config) + let problem = L.find (\p -> p ^. #repo_name == repoName) $ conf ^. #problems + case problem of + Nothing -> logError $ "repo is not found: " <> display repoName + Just problem' -> tryAnyWithLogError $ act team problem' + createRepo :: Team -> Problem -> Plant () createRepo team problem = do logInfo $ mconcat @@ -61,14 +71,6 @@ createRepo team problem = do Git.push ["-u", "origin", team ^. #name] logInfo $ "Success: create ci branch in " <> displayShow problemUrl -createRepoByRepoName :: Team -> Text -> Plant () -createRepoByRepoName team repoName = do - conf <- asks (view #config) - let problem = L.find (\p -> p ^. #repo_name == repoName) $ conf ^. #problems - case problem of - Nothing -> logError $ "repo is not found: " <> display repoName - Just problem' -> tryAnyWithLogError $ createRepo team problem' - createRepoInGitHub :: Team -> Problem -> Plant Text createRepoInGitHub team problem = do token <- asks (view #token) From 3f8519994d48758d24632fb5d06ba9e880e50d41 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Sat, 2 Mar 2019 11:22:26 +0900 Subject: [PATCH 19/71] Feat: add step cmd to create team repository --- src/Git/Plantation/Cmd/Options.hs | 40 +++++++++++++---- src/Git/Plantation/Cmd/Repo.hs | 73 +++++++++++++++++++------------ src/Git/Plantation/Env.hs | 2 +- tool/Main.hs | 16 +++++-- 4 files changed, 91 insertions(+), 40 deletions(-) diff --git a/src/Git/Plantation/Cmd/Options.hs b/src/Git/Plantation/Cmd/Options.hs index ab87057..ef7d25e 100644 --- a/src/Git/Plantation/Cmd/Options.hs +++ b/src/Git/Plantation/Cmd/Options.hs @@ -6,12 +6,13 @@ module Git.Plantation.Cmd.Options where import RIO -import qualified RIO.List as L +import qualified RIO.List as L import Data.Extensible import Git.Plantation.Cmd.Repo import Git.Plantation.Cmd.Run -import Git.Plantation.Data (Problem, Team) +import Git.Plantation.Data (Problem, Team) +import qualified Git.Plantation.Data.Team as Team import Git.Plantation.Env type Options = Record @@ -24,17 +25,40 @@ type Options = Record type SubCmd = Variant SubCmdFields type SubCmdFields = - '[ "new_repo" >: NewRepoCmd + '[ "new_repo" >: NewRepoCmd + , "new_github_repo" >: NewGitHubRepoCmd + , "init_github_repo" >: InitGitHubRepoCmd + , "init_ci" >: InitCICmd ] instance Run ("new_repo" >: NewRepoCmd) where - run' _ = runRepoCmd createRepo + run' _ args = do + conf <- asks (view #config) + let team = L.find (\t -> t ^. #name == args ^. #team) $ conf ^. #teams + case (team, args ^. #repo) of + (Nothing, _) -> logError $ "team is not found: " <> display (args ^. #team) + (Just team', Just name) -> actByRepoName createRepo team' name + (Just team', _) -> forM_ (conf ^. #problems) (tryAnyWithLogError . createRepo team') -runRepoCmd :: (Team -> Problem -> Plant ()) -> Record RepoFields -> Plant () +instance Run ("new_github_repo" >: NewGitHubRepoCmd) where + run' _ = runRepoCmd $ \team problem -> do + info <- Team.lookupRepo problem team `fromJustWithThrow` UndefinedTeamProblem team problem + createRepoInGitHub info team problem + +instance Run ("init_github_repo" >: InitGitHubRepoCmd) where + run' _ = runRepoCmd $ \team problem -> do + info <- Team.lookupRepo problem team `fromJustWithThrow` UndefinedTeamProblem team problem + initRepoInGitHub info team problem + +instance Run ("init_ci" >: InitCICmd) where + run' _ = runRepoCmd $ \team problem -> do + info <- Team.lookupRepo problem team `fromJustWithThrow` UndefinedTeamProblem team problem + initProblemCI info team problem + +runRepoCmd :: (Team -> Problem -> Plant ()) -> Record RepoCmdFields -> Plant () runRepoCmd act args = do conf <- asks (view #config) let team = L.find (\t -> t ^. #name == args ^. #team) $ conf ^. #teams case (team, args ^. #repo) of - (Nothing, _) -> logError $ "team is not found: " <> display (args ^. #team) - (Just team', Just name) -> actByRepoName act team' name - (Just team', _) -> forM_ (conf ^. #problems) (tryAnyWithLogError . act team') + (Nothing, _) -> logError $ "team is not found: " <> display (args ^. #team) + (Just team', name) -> actByRepoName act team' name diff --git a/src/Git/Plantation/Cmd/Repo.hs b/src/Git/Plantation/Cmd/Repo.hs index 9c5dba0..56cde62 100644 --- a/src/Git/Plantation/Cmd/Repo.hs +++ b/src/Git/Plantation/Cmd/Repo.hs @@ -12,7 +12,7 @@ import qualified RIO.Text as Text import Data.Extensible import qualified Git.Cmd as Git -import Git.Plantation.Data (Problem, Team) +import Git.Plantation.Data (Problem, Repo, Team) import qualified Git.Plantation.Data.Team as Team import Git.Plantation.Env import GitHub.Data.Name (mkName) @@ -21,14 +21,23 @@ import GitHub.Endpoints.Repos (Auth (..)) import qualified GitHub.Endpoints.Repos as GitHub import Shelly hiding (FilePath) -type RepoFields = - '[ "repo" >: Maybe Text - , "team" >: Text +type NewRepoCmd = Record + '[ "repo" >: Maybe Text + , "team" >: Text ] -type NewRepoCmd = Record RepoFields +type RepoCmdFields = + '[ "repo" >: Text + , "team" >: Text + ] + +type NewGitHubRepoCmd = Record RepoCmdFields + +type InitGitHubRepoCmd = Record RepoCmdFields + +type InitCICmd = Record RepoCmdFields -actByRepoName :: (Team -> Problem -> Plant ()) -> Team -> Text -> Plant () +actByRepoName :: (Team -> Problem -> Plant a) -> Team -> Text -> Plant () actByRepoName act team repoName = do conf <- asks (view #config) let problem = L.find (\p -> p ^. #repo_name == repoName) $ conf ^. #problems @@ -42,12 +51,30 @@ createRepo team problem = do [ "create repo: ", displayShow $ problem ^. #repo_name , " to team: ", displayShow $ team ^. #name ] + info <- Team.lookupRepo problem team `fromJustWithThrow` UndefinedTeamProblem team problem + createRepoInGitHub info team problem + initRepoInGitHub info team problem + initProblemCI info team problem - teamRepo <- createRepoInGitHub team problem - token <- getTextToken - workDir <- asks (view #work) +createRepoInGitHub :: Repo -> Team -> Problem -> Plant () +createRepoInGitHub info team problem = do + let (owner, repo) = splitRepoName $ info ^. #github + token <- asks (view #token) + logInfo $ "create repo in github: " <> displayShow (owner <> "/" <> repo) + resp <- liftIO $ GitHub.createOrganizationRepo' + (OAuth token) + (mkName Proxy owner) + (newRepo $ mkName Proxy repo) + case resp of + Left err -> logDebug (displayShow err) >> throwIO (CreateRepoError err team problem) + Right _ -> logInfo "Success: create repository in GitHub" + +initRepoInGitHub :: Repo -> Team -> Problem -> Plant () +initRepoInGitHub info team problem = do + token <- getTextToken + workDir <- asks (view #work) let (owner, repo) = splitRepoName $ problem ^. #repo_name - teamUrl = mconcat ["https://", token, "@github.com/", teamRepo, ".git"] + teamUrl = mconcat ["https://", token, "@github.com/", info ^. #github, ".git"] problemUrl = mconcat ["https://", token, "@github.com/", owner, "/", repo, ".git"] shelly' $ chdir_p (workDir (team ^. #name)) (Git.cloneOrFetch teamUrl repo) @@ -57,7 +84,14 @@ createRepo team problem = do forM_ (problem ^. #challenge_branches) $ \branch -> Git.checkout ["-b", branch, "problem/" <> branch] Git.push $ "-u" : "origin" : problem ^. #challenge_branches - logInfo $ "Success: create repo as " <> displayShow teamRepo + logInfo $ "Success: create repo as " <> displayShow (info ^. #github) + +initProblemCI :: Repo -> Team -> Problem -> Plant () +initProblemCI info team problem = do + token <- getTextToken + workDir <- asks (view #work) + let (owner, repo) = splitRepoName $ problem ^. #repo_name + problemUrl = mconcat ["https://", token, "@github.com/", owner, "/", repo, ".git"] shelly' $ chdir_p (workDir owner) (Git.cloneOrFetch problemUrl repo) shelly' $ chdir_p (workDir owner repo) $ do @@ -65,27 +99,12 @@ createRepo team problem = do Git.existBranch (team ^. #name) >>= \case False -> Git.checkout ["-b", team ^. #name] True -> Git.checkout [team ^. #name] - writefile ciFileName teamRepo + writefile ciFileName $ info ^. #github Git.add [ciFileName] Git.commit ["-m", "[CI SKIP] Add ci branch"] Git.push ["-u", "origin", team ^. #name] logInfo $ "Success: create ci branch in " <> displayShow problemUrl -createRepoInGitHub :: Team -> Problem -> Plant Text -createRepoInGitHub team problem = do - token <- asks (view #token) - (owner, repo) <- fromJustWithThrow - ((splitRepoName . view #github) <$> Team.lookupRepo problem team) - (UndefinedTeamProblem team problem) - logInfo $ "create repo in github: " <> displayShow (owner <> "/" <> repo) - resp <- liftIO $ GitHub.createOrganizationRepo' - (OAuth token) - (mkName Proxy owner) - (newRepo $ mkName Proxy repo) - case resp of - Left err -> logDebug (displayShow err) >> throwIO (CreateRepoError err team problem) - Right _ -> pure (owner <> "/" <> repo) - pushForCI :: Team -> Problem -> Plant () pushForCI team problem = do token <- getTextToken diff --git a/src/Git/Plantation/Env.hs b/src/Git/Plantation/Env.hs index a4b8b72..29dd8ff 100644 --- a/src/Git/Plantation/Env.hs +++ b/src/Git/Plantation/Env.hs @@ -37,7 +37,7 @@ fromJustWithThrow :: Exception e => Maybe a -> e -> Plant a fromJustWithThrow (Just x) _ = pure x fromJustWithThrow Nothing e = throwIO e -tryAnyWithLogError :: Plant () -> Plant () +tryAnyWithLogError :: Plant a -> Plant () tryAnyWithLogError act = tryAny act >>= \case Left e -> logError $ display e Right _ -> pure () diff --git a/tool/Main.hs b/tool/Main.hs index 5a29c85..e0f1583 100644 --- a/tool/Main.hs +++ b/tool/Main.hs @@ -41,14 +41,22 @@ options = hsequence subcmdParser :: Parser SubCmd subcmdParser = variantFrom - $ #new_repo @= newRepoCmdParser `withInfo` "Create repository to team." + $ #new_repo @= newRepoCmdParser `withInfo` "Create repository for team." + <: #new_github_repo @= singleRepoCmdParser `withInfo` "Create new repository for team in GitHub" + <: #init_github_repo @= singleRepoCmdParser `withInfo` "Init repository for team in GitHub" + <: #init_ci @= singleRepoCmdParser `withInfo` "Init CI repository by team repository" <: nil - newRepoCmdParser :: Parser NewRepoCmd newRepoCmdParser = hsequence - $ #repo <@=> option (Just <$> str) (long "repo" <> value Nothing <> metavar "TEXT" <> help "Sets reopsitory that wont to controll.") - <: #team <@=> strArgument (metavar "TEXT" <> help "Sets team that wont to controll.") + $ #repo <@=> option (Just <$> str) (long "repo" <> value Nothing <> metavar "TEXT" <> help "Sets reopsitory that wont to controll.") + <: #team <@=> strArgument (metavar "TEXT" <> help "Sets team that wont to controll.") + <: nil + +singleRepoCmdParser :: Parser (Record RepoCmdFields) +singleRepoCmdParser = hsequence + $ #repo <@=> strOption (long "repo" <> metavar "TEXT" <> help "Sets reopsitory that wont to controll.") + <: #team <@=> strArgument (metavar "TEXT" <> help "Sets team that wont to controll.") <: nil variantFrom :: From 68987e765c0239d1684e201138b41b273a84d97e Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Sat, 2 Mar 2019 16:04:19 +0900 Subject: [PATCH 20/71] Feat: invite member command --- src/Git/Plantation/Cmd.hs | 1 + src/Git/Plantation/Cmd/Member.hs | 50 +++++++++++++++++++++++++++++++ src/Git/Plantation/Cmd/Options.hs | 16 ++++++++-- src/Git/Plantation/Data/Team.hs | 4 +++ src/Git/Plantation/Env.hs | 7 ++++- stack.yaml | 2 +- tool/Main.hs | 16 +++++++--- 7 files changed, 87 insertions(+), 9 deletions(-) create mode 100644 src/Git/Plantation/Cmd/Member.hs diff --git a/src/Git/Plantation/Cmd.hs b/src/Git/Plantation/Cmd.hs index e4e9c17..80947d2 100644 --- a/src/Git/Plantation/Cmd.hs +++ b/src/Git/Plantation/Cmd.hs @@ -12,6 +12,7 @@ import RIO import Data.Extensible import qualified Drone.Client as Drone +import Git.Plantation.Cmd.Member as X import Git.Plantation.Cmd.Options as X import Git.Plantation.Cmd.Repo as X import Git.Plantation.Cmd.Run as X diff --git a/src/Git/Plantation/Cmd/Member.hs b/src/Git/Plantation/Cmd/Member.hs new file mode 100644 index 0000000..b14a07c --- /dev/null +++ b/src/Git/Plantation/Cmd/Member.hs @@ -0,0 +1,50 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedLabels #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeOperators #-} + +module Git.Plantation.Cmd.Member where + +import RIO + +import Data.Extensible +import Git.Plantation.Cmd.Repo (splitRepoName) +import Git.Plantation.Data +import Git.Plantation.Env +import GitHub.Data.Name (mkName) +import GitHub.Endpoints.Repos (Auth (..)) +import qualified GitHub.Endpoints.Repos.Collaborators as GitHub + +type InviteMemberCmd = Record + '[ "team" >: Text + , "repo" >: Maybe Text + , "user" >: Maybe Text + ] + +inviteMember :: InviteMemberCmd -> Team -> Plant () +inviteMember args team = + forM_ repos $ \repo -> forM_ member $ \user -> + tryAnyWithLogError $ inviteUserToRepo user repo + where + repos = maybe (team ^. #repos) (: []) $ flip lookupRepo' team =<< args ^. #repo + member = maybe (team ^. #member) (: []) $ flip lookupUser team =<< args ^. #user + +inviteUserToRepo :: User -> Repo -> Plant () +inviteUserToRepo user target = do + token <- asks (view #token) + resp <- liftIO $ GitHub.addCollaborator + (OAuth token) + (mkName Proxy owner) + (mkName Proxy repo) + (mkName Proxy $ user ^. #github) + case resp of + Left err -> logDebug (displayShow err) >> throwIO (InviteUserError err user target) + Right _ -> logInfo $ display success + where + (owner, repo) = splitRepoName $ target ^. #github + success = mconcat + [ "Success: invite " + , user ^. #name, "(", user ^. #github, ")" + , " to ", target ^. #github, "." + ] diff --git a/src/Git/Plantation/Cmd/Options.hs b/src/Git/Plantation/Cmd/Options.hs index ef7d25e..5375cc8 100644 --- a/src/Git/Plantation/Cmd/Options.hs +++ b/src/Git/Plantation/Cmd/Options.hs @@ -6,13 +6,14 @@ module Git.Plantation.Cmd.Options where import RIO -import qualified RIO.List as L +import qualified RIO.List as L import Data.Extensible +import Git.Plantation.Cmd.Member import Git.Plantation.Cmd.Repo import Git.Plantation.Cmd.Run -import Git.Plantation.Data (Problem, Team) -import qualified Git.Plantation.Data.Team as Team +import Git.Plantation.Data (Problem, Team) +import qualified Git.Plantation.Data.Team as Team import Git.Plantation.Env type Options = Record @@ -29,6 +30,7 @@ type SubCmdFields = , "new_github_repo" >: NewGitHubRepoCmd , "init_github_repo" >: InitGitHubRepoCmd , "init_ci" >: InitCICmd + , "invite_member" >: InviteMemberCmd ] instance Run ("new_repo" >: NewRepoCmd) where @@ -62,3 +64,11 @@ runRepoCmd act args = do case (team, args ^. #repo) of (Nothing, _) -> logError $ "team is not found: " <> display (args ^. #team) (Just team', name) -> actByRepoName act team' name + +instance Run ("invite_member" >: InviteMemberCmd) where + run' _ args = do + conf <- asks (view #config) + let team = L.find (\t -> t ^. #name == args ^. #team) $ conf ^. #teams + case team of + Nothing -> logError $ "team is not found: " <> display (args ^. #team) + Just team' -> inviteMember args team' diff --git a/src/Git/Plantation/Data/Team.hs b/src/Git/Plantation/Data/Team.hs index b5f9b70..a5a43e4 100644 --- a/src/Git/Plantation/Data/Team.hs +++ b/src/Git/Plantation/Data/Team.hs @@ -44,3 +44,7 @@ lookupRepo problem = lookupRepo' (problem ^. #repo_name) lookupRepo' :: Text -> Team -> Maybe Repo lookupRepo' repoName team = L.find (\repo -> repoName == repo ^. #problem) (team ^. #repos) + +lookupUser :: Text -> Team -> Maybe User +lookupUser github team = + L.find (\user -> github == user ^. #github) (team ^. #member) diff --git a/src/Git/Plantation/Env.hs b/src/Git/Plantation/Env.hs index 29dd8ff..97776bd 100644 --- a/src/Git/Plantation/Env.hs +++ b/src/Git/Plantation/Env.hs @@ -14,7 +14,7 @@ import qualified Data.Aeson.Text as Json import Data.Extensible import qualified Drone.Client as Drone import Git.Plantation.Config -import Git.Plantation.Data (Problem, Team) +import Git.Plantation.Data (Problem, Repo, Team, User) import qualified GitHub.Auth as GitHub import qualified GitHub.Data as GitHub import qualified RIO.Text.Lazy as TL @@ -62,6 +62,7 @@ mkLogMessage' message = data GitPlantException = UndefinedTeamProblem Team Problem | CreateRepoError GitHub.Error Team Problem + | InviteUserError GitHub.Error User Repo deriving (Typeable) instance Exception GitPlantException @@ -76,3 +77,7 @@ instance Show GitPlantException where mkLogMessage' "can't create repository" (#team @= team <: #problem @= problem <: nil) + InviteUserError _err user repo -> + mkLogMessage' + "can't invite user to repository" + (#user @= user <: #repo @= repo <: nil) diff --git a/stack.yaml b/stack.yaml index 60dc54d..47bff1c 100644 --- a/stack.yaml +++ b/stack.yaml @@ -3,7 +3,7 @@ packages: - . extra-deps: - extensible-0.5 -- github-0.20 +- github-0.21 - servant-github-webhook-0.4.1.0 - github: matsubara0507/drone-haskell commit: aa6f5152dd9ea72cb48a32bdc58c91de2bdc21a9 diff --git a/tool/Main.hs b/tool/Main.hs index e0f1583..169978a 100644 --- a/tool/Main.hs +++ b/tool/Main.hs @@ -41,10 +41,11 @@ options = hsequence subcmdParser :: Parser SubCmd subcmdParser = variantFrom - $ #new_repo @= newRepoCmdParser `withInfo` "Create repository for team." - <: #new_github_repo @= singleRepoCmdParser `withInfo` "Create new repository for team in GitHub" - <: #init_github_repo @= singleRepoCmdParser `withInfo` "Init repository for team in GitHub" - <: #init_ci @= singleRepoCmdParser `withInfo` "Init CI repository by team repository" + $ #new_repo @= newRepoCmdParser `withInfo` "Create repository for team." + <: #new_github_repo @= singleRepoCmdParser `withInfo` "Create new repository for team in GitHub" + <: #init_github_repo @= singleRepoCmdParser `withInfo` "Init repository for team in GitHub" + <: #init_ci @= singleRepoCmdParser `withInfo` "Init CI repository by team repository" + <: #invite_member @= inviteMemberCmdParser `withInfo` "Invite Member to Team Repository" <: nil newRepoCmdParser :: Parser NewRepoCmd @@ -59,6 +60,13 @@ singleRepoCmdParser = hsequence <: #team <@=> strArgument (metavar "TEXT" <> help "Sets team that wont to controll.") <: nil +inviteMemberCmdParser :: Parser InviteMemberCmd +inviteMemberCmdParser = hsequence + $ #team <@=> strArgument (metavar "TEXT" <> help "Sets team that wont to controll.") + <: #repo <@=> option (Just <$> str) (long "repo" <> value Nothing <> metavar "TEXT" <> help "Sets reopsitory that wont to controll.") + <: #user <@=> option (Just <$> str) (long "user" <> value Nothing <> metavar "TEXT" <> help "Sets user that wont to controll.") + <: nil + variantFrom :: Forall (KeyIs KnownSymbol) xs => RecordOf ParserInfo xs -> Parser (Variant xs) variantFrom = subparser . subcmdVariant From 2f3cf9911b73a05afa47417f7c8a700fb64ba355 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Sat, 2 Mar 2019 20:58:35 +0900 Subject: [PATCH 21/71] Feat: reset command --- src/Git/Cmd.hs | 3 +++ src/Git/Plantation/Cmd/Options.hs | 6 ++++++ src/Git/Plantation/Cmd/Repo.hs | 16 +++++++++++++++- tool/Main.hs | 1 + 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Git/Cmd.hs b/src/Git/Cmd.hs index 51e5372..e2e1528 100644 --- a/src/Git/Cmd.hs +++ b/src/Git/Cmd.hs @@ -26,6 +26,9 @@ commit = command1_ "git" [] "commit" add :: [Text] -> Sh () add = command1_ "git" [] "add" +branch :: [Text] -> Sh () +branch = command1_ "git" [] "branch" + cloneOrFetch :: Text -> Text -> Sh () cloneOrFetch repoUrl repoName = do dir <- pwd diff --git a/src/Git/Plantation/Cmd/Options.hs b/src/Git/Plantation/Cmd/Options.hs index 5375cc8..d194ee7 100644 --- a/src/Git/Plantation/Cmd/Options.hs +++ b/src/Git/Plantation/Cmd/Options.hs @@ -30,6 +30,7 @@ type SubCmdFields = , "new_github_repo" >: NewGitHubRepoCmd , "init_github_repo" >: InitGitHubRepoCmd , "init_ci" >: InitCICmd + , "reset_repo" >: ResetRepoCmd , "invite_member" >: InviteMemberCmd ] @@ -57,6 +58,11 @@ instance Run ("init_ci" >: InitCICmd) where info <- Team.lookupRepo problem team `fromJustWithThrow` UndefinedTeamProblem team problem initProblemCI info team problem +instance Run ("reset_repo" >: ResetRepoCmd) where + run' _ = runRepoCmd $ \team problem -> do + info <- Team.lookupRepo problem team `fromJustWithThrow` UndefinedTeamProblem team problem + resetRepo info team problem + runRepoCmd :: (Team -> Problem -> Plant ()) -> Record RepoCmdFields -> Plant () runRepoCmd act args = do conf <- asks (view #config) diff --git a/src/Git/Plantation/Cmd/Repo.hs b/src/Git/Plantation/Cmd/Repo.hs index 56cde62..2ce87db 100644 --- a/src/Git/Plantation/Cmd/Repo.hs +++ b/src/Git/Plantation/Cmd/Repo.hs @@ -37,6 +37,8 @@ type InitGitHubRepoCmd = Record RepoCmdFields type InitCICmd = Record RepoCmdFields +type ResetRepoCmd = Record RepoCmdFields + actByRepoName :: (Team -> Problem -> Plant a) -> Team -> Text -> Plant () actByRepoName act team repoName = do conf <- asks (view #config) @@ -79,11 +81,14 @@ initRepoInGitHub info team problem = do shelly' $ chdir_p (workDir (team ^. #name)) (Git.cloneOrFetch teamUrl repo) shelly' $ chdir_p (workDir (team ^. #name) repo) $ do + Git.checkout [ "-b", "temp"] + errExit False $ Git.branch $ "-D" : problem ^. #challenge_branches Git.remote ["add", "problem", problemUrl] Git.fetch ["--all"] forM_ (problem ^. #challenge_branches) $ \branch -> Git.checkout ["-b", branch, "problem/" <> branch] - Git.push $ "-u" : "origin" : problem ^. #challenge_branches + Git.push $ "-f" : "-u" : "origin" : problem ^. #challenge_branches + Git.branch ["-D", "temp"] logInfo $ "Success: create repo as " <> displayShow (info ^. #github) initProblemCI :: Repo -> Team -> Problem -> Plant () @@ -105,6 +110,15 @@ initProblemCI info team problem = do Git.push ["-u", "origin", team ^. #name] logInfo $ "Success: create ci branch in " <> displayShow problemUrl +resetRepo :: Repo -> Team -> Problem -> Plant () +resetRepo info team problem = do + workDir <- asks (view #work) + let (_, repo) = splitRepoName $ problem ^. #repo_name + paths <- shelly' $ chdir_p (workDir (team ^. #name) repo) $ ls "." + logDebug $ "Remove file: " <> display (Text.intercalate " " $ map toTextIgnore paths) + shelly' $ chdir_p (workDir (team ^. #name)) $ rm_rf (fromText repo) + initRepoInGitHub info team problem + pushForCI :: Team -> Problem -> Plant () pushForCI team problem = do token <- getTextToken diff --git a/tool/Main.hs b/tool/Main.hs index 169978a..d587e39 100644 --- a/tool/Main.hs +++ b/tool/Main.hs @@ -45,6 +45,7 @@ subcmdParser = variantFrom <: #new_github_repo @= singleRepoCmdParser `withInfo` "Create new repository for team in GitHub" <: #init_github_repo @= singleRepoCmdParser `withInfo` "Init repository for team in GitHub" <: #init_ci @= singleRepoCmdParser `withInfo` "Init CI repository by team repository" + <: #reset_repo @= singleRepoCmdParser `withInfo` "Reset repository for team" <: #invite_member @= inviteMemberCmdParser `withInfo` "Invite Member to Team Repository" <: nil From ef57f291aa57a98f4c11a66796de4224e773045f Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Sun, 3 Mar 2019 23:22:43 +0900 Subject: [PATCH 22/71] Doc: update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b6c680..bf06ead 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,3 +7,9 @@ * `/score` エンドポイントで drone から取得できなくても空リストを返す (#10) * `fail` の代わりに例外処理を追加 (#11) * JSON 形式のログを追加(#11) +* 回答リポジトリの生成の各ステップコマンドを追加(#12) + * GitHub に空リポジトリを作成(#12) + * リポジトリを初期化(問題リポジトリを参照して)(#12) + * 回答のためCIを問題リポジトリに設定(#12) +* メンバーを回答リポジトリに招待する(#12) +* 回答リポジトリのリセット(#12) From 7c69e5d2cf98e117bf55486619ba8691c85cda59 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Sat, 16 Mar 2019 16:57:26 +0900 Subject: [PATCH 23/71] Feat: simple verify command --- src/Git/Plantation/Cmd/Options.hs | 11 ++++++++++- src/Git/Plantation/Config.hs | 3 +++ tool/Main.hs | 3 ++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Git/Plantation/Cmd/Options.hs b/src/Git/Plantation/Cmd/Options.hs index d194ee7..15ca495 100644 --- a/src/Git/Plantation/Cmd/Options.hs +++ b/src/Git/Plantation/Cmd/Options.hs @@ -12,6 +12,7 @@ import Data.Extensible import Git.Plantation.Cmd.Member import Git.Plantation.Cmd.Repo import Git.Plantation.Cmd.Run +import Git.Plantation.Config as Config import Git.Plantation.Data (Problem, Team) import qualified Git.Plantation.Data.Team as Team import Git.Plantation.Env @@ -26,7 +27,8 @@ type Options = Record type SubCmd = Variant SubCmdFields type SubCmdFields = - '[ "new_repo" >: NewRepoCmd + '[ "verify" >: () + , "new_repo" >: NewRepoCmd , "new_github_repo" >: NewGitHubRepoCmd , "init_github_repo" >: InitGitHubRepoCmd , "init_ci" >: InitCICmd @@ -34,6 +36,13 @@ type SubCmdFields = , "invite_member" >: InviteMemberCmd ] +instance Run ("verify" >: ()) where + run' _ args = do + conf <- asks (view #config) + case Config.verify conf of + Left err -> logError $ "invalid config: " <> display err + Right _ -> logInfo "valid config" + instance Run ("new_repo" >: NewRepoCmd) where run' _ args = do conf <- asks (view #config) diff --git a/src/Git/Plantation/Config.hs b/src/Git/Plantation/Config.hs index d8336d0..20bbda3 100644 --- a/src/Git/Plantation/Config.hs +++ b/src/Git/Plantation/Config.hs @@ -22,3 +22,6 @@ readConfig = Y.decodeFileThrow instance ElmType Config where toElmType = toElmRecordType "Config" + +verify :: Config -> Either Text Config +verify = pure diff --git a/tool/Main.hs b/tool/Main.hs index d587e39..9c6fdbd 100644 --- a/tool/Main.hs +++ b/tool/Main.hs @@ -41,7 +41,8 @@ options = hsequence subcmdParser :: Parser SubCmd subcmdParser = variantFrom - $ #new_repo @= newRepoCmdParser `withInfo` "Create repository for team." + $ #verify @= pure () `withInfo` "Verify config file." + <: #new_repo @= newRepoCmdParser `withInfo` "Create repository for team." <: #new_github_repo @= singleRepoCmdParser `withInfo` "Create new repository for team in GitHub" <: #init_github_repo @= singleRepoCmdParser `withInfo` "Init repository for team in GitHub" <: #init_ci @= singleRepoCmdParser `withInfo` "Init CI repository by team repository" From 05c464fc7af60c328c42365b2b9a0a6cbf8f8f65 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Tue, 19 Mar 2019 17:25:11 +0900 Subject: [PATCH 24/71] Fix: team repo config for user --- config/.git-plantation.yaml | 33 +++++++++++++-------- elm-src/Generated/API.elm | 46 +++++++++++++++++++----------- elm-src/Main.elm | 6 ++-- src/Git/Plantation/API/CRUD.hs | 6 ++-- src/Git/Plantation/API/Webhook.hs | 4 +-- src/Git/Plantation/Cmd/Member.hs | 20 +++++++------ src/Git/Plantation/Cmd/Options.hs | 2 +- src/Git/Plantation/Cmd/Repo.hs | 42 ++++++++++++++++----------- src/Git/Plantation/Data/Problem.hs | 9 +++--- src/Git/Plantation/Data/Team.hs | 32 +++++++++++++++------ src/Git/Plantation/Env.hs | 5 ++++ tool/Main.hs | 2 +- 12 files changed, 131 insertions(+), 76 deletions(-) diff --git a/config/.git-plantation.yaml b/config/.git-plantation.yaml index d1e8547..43edd50 100644 --- a/config/.git-plantation.yaml +++ b/config/.git-plantation.yaml @@ -1,6 +1,7 @@ problems: -- problem_name: tutorial - repo_name: matsubara0507/git-challenge-tutorial +- id: 1 + name: tutorial + repo: matsubara0507/git-challenge-tutorial difficulty: 1 challenge_branches: - readme @@ -9,16 +10,18 @@ problems: - task-2 ci_branch: ci -- problem_name: is-order-an-adding - repo_name: matsubara0507/git-challenge-is-order-an-adding +- id: 2 + name: is-order-an-adding + repo: matsubara0507/git-challenge-is-order-an-adding difficulty: 1 challenge_branches: - readme - master ci_branch: ci -- problem_name: minesweeper - repo_name: matsubara0507/git-challenge-minesweeper +- id: 3 + name: minesweeper + repo: matsubara0507/git-challenge-minesweeper difficulty: 1 challenge_branches: - readme @@ -33,9 +36,15 @@ teams: - name: MATSUBARA Nobutada github: matsubara0507 repos: - - problem: matsubara0507/git-challenge-tutorial - github: sample-hige/git-challenge-tutorial - - problem: matsubara0507/git-challenge-is-order-an-adding - github: sample-hige/git-challenge-is-order-an-adding - - problem: matsubara0507/git-challenge-minesweeper - github: sample-hige/git-challenge-minesweeper + - name: git-challenge-tutorial + org: sample-hige + problem: 1 + private: false + - name: git-challenge-is-order-an-adding + org: sample-hige + problem: 2 + private: false + - name: git-challenge-minesweeper + org: sample-hige + problem: 3 + private: false diff --git a/elm-src/Generated/API.elm b/elm-src/Generated/API.elm index 7d83a8b..7f85318 100644 --- a/elm-src/Generated/API.elm +++ b/elm-src/Generated/API.elm @@ -8,8 +8,8 @@ import String type alias Team = - { name : String - , id : String + { id : String + , name : String , repos : List Repo , member : List User } @@ -18,8 +18,8 @@ type alias Team = decodeTeam : Decoder Team decodeTeam = Json.Decode.succeed Team - |> required "name" string |> required "id" string + |> required "name" string |> required "repos" (list decodeRepo) |> required "member" (list decodeUser) @@ -27,8 +27,8 @@ decodeTeam = encodeTeam : Team -> Json.Encode.Value encodeTeam x = Json.Encode.object - [ ( "name", Json.Encode.string x.name ) - , ( "id", Json.Encode.string x.id ) + [ ( "id", Json.Encode.string x.id ) + , ( "name", Json.Encode.string x.name ) , ( "repos", Json.Encode.list encodeRepo x.repos ) , ( "member", Json.Encode.list encodeUser x.member ) ] @@ -56,29 +56,39 @@ encodeUser x = type alias Repo = - { problem : String - , github : String + { name : String + , owner : Maybe String + , org : Maybe String + , problem : Int + , private : Bool } decodeRepo : Decoder Repo decodeRepo = Json.Decode.succeed Repo - |> required "problem" string - |> required "github" string + |> required "name" string + |> required "owner" (maybe string) + |> required "org" (maybe string) + |> required "problem" int + |> required "private" bool encodeRepo : Repo -> Json.Encode.Value encodeRepo x = Json.Encode.object - [ ( "problem", Json.Encode.string x.problem ) - , ( "github", Json.Encode.string x.github ) + [ ( "name", Json.Encode.string x.name ) + , ( "owner", (Maybe.withDefault Json.Encode.null << Maybe.map Json.Encode.string) x.owner ) + , ( "org", (Maybe.withDefault Json.Encode.null << Maybe.map Json.Encode.string) x.org ) + , ( "problem", Json.Encode.int x.problem ) + , ( "private", Json.Encode.bool x.private ) ] type alias Problem = - { problem_name : String - , repo_name : String + { id : Int + , name : String + , repo : String , difficulty : Int , challenge_branches : List String , ci_branch : String @@ -88,8 +98,9 @@ type alias Problem = decodeProblem : Decoder Problem decodeProblem = Json.Decode.succeed Problem - |> required "problem_name" string - |> required "repo_name" string + |> required "id" int + |> required "name" string + |> required "repo" string |> required "difficulty" int |> required "challenge_branches" (list string) |> required "ci_branch" string @@ -98,8 +109,9 @@ decodeProblem = encodeProblem : Problem -> Json.Encode.Value encodeProblem x = Json.Encode.object - [ ( "problem_name", Json.Encode.string x.problem_name ) - , ( "repo_name", Json.Encode.string x.repo_name ) + [ ( "id", Json.Encode.int x.id ) + , ( "name", Json.Encode.string x.name ) + , ( "repo", Json.Encode.string x.repo ) , ( "difficulty", Json.Encode.int x.difficulty ) , ( "challenge_branches", Json.Encode.list Json.Encode.string x.challenge_branches ) , ( "ci_branch", Json.Encode.string x.ci_branch ) diff --git a/elm-src/Main.elm b/elm-src/Main.elm index ee84a51..bbbce0d 100644 --- a/elm-src/Main.elm +++ b/elm-src/Main.elm @@ -107,8 +107,8 @@ viewHeader model = viewHeaderCol : API.Problem -> Html msg viewHeaderCol problem = th - [ id problem.problem_name, class "text-center p-2 f4", style "width" "100px" ] - [ text problem.problem_name ] + [ id problem.name, class "text-center p-2 f4", style "width" "100px" ] + [ text problem.name ] viewBody : Model -> List (Html msg) @@ -145,7 +145,7 @@ viewStatus : List API.Status -> API.Problem -> Html msg viewStatus stats problem = let status = - List.find (\st -> st.problem == problem.problem_name) stats + List.find (\st -> st.problem == problem.name) stats in th [ class "text-center p-2" ] diff --git a/src/Git/Plantation/API/CRUD.hs b/src/Git/Plantation/API/CRUD.hs index c9180f9..789ed6c 100644 --- a/src/Git/Plantation/API/CRUD.hs +++ b/src/Git/Plantation/API/CRUD.hs @@ -52,11 +52,11 @@ getScores = do fetchBuilds :: Drone.Client c => c -> Problem -> Plant (Text, [Drone.Build]) fetchBuilds client problem = do - let (owner, repo) = splitRepoName $ problem ^. #repo_name + let (owner, repo) = splitRepoName $ problem ^. #repo builds <- tryAny (runReq def $ Drone.getBuilds client owner repo Nothing) >>= \case Left err -> logError (display err) >> pure [] Right resp -> pure $ responseBody resp - pure (problem ^. #problem_name, builds) + pure (problem ^. #name, builds) mkScore :: [Problem] -> Map Text [Drone.Build] -> Team -> Score mkScore problems builds team @@ -77,7 +77,7 @@ toStatus name builds toPoint :: [Status] -> Problem -> Int toPoint stats problem = - if elem (problem ^. #problem_name) $ map (view #problem) stats then + if elem (problem ^. #name) $ map (view #problem) stats then problem ^. #difficulty else 0 diff --git a/src/Git/Plantation/API/Webhook.hs b/src/Git/Plantation/API/Webhook.hs index 40ee84b..465f588 100644 --- a/src/Git/Plantation/API/Webhook.hs +++ b/src/Git/Plantation/API/Webhook.hs @@ -41,12 +41,12 @@ pushWebhook _ (_, ev) = do _ -> logError "Team or Problem not found." findTeamByPushEvent :: PushEvent -> [Team] -> Maybe Team -findTeamByPushEvent ev = L.find (isJust . Team.lookupRepo' repoName) +findTeamByPushEvent ev = L.find (isJust . Team.lookupRepoByGithub repoName) where repoName = whRepoFullName $ evPushRepository ev findProblemByPushEvent :: PushEvent -> [Problem] -> Maybe Problem findProblemByPushEvent ev = - L.find $ \p -> let (_, repo') = splitRepoName (p ^. #repo_name) in repo' == repo + L.find $ \p -> let (_, repo') = splitRepoName (p ^. #repo) in repo' == repo where repo = whRepoName $ evPushRepository ev diff --git a/src/Git/Plantation/Cmd/Member.hs b/src/Git/Plantation/Cmd/Member.hs index b14a07c..0508bf1 100644 --- a/src/Git/Plantation/Cmd/Member.hs +++ b/src/Git/Plantation/Cmd/Member.hs @@ -9,7 +9,8 @@ module Git.Plantation.Cmd.Member where import RIO import Data.Extensible -import Git.Plantation.Cmd.Repo (splitRepoName) +import Git.Plantation.Cmd.Repo (repoGithub, + splitRepoName) import Git.Plantation.Data import Git.Plantation.Env import GitHub.Data.Name (mkName) @@ -18,7 +19,7 @@ import qualified GitHub.Endpoints.Repos.Collaborators as GitHub type InviteMemberCmd = Record '[ "team" >: Text - , "repo" >: Maybe Text + , "repo" >: Maybe Int , "user" >: Maybe Text ] @@ -27,24 +28,25 @@ inviteMember args team = forM_ repos $ \repo -> forM_ member $ \user -> tryAnyWithLogError $ inviteUserToRepo user repo where - repos = maybe (team ^. #repos) (: []) $ flip lookupRepo' team =<< args ^. #repo + repos = maybe (team ^. #repos) (: []) $ flip lookupRepoByProblemId team =<< args ^. #repo member = maybe (team ^. #member) (: []) $ flip lookupUser team =<< args ^. #user inviteUserToRepo :: User -> Repo -> Plant () inviteUserToRepo user target = do - token <- asks (view #token) + token <- asks (view #token) + github <- repoGithub target + let (owner, repo) = splitRepoName github resp <- liftIO $ GitHub.addCollaborator (OAuth token) (mkName Proxy owner) (mkName Proxy repo) - (mkName Proxy $ user ^. #github) + (mkName Proxy github) case resp of Left err -> logDebug (displayShow err) >> throwIO (InviteUserError err user target) - Right _ -> logInfo $ display success + Right _ -> logInfo $ display (success github) where - (owner, repo) = splitRepoName $ target ^. #github - success = mconcat + success githubPath = mconcat [ "Success: invite " , user ^. #name, "(", user ^. #github, ")" - , " to ", target ^. #github, "." + , " to ", githubPath, "." ] diff --git a/src/Git/Plantation/Cmd/Options.hs b/src/Git/Plantation/Cmd/Options.hs index 15ca495..1f5b90a 100644 --- a/src/Git/Plantation/Cmd/Options.hs +++ b/src/Git/Plantation/Cmd/Options.hs @@ -37,7 +37,7 @@ type SubCmdFields = ] instance Run ("verify" >: ()) where - run' _ args = do + run' _ _ = do conf <- asks (view #config) case Config.verify conf of Left err -> logError $ "invalid config: " <> display err diff --git a/src/Git/Plantation/Cmd/Repo.hs b/src/Git/Plantation/Cmd/Repo.hs index 2ce87db..2b1521f 100644 --- a/src/Git/Plantation/Cmd/Repo.hs +++ b/src/Git/Plantation/Cmd/Repo.hs @@ -16,7 +16,7 @@ import Git.Plantation.Data (Problem, Repo, Team) import qualified Git.Plantation.Data.Team as Team import Git.Plantation.Env import GitHub.Data.Name (mkName) -import GitHub.Data.Repos (newRepo) +import GitHub.Data.Repos (newRepo, newRepoPrivate) import GitHub.Endpoints.Repos (Auth (..)) import qualified GitHub.Endpoints.Repos as GitHub import Shelly hiding (FilePath) @@ -42,7 +42,7 @@ type ResetRepoCmd = Record RepoCmdFields actByRepoName :: (Team -> Problem -> Plant a) -> Team -> Text -> Plant () actByRepoName act team repoName = do conf <- asks (view #config) - let problem = L.find (\p -> p ^. #repo_name == repoName) $ conf ^. #problems + let problem = L.find (\p -> p ^. #repo == repoName) $ conf ^. #problems case problem of Nothing -> logError $ "repo is not found: " <> display repoName Just problem' -> tryAnyWithLogError $ act team problem' @@ -50,7 +50,7 @@ actByRepoName act team repoName = do createRepo :: Team -> Problem -> Plant () createRepo team problem = do logInfo $ mconcat - [ "create repo: ", displayShow $ problem ^. #repo_name + [ "create repo: ", displayShow $ problem ^. #repo , " to team: ", displayShow $ team ^. #name ] info <- Team.lookupRepo problem team `fromJustWithThrow` UndefinedTeamProblem team problem @@ -60,23 +60,28 @@ createRepo team problem = do createRepoInGitHub :: Repo -> Team -> Problem -> Plant () createRepoInGitHub info team problem = do - let (owner, repo) = splitRepoName $ info ^. #github + (owner, repo) <- splitRepoName <$> repoGithub info token <- asks (view #token) logInfo $ "create repo in github: " <> displayShow (owner <> "/" <> repo) - resp <- liftIO $ GitHub.createOrganizationRepo' - (OAuth token) - (mkName Proxy owner) - (newRepo $ mkName Proxy repo) + resp <- liftIO $ request owner (OAuth token) + ((newRepo $ mkName Proxy repo) { newRepoPrivate = Just (info ^. #private) }) case resp of Left err -> logDebug (displayShow err) >> throwIO (CreateRepoError err team problem) Right _ -> logInfo "Success: create repository in GitHub" + where + request owner = + if Team.repoIsOrg info then + flip GitHub.createOrganizationRepo' (mkName Proxy owner) + else + GitHub.createRepo' initRepoInGitHub :: Repo -> Team -> Problem -> Plant () initRepoInGitHub info team problem = do token <- getTextToken workDir <- asks (view #work) - let (owner, repo) = splitRepoName $ problem ^. #repo_name - teamUrl = mconcat ["https://", token, "@github.com/", info ^. #github, ".git"] + github <- repoGithub info + let (owner, repo) = splitRepoName $ problem ^. #repo + teamUrl = mconcat ["https://", token, "@github.com/", github, ".git"] problemUrl = mconcat ["https://", token, "@github.com/", owner, "/", repo, ".git"] shelly' $ chdir_p (workDir (team ^. #name)) (Git.cloneOrFetch teamUrl repo) @@ -88,14 +93,15 @@ initRepoInGitHub info team problem = do forM_ (problem ^. #challenge_branches) $ \branch -> Git.checkout ["-b", branch, "problem/" <> branch] Git.push $ "-f" : "-u" : "origin" : problem ^. #challenge_branches - Git.branch ["-D", "temp"] - logInfo $ "Success: create repo as " <> displayShow (info ^. #github) + errExit False $ Git.branch ["-D", "temp"] + logInfo $ "Success: create repo as " <> displayShow github initProblemCI :: Repo -> Team -> Problem -> Plant () initProblemCI info team problem = do token <- getTextToken workDir <- asks (view #work) - let (owner, repo) = splitRepoName $ problem ^. #repo_name + github <- repoGithub info + let (owner, repo) = splitRepoName $ problem ^. #repo problemUrl = mconcat ["https://", token, "@github.com/", owner, "/", repo, ".git"] shelly' $ chdir_p (workDir owner) (Git.cloneOrFetch problemUrl repo) @@ -104,7 +110,7 @@ initProblemCI info team problem = do Git.existBranch (team ^. #name) >>= \case False -> Git.checkout ["-b", team ^. #name] True -> Git.checkout [team ^. #name] - writefile ciFileName $ info ^. #github + writefile ciFileName github Git.add [ciFileName] Git.commit ["-m", "[CI SKIP] Add ci branch"] Git.push ["-u", "origin", team ^. #name] @@ -113,7 +119,7 @@ initProblemCI info team problem = do resetRepo :: Repo -> Team -> Problem -> Plant () resetRepo info team problem = do workDir <- asks (view #work) - let (_, repo) = splitRepoName $ problem ^. #repo_name + let (_, repo) = splitRepoName $ problem ^. #repo paths <- shelly' $ chdir_p (workDir (team ^. #name) repo) $ ls "." logDebug $ "Remove file: " <> display (Text.intercalate " " $ map toTextIgnore paths) shelly' $ chdir_p (workDir (team ^. #name)) $ rm_rf (fromText repo) @@ -123,7 +129,7 @@ pushForCI :: Team -> Problem -> Plant () pushForCI team problem = do token <- getTextToken workDir <- asks (view #work) - let (owner, repo) = splitRepoName $ problem ^. #repo_name + let (owner, repo) = splitRepoName $ problem ^. #repo problemUrl = mconcat ["https://", token, "@github.com/", owner, "/", repo, ".git"] shelly' $ chdir_p (workDir owner) (Git.cloneOrFetch problemUrl repo) shelly' $ chdir_p (workDir owner repo) $ do @@ -142,5 +148,9 @@ getTextToken = Left _ -> logError "cannot decode token to utf8." >> pure "" Right t -> pure t +repoGithub :: Repo -> Plant Text +repoGithub repo = + Team.repoGithubPath repo `fromJustWithThrow` InvalidRepoConfig repo + ciFileName :: IsString s => s ciFileName = "REPOSITORY" diff --git a/src/Git/Plantation/Data/Problem.hs b/src/Git/Plantation/Data/Problem.hs index 196037c..1c6b514 100644 --- a/src/Git/Plantation/Data/Problem.hs +++ b/src/Git/Plantation/Data/Problem.hs @@ -10,11 +10,12 @@ import Data.Extensible import Language.Elm type Problem = Record - '[ "problem_name" >: Text - , "repo_name" >: Text - , "difficulty" >: Int + '[ "id" >: Int + , "name" >: Text + , "repo" >: Text + , "difficulty" >: Int , "challenge_branches" >: [Branch] - , "ci_branch" >: Branch + , "ci_branch" >: Branch ] instance ElmType Problem where diff --git a/src/Git/Plantation/Data/Team.hs b/src/Git/Plantation/Data/Team.hs index a5a43e4..a2d512c 100644 --- a/src/Git/Plantation/Data/Team.hs +++ b/src/Git/Plantation/Data/Team.hs @@ -13,8 +13,8 @@ import Git.Plantation.Data.Problem import Language.Elm type Team = Record - '[ "name" >: Text - , "id" >: Text + '[ "id" >: Text + , "name" >: Text , "repos" >: [Repo] , "member" >: [User] ] @@ -31,20 +31,36 @@ instance ElmType User where toElmType = toElmRecordType "User" type Repo = Record - '[ "problem" >: Text - , "github" >: Text + '[ "name" >: Text + , "owner" >: Maybe Text + , "org" >: Maybe Text + , "problem" >: Int + , "private" >: Bool ] instance ElmType Repo where toElmType = toElmRecordType "Repo" lookupRepo :: Problem -> Team -> Maybe Repo -lookupRepo problem = lookupRepo' (problem ^. #repo_name) +lookupRepo problem = lookupRepoByProblemId (problem ^. #id) + +lookupRepoByProblemId :: Int -> Team -> Maybe Repo +lookupRepoByProblemId pid team = + L.find (\repo -> repo ^. #problem == pid) (team ^. #repos) -lookupRepo' :: Text -> Team -> Maybe Repo -lookupRepo' repoName team = - L.find (\repo -> repoName == repo ^. #problem) (team ^. #repos) +lookupRepoByGithub :: Text -> Team -> Maybe Repo +lookupRepoByGithub github team = + L.find (\repo -> repoGithubPath repo == Just github) (team ^. #repos) lookupUser :: Text -> Team -> Maybe User lookupUser github team = L.find (\user -> github == user ^. #github) (team ^. #member) + +repoGithubPath :: Repo -> Maybe Text +repoGithubPath repo = case (repo ^. #owner, repo ^. #org) of + (Just owner, _) -> Just $ owner <> "/" <> repo ^. #name + (_, Just org) -> Just $ org <> "/" <> repo ^. #name + _ -> Nothing + +repoIsOrg :: Repo -> Bool +repoIsOrg repo = isJust $ repo ^. #org diff --git a/src/Git/Plantation/Env.hs b/src/Git/Plantation/Env.hs index 97776bd..443f534 100644 --- a/src/Git/Plantation/Env.hs +++ b/src/Git/Plantation/Env.hs @@ -63,6 +63,7 @@ data GitPlantException = UndefinedTeamProblem Team Problem | CreateRepoError GitHub.Error Team Problem | InviteUserError GitHub.Error User Repo + | InvalidRepoConfig Repo deriving (Typeable) instance Exception GitPlantException @@ -81,3 +82,7 @@ instance Show GitPlantException where mkLogMessage' "can't invite user to repository" (#user @= user <: #repo @= repo <: nil) + InvalidRepoConfig repo -> + mkLogMessage' + "invalid repo config" + (#repo @= repo <: nil) diff --git a/tool/Main.hs b/tool/Main.hs index 9c6fdbd..b6f7669 100644 --- a/tool/Main.hs +++ b/tool/Main.hs @@ -65,7 +65,7 @@ singleRepoCmdParser = hsequence inviteMemberCmdParser :: Parser InviteMemberCmd inviteMemberCmdParser = hsequence $ #team <@=> strArgument (metavar "TEXT" <> help "Sets team that wont to controll.") - <: #repo <@=> option (Just <$> str) (long "repo" <> value Nothing <> metavar "TEXT" <> help "Sets reopsitory that wont to controll.") + <: #repo <@=> option (Just <$> auto) (long "repo" <> value Nothing <> metavar "ID" <> help "Sets reopsitory by problem id that wont to controll.") <: #user <@=> option (Just <$> str) (long "user" <> value Nothing <> metavar "TEXT" <> help "Sets user that wont to controll.") <: nil From 75bafa05c155d2a4f15a3137a0462191442dd27c Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Thu, 21 Mar 2019 17:43:35 +0900 Subject: [PATCH 25/71] Feat: delete command --- src/Git/Plantation/Cmd/Options.hs | 10 +++++++ src/Git/Plantation/Cmd/Repo.hs | 43 ++++++++++++++++++++++++++++++- src/Git/Plantation/Env.hs | 5 ++++ stack.yaml | 3 ++- tool/Main.hs | 4 +++ 5 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/Git/Plantation/Cmd/Options.hs b/src/Git/Plantation/Cmd/Options.hs index 1f5b90a..a4e84ca 100644 --- a/src/Git/Plantation/Cmd/Options.hs +++ b/src/Git/Plantation/Cmd/Options.hs @@ -33,6 +33,7 @@ type SubCmdFields = , "init_github_repo" >: InitGitHubRepoCmd , "init_ci" >: InitCICmd , "reset_repo" >: ResetRepoCmd + , "delete_repo" >: DeleteRepoCmd , "invite_member" >: InviteMemberCmd ] @@ -80,6 +81,15 @@ runRepoCmd act args = do (Nothing, _) -> logError $ "team is not found: " <> display (args ^. #team) (Just team', name) -> actByRepoName act team' name +instance Run ("delete_repo" >: DeleteRepoCmd) where + run' _ args = do + conf <- asks (view #config) + let team = L.find (\t -> t ^. #name == args ^. #team) $ conf ^. #teams + case (team, args ^. #repo) of + (Nothing, _) -> logError $ "team is not found: " <> display (args ^. #team) + (Just team', Just name) -> actByRepoName deleteRepo team' name + (Just team', _) -> forM_ (conf ^. #problems) (tryAnyWithLogError . deleteRepo team') + instance Run ("invite_member" >: InviteMemberCmd) where run' _ args = do conf <- asks (view #config) diff --git a/src/Git/Plantation/Cmd/Repo.hs b/src/Git/Plantation/Cmd/Repo.hs index 2b1521f..eb519ec 100644 --- a/src/Git/Plantation/Cmd/Repo.hs +++ b/src/Git/Plantation/Cmd/Repo.hs @@ -26,6 +26,11 @@ type NewRepoCmd = Record , "team" >: Text ] +type DeleteRepoCmd = Record + '[ "repo" >: Maybe Text + , "team" >: Text + ] + type RepoCmdFields = '[ "repo" >: Text , "team" >: Text @@ -114,7 +119,7 @@ initProblemCI info team problem = do Git.add [ciFileName] Git.commit ["-m", "[CI SKIP] Add ci branch"] Git.push ["-u", "origin", team ^. #name] - logInfo $ "Success: create ci branch in " <> displayShow problemUrl + logInfo $ "Success: create ci branch in " <> displayShow (problem ^. #repo) resetRepo :: Repo -> Team -> Problem -> Plant () resetRepo info team problem = do @@ -139,6 +144,42 @@ pushForCI team problem = do Git.push ["origin", team ^. #name] logInfo "Success push" +deleteRepo :: Team -> Problem -> Plant () +deleteRepo team problem = do + logInfo $ mconcat + [ "delete repo: ", displayShow $ problem ^. #repo + , " to team: ", displayShow $ team ^. #name + ] + info <- Team.lookupRepo problem team `fromJustWithThrow` UndefinedTeamProblem team problem + deleteRepoInGithub info + deleteProblemCI team problem + +deleteRepoInGithub :: Repo -> Plant () +deleteRepoInGithub info = do + (owner, repo) <- splitRepoName <$> repoGithub info + token <- asks (view #token) + logInfo $ "delete repo in github: " <> displayShow (owner <> "/" <> repo) + resp <- liftIO $ GitHub.deleteRepo (OAuth token) (mkName Proxy owner) (mkName Proxy repo) + case resp of + Left err -> logDebug (displayShow err) >> throwIO (DeleteRepoError err info) + Right _ -> logDebug "Success: delete repository in GitHub" + +deleteProblemCI :: Team -> Problem -> Plant () +deleteProblemCI team problem = do + token <- getTextToken + workDir <- asks (view #work) + let (owner, repo) = splitRepoName $ problem ^. #repo + problemUrl = mconcat ["https://", token, "@github.com/", owner, "/", repo, ".git"] + + shelly' $ chdir_p (workDir owner) (Git.cloneOrFetch problemUrl repo) + shelly' $ chdir_p (workDir owner repo) $ do + errExit False $ Git.push [ "--delete", "origin", team ^. #name] + logInfo $ "Success: delete ci branch in " <> displayShow (problem ^. #repo) + + +-- | +-- helper functions + splitRepoName :: Text -> (Text, Text) splitRepoName = fmap (Text.drop 1) . Text.span(/= '/') diff --git a/src/Git/Plantation/Env.hs b/src/Git/Plantation/Env.hs index 443f534..9d7d44c 100644 --- a/src/Git/Plantation/Env.hs +++ b/src/Git/Plantation/Env.hs @@ -62,6 +62,7 @@ mkLogMessage' message = data GitPlantException = UndefinedTeamProblem Team Problem | CreateRepoError GitHub.Error Team Problem + | DeleteRepoError GitHub.Error Repo | InviteUserError GitHub.Error User Repo | InvalidRepoConfig Repo deriving (Typeable) @@ -78,6 +79,10 @@ instance Show GitPlantException where mkLogMessage' "can't create repository" (#team @= team <: #problem @= problem <: nil) + DeleteRepoError _err repo -> + mkLogMessage' + "can't delete repository" + (#repo @= repo <: nil) InviteUserError _err user repo -> mkLogMessage' "can't invite user to repository" diff --git a/stack.yaml b/stack.yaml index 47bff1c..146a40a 100644 --- a/stack.yaml +++ b/stack.yaml @@ -3,8 +3,9 @@ packages: - . extra-deps: - extensible-0.5 -- github-0.21 - servant-github-webhook-0.4.1.0 +- github: matsubara0507/github + commit: 08f409a9cbc0ab46a3e68f84ddfb14fc89c3fe7a - github: matsubara0507/drone-haskell commit: aa6f5152dd9ea72cb48a32bdc58c91de2bdc21a9 - github: matsubara0507/elm-export diff --git a/tool/Main.hs b/tool/Main.hs index b6f7669..701888b 100644 --- a/tool/Main.hs +++ b/tool/Main.hs @@ -47,6 +47,7 @@ subcmdParser = variantFrom <: #init_github_repo @= singleRepoCmdParser `withInfo` "Init repository for team in GitHub" <: #init_ci @= singleRepoCmdParser `withInfo` "Init CI repository by team repository" <: #reset_repo @= singleRepoCmdParser `withInfo` "Reset repository for team" + <: #delete_repo @= deleteRepoCmdParser `withInfo` "Delete repository for team." <: #invite_member @= inviteMemberCmdParser `withInfo` "Invite Member to Team Repository" <: nil @@ -62,6 +63,9 @@ singleRepoCmdParser = hsequence <: #team <@=> strArgument (metavar "TEXT" <> help "Sets team that wont to controll.") <: nil +deleteRepoCmdParser :: Parser DeleteRepoCmd +deleteRepoCmdParser = newRepoCmdParser + inviteMemberCmdParser :: Parser InviteMemberCmd inviteMemberCmdParser = hsequence $ #team <@=> strArgument (metavar "TEXT" <> help "Sets team that wont to controll.") From 6cc7359ba8b629baa96d11d9ab92585deceb3eaf Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Thu, 21 Mar 2019 17:46:59 +0900 Subject: [PATCH 26/71] Doc: update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf06ead..3ab0359 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,3 +13,8 @@ * 回答のためCIを問題リポジトリに設定(#12) * メンバーを回答リポジトリに招待する(#12) * 回答リポジトリのリセット(#12) +* コマンドの追加 + * `verify` : 設定ファイルの検査 + * `delete_repo` : 回答リポジトリとCIの設定の削除 +* 設定周りの更新 + * `Problem` と 回答リポジトリの対応関係を `id` にした From 81dfbd93e1a24665b981c630c547a981af7ba30b Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Fri, 22 Mar 2019 13:43:52 +0900 Subject: [PATCH 27/71] Doc: update changelog --- CHANGELOG.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ab0359..334e2d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,14 +7,18 @@ * `/score` エンドポイントで drone から取得できなくても空リストを返す (#10) * `fail` の代わりに例外処理を追加 (#11) * JSON 形式のログを追加(#11) -* 回答リポジトリの生成の各ステップコマンドを追加(#12) - * GitHub に空リポジトリを作成(#12) - * リポジトリを初期化(問題リポジトリを参照して)(#12) - * 回答のためCIを問題リポジトリに設定(#12) -* メンバーを回答リポジトリに招待する(#12) -* 回答リポジトリのリセット(#12) -* コマンドの追加 +* 回答リポジトリの生成の各ステップコマンドを追加(#13) + * GitHub に空リポジトリを作成 + * リポジトリを初期化(問題リポジトリを参照して) + * 回答のためCIを問題リポジトリに設定 +* メンバーを回答リポジトリに招待する(#13) +* 回答リポジトリのリセット(#13) +* コマンドの追加と変更(#14) * `verify` : 設定ファイルの検査 * `delete_repo` : 回答リポジトリとCIの設定の削除 -* 設定周りの更新 + * private リポジトリを生成できるように変更 + * org アカウント以外でもちゃんと動作するように修正 +* 設定周りの更新(#14) * `Problem` と 回答リポジトリの対応関係を `id` にした + * `provate` 設定の追加 + * `org` と `owner` を明示的に指定するように変更 From 8c0d3630c7be84ad60e6d588e4f0e8faede31aa5 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Fri, 22 Mar 2019 15:34:58 +0900 Subject: [PATCH 28/71] Refactor: change path --- {app => exec/app}/Main.hs | 0 {tool => exec/tool}/Main.hs | 0 package.yaml | 12 ++---------- 3 files changed, 2 insertions(+), 10 deletions(-) rename {app => exec/app}/Main.hs (100%) rename {tool => exec/tool}/Main.hs (100%) diff --git a/app/Main.hs b/exec/app/Main.hs similarity index 100% rename from app/Main.hs rename to exec/app/Main.hs diff --git a/tool/Main.hs b/exec/tool/Main.hs similarity index 100% rename from tool/Main.hs rename to exec/tool/Main.hs diff --git a/package.yaml b/package.yaml index 04b9416..1fc51cb 100644 --- a/package.yaml +++ b/package.yaml @@ -60,22 +60,14 @@ library: executables: git-plantation-app: main: Main.hs - source-dirs: app - ghc-options: - - -threaded - - -rtsopts - - -with-rtsopts=-N + source-dirs: exec/app dependencies: - git-plantation - gitrev - warp git-plantation-tool: main: Main.hs - source-dirs: tool - ghc-options: - - -threaded - - -rtsopts - - -with-rtsopts=-N + source-dirs: exec/tool dependencies: - git-plantation - gitrev From a5f9f3af0755e95a4a58735beeb978c8e6209067 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Fri, 22 Mar 2019 22:34:37 +0900 Subject: [PATCH 29/71] Fix: run dotenv if .env is not exist --- exec/app/Main.hs | 2 +- exec/tool/Main.hs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exec/app/Main.hs b/exec/app/Main.hs index b6e6f47..d614d4f 100644 --- a/exec/app/Main.hs +++ b/exec/app/Main.hs @@ -27,7 +27,7 @@ import System.Environment (getEnv) main :: IO () main = withGetOpt "[options] [config-file]" opts $ \r args -> do - _ <- loadFile defaultConfig + _ <- tryIO $ loadFile defaultConfig case (r ^. #version, listToMaybe args) of (True, _) -> B.putStr $ fromString (showVersion version) <> "\n" (_, Nothing) -> error "please input config file path." diff --git a/exec/tool/Main.hs b/exec/tool/Main.hs index 701888b..3e33bde 100644 --- a/exec/tool/Main.hs +++ b/exec/tool/Main.hs @@ -24,7 +24,7 @@ import Options.Applicative main :: IO () main = do - _ <- loadFile defaultConfig + _ <- tryIO $ loadFile defaultConfig run =<< execParser opts where opts = info (options <**> version Meta.version <**> helper) From 6bb1a224a534409b92fca1ad91754cc4385e26e2 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Fri, 22 Mar 2019 22:35:33 +0900 Subject: [PATCH 30/71] Misc: update resolver --- stack.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stack.yaml b/stack.yaml index 146a40a..646b6a8 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,4 +1,4 @@ -resolver: lts-13.6 +resolver: lts-13.13 packages: - . extra-deps: From f88176dec5defd7bb956d73f803c9c3c6dbfeae4 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Fri, 22 Mar 2019 22:37:11 +0900 Subject: [PATCH 31/71] Fix: docker app to add static files --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 0bae022..0b44116 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,5 +6,6 @@ RUN apt-get update && apt-get install -y \ && rm -rf /var/lib/apt/lists/* WORKDIR /work COPY script /usr/local/bin/ +COPY static /work/static CMD ["run-app.sh"] From cac4cc157eb614cb0d63a099c1c5eb271da7fa7e Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Sat, 23 Mar 2019 12:28:13 +0900 Subject: [PATCH 32/71] Doc: update README --- README.md | 55 +++++++++++++++++++++++++--------------- drone/docker-compose.yml | 2 +- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index a2eb19d..078e0de 100644 --- a/README.md +++ b/README.md @@ -11,48 +11,61 @@ ## Usage -### 1. Write config file +### 0. Drone の起動 -ref. `example/config.yaml` +もしローカルで起動する場合は: -### 2. Create problem repository in team +1. ngrok などで外への通信を開ける(`ngrok http 8000`) +2. Drone の GitHub App を作成し `Authorization callback URL` に `https://{ngrok's url}/login` を追加 +2. `drone/.env` を設定する(ref. `drone/.env.template`) + - `DRONE_HOST` に ngrok の URL を設定する + - `DRONE_GITHUB_CLIENT` と `DRONE_GITHUB_SECRET` に GitHub App のものを設定 + - `DRONE_SECRET` には適当な文字列を設定 +4. `drone` ディレクトリで `docker-compose up` -using `git-plantation-tool`: +これで ngrok の生成した URL にアクセスすると Drone CI にアクセスできる -``` -$ GH_TOKEN=XXX stack exec -- git-plantation-tool -c example/config.yaml --work .temp new_repo sample -``` +### 1. git-plantation の設定を記述 -### 3. Run app and drone +ref. `config/.git-plantation.yaml` -run app: +### 2. 環境変数を設定する -``` -$ GH_TOKEN=XXX GH_SECRET=YYY stack exec -- git-plantation-app --port 8080 --work ".temp" --verbose example/config.yaml -``` +ref. `.env.template` -run drone ci: +- `PORT` は app のポート (app を docker で起動する場合) +- `WORK` は git コマンドを実行するワークディレクトリ (app を docker で起動する場合) +- `CONFIG` は git-plantation の設定のパス (app を docker で起動する場合) +- `DRONE_HOST` は Drone CI の URL +- `DRONE_PORT` は Drone CI のポート +- `DRONE_TOKEN` は Drone CI のトークン +- `GH_TOKEN` は GitHub のトークン +- `GH_SECRET` は GitHub Webhook のシークレットキー + +### 3. Create team's repository in team + +using `git-plantation-tool`: ``` -$ cd drone -$ docker-compose up +$ stack exec -- git-plantation-tool -c .git-plantation.yaml --work .temp new_repo sample ``` -run ngrok: +### 4. Run app + +run app: ``` -$ ngrok start --config ngrok/config.yml app drone +$ stack exec -- git-plantation-app --port 8080 --work ".temp" --verbose example/config.yaml ``` -setting ngrok URL in GitHub Webhook. - ## Build with Docker Define environment to `.env` from `.env.template`. ``` $ stack docker pull -$ stack build --docker +$ stack --docker --no-terminal build -j 1 Cabal # if `out of memory` +$ stack test --docker $ docker build -t git-plantation . -$ docker run --rm -it -v `pwd`:/work -p 8080:8080 --env-file .env git-plantation +$ docker run --rm -it -v `pwd`/config.yaml:/work/config.yaml -p 8080:8080 --env-file .env git-plantation ``` diff --git a/drone/docker-compose.yml b/drone/docker-compose.yml index 541c99d..11ed681 100644 --- a/drone/docker-compose.yml +++ b/drone/docker-compose.yml @@ -16,7 +16,7 @@ services: - DRONE_GITHUB_CLIENT_ID=${DRONE_GITHUB_CLIENT} - DRONE_GITHUB_CLIENT_SECRET=${DRONE_GITHUB_SECRET} - DRONE_RPC_SECRET=${DRONE_SECRET} - - DRONE_SERVER_HOST=abcdef.ngrok.io + - DRONE_SERVER_HOST=${DRONE_HOST} - DRONE_SERVER_PROTO=https - DRONE_TLS_AUTOCERT=true From 0e5fcb871719fd8c6f94377aac45bc7d7d5bb56b Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Tue, 26 Mar 2019 03:24:18 +0900 Subject: [PATCH 33/71] Fix: init repo cmd --- src/Git/Plantation/Cmd/Repo.hs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Git/Plantation/Cmd/Repo.hs b/src/Git/Plantation/Cmd/Repo.hs index eb519ec..9f411ac 100644 --- a/src/Git/Plantation/Cmd/Repo.hs +++ b/src/Git/Plantation/Cmd/Repo.hs @@ -91,9 +91,11 @@ initRepoInGitHub info team problem = do shelly' $ chdir_p (workDir (team ^. #name)) (Git.cloneOrFetch teamUrl repo) shelly' $ chdir_p (workDir (team ^. #name) repo) $ do - Git.checkout [ "-b", "temp"] + Git.existBranch "temp" >>= \case + False -> Git.checkout ["-b", "temp"] + True -> Git.checkout ["temp"] errExit False $ Git.branch $ "-D" : problem ^. #challenge_branches - Git.remote ["add", "problem", problemUrl] + errExit False $ Git.remote ["add", "problem", problemUrl] Git.fetch ["--all"] forM_ (problem ^. #challenge_branches) $ \branch -> Git.checkout ["-b", branch, "problem/" <> branch] From 46698b4e2ee102b6beda76c837a8b73d9a71fbc2 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Tue, 26 Mar 2019 03:33:35 +0900 Subject: [PATCH 34/71] Fix; webhook for problem struct --- src/Git/Plantation/API/Webhook.hs | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/Git/Plantation/API/Webhook.hs b/src/Git/Plantation/API/Webhook.hs index 465f588..4017317 100644 --- a/src/Git/Plantation/API/Webhook.hs +++ b/src/Git/Plantation/API/Webhook.hs @@ -1,5 +1,6 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE OverloadedLabels #-} +{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeOperators #-} module Git.Plantation.API.Webhook where @@ -32,21 +33,15 @@ pushWebhook :: RepoWebhookEvent -> ((), PushEvent) -> Plant () pushWebhook _ (_, ev) = do logInfo $ "Hook Push Event: " <> displayShow ev config <- asks (view #config) - let team = findTeamByPushEvent ev $ config ^. #teams - logInfo $ "Team: " <> displayShow team - let problem = findProblemByPushEvent ev $ config ^. #problems - logInfo $ "Problem: " <> displayShow problem - case (team, problem) of - (Just t, Just p) -> pushForCI t p - _ -> logError "Team or Problem not found." - -findTeamByPushEvent :: PushEvent -> [Team] -> Maybe Team -findTeamByPushEvent ev = L.find (isJust . Team.lookupRepoByGithub repoName) + case findByPushEvent ev (config ^. #teams) (config ^. #problems) of + Just (team, problem) -> pushForCI team problem + Nothing -> logError "team or problem is not found." + +findByPushEvent :: PushEvent -> [Team] -> [Problem] -> Maybe (Team, Problem) +findByPushEvent ev teams problems = do + (team, repo) <- join $ L.find isJust repos + problem <- L.find (\p -> p ^. #id == repo ^. #problem) problems + pure (team, problem) where + repos = map (\t -> (t,) <$> Team.lookupRepoByGithub repoName t) teams repoName = whRepoFullName $ evPushRepository ev - -findProblemByPushEvent :: PushEvent -> [Problem] -> Maybe Problem -findProblemByPushEvent ev = - L.find $ \p -> let (_, repo') = splitRepoName (p ^. #repo) in repo' == repo - where - repo = whRepoName $ evPushRepository ev From 582dae3847921ca49b60eac1617724af40b93754 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Tue, 26 Mar 2019 03:34:00 +0900 Subject: [PATCH 35/71] Fix: git config in Dockerfile --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index 0b44116..0c1ffef 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,8 @@ RUN apt-get update && apt-get install -y \ git \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* +RUN git config --global user.email "bot@example.com" \ + && git config --global user.name "Bot" WORKDIR /work COPY script /usr/local/bin/ COPY static /work/static From 66453ac5a5fcff880f63a7dfecc7f187d1b59914 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Tue, 26 Mar 2019 03:34:22 +0900 Subject: [PATCH 36/71] Feat: add webhook to repo cmd --- .env.template | 2 ++ exec/app/Main.hs | 11 ++++---- exec/tool/Main.hs | 1 + src/Git/Plantation/Cmd.hs | 17 +++++++---- src/Git/Plantation/Cmd/Options.hs | 6 ++++ src/Git/Plantation/Cmd/Repo.hs | 47 ++++++++++++++++++++++++------- src/Git/Plantation/Env.hs | 16 +++++++---- 7 files changed, 74 insertions(+), 26 deletions(-) diff --git a/.env.template b/.env.template index a558b44..9795d08 100644 --- a/.env.template +++ b/.env.template @@ -6,3 +6,5 @@ DRONE_PORT= DRONE_TOKEN= GH_TOKEN= GH_SECRET= +APP_HOST= +APP_PORT= diff --git a/exec/app/Main.hs b/exec/app/Main.hs index d614d4f..695cb9c 100644 --- a/exec/app/Main.hs +++ b/exec/app/Main.hs @@ -71,11 +71,12 @@ runServer opts config = do dPort <- liftIO $ readMaybe <$> getEnv "DRONE_PORT" let client = #host @= dHost <: #port @= dPort <: #token @= dToken <: nil withLogFunc logOpts $ \logger -> do - let env = #config @= config - <: #token @= token - <: #work @= (opts ^. #work) - <: #client @= Drone.HttpsClient client - <: #logger @= logger + let env = #config @= config + <: #token @= token + <: #work @= (opts ^. #work) + <: #client @= Drone.HttpsClient client + <: #webhook @= "" + <: #logger @= logger <: nil :: Env B.putStr $ "Listening on port " <> (fromString . show) (opts ^. #port) <> "\n" Warp.run (opts ^. #port) $ diff --git a/exec/tool/Main.hs b/exec/tool/Main.hs index 3e33bde..57463d1 100644 --- a/exec/tool/Main.hs +++ b/exec/tool/Main.hs @@ -45,6 +45,7 @@ subcmdParser = variantFrom <: #new_repo @= newRepoCmdParser `withInfo` "Create repository for team." <: #new_github_repo @= singleRepoCmdParser `withInfo` "Create new repository for team in GitHub" <: #init_github_repo @= singleRepoCmdParser `withInfo` "Init repository for team in GitHub" + <: #setup_webhook @= singleRepoCmdParser `withInfo` "Setup GitHub Webhook to team repository" <: #init_ci @= singleRepoCmdParser `withInfo` "Init CI repository by team repository" <: #reset_repo @= singleRepoCmdParser `withInfo` "Reset repository for team" <: #delete_repo @= deleteRepoCmdParser `withInfo` "Delete repository for team." diff --git a/src/Git/Plantation/Cmd.hs b/src/Git/Plantation/Cmd.hs index 80947d2..43f6a16 100644 --- a/src/Git/Plantation/Cmd.hs +++ b/src/Git/Plantation/Cmd.hs @@ -9,6 +9,7 @@ module Git.Plantation.Cmd ) where import RIO +import qualified RIO.Text as Text import Data.Extensible import qualified Drone.Client as Drone @@ -17,20 +18,24 @@ import Git.Plantation.Cmd.Options as X import Git.Plantation.Cmd.Repo as X import Git.Plantation.Cmd.Run as X import Git.Plantation.Config (readConfig) -import System.Environment (getEnv) +import System.Environment (getEnv, lookupEnv) run :: MonadUnliftIO m => Options -> m () run opts = do config <- readConfig (opts ^. #config) logOpts <- logOptionsHandle stdout (opts ^. #verbose) token <- liftIO $ fromString <$> getEnv "GH_TOKEN" + appHost <- liftIO $ fromString <$> getEnv "APP_HOST" + appPort <- liftIO $ maybe "" fromString <$> lookupEnv "APP_PORT" withLogFunc logOpts $ \logger -> do let client = #host @= "" <: #port @= Nothing <: #token @= "" <: nil - env = #config @= config - <: #token @= token - <: #work @= opts ^. #work - <: #client @= Drone.HttpsClient client - <: #logger @= logger + webhookUrl = mconcat ["http://", appHost, if Text.null appPort then "" else ":", appPort, "/api"] + env = #config @= config + <: #token @= token + <: #work @= opts ^. #work + <: #client @= Drone.HttpsClient client + <: #webhook @= webhookUrl + <: #logger @= logger <: nil runRIO env $ matchField (htabulateFor (Proxy @ Run) $ \m -> Field (Match $ run' m . runIdentity)) diff --git a/src/Git/Plantation/Cmd/Options.hs b/src/Git/Plantation/Cmd/Options.hs index a4e84ca..5a112ee 100644 --- a/src/Git/Plantation/Cmd/Options.hs +++ b/src/Git/Plantation/Cmd/Options.hs @@ -31,6 +31,7 @@ type SubCmdFields = , "new_repo" >: NewRepoCmd , "new_github_repo" >: NewGitHubRepoCmd , "init_github_repo" >: InitGitHubRepoCmd + , "setup_webhook" >: SetupWebhookCmd , "init_ci" >: InitCICmd , "reset_repo" >: ResetRepoCmd , "delete_repo" >: DeleteRepoCmd @@ -63,6 +64,11 @@ instance Run ("init_github_repo" >: InitGitHubRepoCmd) where info <- Team.lookupRepo problem team `fromJustWithThrow` UndefinedTeamProblem team problem initRepoInGitHub info team problem +instance Run ("setup_webhook" >: SetupWebhookCmd) where + run' _ = runRepoCmd $ \team problem -> do + info <- Team.lookupRepo problem team `fromJustWithThrow` UndefinedTeamProblem team problem + setupWebhook info + instance Run ("init_ci" >: InitCICmd) where run' _ = runRepoCmd $ \team problem -> do info <- Team.lookupRepo problem team `fromJustWithThrow` UndefinedTeamProblem team problem diff --git a/src/Git/Plantation/Cmd/Repo.hs b/src/Git/Plantation/Cmd/Repo.hs index 9f411ac..8c11299 100644 --- a/src/Git/Plantation/Cmd/Repo.hs +++ b/src/Git/Plantation/Cmd/Repo.hs @@ -7,19 +7,24 @@ module Git.Plantation.Cmd.Repo where import RIO -import qualified RIO.List as L -import qualified RIO.Text as Text +import qualified RIO.List as L +import qualified RIO.Map as Map +import qualified RIO.Text as Text +import qualified RIO.Vector as V import Data.Extensible -import qualified Git.Cmd as Git -import Git.Plantation.Data (Problem, Repo, Team) -import qualified Git.Plantation.Data.Team as Team +import qualified Git.Cmd as Git +import Git.Plantation.Data (Problem, Repo, Team) +import qualified Git.Plantation.Data.Team as Team import Git.Plantation.Env -import GitHub.Data.Name (mkName) -import GitHub.Data.Repos (newRepo, newRepoPrivate) -import GitHub.Endpoints.Repos (Auth (..)) -import qualified GitHub.Endpoints.Repos as GitHub -import Shelly hiding (FilePath) +import GitHub.Data.Name (mkName) +import GitHub.Data.Repos (newRepo, newRepoPrivate) +import GitHub.Data.Webhooks (NewRepoWebhook (..), + RepoWebhookEvent (..)) +import GitHub.Endpoints.Repos (Auth (..)) +import qualified GitHub.Endpoints.Repos as GitHub +import qualified GitHub.Endpoints.Repos.Webhooks as GitHub +import Shelly hiding (FilePath) type NewRepoCmd = Record '[ "repo" >: Maybe Text @@ -40,6 +45,8 @@ type NewGitHubRepoCmd = Record RepoCmdFields type InitGitHubRepoCmd = Record RepoCmdFields +type SetupWebhookCmd = Record RepoCmdFields + type InitCICmd = Record RepoCmdFields type ResetRepoCmd = Record RepoCmdFields @@ -61,6 +68,7 @@ createRepo team problem = do info <- Team.lookupRepo problem team `fromJustWithThrow` UndefinedTeamProblem team problem createRepoInGitHub info team problem initRepoInGitHub info team problem + setupWebhook info initProblemCI info team problem createRepoInGitHub :: Repo -> Team -> Problem -> Plant () @@ -103,6 +111,25 @@ initRepoInGitHub info team problem = do errExit False $ Git.branch ["-D", "temp"] logInfo $ "Success: create repo as " <> displayShow github +setupWebhook :: Repo -> Plant () +setupWebhook info = do + (owner, repo) <- splitRepoName <$> repoGithub info + token <- asks (view #token) + webhookUrl <- asks (view #webhook) + logInfo $ "setup github webhook to repo: " <> displayShow (owner <> "/" <> repo) + resp <- liftIO $ GitHub.createRepoWebhook' + (OAuth token) (mkName Proxy owner) (mkName Proxy repo) (webhook webhookUrl) + case resp of + Left err -> logDebug (displayShow err) >> throwIO (SetupWebhookError err info) + Right _ -> logDebug "Success: setup GitHub Webhook to repository" + where + webhook url = NewRepoWebhook + { newRepoWebhookName = "web" + , newRepoWebhookConfig = Map.fromList [("url", url), ("content_type", "json")] + , newRepoWebhookEvents = Just $ V.fromList [WebhookPushEvent] + , newRepoWebhookActive = Just True + } + initProblemCI :: Repo -> Team -> Problem -> Plant () initProblemCI info team problem = do token <- getTextToken diff --git a/src/Git/Plantation/Env.hs b/src/Git/Plantation/Env.hs index 9d7d44c..872f09b 100644 --- a/src/Git/Plantation/Env.hs +++ b/src/Git/Plantation/Env.hs @@ -23,11 +23,12 @@ import Shelly hiding (FilePath) type Plant = RIO Env type Env = Record - '[ "config" >: Config - , "token" >: GitHub.Token - , "work" >: FilePath - , "client" >: Drone.HttpsClient - , "logger" >: LogFunc + '[ "config" >: Config + , "token" >: GitHub.Token + , "work" >: FilePath + , "client" >: Drone.HttpsClient + , "webhook" >: Text + , "logger" >: LogFunc ] instance HasLogFunc Env where @@ -63,6 +64,7 @@ data GitPlantException = UndefinedTeamProblem Team Problem | CreateRepoError GitHub.Error Team Problem | DeleteRepoError GitHub.Error Repo + | SetupWebhookError GitHub.Error Repo | InviteUserError GitHub.Error User Repo | InvalidRepoConfig Repo deriving (Typeable) @@ -83,6 +85,10 @@ instance Show GitPlantException where mkLogMessage' "can't delete repository" (#repo @= repo <: nil) + SetupWebhookError _err repo -> + mkLogMessage' + "can't setup github webhook" + (#repo @= repo <: nil) InviteUserError _err user repo -> mkLogMessage' "can't invite user to repository" From 4f45a976ec8cc6d495fba78bba7f0f3b45d43616 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Tue, 26 Mar 2019 17:44:10 +0900 Subject: [PATCH 37/71] Update change log --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 334e2d1..a8b0aa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,3 +22,11 @@ * `Problem` と 回答リポジトリの対応関係を `id` にした * `provate` 設定の追加 * `org` と `owner` を明示的に指定するように変更 +* 解答リポジトリの生成時に GitHub Webhook の設定をする(#15) +* GitHub Webhook の設定をするコマンドを追加(#15) +* Webhook API の修正(#15) + * 問題の検索周りの処理が間違っていた +* Docker イメージの修正(#15) + * `static` ディレクトリの追加 + * git コマンドの設定を追加 +* `.env` ファイルがなくても動作するように修正(#15) From 746fddbafe2842dac1c158d3c44e61ed6047eda4 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Thu, 28 Mar 2019 02:04:21 +0900 Subject: [PATCH 38/71] Image: use matsubara0507/ubuntu-for-haskell:git --- Dockerfile | 7 ------- stack.yaml | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0c1ffef..c360410 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,4 @@ FROM git-plantation-bin -RUN apt-get update && apt-get install -y \ - ca-certificates \ - git \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* -RUN git config --global user.email "bot@example.com" \ - && git config --global user.name "Bot" WORKDIR /work COPY script /usr/local/bin/ COPY static /work/static diff --git a/stack.yaml b/stack.yaml index 646b6a8..30f8fc2 100644 --- a/stack.yaml +++ b/stack.yaml @@ -16,4 +16,4 @@ docker: image: container: name: git-plantation-bin - base: fpco/ubuntu-with-libgmp + base: matsubara0507/ubuntu-for-haskell:git From c988769fc03e49364af8156beaca70ff98ca6215 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Tue, 2 Apr 2019 17:43:49 +0900 Subject: [PATCH 39/71] Doc: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8b0aa5..d9a9e40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,3 +30,4 @@ * `static` ディレクトリの追加 * git コマンドの設定を追加 * `.env` ファイルがなくても動作するように修正(#15) +* app の base image を変更(#16) From a080e6ad815c50b03b640cbbd0042f11adca83a0 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Tue, 2 Apr 2019 21:29:46 +0900 Subject: [PATCH 40/71] Fix: invite member cmd --- src/Git/Plantation/Cmd/Member.hs | 2 +- stack.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Git/Plantation/Cmd/Member.hs b/src/Git/Plantation/Cmd/Member.hs index 0508bf1..d3922d5 100644 --- a/src/Git/Plantation/Cmd/Member.hs +++ b/src/Git/Plantation/Cmd/Member.hs @@ -40,7 +40,7 @@ inviteUserToRepo user target = do (OAuth token) (mkName Proxy owner) (mkName Proxy repo) - (mkName Proxy github) + (mkName Proxy $ user ^. #github) case resp of Left err -> logDebug (displayShow err) >> throwIO (InviteUserError err user target) Right _ -> logInfo $ display (success github) diff --git a/stack.yaml b/stack.yaml index 30f8fc2..006ccd9 100644 --- a/stack.yaml +++ b/stack.yaml @@ -5,7 +5,7 @@ extra-deps: - extensible-0.5 - servant-github-webhook-0.4.1.0 - github: matsubara0507/github - commit: 08f409a9cbc0ab46a3e68f84ddfb14fc89c3fe7a + commit: 472e10b7bb096fa3f8917a5ab1cb45513f832140 - github: matsubara0507/drone-haskell commit: aa6f5152dd9ea72cb48a32bdc58c91de2bdc21a9 - github: matsubara0507/elm-export From b8084df8347684f8177eea0a7976bc75bbaeac6f Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Tue, 2 Apr 2019 21:31:20 +0900 Subject: [PATCH 41/71] Fix: webhook with pull --- src/Git/Cmd.hs | 3 +++ src/Git/Plantation/Cmd/Repo.hs | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Git/Cmd.hs b/src/Git/Cmd.hs index e2e1528..398e0bd 100644 --- a/src/Git/Cmd.hs +++ b/src/Git/Cmd.hs @@ -11,6 +11,9 @@ clone = command1_ "git" [] "clone" fetch :: [Text] -> Sh () fetch = command1_ "git" [] "fetch" +pull :: [Text] -> Sh () +pull = command1_ "git" [] "pull" + remote :: [Text] -> Sh () remote = command1_ "git" [] "remote" diff --git a/src/Git/Plantation/Cmd/Repo.hs b/src/Git/Plantation/Cmd/Repo.hs index 8c11299..e81bff0 100644 --- a/src/Git/Plantation/Cmd/Repo.hs +++ b/src/Git/Plantation/Cmd/Repo.hs @@ -141,6 +141,7 @@ initProblemCI info team problem = do shelly' $ chdir_p (workDir owner) (Git.cloneOrFetch problemUrl repo) shelly' $ chdir_p (workDir owner repo) $ do Git.checkout [problem ^. #ci_branch] + Git.pull [] Git.existBranch (team ^. #name) >>= \case False -> Git.checkout ["-b", team ^. #name] True -> Git.checkout [team ^. #name] @@ -167,8 +168,8 @@ pushForCI team problem = do problemUrl = mconcat ["https://", token, "@github.com/", owner, "/", repo, ".git"] shelly' $ chdir_p (workDir owner) (Git.cloneOrFetch problemUrl repo) shelly' $ chdir_p (workDir owner repo) $ do - Git.fetch [] Git.checkout [team ^. #name] + Git.pull [] Git.commit ["--allow-empty", "-m", "Empty Commit!!"] Git.push ["origin", team ^. #name] logInfo "Success push" From d88250dbb5eaa51cb140460e679e230818aaaa2f Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Tue, 2 Apr 2019 22:00:21 +0900 Subject: [PATCH 42/71] Modify: work space path --- src/Git/Plantation/Cmd/Repo.hs | 36 ++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/Git/Plantation/Cmd/Repo.hs b/src/Git/Plantation/Cmd/Repo.hs index e81bff0..a41d63d 100644 --- a/src/Git/Plantation/Cmd/Repo.hs +++ b/src/Git/Plantation/Cmd/Repo.hs @@ -25,6 +25,7 @@ import GitHub.Endpoints.Repos (Auth (..)) import qualified GitHub.Endpoints.Repos as GitHub import qualified GitHub.Endpoints.Repos.Webhooks as GitHub import Shelly hiding (FilePath) +import qualified Shelly as S type NewRepoCmd = Record '[ "repo" >: Maybe Text @@ -91,14 +92,14 @@ createRepoInGitHub info team problem = do initRepoInGitHub :: Repo -> Team -> Problem -> Plant () initRepoInGitHub info team problem = do token <- getTextToken - workDir <- asks (view #work) + workDir <- getWorkDir team github <- repoGithub info let (owner, repo) = splitRepoName $ problem ^. #repo teamUrl = mconcat ["https://", token, "@github.com/", github, ".git"] problemUrl = mconcat ["https://", token, "@github.com/", owner, "/", repo, ".git"] - shelly' $ chdir_p (workDir (team ^. #name)) (Git.cloneOrFetch teamUrl repo) - shelly' $ chdir_p (workDir (team ^. #name) repo) $ do + shelly' $ chdir_p workDir (Git.cloneOrFetch teamUrl repo) + shelly' $ chdir_p (workDir repo) $ do Git.existBranch "temp" >>= \case False -> Git.checkout ["-b", "temp"] True -> Git.checkout ["temp"] @@ -133,13 +134,13 @@ setupWebhook info = do initProblemCI :: Repo -> Team -> Problem -> Plant () initProblemCI info team problem = do token <- getTextToken - workDir <- asks (view #work) + workDir <- getWorkDir team github <- repoGithub info let (owner, repo) = splitRepoName $ problem ^. #repo problemUrl = mconcat ["https://", token, "@github.com/", owner, "/", repo, ".git"] - shelly' $ chdir_p (workDir owner) (Git.cloneOrFetch problemUrl repo) - shelly' $ chdir_p (workDir owner repo) $ do + shelly' $ chdir_p workDir (Git.cloneOrFetch problemUrl repo) + shelly' $ chdir_p (workDir repo) $ do Git.checkout [problem ^. #ci_branch] Git.pull [] Git.existBranch (team ^. #name) >>= \case @@ -153,21 +154,21 @@ initProblemCI info team problem = do resetRepo :: Repo -> Team -> Problem -> Plant () resetRepo info team problem = do - workDir <- asks (view #work) + workDir <- getWorkDir team let (_, repo) = splitRepoName $ problem ^. #repo - paths <- shelly' $ chdir_p (workDir (team ^. #name) repo) $ ls "." + paths <- shelly' $ chdir_p (workDir repo) $ ls "." logDebug $ "Remove file: " <> display (Text.intercalate " " $ map toTextIgnore paths) - shelly' $ chdir_p (workDir (team ^. #name)) $ rm_rf (fromText repo) + shelly' $ chdir_p workDir $ rm_rf (fromText repo) initRepoInGitHub info team problem pushForCI :: Team -> Problem -> Plant () pushForCI team problem = do token <- getTextToken - workDir <- asks (view #work) + workDir <- getWorkDir team let (owner, repo) = splitRepoName $ problem ^. #repo problemUrl = mconcat ["https://", token, "@github.com/", owner, "/", repo, ".git"] - shelly' $ chdir_p (workDir owner) (Git.cloneOrFetch problemUrl repo) - shelly' $ chdir_p (workDir owner repo) $ do + shelly' $ chdir_p workDir (Git.cloneOrFetch problemUrl repo) + shelly' $ chdir_p (workDir repo) $ do Git.checkout [team ^. #name] Git.pull [] Git.commit ["--allow-empty", "-m", "Empty Commit!!"] @@ -197,12 +198,12 @@ deleteRepoInGithub info = do deleteProblemCI :: Team -> Problem -> Plant () deleteProblemCI team problem = do token <- getTextToken - workDir <- asks (view #work) + workDir <- getWorkDir team let (owner, repo) = splitRepoName $ problem ^. #repo problemUrl = mconcat ["https://", token, "@github.com/", owner, "/", repo, ".git"] - shelly' $ chdir_p (workDir owner) (Git.cloneOrFetch problemUrl repo) - shelly' $ chdir_p (workDir owner repo) $ do + shelly' $ chdir_p workDir (Git.cloneOrFetch problemUrl repo) + shelly' $ chdir_p (workDir repo) $ do errExit False $ Git.push [ "--delete", "origin", team ^. #name] logInfo $ "Success: delete ci branch in " <> displayShow (problem ^. #repo) @@ -225,3 +226,8 @@ repoGithub repo = ciFileName :: IsString s => s ciFileName = "REPOSITORY" + +getWorkDir :: Team -> Plant S.FilePath +getWorkDir team = do + base <- asks (view #work) + pure $ base (team ^. #name) From e2a5e68baab4d30baa438a47a60b8364cd55256d Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Tue, 2 Apr 2019 22:27:45 +0900 Subject: [PATCH 43/71] Fix: webhook config --- .env.template | 3 +-- exec/app/Main.hs | 2 +- src/Git/Plantation/Cmd.hs | 11 +++++------ src/Git/Plantation/Cmd/Repo.hs | 8 ++++---- src/Git/Plantation/Env.hs | 11 ++++++++++- 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/.env.template b/.env.template index 9795d08..f2405e3 100644 --- a/.env.template +++ b/.env.template @@ -6,5 +6,4 @@ DRONE_PORT= DRONE_TOKEN= GH_TOKEN= GH_SECRET= -APP_HOST= -APP_PORT= +APP_SERVER= diff --git a/exec/app/Main.hs b/exec/app/Main.hs index 695cb9c..2d941b0 100644 --- a/exec/app/Main.hs +++ b/exec/app/Main.hs @@ -75,7 +75,7 @@ runServer opts config = do <: #token @= token <: #work @= (opts ^. #work) <: #client @= Drone.HttpsClient client - <: #webhook @= "" + <: #webhook @= mempty <: #logger @= logger <: nil :: Env B.putStr $ "Listening on port " <> (fromString . show) (opts ^. #port) <> "\n" diff --git a/src/Git/Plantation/Cmd.hs b/src/Git/Plantation/Cmd.hs index 43f6a16..692c728 100644 --- a/src/Git/Plantation/Cmd.hs +++ b/src/Git/Plantation/Cmd.hs @@ -9,7 +9,6 @@ module Git.Plantation.Cmd ) where import RIO -import qualified RIO.Text as Text import Data.Extensible import qualified Drone.Client as Drone @@ -18,23 +17,23 @@ import Git.Plantation.Cmd.Options as X import Git.Plantation.Cmd.Repo as X import Git.Plantation.Cmd.Run as X import Git.Plantation.Config (readConfig) -import System.Environment (getEnv, lookupEnv) +import Git.Plantation.Env (mkWebhookConf) +import System.Environment (getEnv) run :: MonadUnliftIO m => Options -> m () run opts = do config <- readConfig (opts ^. #config) logOpts <- logOptionsHandle stdout (opts ^. #verbose) token <- liftIO $ fromString <$> getEnv "GH_TOKEN" - appHost <- liftIO $ fromString <$> getEnv "APP_HOST" - appPort <- liftIO $ maybe "" fromString <$> lookupEnv "APP_PORT" + secret <- liftIO $ fromString <$> getEnv "GH_SECRET" + appUrl <- liftIO $ fromString <$> getEnv "APP_SERVER" withLogFunc logOpts $ \logger -> do let client = #host @= "" <: #port @= Nothing <: #token @= "" <: nil - webhookUrl = mconcat ["http://", appHost, if Text.null appPort then "" else ":", appPort, "/api"] env = #config @= config <: #token @= token <: #work @= opts ^. #work <: #client @= Drone.HttpsClient client - <: #webhook @= webhookUrl + <: #webhook @= mkWebhookConf (appUrl <> "/hook") secret <: #logger @= logger <: nil runRIO env $ matchField diff --git a/src/Git/Plantation/Cmd/Repo.hs b/src/Git/Plantation/Cmd/Repo.hs index a41d63d..3de2f03 100644 --- a/src/Git/Plantation/Cmd/Repo.hs +++ b/src/Git/Plantation/Cmd/Repo.hs @@ -116,17 +116,17 @@ setupWebhook :: Repo -> Plant () setupWebhook info = do (owner, repo) <- splitRepoName <$> repoGithub info token <- asks (view #token) - webhookUrl <- asks (view #webhook) + webhookConfig <- asks (view #webhook) logInfo $ "setup github webhook to repo: " <> displayShow (owner <> "/" <> repo) resp <- liftIO $ GitHub.createRepoWebhook' - (OAuth token) (mkName Proxy owner) (mkName Proxy repo) (webhook webhookUrl) + (OAuth token) (mkName Proxy owner) (mkName Proxy repo) (webhook webhookConfig) case resp of Left err -> logDebug (displayShow err) >> throwIO (SetupWebhookError err info) Right _ -> logDebug "Success: setup GitHub Webhook to repository" where - webhook url = NewRepoWebhook + webhook conf = NewRepoWebhook { newRepoWebhookName = "web" - , newRepoWebhookConfig = Map.fromList [("url", url), ("content_type", "json")] + , newRepoWebhookConfig = Map.fromList conf , newRepoWebhookEvents = Just $ V.fromList [WebhookPushEvent] , newRepoWebhookActive = Just True } diff --git a/src/Git/Plantation/Env.hs b/src/Git/Plantation/Env.hs index 872f09b..235d631 100644 --- a/src/Git/Plantation/Env.hs +++ b/src/Git/Plantation/Env.hs @@ -27,10 +27,19 @@ type Env = Record , "token" >: GitHub.Token , "work" >: FilePath , "client" >: Drone.HttpsClient - , "webhook" >: Text + , "webhook" >: WebhookConfig , "logger" >: LogFunc ] +type WebhookConfig = [(Text, Text)] + +mkWebhookConf :: Text -> Text -> WebhookConfig +mkWebhookConf url secret = + [ ("url", url) + , ("content_type", "json") + , ("secret", secret) + ] + instance HasLogFunc Env where logFuncL = lens (view #logger) (\x y -> x & #logger `set` y) From 7ed804a60cff4e71814a1158c59b0ac99d490a25 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Tue, 2 Apr 2019 22:45:34 +0900 Subject: [PATCH 44/71] Modify: repo cmd arg --- exec/tool/Main.hs | 14 +++++++------- src/Git/Plantation/Cmd/Options.hs | 12 ++++++------ src/Git/Plantation/Cmd/Repo.hs | 14 +++++++------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/exec/tool/Main.hs b/exec/tool/Main.hs index 57463d1..77e67be 100644 --- a/exec/tool/Main.hs +++ b/exec/tool/Main.hs @@ -54,14 +54,14 @@ subcmdParser = variantFrom newRepoCmdParser :: Parser NewRepoCmd newRepoCmdParser = hsequence - $ #repo <@=> option (Just <$> str) (long "repo" <> value Nothing <> metavar "TEXT" <> help "Sets reopsitory that wont to controll.") - <: #team <@=> strArgument (metavar "TEXT" <> help "Sets team that wont to controll.") + $ #repo <@=> option (Just <$> auto) (long "repo" <> value Nothing <> metavar "ID" <> help "Sets reopsitory that want to controll by problem id.") + <: #team <@=> strArgument (metavar "TEXT" <> help "Sets team that want to controll.") <: nil singleRepoCmdParser :: Parser (Record RepoCmdFields) singleRepoCmdParser = hsequence - $ #repo <@=> strOption (long "repo" <> metavar "TEXT" <> help "Sets reopsitory that wont to controll.") - <: #team <@=> strArgument (metavar "TEXT" <> help "Sets team that wont to controll.") + $ #repo <@=> option auto (long "repo" <> metavar "ID" <> help "Sets reopsitory that want to controll by problem id.") + <: #team <@=> strArgument (metavar "TEXT" <> help "Sets team that want to controll.") <: nil deleteRepoCmdParser :: Parser DeleteRepoCmd @@ -69,9 +69,9 @@ deleteRepoCmdParser = newRepoCmdParser inviteMemberCmdParser :: Parser InviteMemberCmd inviteMemberCmdParser = hsequence - $ #team <@=> strArgument (metavar "TEXT" <> help "Sets team that wont to controll.") - <: #repo <@=> option (Just <$> auto) (long "repo" <> value Nothing <> metavar "ID" <> help "Sets reopsitory by problem id that wont to controll.") - <: #user <@=> option (Just <$> str) (long "user" <> value Nothing <> metavar "TEXT" <> help "Sets user that wont to controll.") + $ #team <@=> strArgument (metavar "TEXT" <> help "Sets team that want to controll.") + <: #repo <@=> option (Just <$> auto) (long "repo" <> value Nothing <> metavar "ID" <> help "Sets reopsitory that want to controll by problem id.") + <: #user <@=> option (Just <$> str) (long "user" <> value Nothing <> metavar "TEXT" <> help "Sets user that want to controll.") <: nil variantFrom :: diff --git a/src/Git/Plantation/Cmd/Options.hs b/src/Git/Plantation/Cmd/Options.hs index 5a112ee..aacd7c3 100644 --- a/src/Git/Plantation/Cmd/Options.hs +++ b/src/Git/Plantation/Cmd/Options.hs @@ -50,9 +50,9 @@ instance Run ("new_repo" >: NewRepoCmd) where conf <- asks (view #config) let team = L.find (\t -> t ^. #name == args ^. #team) $ conf ^. #teams case (team, args ^. #repo) of - (Nothing, _) -> logError $ "team is not found: " <> display (args ^. #team) - (Just team', Just name) -> actByRepoName createRepo team' name - (Just team', _) -> forM_ (conf ^. #problems) (tryAnyWithLogError . createRepo team') + (Nothing, _) -> logError $ "team is not found: " <> display (args ^. #team) + (Just team', Just pid) -> actByProblemId createRepo team' pid + (Just team', _) -> forM_ (conf ^. #problems) (tryAnyWithLogError . createRepo team') instance Run ("new_github_repo" >: NewGitHubRepoCmd) where run' _ = runRepoCmd $ \team problem -> do @@ -84,8 +84,8 @@ runRepoCmd act args = do conf <- asks (view #config) let team = L.find (\t -> t ^. #name == args ^. #team) $ conf ^. #teams case (team, args ^. #repo) of - (Nothing, _) -> logError $ "team is not found: " <> display (args ^. #team) - (Just team', name) -> actByRepoName act team' name + (Nothing, _) -> logError $ "team is not found: " <> display (args ^. #team) + (Just team', pid) -> actByProblemId act team' pid instance Run ("delete_repo" >: DeleteRepoCmd) where run' _ args = do @@ -93,7 +93,7 @@ instance Run ("delete_repo" >: DeleteRepoCmd) where let team = L.find (\t -> t ^. #name == args ^. #team) $ conf ^. #teams case (team, args ^. #repo) of (Nothing, _) -> logError $ "team is not found: " <> display (args ^. #team) - (Just team', Just name) -> actByRepoName deleteRepo team' name + (Just team', Just pid) -> actByProblemId deleteRepo team' pid (Just team', _) -> forM_ (conf ^. #problems) (tryAnyWithLogError . deleteRepo team') instance Run ("invite_member" >: InviteMemberCmd) where diff --git a/src/Git/Plantation/Cmd/Repo.hs b/src/Git/Plantation/Cmd/Repo.hs index 3de2f03..d6a70f3 100644 --- a/src/Git/Plantation/Cmd/Repo.hs +++ b/src/Git/Plantation/Cmd/Repo.hs @@ -28,17 +28,17 @@ import Shelly hiding (FilePath) import qualified Shelly as S type NewRepoCmd = Record - '[ "repo" >: Maybe Text + '[ "repo" >: Maybe Int , "team" >: Text ] type DeleteRepoCmd = Record - '[ "repo" >: Maybe Text + '[ "repo" >: Maybe Int , "team" >: Text ] type RepoCmdFields = - '[ "repo" >: Text + '[ "repo" >: Int , "team" >: Text ] @@ -52,12 +52,12 @@ type InitCICmd = Record RepoCmdFields type ResetRepoCmd = Record RepoCmdFields -actByRepoName :: (Team -> Problem -> Plant a) -> Team -> Text -> Plant () -actByRepoName act team repoName = do +actByProblemId :: (Team -> Problem -> Plant a) -> Team -> Int -> Plant () +actByProblemId act team pid = do conf <- asks (view #config) - let problem = L.find (\p -> p ^. #repo == repoName) $ conf ^. #problems + let problem = L.find (\p -> p ^. #id == pid) $ conf ^. #problems case problem of - Nothing -> logError $ "repo is not found: " <> display repoName + Nothing -> logError $ "repo is not found by problem id: " <> display pid Just problem' -> tryAnyWithLogError $ act team problem' createRepo :: Team -> Problem -> Plant () From 33dd1f877cebfc0a40fab303bcb480ffd492d6e7 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Tue, 2 Apr 2019 23:46:31 +0900 Subject: [PATCH 45/71] Feat: run ci if push answer branch --- config/.git-plantation.yaml | 3 +++ elm-src/Generated/API.elm | 3 +++ src/Git/Plantation/API/Webhook.hs | 5 ++++- src/Git/Plantation/Data/Problem.hs | 1 + 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/config/.git-plantation.yaml b/config/.git-plantation.yaml index 43edd50..4ee1b86 100644 --- a/config/.git-plantation.yaml +++ b/config/.git-plantation.yaml @@ -8,6 +8,7 @@ problems: - master - task-1 - task-2 + answer_branch: master ci_branch: ci - id: 2 @@ -17,6 +18,7 @@ problems: challenge_branches: - readme - master + answer_branch: master ci_branch: ci - id: 3 @@ -27,6 +29,7 @@ problems: - readme - master - checker + answer_branch: master ci_branch: ci teams: diff --git a/elm-src/Generated/API.elm b/elm-src/Generated/API.elm index 7f85318..6dd15a1 100644 --- a/elm-src/Generated/API.elm +++ b/elm-src/Generated/API.elm @@ -91,6 +91,7 @@ type alias Problem = , repo : String , difficulty : Int , challenge_branches : List String + , answer_branch : String , ci_branch : String } @@ -103,6 +104,7 @@ decodeProblem = |> required "repo" string |> required "difficulty" int |> required "challenge_branches" (list string) + |> required "answer_branch" string |> required "ci_branch" string @@ -114,6 +116,7 @@ encodeProblem x = , ( "repo", Json.Encode.string x.repo ) , ( "difficulty", Json.Encode.int x.difficulty ) , ( "challenge_branches", Json.Encode.list Json.Encode.string x.challenge_branches ) + , ( "answer_branch", Json.Encode.string x.answer_branch ) , ( "ci_branch", Json.Encode.string x.ci_branch ) ] diff --git a/src/Git/Plantation/API/Webhook.hs b/src/Git/Plantation/API/Webhook.hs index 4017317..3e77911 100644 --- a/src/Git/Plantation/API/Webhook.hs +++ b/src/Git/Plantation/API/Webhook.hs @@ -41,7 +41,10 @@ findByPushEvent :: PushEvent -> [Team] -> [Problem] -> Maybe (Team, Problem) findByPushEvent ev teams problems = do (team, repo) <- join $ L.find isJust repos problem <- L.find (\p -> p ^. #id == repo ^. #problem) problems - pure (team, problem) + if evPushRef ev == "refs/heads/" <> problem ^. #answer_branch then + pure (team, problem) + else + Nothing where repos = map (\t -> (t,) <$> Team.lookupRepoByGithub repoName t) teams repoName = whRepoFullName $ evPushRepository ev diff --git a/src/Git/Plantation/Data/Problem.hs b/src/Git/Plantation/Data/Problem.hs index 1c6b514..c8f1e6c 100644 --- a/src/Git/Plantation/Data/Problem.hs +++ b/src/Git/Plantation/Data/Problem.hs @@ -15,6 +15,7 @@ type Problem = Record , "repo" >: Text , "difficulty" >: Int , "challenge_branches" >: [Branch] + , "answer_branch" >: Branch , "ci_branch" >: Branch ] From 86dc7cdae9da2cdb60781ce24ffb229e572f399c Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Tue, 2 Apr 2019 23:57:39 +0900 Subject: [PATCH 46/71] Doc: update README --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 078e0de..99ac818 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ ref. `.env.template` - `DRONE_TOKEN` は Drone CI のトークン - `GH_TOKEN` は GitHub のトークン - `GH_SECRET` は GitHub Webhook のシークレットキー +- `APP_SERVER` は `git-plantation-app` が動作してる URL (例: `https://example.com`) ### 3. Create team's repository in team @@ -55,7 +56,7 @@ $ stack exec -- git-plantation-tool -c .git-plantation.yaml --work .temp new_rep run app: ``` -$ stack exec -- git-plantation-app --port 8080 --work ".temp" --verbose example/config.yaml +$ stack exec -- git-plantation-app --port 8080 --work ".temp" --verbose .git-plantation.yaml ``` ## Build with Docker @@ -63,9 +64,10 @@ $ stack exec -- git-plantation-app --port 8080 --work ".temp" --verbose example/ Define environment to `.env` from `.env.template`. ``` +$ stack test # ganerate elm code $ stack docker pull $ stack --docker --no-terminal build -j 1 Cabal # if `out of memory` -$ stack test --docker +$ stack --docker image container $ docker build -t git-plantation . $ docker run --rm -it -v `pwd`/config.yaml:/work/config.yaml -p 8080:8080 --env-file .env git-plantation ``` From e2cc3100e1a93941794166e7c5c752ed78c0c8ee Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Tue, 2 Apr 2019 23:57:51 +0900 Subject: [PATCH 47/71] Doc: update change log --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9a9e40..ebdc482 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,3 +31,17 @@ * git コマンドの設定を追加 * `.env` ファイルがなくても動作するように修正(#15) * app の base image を変更(#16) +* `invite_member` コマンドの修正(#17) + * 変数の指定間違い + * 失敗時にエラーを返すように(`github` パッケージから修正) +* `setup_webhook` コマンドの GitHub Webhook の設定の仕方を修正(#17) + * `APP_HOST` や `APP_PORT` を `APP_SERVER` 環境変数に変更して URL を修正 + * Secret を追加 (`GH_SECRET`) +* 各コマンドで `git checkout` の前に `git pull` をするように修正(#17) +* リポジトリ系のコマンドの `--repo` 引数を `problem.id` に変更(#17) +* work space をチームごとに区切るように修正(#17) + * 別々のチームから同じ問題に対し同時にプッシュが来ても問題ないようになった + * 同じチームから同じ問題で別々のブランチなどに対し同時にプッシュが来たらおそらくまずい +* webhook の時に `ci` ブランチにプッシュするのを `answer_branch` だけに限定(#17) + * これで「同じチームから同じ問題で別々のブランチなどに対し同時にプッシュ」も平気 + * 一つのブランチでしか動作しないので From 7641acbce553bf67878b09da38b07810fcafceb7 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Wed, 3 Apr 2019 01:19:51 +0900 Subject: [PATCH 48/71] Fix: docker-compose for drone --- drone/.env.template | 1 + drone/docker-compose.yml | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drone/.env.template b/drone/.env.template index 9137780..0f391ac 100644 --- a/drone/.env.template +++ b/drone/.env.template @@ -2,3 +2,4 @@ DRONE_HOST= DRONE_GITHUB_CLIENT= DRONE_GITHUB_SECRET= DRONE_SECRET= +HOSTNAME= diff --git a/drone/docker-compose.yml b/drone/docker-compose.yml index 11ed681..589eb5f 100644 --- a/drone/docker-compose.yml +++ b/drone/docker-compose.yml @@ -1,12 +1,11 @@ version: '2' services: - drone-server: - image: drone/drone:1.0.0-rc.5 + drone: + image: drone/drone:1 ports: - 8000:80 - 443:443 - - 9000 volumes: - /var/run/docker.sock:/var/run/docker.sock - .:/data @@ -15,20 +14,21 @@ services: - DRONE_GITHUB_SERVER=https://github.com - DRONE_GITHUB_CLIENT_ID=${DRONE_GITHUB_CLIENT} - DRONE_GITHUB_CLIENT_SECRET=${DRONE_GITHUB_SECRET} + - DRONE_AGENTS_ENABLED=true - DRONE_RPC_SECRET=${DRONE_SECRET} - DRONE_SERVER_HOST=${DRONE_HOST} - DRONE_SERVER_PROTO=https - DRONE_TLS_AUTOCERT=true - drone-agent: - image: drone/agent:1.0.0-rc.5 - command: agent + agent: + image: drone/agent:1 restart: always depends_on: - - drone-server + - drone volumes: - /var/run/docker.sock:/var/run/docker.sock environment: - - DRONE_RPC_SERVER=drone-server:9000 + - DRONE_RPC_SERVER=http://drone - DRONE_RPC_SECRET=${DRONE_SECRET} - DRONE_RUNNER_CAPACITY=2 + - DRONE_RUNNER_NAME=${HOSTNAME} From 4be61d059f2e6505f7dbece1dd1d3f3f563a18ea Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Wed, 3 Apr 2019 01:18:04 +0900 Subject: [PATCH 49/71] Feat: pending status in scoreboard --- elm-src/Generated/API.elm | 3 +++ elm-src/Main.elm | 11 +++++++---- src/Git/Plantation/API/CRUD.hs | 1 + src/Git/Plantation/Score.hs | 1 + 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/elm-src/Generated/API.elm b/elm-src/Generated/API.elm index 6dd15a1..6d09dcd 100644 --- a/elm-src/Generated/API.elm +++ b/elm-src/Generated/API.elm @@ -169,6 +169,7 @@ encodeScore x = type alias Status = { problem : String , correct : Bool + , pending : Bool } @@ -177,6 +178,7 @@ decodeStatus = Json.Decode.succeed Status |> required "problem" string |> required "correct" bool + |> required "pending" bool encodeStatus : Status -> Json.Encode.Value @@ -184,6 +186,7 @@ encodeStatus x = Json.Encode.object [ ( "problem", Json.Encode.string x.problem ) , ( "correct", Json.Encode.bool x.correct ) + , ( "pending", Json.Encode.bool x.pending ) ] diff --git a/elm-src/Main.elm b/elm-src/Main.elm index bbbce0d..843a09d 100644 --- a/elm-src/Main.elm +++ b/elm-src/Main.elm @@ -156,14 +156,17 @@ viewStatus stats problem = statBadge : Maybe API.Status -> Html msg statBadge status = - case Maybe.map .correct status of - Nothing -> + case ( Maybe.map .correct status, Maybe.map .pending status ) of + ( Nothing, _ ) -> span [ class "Label Label--gray-darker" ] [ text "未提出" ] - Just False -> + ( _, Just True ) -> + span [ class "Label bg-yellow" ] [ text "採点中" ] + + ( Just False, _ ) -> span [ class "Label bg-red" ] [ text "不正解" ] - Just True -> + ( Just True, _ ) -> span [ class "Label bg-green" ] [ text "正解" ] diff --git a/src/Git/Plantation/API/CRUD.hs b/src/Git/Plantation/API/CRUD.hs index 789ed6c..b5ad48c 100644 --- a/src/Git/Plantation/API/CRUD.hs +++ b/src/Git/Plantation/API/CRUD.hs @@ -73,6 +73,7 @@ toStatus :: Text -> [Drone.Build] -> Status toStatus name builds = #problem @= name <: #correct @= any (\b -> b ^. #status == "success") builds + <: #pending @= any (\b -> b ^. #status == "pending") builds <: nil toPoint :: [Status] -> Problem -> Int diff --git a/src/Git/Plantation/Score.hs b/src/Git/Plantation/Score.hs index 7958b56..2b390fe 100644 --- a/src/Git/Plantation/Score.hs +++ b/src/Git/Plantation/Score.hs @@ -19,6 +19,7 @@ type Score = Record type Status = Record '[ "problem" >: Text , "correct" >: Bool + , "pending" >: Bool ] instance ElmType Score where From ee451ffc69fae0800e1db800360484a8ad3192c0 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Wed, 3 Apr 2019 01:33:07 +0900 Subject: [PATCH 50/71] Doc: update change log --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebdc482..4d291e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,3 +45,4 @@ * webhook の時に `ci` ブランチにプッシュするのを `answer_branch` だけに限定(#17) * これで「同じチームから同じ問題で別々のブランチなどに対し同時にプッシュ」も平気 * 一つのブランチでしか動作しないので +* スコアボードに「採点中」を追加(#19) From f151309a5770894ea20b19d0a409fd445ade5e32 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Wed, 3 Apr 2019 11:25:21 +0900 Subject: [PATCH 51/71] Fix: invite cmd with 201 --- stack.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stack.yaml b/stack.yaml index 006ccd9..c329e89 100644 --- a/stack.yaml +++ b/stack.yaml @@ -5,7 +5,7 @@ extra-deps: - extensible-0.5 - servant-github-webhook-0.4.1.0 - github: matsubara0507/github - commit: 472e10b7bb096fa3f8917a5ab1cb45513f832140 + commit: b0c8d71701a42bff143b33ac1067f5818a9804ce - github: matsubara0507/drone-haskell commit: aa6f5152dd9ea72cb48a32bdc58c91de2bdc21a9 - github: matsubara0507/elm-export From c2381f81bf6736aef6852868280f0687418da322 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Wed, 3 Apr 2019 11:25:55 +0900 Subject: [PATCH 52/71] Fix: new repo cmd with clone name --- src/Git/Plantation/Cmd/Repo.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Git/Plantation/Cmd/Repo.hs b/src/Git/Plantation/Cmd/Repo.hs index d6a70f3..187294d 100644 --- a/src/Git/Plantation/Cmd/Repo.hs +++ b/src/Git/Plantation/Cmd/Repo.hs @@ -95,11 +95,12 @@ initRepoInGitHub info team problem = do workDir <- getWorkDir team github <- repoGithub info let (owner, repo) = splitRepoName $ problem ^. #repo + (_, teamRepo) = splitRepoName github teamUrl = mconcat ["https://", token, "@github.com/", github, ".git"] problemUrl = mconcat ["https://", token, "@github.com/", owner, "/", repo, ".git"] - shelly' $ chdir_p workDir (Git.cloneOrFetch teamUrl repo) - shelly' $ chdir_p (workDir repo) $ do + shelly' $ chdir_p workDir (Git.cloneOrFetch teamUrl teamRepo) + shelly' $ chdir_p (workDir teamRepo) $ do Git.existBranch "temp" >>= \case False -> Git.checkout ["-b", "temp"] True -> Git.checkout ["temp"] @@ -143,13 +144,12 @@ initProblemCI info team problem = do shelly' $ chdir_p (workDir repo) $ do Git.checkout [problem ^. #ci_branch] Git.pull [] - Git.existBranch (team ^. #name) >>= \case - False -> Git.checkout ["-b", team ^. #name] - True -> Git.checkout [team ^. #name] + errExit False $ Git.branch ["-D", team ^. #name] + Git.checkout ["-b", team ^. #name] writefile ciFileName github Git.add [ciFileName] Git.commit ["-m", "[CI SKIP] Add ci branch"] - Git.push ["-u", "origin", team ^. #name] + Git.push ["-f", "-u", "origin", team ^. #name] logInfo $ "Success: create ci branch in " <> displayShow (problem ^. #repo) resetRepo :: Repo -> Team -> Problem -> Plant () From 8efb007b4032e45dd7ae49214eebe554ad38792d Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Wed, 3 Apr 2019 14:50:32 +0900 Subject: [PATCH 53/71] Fix score in scoreboard --- src/Git/Plantation/API/CRUD.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Git/Plantation/API/CRUD.hs b/src/Git/Plantation/API/CRUD.hs index b5ad48c..8afdbcf 100644 --- a/src/Git/Plantation/API/CRUD.hs +++ b/src/Git/Plantation/API/CRUD.hs @@ -6,6 +6,7 @@ module Git.Plantation.API.CRUD where import RIO +import qualified RIO.List as L import qualified RIO.Map as Map import Data.Default.Class @@ -78,7 +79,6 @@ toStatus name builds toPoint :: [Status] -> Problem -> Int toPoint stats problem = - if elem (problem ^. #name) $ map (view #problem) stats then - problem ^. #difficulty - else - 0 + case L.find (\s -> s ^. #problem == problem ^. #name) stats of + Just s | s ^. #correct -> problem ^. #difficulty + _ -> 0 From 8de80a2b395894dda43f3774203ab33aacc201b9 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Wed, 3 Apr 2019 14:50:49 +0900 Subject: [PATCH 54/71] Modify repos arg --- exec/tool/Main.hs | 17 +++++++++++------ src/Git/Plantation/Cmd/Member.hs | 15 ++++++++------- src/Git/Plantation/Cmd/Options.hs | 16 ++++++++-------- src/Git/Plantation/Cmd/Repo.hs | 8 ++++---- 4 files changed, 31 insertions(+), 25 deletions(-) diff --git a/exec/tool/Main.hs b/exec/tool/Main.hs index 77e67be..264b7e4 100644 --- a/exec/tool/Main.hs +++ b/exec/tool/Main.hs @@ -18,7 +18,7 @@ import Data.Extensible import Data.Version (Version) import qualified Data.Version as Version import Development.GitRev -import GHC.TypeLits +import GHC.TypeLits hiding (Mod) import Git.Plantation.Cmd import Options.Applicative @@ -54,8 +54,8 @@ subcmdParser = variantFrom newRepoCmdParser :: Parser NewRepoCmd newRepoCmdParser = hsequence - $ #repo <@=> option (Just <$> auto) (long "repo" <> value Nothing <> metavar "ID" <> help "Sets reopsitory that want to controll by problem id.") - <: #team <@=> strArgument (metavar "TEXT" <> help "Sets team that want to controll.") + $ #repos <@=> option comma (long "repos" <> value [] <> metavar "IDS" <> help "Sets reopsitory that want to controll by problem id.") + <: #team <@=> strArgument (metavar "TEXT" <> help "Sets team that want to controll.") <: nil singleRepoCmdParser :: Parser (Record RepoCmdFields) @@ -69,9 +69,9 @@ deleteRepoCmdParser = newRepoCmdParser inviteMemberCmdParser :: Parser InviteMemberCmd inviteMemberCmdParser = hsequence - $ #team <@=> strArgument (metavar "TEXT" <> help "Sets team that want to controll.") - <: #repo <@=> option (Just <$> auto) (long "repo" <> value Nothing <> metavar "ID" <> help "Sets reopsitory that want to controll by problem id.") - <: #user <@=> option (Just <$> str) (long "user" <> value Nothing <> metavar "TEXT" <> help "Sets user that want to controll.") + $ #team <@=> strArgument (metavar "TEXT" <> help "Sets team that want to controll.") + <: #repos <@=> option comma (long "repos" <> value [] <> metavar "ID" <> help "Sets reopsitory that want to controll by problem id.") + <: #user <@=> option (Just <$> str) (long "user" <> value Nothing <> metavar "TEXT" <> help "Sets user that want to controll.") <: nil variantFrom :: @@ -86,6 +86,11 @@ instance Wrapper ParserInfo where type Repr ParserInfo a = ParserInfo a _Wrapper = id +-- | +-- support `--hoge 1,2,3` +comma :: Read a => ReadM [a] +comma = maybeReader (\s -> readMaybe $ "[" ++ s ++ "]") + withInfo :: Parser a -> String -> ParserInfo a withInfo opts = info (helper <*> opts) . progDesc diff --git a/src/Git/Plantation/Cmd/Member.hs b/src/Git/Plantation/Cmd/Member.hs index d3922d5..8ac656d 100644 --- a/src/Git/Plantation/Cmd/Member.hs +++ b/src/Git/Plantation/Cmd/Member.hs @@ -18,17 +18,18 @@ import GitHub.Endpoints.Repos (Auth (..)) import qualified GitHub.Endpoints.Repos.Collaborators as GitHub type InviteMemberCmd = Record - '[ "team" >: Text - , "repo" >: Maybe Int - , "user" >: Maybe Text + '[ "team" >: Text + , "repos" >: [Int] + , "user" >: Maybe Text ] inviteMember :: InviteMemberCmd -> Team -> Plant () -inviteMember args team = - forM_ repos $ \repo -> forM_ member $ \user -> - tryAnyWithLogError $ inviteUserToRepo user repo +inviteMember args team = case args ^. #repos of + [] -> forM_ (team ^. #repos) $ \repo -> forM_ member $ invite repo + _ -> forM_ repos $ \repo -> forM_ member $ invite repo where - repos = maybe (team ^. #repos) (: []) $ flip lookupRepoByProblemId team =<< args ^. #repo + invite repo user = tryAnyWithLogError $ inviteUserToRepo user repo + repos = catMaybes $ flip lookupRepoByProblemId team <$> args ^. #repos member = maybe (team ^. #member) (: []) $ flip lookupUser team =<< args ^. #user inviteUserToRepo :: User -> Repo -> Plant () diff --git a/src/Git/Plantation/Cmd/Options.hs b/src/Git/Plantation/Cmd/Options.hs index aacd7c3..22ed130 100644 --- a/src/Git/Plantation/Cmd/Options.hs +++ b/src/Git/Plantation/Cmd/Options.hs @@ -49,10 +49,10 @@ instance Run ("new_repo" >: NewRepoCmd) where run' _ args = do conf <- asks (view #config) let team = L.find (\t -> t ^. #name == args ^. #team) $ conf ^. #teams - case (team, args ^. #repo) of - (Nothing, _) -> logError $ "team is not found: " <> display (args ^. #team) - (Just team', Just pid) -> actByProblemId createRepo team' pid - (Just team', _) -> forM_ (conf ^. #problems) (tryAnyWithLogError . createRepo team') + case (team, args ^. #repos) of + (Nothing, _) -> logError $ "team is not found: " <> display (args ^. #team) + (Just team', []) -> forM_ (conf ^. #problems) (tryAnyWithLogError . createRepo team') + (Just team', pids) -> forM_ pids $ actByProblemId createRepo team' instance Run ("new_github_repo" >: NewGitHubRepoCmd) where run' _ = runRepoCmd $ \team problem -> do @@ -91,10 +91,10 @@ instance Run ("delete_repo" >: DeleteRepoCmd) where run' _ args = do conf <- asks (view #config) let team = L.find (\t -> t ^. #name == args ^. #team) $ conf ^. #teams - case (team, args ^. #repo) of - (Nothing, _) -> logError $ "team is not found: " <> display (args ^. #team) - (Just team', Just pid) -> actByProblemId deleteRepo team' pid - (Just team', _) -> forM_ (conf ^. #problems) (tryAnyWithLogError . deleteRepo team') + case (team, args ^. #repos) of + (Nothing, _) -> logError $ "team is not found: " <> display (args ^. #team) + (Just team', []) -> forM_ (conf ^. #problems) (tryAnyWithLogError . deleteRepo team') + (Just team', pids) -> forM_ pids $ actByProblemId deleteRepo team' instance Run ("invite_member" >: InviteMemberCmd) where run' _ args = do diff --git a/src/Git/Plantation/Cmd/Repo.hs b/src/Git/Plantation/Cmd/Repo.hs index 187294d..01c4b1a 100644 --- a/src/Git/Plantation/Cmd/Repo.hs +++ b/src/Git/Plantation/Cmd/Repo.hs @@ -28,13 +28,13 @@ import Shelly hiding (FilePath) import qualified Shelly as S type NewRepoCmd = Record - '[ "repo" >: Maybe Int - , "team" >: Text + '[ "repos" >: [Int] + , "team" >: Text ] type DeleteRepoCmd = Record - '[ "repo" >: Maybe Int - , "team" >: Text + '[ "repos" >: [Int] + , "team" >: Text ] type RepoCmdFields = From f64549ec39b39981e9084af365c33fc6aa3b3e0f Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Wed, 3 Apr 2019 16:04:57 +0900 Subject: [PATCH 55/71] Scoreboard: github repo link --- elm-src/Generated/API.elm | 26 +++++++++++++++++++++++++- elm-src/Main.elm | 19 +++++++++++++------ src/Git/Plantation/API/CRUD.hs | 14 +++++++++++--- src/Git/Plantation/Score.hs | 9 +++++++++ test/GenerateElm.hs | 5 +++-- 5 files changed, 61 insertions(+), 12 deletions(-) diff --git a/elm-src/Generated/API.elm b/elm-src/Generated/API.elm index 6d09dcd..87066d1 100644 --- a/elm-src/Generated/API.elm +++ b/elm-src/Generated/API.elm @@ -1,4 +1,4 @@ -module Generated.API exposing (Config, Problem, Repo, Score, Status, Team, User, decodeConfig, decodeProblem, decodeRepo, decodeScore, decodeStatus, decodeTeam, decodeUser, encodeConfig, encodeProblem, encodeRepo, encodeScore, encodeStatus, encodeTeam, encodeUser, getApiProblems, getApiScores, getApiTeams) +module Generated.API exposing (Config, Link, Problem, Repo, Score, Status, Team, User, decodeConfig, decodeLink, decodeProblem, decodeRepo, decodeScore, decodeStatus, decodeTeam, decodeUser, encodeConfig, encodeLink, encodeProblem, encodeRepo, encodeScore, encodeStatus, encodeTeam, encodeUser, getApiProblems, getApiScores, getApiTeams) import Http import Json.Decode exposing (..) @@ -146,6 +146,7 @@ type alias Score = { team : String , point : Int , stats : List Status + , links : List Link } @@ -155,6 +156,7 @@ decodeScore = |> required "team" string |> required "point" int |> required "stats" (list decodeStatus) + |> required "links" (list decodeLink) encodeScore : Score -> Json.Encode.Value @@ -163,6 +165,7 @@ encodeScore x = [ ( "team", Json.Encode.string x.team ) , ( "point", Json.Encode.int x.point ) , ( "stats", Json.Encode.list encodeStatus x.stats ) + , ( "links", Json.Encode.list encodeLink x.links ) ] @@ -190,6 +193,27 @@ encodeStatus x = ] +type alias Link = + { problem_id : Int + , url : String + } + + +decodeLink : Decoder Link +decodeLink = + Json.Decode.succeed Link + |> required "problem_id" int + |> required "url" string + + +encodeLink : Link -> Json.Encode.Value +encodeLink x = + Json.Encode.object + [ ( "problem_id", Json.Encode.int x.problem_id ) + , ( "url", Json.Encode.string x.url ) + ] + + getApiTeams : Http.Request (List Team) getApiTeams = Http.request diff --git a/elm-src/Main.elm b/elm-src/Main.elm index 843a09d..4e7d657 100644 --- a/elm-src/Main.elm +++ b/elm-src/Main.elm @@ -3,7 +3,7 @@ module Main exposing (Model, Msg(..), init, main, subscriptions, update, view, v import Browser as Browser import Generated.API as API exposing (..) import Html exposing (..) -import Html.Attributes exposing (checked, class, id, style, type_) +import Html.Attributes exposing (checked, class, href, id, style, target, type_) import Html.Events exposing (onCheck, onClick) import Http import List.Extra as List @@ -23,6 +23,7 @@ main = type alias Model = { reload : Bool , problems : List API.Problem + , teams : List API.Team , scores : RemoteData String (List API.Score) } @@ -44,6 +45,7 @@ init flags = model = { reload = True , problems = flags.config.problems + , teams = flags.config.teams , scores = NotAsked } in @@ -135,21 +137,26 @@ viewScore problems idx score = ] (List.concat [ [ th [ class "text-right p-2 f4" ] [ text score.team ] ] - , List.map (viewStatus score.stats) problems + , List.map (viewStatus score) problems , [ th [ class "text-center p-2 f4" ] [ text (String.fromInt score.point) ] ] ] ) -viewStatus : List API.Status -> API.Problem -> Html msg -viewStatus stats problem = +viewStatus : API.Score -> API.Problem -> Html msg +viewStatus score problem = let status = - List.find (\st -> st.problem == problem.name) stats + List.find (\st -> st.problem == problem.name) score.stats + + url = + List.find (\l -> l.problem_id == problem.id) score.links + |> Maybe.map .url + |> Maybe.withDefault "" in th [ class "text-center p-2" ] - [ div [] [ statBadge status ] + [ a [ href url, target "_blank" ] [ statBadge status ] , div [] [ stars problem.difficulty ] ] diff --git a/src/Git/Plantation/API/CRUD.hs b/src/Git/Plantation/API/CRUD.hs index 8afdbcf..5558f78 100644 --- a/src/Git/Plantation/API/CRUD.hs +++ b/src/Git/Plantation/API/CRUD.hs @@ -14,12 +14,12 @@ import Data.Extensible import qualified Drone.Client as Drone import qualified Drone.Endpoints as Drone import qualified Drone.Types as Drone -import Git.Plantation (Problem, Team) +import Git.Plantation (Problem, Repo, Team, repoGithubPath) import Git.Plantation.Cmd (splitRepoName) import Git.Plantation.Env (Plant) -import Git.Plantation.Score (Score, Status) +import Git.Plantation.Score (Link, Score, Status) import Network.HTTP.Req -import Servant +import Servant hiding (Link, toLink) type CRUD = "teams" :> Get '[JSON] [Team] @@ -64,11 +64,13 @@ mkScore problems builds team = #team @= team ^. #name <: #point @= sum (map (toPoint stats) problems) <: #stats @= stats + <: #links @= links <: nil where isTeamBuild b = b ^. #source == team ^. #name builds' = Map.filter (not . null) $ Map.map (filter isTeamBuild) builds stats = Map.elems $ Map.mapWithKey toStatus builds' + links = map toLink $ team ^. #repos toStatus :: Text -> [Drone.Build] -> Status toStatus name builds @@ -82,3 +84,9 @@ toPoint stats problem = case L.find (\s -> s ^. #problem == problem ^. #name) stats of Just s | s ^. #correct -> problem ^. #difficulty _ -> 0 + +toLink :: Repo -> Link +toLink repo + = #problem_id @= repo ^. #problem + <: #url @= fromMaybe "" (("https://github.com/" <>) <$> repoGithubPath repo) + <: nil diff --git a/src/Git/Plantation/Score.hs b/src/Git/Plantation/Score.hs index 2b390fe..c5ea03d 100644 --- a/src/Git/Plantation/Score.hs +++ b/src/Git/Plantation/Score.hs @@ -14,6 +14,7 @@ type Score = Record '[ "team" >: Text , "point" >: Int , "stats" >: [Status] + , "links" >: [Link] ] type Status = Record @@ -22,8 +23,16 @@ type Status = Record , "pending" >: Bool ] +type Link = Record + '[ "problem_id" >: Int + , "url" >: Text + ] + instance ElmType Score where toElmType = toElmRecordType "Score" instance ElmType Status where toElmType = toElmRecordType "Status" + +instance ElmType Link where + toElmType = toElmRecordType "Link" diff --git a/test/GenerateElm.hs b/test/GenerateElm.hs index 9b33ca8..4ecd966 100644 --- a/test/GenerateElm.hs +++ b/test/GenerateElm.hs @@ -10,8 +10,8 @@ import Data.Proxy (Proxy (..)) import Elm (ElmType, Spec (Spec), specsToDir, toElmDecoderSource, toElmEncoderSource, toElmTypeSource) -import Git.Plantation (Config, Problem, Repo, Score, Status, - Team, User) +import Git.Plantation (Config, Link, Problem, Repo, Score, + Status, Team, User) import Git.Plantation.API.CRUD (CRUD) import Servant ((:>)) import Servant.Elm (defElmImports, generateElmForAPI) @@ -27,6 +27,7 @@ spec = Spec ["Generated", "API"] $ concat , toElmTypeAll (Proxy @ Config) , toElmTypeAll (Proxy @ Score) , toElmTypeAll (Proxy @ Status) + , toElmTypeAll (Proxy @ Link) , generateElmForAPI (Proxy @ ("api" :> CRUD)) ] From 9a3a9696a445093081b3a3c5b9985a00e3f02238 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Wed, 3 Apr 2019 16:09:07 +0900 Subject: [PATCH 56/71] Doc: update change log --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d291e7..a0bc951 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,3 +46,10 @@ * これで「同じチームから同じ問題で別々のブランチなどに対し同時にプッシュ」も平気 * 一つのブランチでしか動作しないので * スコアボードに「採点中」を追加(#19) +* `invite_member` コマンドを修正(#20) + * 201 が返ってくる(`github` 側から修正) +* リポジトリ系コマンドの修正・変更(#20) + * work directory の cd 先が間違っていたのを修正 + * `--repos` で複数問題を指定できるように変更 +* スコアボードのスコアの総和の仕方が間違っていたのを修正(#20) +* スコアボードで回答リポジトリに飛べるようにした(#20) From cbd945eef026145a66fddf8cebd3be6f6dd443e9 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Thu, 4 Apr 2019 20:54:07 +0900 Subject: [PATCH 57/71] Feat: add command skip to new_repo --- exec/tool/Main.hs | 10 +++++++--- src/Git/Plantation/Cmd/Options.hs | 7 ++++--- src/Git/Plantation/Cmd/Repo.hs | 23 +++++++++++++++++------ 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/exec/tool/Main.hs b/exec/tool/Main.hs index 264b7e4..4e612a9 100644 --- a/exec/tool/Main.hs +++ b/exec/tool/Main.hs @@ -54,8 +54,12 @@ subcmdParser = variantFrom newRepoCmdParser :: Parser NewRepoCmd newRepoCmdParser = hsequence - $ #repos <@=> option comma (long "repos" <> value [] <> metavar "IDS" <> help "Sets reopsitory that want to controll by problem id.") - <: #team <@=> strArgument (metavar "TEXT" <> help "Sets team that want to controll.") + $ #repos <@=> option comma (long "repos" <> value [] <> metavar "IDS" <> help "Sets reopsitory that want to controll by problem id.") + <: #team <@=> strArgument (metavar "TEXT" <> help "Sets team that want to controll.") + <: #skip_create_repo <@=> switch (long "skip_create_repo" <> help "Flag for skip create new repository in GitHub") + <: #skip_init_repo <@=> switch (long "skip_init_repo" <> help "Flag for skip init repository in GitHub") + <: #skip_setup_webhook <@=> switch (long "skip_setup_webhook" <> help "Flag for skip setup GitHub Webhook to repository") + <: #skip_init_ci <@=> switch (long "skip_init_ci" <> help "Flag for skip init CI by repository") <: nil singleRepoCmdParser :: Parser (Record RepoCmdFields) @@ -65,7 +69,7 @@ singleRepoCmdParser = hsequence <: nil deleteRepoCmdParser :: Parser DeleteRepoCmd -deleteRepoCmdParser = newRepoCmdParser +deleteRepoCmdParser = shrink <$> newRepoCmdParser inviteMemberCmdParser :: Parser InviteMemberCmd inviteMemberCmdParser = hsequence diff --git a/src/Git/Plantation/Cmd/Options.hs b/src/Git/Plantation/Cmd/Options.hs index 22ed130..343dab1 100644 --- a/src/Git/Plantation/Cmd/Options.hs +++ b/src/Git/Plantation/Cmd/Options.hs @@ -48,11 +48,12 @@ instance Run ("verify" >: ()) where instance Run ("new_repo" >: NewRepoCmd) where run' _ args = do conf <- asks (view #config) - let team = L.find (\t -> t ^. #name == args ^. #team) $ conf ^. #teams + let team = L.find (\t -> t ^. #name == args ^. #team) $ conf ^. #teams + flags = shrink args case (team, args ^. #repos) of (Nothing, _) -> logError $ "team is not found: " <> display (args ^. #team) - (Just team', []) -> forM_ (conf ^. #problems) (tryAnyWithLogError . createRepo team') - (Just team', pids) -> forM_ pids $ actByProblemId createRepo team' + (Just team', []) -> forM_ (conf ^. #problems) (tryAnyWithLogError . createRepo flags team') + (Just team', pids) -> forM_ pids $ actByProblemId (createRepo flags) team' instance Run ("new_github_repo" >: NewGitHubRepoCmd) where run' _ = runRepoCmd $ \team problem -> do diff --git a/src/Git/Plantation/Cmd/Repo.hs b/src/Git/Plantation/Cmd/Repo.hs index 01c4b1a..a6724bf 100644 --- a/src/Git/Plantation/Cmd/Repo.hs +++ b/src/Git/Plantation/Cmd/Repo.hs @@ -30,6 +30,17 @@ import qualified Shelly as S type NewRepoCmd = Record '[ "repos" >: [Int] , "team" >: Text + , "skip_create_repo" >: Bool + , "skip_init_repo" >: Bool + , "skip_setup_webhook" >: Bool + , "skip_init_ci" >: Bool + ] + +type NewRepoSkipFlags = Record + '[ "skip_create_repo" >: Bool + , "skip_init_repo" >: Bool + , "skip_setup_webhook" >: Bool + , "skip_init_ci" >: Bool ] type DeleteRepoCmd = Record @@ -60,17 +71,17 @@ actByProblemId act team pid = do Nothing -> logError $ "repo is not found by problem id: " <> display pid Just problem' -> tryAnyWithLogError $ act team problem' -createRepo :: Team -> Problem -> Plant () -createRepo team problem = do +createRepo :: NewRepoSkipFlags -> Team -> Problem -> Plant () +createRepo flags team problem = do logInfo $ mconcat [ "create repo: ", displayShow $ problem ^. #repo , " to team: ", displayShow $ team ^. #name ] info <- Team.lookupRepo problem team `fromJustWithThrow` UndefinedTeamProblem team problem - createRepoInGitHub info team problem - initRepoInGitHub info team problem - setupWebhook info - initProblemCI info team problem + unless (flags ^. #skip_create_repo) $ createRepoInGitHub info team problem + unless (flags ^. #skip_init_repo) $ initRepoInGitHub info team problem + unless (flags ^. #skip_setup_webhook) $ setupWebhook info + unless (flags ^. #skip_init_ci) $ initProblemCI info team problem createRepoInGitHub :: Repo -> Team -> Problem -> Plant () createRepoInGitHub info team problem = do From 7430d3054dd92af1b9c2a65e9d04f90cd90897a9 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Thu, 4 Apr 2019 21:07:13 +0900 Subject: [PATCH 58/71] Feat: scoreboard update interval config --- config/.git-plantation.yaml | 3 +++ elm-src/Generated/API.elm | 27 ++++++++++++++++++++++++--- elm-src/Main.elm | 9 +++------ src/Git/Plantation/Config.hs | 12 ++++++++++-- test/GenerateElm.hs | 3 ++- 5 files changed, 42 insertions(+), 12 deletions(-) diff --git a/config/.git-plantation.yaml b/config/.git-plantation.yaml index 4ee1b86..77ddbbc 100644 --- a/config/.git-plantation.yaml +++ b/config/.git-plantation.yaml @@ -1,3 +1,6 @@ +scoreboard: + interval: 60000 + problems: - id: 1 name: tutorial diff --git a/elm-src/Generated/API.elm b/elm-src/Generated/API.elm index 87066d1..2e13d86 100644 --- a/elm-src/Generated/API.elm +++ b/elm-src/Generated/API.elm @@ -1,4 +1,4 @@ -module Generated.API exposing (Config, Link, Problem, Repo, Score, Status, Team, User, decodeConfig, decodeLink, decodeProblem, decodeRepo, decodeScore, decodeStatus, decodeTeam, decodeUser, encodeConfig, encodeLink, encodeProblem, encodeRepo, encodeScore, encodeStatus, encodeTeam, encodeUser, getApiProblems, getApiScores, getApiTeams) +module Generated.API exposing (Config, Link, Problem, Repo, Score, ScoreBoardConfig, Status, Team, User, decodeConfig, decodeLink, decodeProblem, decodeRepo, decodeScore, decodeScoreBoardConfig, decodeStatus, decodeTeam, decodeUser, encodeConfig, encodeLink, encodeProblem, encodeRepo, encodeScore, encodeScoreBoardConfig, encodeStatus, encodeTeam, encodeUser, getApiProblems, getApiScores, getApiTeams) import Http import Json.Decode exposing (..) @@ -122,7 +122,8 @@ encodeProblem x = type alias Config = - { problems : List Problem + { scoreboard : ScoreBoardConfig + , problems : List Problem , teams : List Team } @@ -130,6 +131,7 @@ type alias Config = decodeConfig : Decoder Config decodeConfig = Json.Decode.succeed Config + |> required "scoreboard" decodeScoreBoardConfig |> required "problems" (list decodeProblem) |> required "teams" (list decodeTeam) @@ -137,11 +139,30 @@ decodeConfig = encodeConfig : Config -> Json.Encode.Value encodeConfig x = Json.Encode.object - [ ( "problems", Json.Encode.list encodeProblem x.problems ) + [ ( "scoreboard", encodeScoreBoardConfig x.scoreboard ) + , ( "problems", Json.Encode.list encodeProblem x.problems ) , ( "teams", Json.Encode.list encodeTeam x.teams ) ] +type alias ScoreBoardConfig = + { interval : Float + } + + +decodeScoreBoardConfig : Decoder ScoreBoardConfig +decodeScoreBoardConfig = + Json.Decode.succeed ScoreBoardConfig + |> required "interval" float + + +encodeScoreBoardConfig : ScoreBoardConfig -> Json.Encode.Value +encodeScoreBoardConfig x = + Json.Encode.object + [ ( "interval", Json.Encode.float x.interval ) + ] + + type alias Score = { team : String , point : Int diff --git a/elm-src/Main.elm b/elm-src/Main.elm index 4e7d657..0b4b64f 100644 --- a/elm-src/Main.elm +++ b/elm-src/Main.elm @@ -25,6 +25,7 @@ type alias Model = , problems : List API.Problem , teams : List API.Team , scores : RemoteData String (List API.Score) + , interval : Float } @@ -47,6 +48,7 @@ init flags = , problems = flags.config.problems , teams = flags.config.teams , scores = NotAsked + , interval = flags.config.scoreboard.interval } in ( model, Cmd.batch [ fetchScores ] ) @@ -220,11 +222,6 @@ fetchScores = Http.send FetchScores API.getApiScores -baseUrl : String -baseUrl = - "localhost:8080" - - subscriptions : Model -> Sub Msg subscriptions model = - Time.every 60000 Tick + Time.every model.interval Tick diff --git a/src/Git/Plantation/Config.hs b/src/Git/Plantation/Config.hs index 20bbda3..5cf99e9 100644 --- a/src/Git/Plantation/Config.hs +++ b/src/Git/Plantation/Config.hs @@ -13,8 +13,13 @@ import Git.Plantation.Data (Problem, Team) import Language.Elm type Config = Record - '[ "problems" >: [Problem] - , "teams" >: [Team] + '[ "scoreboard" >: ScoreBoardConfig + , "problems" >: [Problem] + , "teams" >: [Team] + ] + +type ScoreBoardConfig = Record + '[ "interval" >: Float ] readConfig :: MonadIO m => FilePath -> m Config @@ -23,5 +28,8 @@ readConfig = Y.decodeFileThrow instance ElmType Config where toElmType = toElmRecordType "Config" +instance ElmType ScoreBoardConfig where + toElmType = toElmRecordType "ScoreBoardConfig" + verify :: Config -> Either Text Config verify = pure diff --git a/test/GenerateElm.hs b/test/GenerateElm.hs index 4ecd966..2206235 100644 --- a/test/GenerateElm.hs +++ b/test/GenerateElm.hs @@ -11,7 +11,7 @@ import Elm (ElmType, Spec (Spec), specsToDir, toElmDecoderSource, toElmEncoderSource, toElmTypeSource) import Git.Plantation (Config, Link, Problem, Repo, Score, - Status, Team, User) + ScoreBoardConfig, Status, Team, User) import Git.Plantation.API.CRUD (CRUD) import Servant ((:>)) import Servant.Elm (defElmImports, generateElmForAPI) @@ -25,6 +25,7 @@ spec = Spec ["Generated", "API"] $ concat , toElmTypeAll (Proxy @ Repo) , toElmTypeAll (Proxy @ Problem) , toElmTypeAll (Proxy @ Config) + , toElmTypeAll (Proxy @ ScoreBoardConfig) , toElmTypeAll (Proxy @ Score) , toElmTypeAll (Proxy @ Status) , toElmTypeAll (Proxy @ Link) From d70cd9274e9cea81ec39e176db579c48c4b4b03c Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Thu, 4 Apr 2019 21:08:28 +0900 Subject: [PATCH 59/71] Doc: update change log --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0bc951..0f51354 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,3 +53,5 @@ * `--repos` で複数問題を指定できるように変更 * スコアボードのスコアの総和の仕方が間違っていたのを修正(#20) * スコアボードで回答リポジトリに飛べるようにした(#20) +* `new_repo` コマンドで任意の処理をスキップできるように変更(#21) +* スコアボードの更新間隔を設定ファイルから指定できるように変更(#21) From 98a2cf68c0be18a17e9c7c470a9b93dd6ec60659 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Fri, 5 Apr 2019 20:34:11 +0900 Subject: [PATCH 60/71] Fix: delete_repo opts --- exec/tool/Main.hs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/exec/tool/Main.hs b/exec/tool/Main.hs index 4e612a9..d38c44a 100644 --- a/exec/tool/Main.hs +++ b/exec/tool/Main.hs @@ -69,7 +69,10 @@ singleRepoCmdParser = hsequence <: nil deleteRepoCmdParser :: Parser DeleteRepoCmd -deleteRepoCmdParser = shrink <$> newRepoCmdParser +deleteRepoCmdParser = hsequence + $ #repos <@=> option comma (long "repos" <> value [] <> metavar "IDS" <> help "Sets reopsitory that want to controll by problem id.") + <: #team <@=> strArgument (metavar "TEXT" <> help "Sets team that want to controll.") + <: nil inviteMemberCmdParser :: Parser InviteMemberCmd inviteMemberCmdParser = hsequence From 58eae59d5b33e7bab5473164d33278458a1fd6a7 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Sat, 6 Apr 2019 12:25:44 +0900 Subject: [PATCH 61/71] Feat: add kick user cmd --- exec/tool/Main.hs | 6 +++++- src/Git/Plantation/Cmd/Member.hs | 36 +++++++++++++++++++++++++------ src/Git/Plantation/Cmd/Options.hs | 11 +++++++++- src/Git/Plantation/Env.hs | 5 +++++ stack.yaml | 2 +- 5 files changed, 51 insertions(+), 9 deletions(-) diff --git a/exec/tool/Main.hs b/exec/tool/Main.hs index d38c44a..f589537 100644 --- a/exec/tool/Main.hs +++ b/exec/tool/Main.hs @@ -49,7 +49,8 @@ subcmdParser = variantFrom <: #init_ci @= singleRepoCmdParser `withInfo` "Init CI repository by team repository" <: #reset_repo @= singleRepoCmdParser `withInfo` "Reset repository for team" <: #delete_repo @= deleteRepoCmdParser `withInfo` "Delete repository for team." - <: #invite_member @= inviteMemberCmdParser `withInfo` "Invite Member to Team Repository" + <: #invite_member @= inviteMemberCmdParser `withInfo` "Invite member to team repository" + <: #kick_member @= kickMemberCmdParser `withInfo` "Kick member from team repository" <: nil newRepoCmdParser :: Parser NewRepoCmd @@ -81,6 +82,9 @@ inviteMemberCmdParser = hsequence <: #user <@=> option (Just <$> str) (long "user" <> value Nothing <> metavar "TEXT" <> help "Sets user that want to controll.") <: nil +kickMemberCmdParser :: Parser KickMemberCmd +kickMemberCmdParser = inviteMemberCmdParser + variantFrom :: Forall (KeyIs KnownSymbol) xs => RecordOf ParserInfo xs -> Parser (Variant xs) variantFrom = subparser . subcmdVariant diff --git a/src/Git/Plantation/Cmd/Member.hs b/src/Git/Plantation/Cmd/Member.hs index 8ac656d..013c969 100644 --- a/src/Git/Plantation/Cmd/Member.hs +++ b/src/Git/Plantation/Cmd/Member.hs @@ -17,18 +17,22 @@ import GitHub.Data.Name (mkName) import GitHub.Endpoints.Repos (Auth (..)) import qualified GitHub.Endpoints.Repos.Collaborators as GitHub -type InviteMemberCmd = Record +type InviteMemberCmd = Record MemeberCmdFields + +type KickMemberCmd = Record MemeberCmdFields + +type MemeberCmdFields = '[ "team" >: Text , "repos" >: [Int] , "user" >: Maybe Text ] -inviteMember :: InviteMemberCmd -> Team -> Plant () -inviteMember args team = case args ^. #repos of - [] -> forM_ (team ^. #repos) $ \repo -> forM_ member $ invite repo - _ -> forM_ repos $ \repo -> forM_ member $ invite repo +actForMember :: (User -> Repo -> Plant ()) -> Record MemeberCmdFields -> Team -> Plant () +actForMember act args team = case args ^. #repos of + [] -> forM_ (team ^. #repos) $ \repo -> forM_ member $ act' repo + _ -> forM_ repos $ \repo -> forM_ member $ act' repo where - invite repo user = tryAnyWithLogError $ inviteUserToRepo user repo + act' repo user = tryAnyWithLogError $ act user repo repos = catMaybes $ flip lookupRepoByProblemId team <$> args ^. #repos member = maybe (team ^. #member) (: []) $ flip lookupUser team =<< args ^. #user @@ -51,3 +55,23 @@ inviteUserToRepo user target = do , user ^. #name, "(", user ^. #github, ")" , " to ", githubPath, "." ] + +kickUserFromRepo :: User -> Repo -> Plant () +kickUserFromRepo user target = do + token <- asks (view #token) + github <- repoGithub target + let (owner, repo) = splitRepoName github + resp <- liftIO $ GitHub.removeCollaborator + (OAuth token) + (mkName Proxy owner) + (mkName Proxy repo) + (mkName Proxy $ user ^. #github) + case resp of + Left err -> logDebug (displayShow err) >> throwIO (KickUserError err user target) + Right _ -> logInfo $ display (success github) + where + success githubPath = mconcat + [ "Success: kick " + , user ^. #name, "(", user ^. #github, ")" + , " from ", githubPath, "." + ] diff --git a/src/Git/Plantation/Cmd/Options.hs b/src/Git/Plantation/Cmd/Options.hs index 343dab1..15fd753 100644 --- a/src/Git/Plantation/Cmd/Options.hs +++ b/src/Git/Plantation/Cmd/Options.hs @@ -36,6 +36,7 @@ type SubCmdFields = , "reset_repo" >: ResetRepoCmd , "delete_repo" >: DeleteRepoCmd , "invite_member" >: InviteMemberCmd + , "kick_member" >: KickMemberCmd ] instance Run ("verify" >: ()) where @@ -103,4 +104,12 @@ instance Run ("invite_member" >: InviteMemberCmd) where let team = L.find (\t -> t ^. #name == args ^. #team) $ conf ^. #teams case team of Nothing -> logError $ "team is not found: " <> display (args ^. #team) - Just team' -> inviteMember args team' + Just team' -> actForMember inviteUserToRepo args team' + +instance Run ("kick_member" >: KickMemberCmd) where + run' _ args = do + conf <- asks (view #config) + let team = L.find (\t -> t ^. #name == args ^. #team) $ conf ^. #teams + case team of + Nothing -> logError $ "team is not found: " <> display (args ^. #team) + Just team' -> actForMember kickUserFromRepo args team' diff --git a/src/Git/Plantation/Env.hs b/src/Git/Plantation/Env.hs index 235d631..2b25f12 100644 --- a/src/Git/Plantation/Env.hs +++ b/src/Git/Plantation/Env.hs @@ -75,6 +75,7 @@ data GitPlantException | DeleteRepoError GitHub.Error Repo | SetupWebhookError GitHub.Error Repo | InviteUserError GitHub.Error User Repo + | KickUserError GitHub.Error User Repo | InvalidRepoConfig Repo deriving (Typeable) @@ -102,6 +103,10 @@ instance Show GitPlantException where mkLogMessage' "can't invite user to repository" (#user @= user <: #repo @= repo <: nil) + KickUserError _err user repo -> + mkLogMessage' + "can't kick user from repository" + (#user @= user <: #repo @= repo <: nil) InvalidRepoConfig repo -> mkLogMessage' "invalid repo config" diff --git a/stack.yaml b/stack.yaml index c329e89..088410d 100644 --- a/stack.yaml +++ b/stack.yaml @@ -5,7 +5,7 @@ extra-deps: - extensible-0.5 - servant-github-webhook-0.4.1.0 - github: matsubara0507/github - commit: b0c8d71701a42bff143b33ac1067f5818a9804ce + commit: df371930d6369d1b2f37901619d8e8c56fb8fb60 - github: matsubara0507/drone-haskell commit: aa6f5152dd9ea72cb48a32bdc58c91de2bdc21a9 - github: matsubara0507/elm-export From a1f6d90d710462ac15456d501496e8c5a2140072 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Sat, 6 Apr 2019 12:29:51 +0900 Subject: [PATCH 62/71] Doc: update change log --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f51354..75c63cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,3 +55,4 @@ * スコアボードで回答リポジトリに飛べるようにした(#20) * `new_repo` コマンドで任意の処理をスキップできるように変更(#21) * スコアボードの更新間隔を設定ファイルから指定できるように変更(#21) +* 参加者をリポジトリからキックするコマンドを追加(#24) From 4902d12a0cc42256067e8e3190ecbe73619f39cd Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Sat, 6 Apr 2019 13:46:42 +0900 Subject: [PATCH 63/71] CI: build docker image --- .travis.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e9395c1..ae4315f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,14 +7,25 @@ cache: directories: - "$HOME/.stack/" - "$HOME/.local/bin/" - - ".stack-work/" + - "./.stack-work/" + - "./elm-stuff/" install: - mkdir -p ~/.local/bin - export PATH=$HOME/.local/bin:$PATH - travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack' +- stack docker pull jobs: include: - stage: build dependencies script: stack --no-terminal --install-ghc test --bench --only-dependencies - stage: run test script: stack --no-terminal test --bench --no-run-benchmarks --no-haddock-deps --pedantic + - stage: push docker image + if: branch = master AND type = push + script: + - stack test + - stack --docker --no-terminal build -j 1 Cabal + - stack --docker image container + - docker build -t matsubara0507/git-plantation . + - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin + - docker push matsubara0507/git-plantation From b0902727cce15e3b52c94977585d3772fe38484d Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Sat, 6 Apr 2019 16:19:53 +0900 Subject: [PATCH 64/71] Doc: update README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 99ac818..efa4003 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # git-plantation +[![Build Status](https://travis-ci.org/matsubara0507/git-plantation.svg?branch=master)](https://travis-ci.org/matsubara0507/git-plantation) +[![](https://images.microbadger.com/badges/image/matsubara0507/git-plantation.svg)](https://microbadger.com/images/matsubara0507/git-plantation "Get your own image badge on microbadger.com") + ![](./image/scoreboard.png) ## Requirement From efee9e44464832fb405bb13bc4427a0302b77c50 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Sat, 6 Apr 2019 16:54:56 +0900 Subject: [PATCH 65/71] Refactor: with hlint --- exec/tool/Main.hs | 2 +- src/Git/Plantation/API/CRUD.hs | 2 +- src/Git/Plantation/API/Webhook.hs | 2 +- src/Git/Plantation/Cmd/Member.hs | 2 -- src/Git/Plantation/Cmd/Repo.hs | 3 +-- src/Git/Plantation/Cmd/Run.hs | 1 - src/Git/Plantation/Env.hs | 5 ++--- 7 files changed, 6 insertions(+), 11 deletions(-) diff --git a/exec/tool/Main.hs b/exec/tool/Main.hs index f589537..5953ea9 100644 --- a/exec/tool/Main.hs +++ b/exec/tool/Main.hs @@ -91,7 +91,7 @@ variantFrom = subparser . subcmdVariant where subcmdVariant = hfoldMapWithIndexFor (Proxy @ (KeyIs KnownSymbol)) $ \m x -> let k = symbolVal (proxyAssocKey m) - in command k ((EmbedAt m . Field . pure) <$> getField x) + in command k (EmbedAt m . Field . pure <$> getField x) instance Wrapper ParserInfo where type Repr ParserInfo a = ParserInfo a diff --git a/src/Git/Plantation/API/CRUD.hs b/src/Git/Plantation/API/CRUD.hs index 5558f78..14cd3e9 100644 --- a/src/Git/Plantation/API/CRUD.hs +++ b/src/Git/Plantation/API/CRUD.hs @@ -88,5 +88,5 @@ toPoint stats problem = toLink :: Repo -> Link toLink repo = #problem_id @= repo ^. #problem - <: #url @= fromMaybe "" (("https://github.com/" <>) <$> repoGithubPath repo) + <: #url @= maybe "" ("https://github.com/" <>) (repoGithubPath repo) <: nil diff --git a/src/Git/Plantation/API/Webhook.hs b/src/Git/Plantation/API/Webhook.hs index 3e77911..db25fb2 100644 --- a/src/Git/Plantation/API/Webhook.hs +++ b/src/Git/Plantation/API/Webhook.hs @@ -26,7 +26,7 @@ webhook = pingWebhook :<|> pushWebhook pingWebhook :: RepoWebhookEvent -> ((), PublicEvent) -> Plant () -pingWebhook _ (_, ev) = do +pingWebhook _ (_, ev) = logInfo $ "Hook Ping Event: " <> displayShow ev pushWebhook :: RepoWebhookEvent -> ((), PushEvent) -> Plant () diff --git a/src/Git/Plantation/Cmd/Member.hs b/src/Git/Plantation/Cmd/Member.hs index 013c969..cc9a334 100644 --- a/src/Git/Plantation/Cmd/Member.hs +++ b/src/Git/Plantation/Cmd/Member.hs @@ -1,7 +1,5 @@ {-# LANGUAGE DataKinds #-} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedLabels #-} -{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-} module Git.Plantation.Cmd.Member where diff --git a/src/Git/Plantation/Cmd/Repo.hs b/src/Git/Plantation/Cmd/Repo.hs index a6724bf..c6ade25 100644 --- a/src/Git/Plantation/Cmd/Repo.hs +++ b/src/Git/Plantation/Cmd/Repo.hs @@ -1,7 +1,6 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedLabels #-} -{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-} module Git.Plantation.Cmd.Repo where @@ -214,7 +213,7 @@ deleteProblemCI team problem = do problemUrl = mconcat ["https://", token, "@github.com/", owner, "/", repo, ".git"] shelly' $ chdir_p workDir (Git.cloneOrFetch problemUrl repo) - shelly' $ chdir_p (workDir repo) $ do + shelly' $ chdir_p (workDir repo) $ errExit False $ Git.push [ "--delete", "origin", team ^. #name] logInfo $ "Success: delete ci branch in " <> displayShow (problem ^. #repo) diff --git a/src/Git/Plantation/Cmd/Run.hs b/src/Git/Plantation/Cmd/Run.hs index c5183c0..af283c4 100644 --- a/src/Git/Plantation/Cmd/Run.hs +++ b/src/Git/Plantation/Cmd/Run.hs @@ -1,6 +1,5 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE OverloadedLabels #-} -{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeOperators #-} {-# OPTIONS_GHC -fno-warn-orphans #-} diff --git a/src/Git/Plantation/Env.hs b/src/Git/Plantation/Env.hs index 2b25f12..6c83bdf 100644 --- a/src/Git/Plantation/Env.hs +++ b/src/Git/Plantation/Env.hs @@ -56,9 +56,8 @@ shelly' :: Sh a -> Plant a shelly' sh = do env <- ask shelly - $ (log_stdout_with (runRIO env . logDebug . display)) - $ (log_stderr_with (runRIO env . logDebug . display)) - $ sh + $ log_stdout_with (runRIO env . logDebug . display) + $ log_stderr_with (runRIO env . logDebug . display) sh mkLogMessage :: Text -> Record xs -> Record ("error_message" >: Text ': xs) mkLogMessage message r = #error_message @= message <: r From dd08a2d2ab3e8521dfcb5f9a459467c9845a6dff Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Sat, 6 Apr 2019 16:55:50 +0900 Subject: [PATCH 66/71] Dump version 0.2.0 --- CHANGELOG.md | 2 ++ package.yaml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75c63cf..9f17af3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased changes +## v0.2.0 + * org アカウント以外で config 設定 (#10) * dotenv ファイルが使えるようになる (#10) * `/score` エンドポイントで drone から取得できなくても空リストを返す (#10) diff --git a/package.yaml b/package.yaml index 1fc51cb..3ee45e2 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: git-plantation -version: 0.1.0.0 +version: 0.2.0.0 github: "githubuser/git-plantation" license: BSD3 author: "Author name here" From b61ce533832364046dcce5777f495292462eba9a Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Sun, 7 Apr 2019 10:59:18 +0900 Subject: [PATCH 67/71] CI: fix cache --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ae4315f..1be333b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,8 +7,7 @@ cache: directories: - "$HOME/.stack/" - "$HOME/.local/bin/" - - "./.stack-work/" - - "./elm-stuff/" + - ".stack-work/" install: - mkdir -p ~/.local/bin - export PATH=$HOME/.local/bin:$PATH From 3ad1346bfd74919ed9ab3917d9ea24518648537f Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Mon, 8 Apr 2019 14:42:35 +0900 Subject: [PATCH 68/71] Fix: author --- package.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.yaml b/package.yaml index 3ee45e2..5e2ad1e 100644 --- a/package.yaml +++ b/package.yaml @@ -1,16 +1,16 @@ name: git-plantation version: 0.2.0.0 -github: "githubuser/git-plantation" -license: BSD3 -author: "Author name here" -maintainer: "example@example.com" -copyright: "2018 Author name here" +github: "matsubara0507/git-plantation" +license: MIT +author: "MATSUBARA Nobutada" +maintainer: "t12307043@gunma-u.ac.jp" +copyright: "2018 MATSUBARA Nobutada" extra-source-files: - README.md - CHANGELOG.md -description: Please see the README on GitHub at +description: Please see the README on GitHub at ghc-options: - -Wall From b71fc1d2009af12dbd22bdbe08b9388601658a18 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Mon, 8 Apr 2019 15:43:55 +0900 Subject: [PATCH 69/71] Scoreboard: fix pending --- src/Git/Plantation/API/CRUD.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Git/Plantation/API/CRUD.hs b/src/Git/Plantation/API/CRUD.hs index 14cd3e9..6aecc64 100644 --- a/src/Git/Plantation/API/CRUD.hs +++ b/src/Git/Plantation/API/CRUD.hs @@ -76,7 +76,7 @@ toStatus :: Text -> [Drone.Build] -> Status toStatus name builds = #problem @= name <: #correct @= any (\b -> b ^. #status == "success") builds - <: #pending @= any (\b -> b ^. #status == "pending") builds + <: #pending @= any (\b -> b ^. #status == "running") builds <: nil toPoint :: [Status] -> Problem -> Int From 6e4e32641a8e3995bca3dfd2813ce93918df3fc8 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Mon, 8 Apr 2019 16:15:25 +0900 Subject: [PATCH 70/71] Scoreboard: fix pending --- src/Git/Plantation/API/CRUD.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Git/Plantation/API/CRUD.hs b/src/Git/Plantation/API/CRUD.hs index 6aecc64..aaabbde 100644 --- a/src/Git/Plantation/API/CRUD.hs +++ b/src/Git/Plantation/API/CRUD.hs @@ -76,7 +76,7 @@ toStatus :: Text -> [Drone.Build] -> Status toStatus name builds = #problem @= name <: #correct @= any (\b -> b ^. #status == "success") builds - <: #pending @= any (\b -> b ^. #status == "running") builds + <: #pending @= any (\b -> b ^. #status == "running" || b ^. #status == "pending") builds <: nil toPoint :: [Status] -> Problem -> Int From c781ee63a77700507e6e66c0b615415fda3a5462 Mon Sep 17 00:00:00 2001 From: MATSUBARA Nobutada Date: Mon, 8 Apr 2019 18:44:41 +0900 Subject: [PATCH 71/71] Fix: all builds --- src/Git/Plantation/API/CRUD.hs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Git/Plantation/API/CRUD.hs b/src/Git/Plantation/API/CRUD.hs index aaabbde..f8c55db 100644 --- a/src/Git/Plantation/API/CRUD.hs +++ b/src/Git/Plantation/API/CRUD.hs @@ -54,11 +54,20 @@ getScores = do fetchBuilds :: Drone.Client c => c -> Problem -> Plant (Text, [Drone.Build]) fetchBuilds client problem = do let (owner, repo) = splitRepoName $ problem ^. #repo - builds <- tryAny (runReq def $ Drone.getBuilds client owner repo Nothing) >>= \case - Left err -> logError (display err) >> pure [] - Right resp -> pure $ responseBody resp + builds <- tryAny (getAllBuilds client owner repo) >>= \case + Left err -> logError (display err) >> pure [] + Right builds -> pure builds pure (problem ^. #name, builds) +getAllBuilds :: (MonadIO m, Drone.Client c) => c -> Text -> Text -> m [Drone.Build] +getAllBuilds client owner repo = mconcat <$> getAllBuilds' [] 1 + where + getAllBuilds' xss n = do + resp <- runReq def $ Drone.getBuilds client owner repo (Just n) + case responseBody resp of + [] -> pure xss + builds -> getAllBuilds' (builds : xss) (n + 1) + mkScore :: [Problem] -> Map Text [Drone.Build] -> Team -> Score mkScore problems builds team = #team @= team ^. #name