Skip to content

Commit

Permalink
Use checks instead of posting comments (#122)
Browse files Browse the repository at this point in the history
* Fixed warnings

* wip

* wip

* wip

* sha

* wip

* Added project name

* Made check not fail

* Removed Pull Request

* Removed testing things

* Updated docs
  • Loading branch information
ilyakooo0 authored Sep 8, 2021
1 parent a1ea91f commit 6d8a673
Show file tree
Hide file tree
Showing 12 changed files with 276 additions and 80 deletions.
10 changes: 5 additions & 5 deletions action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ inputs:
description: The owner of the repo in which to post the comment.
default: "${{ github.repository }}"
required: false
pull_request:
description: The pull request in which to post the comment.
default: "${{ github.event.pull_request.number }}"
required: false
project_name:
description: The name of the project to which the API pertains.
required: true
Expand All @@ -27,18 +23,22 @@ inputs:
new:
description: The path to new specification of the API.
required: true
sha:
description: The sha of the commit to post the check for.
required: false
default: "${{ github.event.pull_request.head.sha }}"
runs:
using: "docker"
image: "typeable/comparest-github-action:latest"
env:
GITHUB_TOKEN: "${{ inputs.GITHUB_TOKEN }}"
REPO: "${{ inputs.repo }}"
PR_NUMBER: "${{ inputs.pull_request }}"
PROJECT_NAME: "${{ inputs.project_name }}"
FOOTER: "${{ inputs.footer }}"
ROOT: "/github/workspace"
OLD: "${{ inputs.old }}"
NEW: "${{ inputs.new }}"
SHA: "${{ inputs.sha }}"
pre-entrypoint: "/bin/pre"
entrypoint: "/bin/run"

Expand Down
3 changes: 3 additions & 0 deletions comparest.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,16 @@ executable comparest-github-action
, pandoc-types
, envy
, filepath
, bytestring
ghc-options: -threaded
-rtsopts
-with-rtsopts=-N
other-modules:
Control.Monad.Freer.GitHub
CompaREST.GitHub.API
CompaREST.GitHub.Action.Config
GitHub.Data.Checks
GitHub.Endpoints.Checks

test-suite comparest-tests
import: common-options
Expand Down
6 changes: 3 additions & 3 deletions docs/Integration_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ We can now run our action:
project_name: TEST
```

This will create a comment on the pull request displaying the changes (if there are any) similar to this:
This will create a check on the pull request displaying the changes (if there are any) similar to this:

![](img/github-action-comment.png)
![](img/github-action-report.png)

Consecutive runs of the action will update the comment instead of creating new ones. This will prevent the pile-up of compaREST comments and save you from distracting notifications.
The check will show success when there are no breaking changes, and be neutral otherwise.

## Integrating into something other than Github Actions

Expand Down
Binary file removed docs/img/github-action-comment.png
Binary file not shown.
Binary file added docs/img/github-action-report.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
109 changes: 70 additions & 39 deletions github-action/CompaREST/GitHub/API.hs
Original file line number Diff line number Diff line change
@@ -1,55 +1,86 @@
module CompaREST.GitHub.API
( mapComment,
createOrUpdateComment,
( postStatus,
postStatusProcessing,
)
where

import CompaREST.GitHub.Action.Config
import Control.Monad
import Control.Monad.Freer
import Control.Monad.Freer.GitHub
import Control.Monad.Freer.Reader
import Data.Foldable
import Control.Monad.IO.Class
import Data.Aeson
import qualified Data.ByteString.Lazy.Char8 as BSLC
import Data.OpenApi.Compare.Report
import Data.Proxy
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Vector as V
import GitHub
import qualified GitHub as GH
import GitHub.Data.Checks
import GitHub.Endpoints.Checks

findComment :: Members '[GitHub, Reader Config] effs => Eff effs (Maybe GH.IssueComment)
findComment = do
postStatusProcessing ::
(Members '[GitHub, Reader Config] effs, MonadIO (Eff effs)) =>
Eff effs ()
postStatusProcessing = do
Config {..} <- ask
comments <- sendGitHub $ GH.commentsR repoOwner repoName issue GH.FetchAll
htmlComment <- getHTMLComment
let tryStripPrefix :: GH.IssueComment -> Maybe GH.IssueComment
tryStripPrefix c@GH.IssueComment {issueCommentBody = (T.stripSuffix htmlComment -> Just b)} =
Just $ c {GH.issueCommentBody = b}
tryStripPrefix _ = Nothing
pure . (V.!? 0) $ V.mapMaybe tryStripPrefix comments
printJSON $
sendGitHub $
checkR
repoOwner
repoName
Check
{ checkName = mkName Proxy $ "compaREST – " <> projectName
, checkSha = sha
, checkDetailsURL = Nothing
, checkExternalId = Nothing
, checkStatus = Just CheckInProgress
, checkStartedAt = Nothing
, checkConclusion = Nothing
, checkCompletedAt = Nothing
, checkOutput = Nothing
, checkActions = Nothing
}

mapComment :: Members '[GitHub, Reader Config] effs => (Text -> Text) -> Eff effs ()
mapComment f = do
findComment
>>= traverse_
( \comment -> do
Config {..} <- ask
htmlComment <- getHTMLComment
sendGitHub $ editCommentR repoOwner repoName (GH.mkId Proxy $ GH.issueCommentId comment) ((<> htmlComment) . f $ GH.issueCommentBody comment)
pure ()
)

createOrUpdateComment :: Members '[GitHub, Reader Config] effs => Text -> Eff effs ()
createOrUpdateComment body' = do
postStatus ::
(Members '[GitHub, Reader Config] effs, MonadIO (Eff effs)) =>
-- | 'Nothing' means that there were no changes at all
Maybe (Text, ReportStatus) ->
Eff effs ()
postStatus x = do
let (body, (title, conclusion)) = case x of
Just (b, s) -> (b,) $ case s of
BreakingChanges -> ("⚠️ Breaking changes found!", CheckNeutral)
NoBreakingChanges -> ("No breaking changes found ✨", CheckSuccess)
OnlyUnsupportedChanges -> ("🤷 Couldn't determine compatibility", CheckNeutral)
Nothing -> ("", ("✅ The API did not change", CheckSuccess))
Config {..} <- ask
htmlComment <- getHTMLComment
let body = body' <> htmlComment
void $
findComment >>= \case
Just comment -> sendGitHub $ editCommentR repoOwner repoName (GH.mkId Proxy $ GH.issueCommentId comment) body
Nothing -> sendGitHub $ createCommentR repoOwner repoName issue body
printJSON $
sendGitHub $
checkR
repoOwner
repoName
Check
{ checkName = mkName Proxy $ "compaREST – " <> projectName
, checkSha = sha
, checkDetailsURL = Nothing
, checkExternalId = Nothing
, checkStatus = Just CheckCompleted
, checkStartedAt = Nothing
, checkConclusion = Just conclusion
, checkCompletedAt = Nothing
, checkOutput =
Just $
CheckOutput
{ checkTitle = title
, checkSummary = body
, checkText = Nothing
, checkAnnotations = Nothing
, checkImages = Nothing
}
, checkActions = Nothing
}

getHTMLComment :: Member (Reader Config) effs => Eff effs Text
getHTMLComment = do
name <- asks projectName
pure $ "\n\n<!-- compaREST comment – " <> name <> " -->"
printJSON :: MonadIO (Eff effs) => Eff effs Value -> Eff effs ()
printJSON m = do
x <- m
liftIO . BSLC.putStrLn $ encode x
6 changes: 3 additions & 3 deletions github-action/CompaREST/GitHub/Action/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ data Config = Config
{ githubToken :: GH.Auth
, repoOwner :: GH.Name GH.Owner
, repoName :: GH.Name GH.Repo
, issue :: GH.IssueNumber
, projectName :: Text
, footerText :: Text
, root :: FilePath
, sha :: GH.Name GH.Commit
}

instance FromEnv Config where
Expand All @@ -27,17 +27,17 @@ instance FromEnv Config where
T.split (== '/') <$> env "REPO" >>= \case
[owner, name] -> pure (owner, name)
_ -> fail "malformed repo"
issue <- GH.IssueNumber <$> env "PR_NUMBER"
projectName <- env "PROJECT_NAME"
footerText <- env "FOOTER"
root <- envMaybe "ROOT" .!= "."
sha <- env "SHA"
pure $
Config
{ githubToken = token
, repoOwner = GH.mkName Proxy owner
, repoName = GH.mkName Proxy repo
, issue = issue
, projectName = projectName
, footerText = footerText
, root = root
, sha = GH.mkName Proxy sha
}
173 changes: 173 additions & 0 deletions github-action/GitHub/Data/Checks.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
module GitHub.Data.Checks
( Check (..),
CheckStatus (..),
CheckConclusion (..),
CheckOutput (..),
CheckAnnotation (..),
CheckAnnotationLevel (..),
CheckImage (..),
CheckAction (..),
)
where

import Data.Aeson
import Data.Aeson.Types
import Data.Text (Text)
import Data.Vector (Vector)
import GHC.Generics (Generic)
import GitHub
import GitHub.Internal.Prelude (UTCTime)

data Check = Check
{ checkName :: !(Name Check)
, checkSha :: !(Name Commit)
, checkDetailsURL :: !(Maybe URL)
, checkExternalId :: !(Maybe (Id Check))
, checkStatus :: !(Maybe CheckStatus)
, checkStartedAt :: !(Maybe UTCTime)
, checkConclusion :: !(Maybe CheckConclusion)
, checkCompletedAt :: !(Maybe UTCTime)
, checkOutput :: !(Maybe CheckOutput)
, checkActions :: !(Maybe (Vector CheckAction))
}
deriving stock (Show, Eq, Ord, Generic)

instance ToJSON Check where
toJSON (Check n sha durl eid s sa c ca o a) =
object'
[ "name" .= n
, "head_sha" .= sha
, "details_url" .= durl
, "external_id" .= eid
, "status" .= s
, "started_at" .= sa
, "conclusion" .= c
, "completed_at" .= ca
, "output" .= o
, "actions" .= a
]

data CheckStatus
= CheckQueued
| CheckInProgress
| CheckCompleted
deriving stock (Show, Enum, Bounded, Eq, Ord, Generic)

instance ToJSON CheckStatus where
toJSON CheckQueued = String "queued"
toJSON CheckInProgress = String "in_progress"
toJSON CheckCompleted = String "completed"

data CheckConclusion
= CheckActionRequired
| CheckCancelled
| CheckFailure
| CheckNeutral
| CheckSuccess
| CheckSkipped
| CheckStale
| CheckTimedOut
deriving stock (Show, Enum, Bounded, Eq, Ord, Generic)

instance ToJSON CheckConclusion where
toJSON CheckActionRequired = String "action_required"
toJSON CheckCancelled = String "cancelled"
toJSON CheckFailure = String "failure"
toJSON CheckNeutral = String "neutral"
toJSON CheckSuccess = String "success"
toJSON CheckSkipped = String "skipped"
toJSON CheckStale = String "stale"
toJSON CheckTimedOut = String "timed_out"

data CheckOutput = CheckOutput
{ checkTitle :: !Text
, checkSummary :: !Text
, checkText :: !(Maybe Text)
, checkAnnotations :: !(Maybe (Vector CheckAnnotation))
, checkImages :: !(Maybe (Vector CheckImage))
}
deriving stock (Show, Eq, Ord, Generic)

instance ToJSON CheckOutput where
toJSON (CheckOutput t s txt a i) =
object'
[ "title" .= t
, "summary" .= s
, "text" .= txt
, "annotations" .= a
, "images" .= i
]

data CheckAnnotation = CheckAnnotation
{ checkPath :: !Text
, checkStartLine :: !Int
, checkEndLine :: !Int
, checkStartColumn :: !(Maybe Int)
, checkEndColumn :: !(Maybe Int)
, checkAnnotationLevel :: !CheckAnnotationLevel
, checkMessage :: !Text
, checkTitle :: !(Maybe Text)
, checkRawDetails :: !(Maybe Text)
}
deriving stock (Show, Eq, Ord, Generic)

instance ToJSON CheckAnnotation where
toJSON (CheckAnnotation p sl el sc ec al m t rd) =
object'
[ "path" .= p
, "start_line" .= sl
, "end_line" .= el
, "start_column" .= sc
, "end_column" .= ec
, "annotation_level" .= al
, "message" .= m
, "title" .= t
, "raw_details" .= rd
]

data CheckAnnotationLevel
= NoticeAnnotation
| WarningAnnotation
| FailureAnnotation
deriving stock (Show, Enum, Bounded, Eq, Ord, Generic)

instance ToJSON CheckAnnotationLevel where
toJSON NoticeAnnotation = String "notice"
toJSON WarningAnnotation = String "warning"
toJSON FailureAnnotation = String "failure"

data CheckImage = CheckImage
{ checkImageAlt :: !Text
, checkImageURL :: !URL
, checkImageCaption :: !(Maybe Text)
}
deriving stock (Show, Eq, Ord, Generic)

instance ToJSON CheckImage where
toJSON (CheckImage a url c) =
object'
[ "alt" .= a
, "image_url" .= url
, "caption" .= c
]

data CheckAction = CheckAction
{ checkActionLabel :: !Text
, checkActionDescription :: !Text
, checkActionIdentifier :: !Text
}
deriving stock (Show, Eq, Ord, Generic)

instance ToJSON CheckAction where
toJSON (CheckAction l d i) =
object'
[ "label" .= l
, "description" .= d
, "identifier" .= i
]

object' :: [Pair] -> Value
object' = object . filter notNull
where
notNull (_, Null) = False
notNull (_, _) = True
Loading

0 comments on commit 6d8a673

Please sign in to comment.