Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow granular selection of GitHub notification method via destination #304

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions docs/services/github.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@

## Parameters

The GitHub notification service changes commit status using [GitHub Apps](https://docs.github.com/en/developers/apps) and requires specifying the following settings:
The GitHub notification service can notify the following notification types:

- Commit statuses
- Deployments
- Pull request comments

It uses a [GitHub App](https://docs.github.com/en/developers/apps) and requires specifying the following settings:

- `appID` - the app id
- `installationID` - the app installation id
Expand Down Expand Up @@ -46,7 +52,7 @@ stringData:
-----END RSA PRIVATE KEY-----
```

6. Create subscription for your GitHub integration
1. Create subscription for your GitHub integration

```yaml
apiVersion: argoproj.io/v1alpha1
Expand All @@ -56,6 +62,18 @@ metadata:
notifications.argoproj.io/subscribe.<trigger-name>.github: ""
```

By default, the integration updates all configured notification types.
If you have multiple GitHub notification types configured, you can subscribe to a specific type by setting it as the annotation value:

```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
annotations:
notifications.argoproj.io/subscribe.<trigger-name>.github: "status" # or deployment or comment
notifications.argoproj.io/subscribe.<trigger-name>.github: "status;deployment" # 2 of the 3 types
```

## Templates

![](https://user-images.githubusercontent.com/18019529/108520497-168ce180-730e-11eb-93cb-b0b91f99bdc5.png)
Expand Down
213 changes: 124 additions & 89 deletions pkg/services/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ import (
"github.com/argoproj/notifications-engine/pkg/util/text"
)

var (
gitSuffix = regexp.MustCompile(`\.git$`)
)
var gitSuffix = regexp.MustCompile(`\.git$`)

type GitHubOptions struct {
AppID interface{} `json:"appID"`
Expand Down Expand Up @@ -317,7 +315,7 @@ func fullNameByRepoURL(rawURL string) string {
return path
}

func (g gitHubService) Send(notification Notification, _ Destination) error {
func (g gitHubService) Send(notification Notification, dest Destination) error {
if notification.GitHub == nil {
return fmt.Errorf("config is empty")
}
Expand All @@ -326,116 +324,153 @@ func (g gitHubService) Send(notification Notification, _ Destination) error {
if len(u) < 2 {
return fmt.Errorf("GitHub.repoURL (%s) does not have a `/`", notification.GitHub.repoURL)
}
if notification.GitHub.Status != nil {
// maximum is 140 characters
description := trunc(notification.Message, 140)
_, _, err := g.client.Repositories.CreateStatus(
context.Background(),
u[0],
u[1],
notification.GitHub.revision,
&github.RepoStatus{
State: &notification.GitHub.Status.State,
Description: &description,
Context: &notification.GitHub.Status.Label,
TargetURL: &notification.GitHub.Status.TargetURL,
},
)

// match previous behavior:
// send all notifications if a destination is not specified
sendAll := dest.Recipient == ""

if dest.Recipient == "status" || sendAll {
err := g.sendStatus(u[0], u[1], notification)
if err != nil {
return err
}
}

if notification.GitHub.Deployment != nil {
// maximum is 140 characters
description := trunc(notification.Message, 140)
deployments, _, err := g.client.Repositories.ListDeployments(
context.Background(),
u[0],
u[1],
&github.DeploymentsListOptions{
Ref: notification.GitHub.revision,
Environment: notification.GitHub.Deployment.Environment,
},
)
if dest.Recipient == "deployment" || sendAll {
err := g.sendDeployment(u[0], u[1], notification)
if err != nil {
return err
}
}

// if no reference is provided, use the revision
ref := notification.GitHub.Deployment.Reference
if ref == "" {
ref = notification.GitHub.revision
if dest.Recipient == "comment" || sendAll {
err := g.sendComment(u[0], u[1], notification)
if err != nil {
return err
}
}

var deployment *github.Deployment
if len(deployments) != 0 {
deployment = deployments[0]
} else {
deployment, _, err = g.client.Repositories.CreateDeployment(
context.Background(),
u[0],
u[1],
&github.DeploymentRequest{
Ref: &ref,
Environment: &notification.GitHub.Deployment.Environment,
RequiredContexts: &notification.GitHub.Deployment.RequiredContexts,
AutoMerge: notification.GitHub.Deployment.AutoMerge,
TransientEnvironment: notification.GitHub.Deployment.TransientEnvironment,
},
)
if err != nil {
return err
}
}
_, _, err = g.client.Repositories.CreateDeploymentStatus(
return nil
}

func (g gitHubService) sendStatus(owner, repo string, notification Notification) error {
if notification.GitHub.Status == nil {
return nil
}

// maximum is 140 characters
description := trunc(notification.Message, 140)
_, _, err := g.client.Repositories.CreateStatus(
context.Background(),
owner,
repo,
notification.GitHub.revision,
&github.RepoStatus{
State: &notification.GitHub.Status.State,
Description: &description,
Context: &notification.GitHub.Status.Label,
TargetURL: &notification.GitHub.Status.TargetURL,
},
)
return err
}

func (g gitHubService) sendDeployment(owner, repo string, notification Notification) error {
if notification.GitHub.Deployment == nil {
return nil
}

// maximum is 140 characters
description := trunc(notification.Message, 140)
deployments, _, err := g.client.Repositories.ListDeployments(
context.Background(),
owner,
repo,
&github.DeploymentsListOptions{
Ref: notification.GitHub.revision,
Environment: notification.GitHub.Deployment.Environment,
},
)
if err != nil {
return err
}

// if no reference is provided, use the revision
ref := notification.GitHub.Deployment.Reference
if ref == "" {
ref = notification.GitHub.revision
}

var deployment *github.Deployment
if len(deployments) != 0 {
deployment = deployments[0]
} else {
deployment, _, err = g.client.Repositories.CreateDeployment(
context.Background(),
u[0],
u[1],
*deployment.ID,
&github.DeploymentStatusRequest{
State: &notification.GitHub.Deployment.State,
LogURL: &notification.GitHub.Deployment.LogURL,
Description: &description,
Environment: &notification.GitHub.Deployment.Environment,
EnvironmentURL: &notification.GitHub.Deployment.EnvironmentURL,
owner,
repo,
&github.DeploymentRequest{
Ref: &ref,
Environment: &notification.GitHub.Deployment.Environment,
RequiredContexts: &notification.GitHub.Deployment.RequiredContexts,
AutoMerge: notification.GitHub.Deployment.AutoMerge,
TransientEnvironment: notification.GitHub.Deployment.TransientEnvironment,
},
)
if err != nil {
return err
}
}
_, _, err = g.client.Repositories.CreateDeploymentStatus(
context.Background(),
owner,
repo,
*deployment.ID,
&github.DeploymentStatusRequest{
State: &notification.GitHub.Deployment.State,
LogURL: &notification.GitHub.Deployment.LogURL,
Description: &description,
Environment: &notification.GitHub.Deployment.Environment,
EnvironmentURL: &notification.GitHub.Deployment.EnvironmentURL,
},
)

return err
}

if notification.GitHub.PullRequestComment != nil {
// maximum is 65536 characters
body := trunc(notification.GitHub.PullRequestComment.Content, 65536)
comment := &github.IssueComment{
Body: &body,
}
func (g gitHubService) sendComment(owner, repo string, notification Notification) error {
if notification.GitHub.PullRequestComment == nil {
return nil
}

// maximum is 65536 characters
body := trunc(notification.GitHub.PullRequestComment.Content, 65536)
comment := &github.IssueComment{
Body: &body,
}

prs, _, err := g.client.PullRequests.ListPullRequestsWithCommit(
prs, _, err := g.client.PullRequests.ListPullRequestsWithCommit(
context.Background(),
owner,
repo,
notification.GitHub.revision,
nil,
)
if err != nil {
return err
}

for _, pr := range prs {
_, _, err = g.client.Issues.CreateComment(
context.Background(),
u[0],
u[1],
notification.GitHub.revision,
nil,
owner,
repo,
pr.GetNumber(),
comment,
)
if err != nil {
return err
}

for _, pr := range prs {
_, _, err = g.client.Issues.CreateComment(
context.Background(),
u[0],
u[1],
pr.GetNumber(),
comment,
)
if err != nil {
return err
}
}
}

return nil
Expand Down
Loading