Skip to content

Commit

Permalink
Merge branch 'master' into dtan4/support-github-actions-pr
Browse files Browse the repository at this point in the history
  • Loading branch information
KeisukeYamashita authored Apr 8, 2022
2 parents e6b616c + a2897c6 commit 1ebb9ac
Show file tree
Hide file tree
Showing 15 changed files with 359 additions and 48 deletions.
10 changes: 0 additions & 10 deletions .codecov.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# https://help.github.com/articles/about-codeowners/
* @b4b4r07 @dtan4
* @b4b4r07 @dtan4 @drlau @micnncim @KeisukeYamashita @tyuhara
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v1
with:
go-version: 1.13
go-version: '1.14.x'
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v1
with:
Expand Down
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
tfnotify
========

[![][release-svg]][release] [![][test-svg]][test] [![][codecov-svg]][codecov] [![][goreportcard-svg]][goreportcard]
[![][release-svg]][release] [![][test-svg]][test] [![][goreportcard-svg]][goreportcard]

[release]: https://github.com/mercari/tfnotify/actions?query=workflow%3Arelease
[release-svg]: https://github.com/mercari/tfnotify/workflows/release/badge.svg
[test]: https://github.com/mercari/tfnotify/actions?query=workflow%3Atest
[test-svg]: https://github.com/mercari/tfnotify/workflows/test/badge.svg
[codecov]: https://codecov.io/gh/mercari/tfnotify
[codecov-svg]: https://codecov.io/gh/mercari/tfnotify/branch/master/graph/badge.svg
[goreportcard]: https://goreportcard.com/report/github.com/mercari/tfnotify
[goreportcard-svg]: https://goreportcard.com/badge/github.com/mercari/tfnotify

Expand Down Expand Up @@ -44,7 +42,7 @@ $ go get -u github.com/mercari/tfnotify
2. Bind parsed results to Go templates
3. Notify it to any platform (e.g. GitHub) as you like

Detailed specifications such as templates and notification destinations can be customized from the configration files (described later).
Detailed specifications such as templates and notification destinations can be customized from the configuration files (described later).

## Usage

Expand Down Expand Up @@ -380,6 +378,7 @@ Currently, supported CI are here:
- Jenkins
- GitLab CI
- GitHub Actions
- Google Cloud Build
### Private Repository Considerations
GitHub private repositories require the `repo` and `write:discussion` permissions.
Expand All @@ -390,6 +389,18 @@ GitHub private repositories require the `repo` and `write:discussion` permission
- Environment Variable
- `PULL_REQUEST_NUMBER` or `PULL_REQUEST_URL` are required to set by user for Pull Request Usage

### Google Cloud Build Considerations

- These environment variables are needed to be set using [substitutions](https://cloud.google.com/cloud-build/docs/configuring-builds/substitute-variable-values)
- `COMMIT_SHA`
- `BUILD_ID`
- `PROJECT_ID`
- `_PR_NUMBER`
- `_REGION`
- Recommended trigger events
- `terraform plan`: Pull request
- `terraform apply`: Push to branch

## Committers

* Masaki ISHIYAMA ([@b4b4r07](https://github.com/b4b4r07))
Expand Down
25 changes: 25 additions & 0 deletions ci.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
)

var (
defaultCloudBuildRegion = "global"

// https://help.github.com/en/actions/reference/events-that-trigger-workflows#pull-request-event-pull_request
githubActionsPRRefRegexp = regexp.MustCompile(`refs/pull/\d+/merge`)
)
Expand Down Expand Up @@ -163,3 +165,26 @@ func githubActions() (ci CI, err error) {

return ci, err
}

func cloudbuild() (ci CI, err error) {
ci.PR.Number = 0
ci.PR.Revision = os.Getenv("COMMIT_SHA")

region := os.Getenv("_REGION")
if region == "" {
region = defaultCloudBuildRegion
}

ci.URL = fmt.Sprintf(
"https://console.cloud.google.com/cloud-build/builds;region=%s/%s?project=%s",
region,
os.Getenv("BUILD_ID"),
os.Getenv("PROJECT_ID"),
)
pr := os.Getenv("_PR_NUMBER")
if pr == "" {
return ci, nil
}
ci.PR.Number, err = strconv.Atoi(pr)
return ci, err
}
89 changes: 89 additions & 0 deletions ci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -776,3 +776,92 @@ func TestGitHubActions(t *testing.T) {
}
}
}

func TestCloudBuild(t *testing.T) {
envs := []string{
"COMMIT_SHA",
"BUILD_ID",
"PROJECT_ID",
"_PR_NUMBER",
"REGION",
}
saveEnvs := make(map[string]string)
for _, key := range envs {
saveEnvs[key] = os.Getenv(key)
os.Unsetenv(key)
}
defer func() {
for key, value := range saveEnvs {
os.Setenv(key, value)
}
}()

// https://cloud.google.com/cloud-build/docs/configuring-builds/substitute-variable-values
testCases := []struct {
fn func()
ci CI
ok bool
}{
{
fn: func() {
os.Setenv("COMMIT_SHA", "abcdefg")
os.Setenv("BUILD_ID", "build-id")
os.Setenv("PROJECT_ID", "gcp-project-id")
os.Setenv("_PR_NUMBER", "123")
os.Setenv("_REGION", "asia-northeast1")
},
ci: CI{
PR: PullRequest{
Revision: "abcdefg",
Number: 123,
},
URL: "https://console.cloud.google.com/cloud-build/builds;region=asia-northeast1/build-id?project=gcp-project-id",
},
ok: true,
},
{
fn: func() {
os.Setenv("COMMIT_SHA", "")
os.Setenv("BUILD_ID", "build-id")
os.Setenv("PROJECT_ID", "gcp-project-id")
os.Setenv("_PR_NUMBER", "")
os.Setenv("_REGION", "")
},
ci: CI{
PR: PullRequest{
Revision: "",
Number: 0,
},
URL: "https://console.cloud.google.com/cloud-build/builds;region=global/build-id?project=gcp-project-id",
},
ok: true,
},
{
fn: func() {
os.Setenv("COMMIT_SHA", "")
os.Setenv("BUILD_ID", "build-id")
os.Setenv("PROJECT_ID", "gcp-project-id")
os.Setenv("_PR_NUMBER", "abc")
},
ci: CI{
PR: PullRequest{
Revision: "",
Number: 0,
},
URL: "https://console.cloud.google.com/cloud-build/builds;region=global/build-id?project=gcp-project-id",
},
ok: false,
},
}

for _, testCase := range testCases {
testCase.fn()
ci, err := cloudbuild()
if !reflect.DeepEqual(ci, testCase.ci) {
t.Errorf("got %q but want %q", ci, testCase.ci)
}
if (err == nil) != testCase.ok {
t.Errorf("got error %q", err)
}
}
}
2 changes: 2 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ func (cfg *Config) Validation() error {
// ok pattern
case "github-actions":
// ok pattern
case "cloud-build", "cloudbuild":
// ok pattern
default:
return fmt.Errorf("%s: not supported yet", cfg.CI)
}
Expand Down
14 changes: 8 additions & 6 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,14 @@ func TestValidation(t *testing.T) {
contents: []byte("ci: gitlabci\n"),
expected: "notifier is missing",
},
{
contents: []byte("ci: cloudbuild\n"),
expected: "notifier is missing",
},
{
contents: []byte("ci: cloud-build\n"),
expected: "notifier is missing",
},
{
contents: []byte("ci: circleci\nnotifier:\n github:\n"),
expected: "notifier is missing",
Expand Down Expand Up @@ -460,12 +468,6 @@ func TestFind(t *testing.T) {
expect: "tfnotify.yml",
ok: true,
},
{
// invalid config
file: "codecov.yml",
expect: "",
ok: false,
},
{
// in case of no args passed
file: "",
Expand Down
5 changes: 5 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ func (t *tfnotify) Run() error {
if err != nil {
return err
}
case "cloud-build", "cloudbuild":
ci, err = cloudbuild()
if err != nil {
return err
}
case "":
return fmt.Errorf("CI service: required (e.g. circleci)")
default:
Expand Down
10 changes: 4 additions & 6 deletions notifier/github/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ import (
"golang.org/x/oauth2"
)

// EnvToken is GitHub API Token
const EnvToken = "GITHUB_TOKEN"

// EnvBaseURL is GitHub base URL. This can be set to a domain endpoint to use with GitHub Enterprise.
const EnvBaseURL = "GITHUB_BASE_URL"

Expand Down Expand Up @@ -69,10 +66,11 @@ type service struct {
// NewClient returns Client initialized with Config
func NewClient(cfg Config) (*Client, error) {
token := cfg.Token
token = strings.TrimPrefix(token, "$")
if token == EnvToken {
token = os.Getenv(EnvToken)

if strings.HasPrefix(token, "$") {
token = os.Getenv(strings.TrimPrefix(token, "$"))
}

if token == "" {
return &Client{}, errors.New("github token is missing")
}
Expand Down
25 changes: 18 additions & 7 deletions notifier/github/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,11 @@ package github

import (
"os"
"strings"
"testing"
)

func TestNewClient(t *testing.T) {
githubToken := os.Getenv(EnvToken)
defer func() {
os.Setenv(EnvToken, githubToken)
}()
os.Setenv(EnvToken, "")

testCases := []struct {
config Config
envToken string
Expand Down Expand Up @@ -41,12 +36,24 @@ func TestNewClient(t *testing.T) {
envToken: "",
expect: "github token is missing",
},
{
// specify via env but not to be set env (part 3)
config: Config{Token: "$TFNOTIFY_GITHUB_TOKEN"},
envToken: "",
expect: "github token is missing",
},
{
// specify via env (part 2)
config: Config{Token: "$GITHUB_TOKEN"},
envToken: "abcdefg",
expect: "",
},
{
// specify via env (part 3)
config: Config{Token: "$TFNOTIFY_GITHUB_TOKEN"},
envToken: "abcdefg",
expect: "",
},
{
// no specification (part 1)
config: Config{},
Expand All @@ -61,7 +68,11 @@ func TestNewClient(t *testing.T) {
},
}
for _, testCase := range testCases {
os.Setenv(EnvToken, testCase.envToken)
if strings.HasPrefix(testCase.config.Token, "$") {
key := strings.TrimPrefix(testCase.config.Token, "$")
os.Setenv(key, testCase.envToken)
}

_, err := NewClient(testCase.config)
if err == nil {
continue
Expand Down
1 change: 1 addition & 0 deletions notifier/slack/notify.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func (s *NotifyService) Notify(body string) (exit int, err error) {
Message: cfg.Message,
Result: result.Result,
Body: body,
Link: cfg.CI,
})
text, err := template.Execute()
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions terraform/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ func NewFmtParser() *FmtParser {
// NewPlanParser is PlanParser initialized with its Regexp
func NewPlanParser() *PlanParser {
return &PlanParser{
Pass: regexp.MustCompile(`(?m)^(Plan: \d|No changes.)`),
Fail: regexp.MustCompile(`(?m)^(Error: )`),
Pass: regexp.MustCompile(`(?m)^((Plan: \d|No changes.)|(Changes to Outputs:))`),
Fail: regexp.MustCompile(`(?m)^(│\s{1})?(Error: )`),
// "0 to destroy" should be treated as "no destroy"
HasDestroy: regexp.MustCompile(`(?m)([1-9][0-9]* to destroy.)`),
HasNoChanges: regexp.MustCompile(`(?m)^(No changes. Infrastructure is up-to-date.)`),
Expand All @@ -73,7 +73,7 @@ func NewPlanParser() *PlanParser {
func NewApplyParser() *ApplyParser {
return &ApplyParser{
Pass: regexp.MustCompile(`(?m)^(Apply complete!)`),
Fail: regexp.MustCompile(`(?m)^(Error: )`),
Fail: regexp.MustCompile(`(?m)^(│\s{1})?(Error: )`),
}
}

Expand Down
Loading

0 comments on commit 1ebb9ac

Please sign in to comment.