Skip to content

Commit

Permalink
feat: Add some settings validations.
Browse files Browse the repository at this point in the history
- All common checks must be present in all repos.
- No checks may be duplicated in the list.
  • Loading branch information
iphydf committed Jan 2, 2024
1 parent b90ef1b commit befa788
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 23 deletions.
88 changes: 69 additions & 19 deletions admin/settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,31 @@ _common:
secret_scanning_push_protection:
status: "enabled"

branchProtection: &branchProtection
enforce_admins: true
required_pull_request_reviews:
require_code_owner_reviews: true
required_signatures: true
restrictions:
users: []
teams: ["reviewers"]
required_linear_history: true
allow_force_pushes: false
allow_deletions: false
block_creations: true
required_conversation_resolution: true
lock_branch: false
branches:
"master": &branchProtection
enforce_admins: true
required_pull_request_reviews:
require_code_owner_reviews: true
required_signatures: true
restrictions:
users: []
teams: ["reviewers"]
required_linear_history: true
allow_force_pushes: false
allow_deletions: false
block_creations: true
required_conversation_resolution: true
lock_branch: false
required_status_checks:
strict: true
contexts:
# Common
- "Hound"
- "Mergeable"
- "Milestone Check"
- "WIP"
- "code-review/reviewable"
- "restyled"

btox:
editRepo:
Expand Down Expand Up @@ -153,6 +164,14 @@ c-toxcore-hs:
required_status_checks:
strict: true
contexts:
# Common
- "Hound"
- "Mergeable"
- "Milestone Check"
- "WIP"
- "code-review/reviewable"
- "restyled"
# Custom
- "bazel-opt"

dockerfiles:
Expand All @@ -168,6 +187,14 @@ dockerfiles:
required_status_checks:
strict: true
contexts:
# Common
- "Hound"
- "Mergeable"
- "Milestone Check"
- "WIP"
- "code-review/reviewable"
- "restyled"
# Custom
- "build (alpine-s390x)"
- "build (bazel)"
- "build (bazel-cache)"
Expand All @@ -178,7 +205,6 @@ dockerfiles:
- "ci/circleci: compcert"
- "ci/circleci: infer"
- "CodeFactor"
- "code-review/reviewable"
- "ghc-base"
- "ghc-android (aarch64)"
- "ghc-android (arm)"
Expand All @@ -198,11 +224,13 @@ experimental:
required_status_checks:
strict: true
contexts:
# Common
- "Hound"
- "Mergeable"
- "Milestone Check"
- "WIP"
- "code-review/reviewable"
- "Hound"
- "restyled"

go-toxcore-c:
editRepo:
Expand All @@ -217,9 +245,15 @@ go-toxcore-c:
required_status_checks:
strict: true
contexts:
- bazel-opt
- Codacy Static Code Analysis
- code-review/reviewable
# Common
- "Hound"
- "Mergeable"
- "Milestone Check"
- "WIP"
- "code-review/reviewable"
- "restyled"
# Custom
- "bazel-opt"

hs-apigen:
editRepo:
Expand All @@ -234,6 +268,14 @@ hs-apigen:
required_status_checks:
strict: true
contexts:
# Common
- "Hound"
- "Mergeable"
- "Milestone Check"
- "WIP"
- "code-review/reviewable"
- "restyled"
# Custom
- "bazel-opt"
- "build / stack"

Expand All @@ -250,6 +292,14 @@ hs-cimple:
required_status_checks:
strict: true
contexts:
# Common
- "Hound"
- "Mergeable"
- "Milestone Check"
- "WIP"
- "code-review/reviewable"
- "restyled"
# Custom
- "bazel-opt"
- "build / stack"
- "checks / check-release"
Expand Down
54 changes: 51 additions & 3 deletions src/GitHub/Tools/Settings.hs
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -Wwarn #-}
module GitHub.Tools.Settings
( syncSettings
, validateSettings
) where

import Control.Monad (forM_)
import Control.Monad (forM_, unless)
import Data.Aeson (Value (Array, Object, String))
import qualified Data.Aeson.KeyMap as KeyMap
import Data.Aeson.TH (Options (fieldLabelModifier),
defaultOptions, deriveJSON)
import qualified Data.ByteString.Char8 as BS
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
import Data.List (isPrefixOf, nub, (\\))
import Data.Maybe (mapMaybe)
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Yaml (Value, encode)
import qualified Data.Vector as V
import Data.Yaml (encode)
import qualified GitHub
import qualified GitHub.Paths.Repos as Repos
import qualified GitHub.Paths.Repos.Branches as Branches
Expand Down Expand Up @@ -44,5 +51,46 @@ syncSettings auth repos repoFilter = do
protRes <- mutate auth mgr (Branches.addProtectionR "TokTok" repo branch update)
BS.putStrLn $ encode protRes
where
each = HashMap.toList . HashMap.filterWithKey (\k _ -> not $ "_" `Text.isPrefixOf` k)
filterRepos = filter ((repoFilter `Text.isPrefixOf`) . fst)


validateSettings :: MonadFail m => HashMap Text Settings -> m ()
validateSettings repos = do
commonBranches <- case HashMap.lookup "_common" repos >>= settingsBranches of
Nothing -> fail "no _common section found"
Just ok -> return ok
commonContexts <- case HashMap.lookup "master" commonBranches of
Nothing -> fail "no \"master\" branch in _common section found"
Just ok -> getContexts "_common" "master" =<< getRequiredStatusChecks "_common" "master" ok
-- Check that each repo's branch protection contexts start with the common ones.
forM_ (each repos) $ \(repo, Settings{..}) ->
forM_ (maybe [] each settingsBranches) $ \(branch, update) -> do
contexts <- getContexts repo branch =<< getRequiredStatusChecks repo branch update
let ctx = repo <> ".branches." <> branch <> ".required_status_checks.contexts"
unless (commonContexts `isPrefixOf` contexts) $
fail . Text.unpack $ ctx <> " should start with " <> Text.pack (show contexts)
let dups = contexts \\ nub contexts
unless (null dups) $
fail . Text.unpack $ ctx <> " has duplicates: " <> Text.pack (show dups)

where
getRequiredStatusChecks repo branch (Object mems) =
case KeyMap.lookup "required_status_checks" mems of
Nothing -> fail . Text.unpack $ repo <> ".branches." <> branch <> " should contain required_status_checks"
Just ok -> return ok
getRequiredStatusChecks repo branch _ =
fail . Text.unpack $ repo <> ".branches." <> branch <> " should be an object"

getContexts repo branch (Object mems) =
case KeyMap.lookup "contexts" mems of
Just (Array arr) -> return $ mapMaybe toString $ V.toList arr
Just _ -> fail . Text.unpack $ repo <> ".branches." <> branch <> ".required_status_checks.contexts should be an array"
Nothing -> fail . Text.unpack $ repo <> ".branches." <> branch <> ".required_status_checks should contain contexts"
getContexts repo branch _ =
fail . Text.unpack $ repo <> ".branches." <> branch <> ".required_status_checks should be an object"

toString (String str) = Just str
toString _ = Nothing

each :: HashMap Text a -> [(Text, a)]
each = HashMap.toList . HashMap.filterWithKey (\k _ -> not $ "_" `Text.isPrefixOf` k)
3 changes: 2 additions & 1 deletion tools/hub-settings.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Data.Maybe (fromMaybe, listToMaybe)
import qualified Data.Text as Text
import Data.Yaml (decodeFileThrow)
import qualified GitHub
import GitHub.Tools.Settings (syncSettings)
import GitHub.Tools.Settings (syncSettings, validateSettings)
import System.Environment (getArgs, lookupEnv)

main :: IO ()
Expand All @@ -20,5 +20,6 @@ main = do
case args of
file:repoFilter -> do
yaml <- decodeFileThrow file
validateSettings yaml
syncSettings token yaml (Text.pack . fromMaybe "" . listToMaybe $ repoFilter)
_ -> fail "Usage: hub-settings <settings.yml>"

0 comments on commit befa788

Please sign in to comment.