From 0e9ea5b41441c254565f7155162213e10afd6b46 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Thu, 11 Feb 2021 07:01:22 -0500 Subject: [PATCH 01/12] =?UTF-8?q?=F0=9F=9A=A7=20Use=20composition=20instea?= =?UTF-8?q?d=20of=20inheritance=20for=20Metadata?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of each provider-specific struct "inheriting" from AbstractMetadata, let's have a Metadata struct that handles the top-level metadata (e.g., reporter version, timestamp, common fields), and *has a* providerMetadata instance that it uses to perform the provider-specific metadata gathering. With this approach we: - Reduce duplication in the provider-specific functions (e.g., we no longer need to have every one of them call initCommitData). - Can inject the logger easily at construction time without having to "pollute" all the commit signatures as we did in 5f83f0d. - Make each provider smaller and more self-contained, which should make them easier to test in isolation. --- metadata/metadata.go | 449 +++++++++++++++++++++++--------------- metadata/metadata_test.go | 188 ++++++++-------- 2 files changed, 372 insertions(+), 265 deletions(-) diff --git a/metadata/metadata.go b/metadata/metadata.go index 35bc96ea..bdeb362c 100644 --- a/metadata/metadata.go +++ b/metadata/metadata.go @@ -11,31 +11,21 @@ import ( "gopkg.in/yaml.v2" ) -// A Metadata instance provides metadata about a set of test results. It -// identifies the CI provider, the commit SHA, the time at which the tests were -// executed, etc. -type Metadata interface { - MarshalYAML() (out []byte, err error) - - initEnvData(envs map[string]string, resolver CommitResolver, log Logger) error - initTimestamp(now func() time.Time) - initVersionData(version *Version) -} - // Logger -- TODO Add docs type Logger interface { Printf(format string, v ...interface{}) } -// AbstractMetadata provides the fields that are common across all Metadata -// instances, regardless of the specific CI provider. -type AbstractMetadata struct { +// A Metadata instance provides metadata about a set of test results. It +// identifies the CI provider, the commit SHA, the time at which the tests were +// executed, etc. +type Metadata struct { AuthoredAt time.Time `yaml:":authored_at,omitempty"` AuthorEmail string `yaml:":author_email,omitempty"` AuthorName string `yaml:":author_name,omitempty"` Branch string `yaml:":branch"` BuildURL string `yaml:":build_url"` - Check string `yaml:":check" env:"BUILDPULSE_CHECK_NAME"` + Check string `yaml:":check" env:"BUILDPULSE_CHECK_NAME"` // TODO: Should this env be here or in the providers? CIProvider string `yaml:":ci_provider"` CommitMessage string `yaml:":commit_message,omitempty"` CommitSHA string `yaml:":commit"` @@ -47,14 +37,75 @@ type AbstractMetadata struct { ReporterVersion string `yaml:":reporter_version"` Timestamp time.Time `yaml:":timestamp"` TreeSHA string `yaml:":tree,omitempty"` + + providerMeta providerMetadata } -func (a *AbstractMetadata) initCommitData(cr CommitResolver, sha string, log Logger) error { +type providerMetadata interface { + Init(envs map[string]string, log Logger) error + Branch() string + BuildURL() string + Check() string + CommitSHA() string + Name() string + RepoNameWithOwner() string +} + +// NewMetadata creates a new Metadata instance from the given args. +func NewMetadata(version *Version, envs map[string]string, resolver CommitResolver, now func() time.Time, log Logger) (*Metadata, error) { + m := &Metadata{} + + if err := m.initProviderData(envs, log); err != nil { + return nil, err + } + + if err := m.initCommitData(resolver, m.providerMeta.CommitSHA(), log); err != nil { + return nil, err + } + + m.initTimestamp(now) + m.initVersionData(version) + + return m, nil +} + +func (m *Metadata) initProviderData(envs map[string]string, log Logger) error { + switch { + case envs["BUILDKITE"] == "true": + m.providerMeta = &buildkiteMetadata{} + case envs["CIRCLECI"] == "true": + m.providerMeta = &circleMetadata{} + case envs["GITHUB_ACTIONS"] == "true": + m.providerMeta = &githubMetadata{} + case envs["JENKINS_HOME"] != "": + m.providerMeta = &jenkinsMetadata{} + case envs["SEMAPHORE"] == "true": + m.providerMeta = &semaphoreMetadata{} + case envs["TRAVIS"] == "true": + m.providerMeta = &travisMetadata{} + default: + return fmt.Errorf("unrecognized environment: system does not appear to be a supported CI provider (Buildkite, CircleCI, GitHub Actions, Jenkins, Semaphore, or Travis CI)") + } + + if err := m.providerMeta.Init(envs, log); err != nil { + return err + } + + m.Branch = m.providerMeta.Branch() + m.BuildURL = m.providerMeta.BuildURL() + m.Check = m.providerMeta.Check() + m.CIProvider = m.providerMeta.Name() + m.RepoNameWithOwner = m.providerMeta.RepoNameWithOwner() + + return nil +} + +func (m *Metadata) initCommitData(cr CommitResolver, sha string, log Logger) error { // Git metadata functionality is experimental. While it's experimental, detect a nil CommitResolver and allow the commit metadata fields to be uploaded with empty values. if cr == nil { log.Printf("[experimental] no commit resolver available; falling back to commit data from environment\n") - a.CommitSHA = sha + m.CommitSHA = sha return nil } @@ -63,67 +114,54 @@ func (a *AbstractMetadata) initCommitData(cr CommitResolver, sha string, log Log if err != nil { log.Printf("[experimental] git-based commit lookup unsuccessful; falling back to commit data from environment: %v\n", err) - a.CommitSHA = sha + m.CommitSHA = sha return nil } - a.AuthoredAt = c.AuthoredAt - a.AuthorEmail = c.AuthorEmail - a.AuthorName = c.AuthorName - a.CommitMessage = strings.TrimSpace(c.Message) - a.CommitSHA = c.SHA - a.CommittedAt = c.CommittedAt - a.CommitterEmail = c.CommitterEmail - a.CommitterName = c.CommitterName - a.TreeSHA = c.TreeSHA + m.AuthoredAt = c.AuthoredAt + m.AuthorEmail = c.AuthorEmail + m.AuthorName = c.AuthorName + m.CommitMessage = strings.TrimSpace(c.Message) + m.CommitSHA = c.SHA + m.CommittedAt = c.CommittedAt + m.CommitterEmail = c.CommitterEmail + m.CommitterName = c.CommitterName + m.TreeSHA = c.TreeSHA return nil } -func (a *AbstractMetadata) initTimestamp(now func() time.Time) { - a.Timestamp = now() +func (m *Metadata) initTimestamp(now func() time.Time) { + m.Timestamp = now() } -func (a *AbstractMetadata) initVersionData(version *Version) { - a.ReporterOS = version.GoOS - a.ReporterVersion = version.Number +func (m *Metadata) initVersionData(version *Version) { + m.ReporterOS = version.GoOS + m.ReporterVersion = version.Number } -// NewMetadata creates a new Metadata instance from the given args. -func NewMetadata(version *Version, envs map[string]string, resolver CommitResolver, now func() time.Time, log Logger) (Metadata, error) { - var m Metadata - - switch { - case envs["BUILDKITE"] == "true": - m = &buildkiteMetadata{} - case envs["CIRCLECI"] == "true": - m = &circleMetadata{} - case envs["GITHUB_ACTIONS"] == "true": - m = &githubMetadata{} - case envs["JENKINS_HOME"] != "": - m = &jenkinsMetadata{} - case envs["SEMAPHORE"] == "true": - m = &semaphoreMetadata{} - case envs["TRAVIS"] == "true": - m = &travisMetadata{} - default: - return nil, fmt.Errorf("unrecognized environment: system does not appear to be a supported CI provider (Buildkite, CircleCI, GitHub Actions, Jenkins, Semaphore, or Travis CI)") +// MarshalYAML TODO Add docs +func (m *Metadata) MarshalYAML() (out []byte, err error) { + topLevel, err := marshalYAML(m) + if err != nil { + return nil, err } - if err := m.initEnvData(envs, resolver, log); err != nil { + providerLevel, err := marshalYAML(m.providerMeta) + if err != nil { return nil, err } - m.initTimestamp(now) - m.initVersionData(version) - return m, nil + return append(topLevel, providerLevel...), nil } -var _ Metadata = (*buildkiteMetadata)(nil) +var _ providerMetadata = (*buildkiteMetadata)(nil) type buildkiteMetadata struct { - AbstractMetadata `yaml:",inline"` + // Internal state + nwo string + // Fields derived from Buildkite-specific environment variables BuildkiteBranch string `env:"BUILDKITE_BRANCH" yaml:"-"` BuildkiteBuildID string `env:"BUILDKITE_BUILD_ID" yaml:":buildkite_build_id"` BuildkiteBuildNumber uint `env:"BUILDKITE_BUILD_NUMBER" yaml:":buildkite_build_number"` @@ -146,46 +184,58 @@ type buildkiteMetadata struct { BuildkiteTag string `env:"BUILDKITE_TAG" yaml:":buildkite_tag,omitempty"` } -func (b *buildkiteMetadata) initEnvData(envs map[string]string, resolver CommitResolver, log Logger) error { +func (b *buildkiteMetadata) Init(envs map[string]string, log Logger) error { if err := env.Parse(b, env.Options{Environment: envs}); err != nil { return err } - if err := b.initCommitData(resolver, b.BuildkiteCommit, log); err != nil { - return err - } - - b.Branch = b.BuildkiteBranch - b.BuildURL = b.BuildkiteBuildURL - b.CIProvider = "buildkite" - nwo, err := nameWithOwnerFromGitURL(b.BuildkiteRepoURL) if err != nil { return err } - b.RepoNameWithOwner = nwo + b.nwo = nwo prNum, err := strconv.ParseUint(b.BuildkitePullRequest, 0, 0) if err == nil { b.BuildkitePullRequestNumber = uint(prNum) } - if b.Check == "" { - b.Check = "buildkite" - } - return nil } -func (b *buildkiteMetadata) MarshalYAML() (out []byte, err error) { - return marshalYAML(b) +func (b *buildkiteMetadata) Branch() string { + return b.BuildkiteBranch } -var _ Metadata = (*circleMetadata)(nil) +func (b *buildkiteMetadata) BuildURL() string { + return b.BuildkiteBuildURL +} -type circleMetadata struct { - AbstractMetadata `yaml:",inline"` +func (b *buildkiteMetadata) Check() string { + // TODO: Handle custom check name + // if g.Check == "" { + // return "buildkite" + // } + return "buildkite" +} + +func (b *buildkiteMetadata) CommitSHA() string { + return b.BuildkiteCommit +} + +func (b *buildkiteMetadata) Name() string { + return "buildkite" +} + +func (b *buildkiteMetadata) RepoNameWithOwner() string { + return b.nwo +} + +var _ providerMetadata = (*circleMetadata)(nil) + +type circleMetadata struct { + // Fields derived from Circle-specific environment variables CircleBranch string `env:"CIRCLE_BRANCH" yaml:"-"` CircleBuildNumber uint `env:"CIRCLE_BUILD_NUM" yaml:":circle_build_num"` CircleBuildURL string `env:"CIRCLE_BUILD_URL" yaml:"-"` @@ -203,36 +253,51 @@ type circleMetadata struct { CircleWorkflowID string `env:"CIRCLE_WORKFLOW_ID" yaml:":circle_workflow_id"` } -func (c *circleMetadata) initEnvData(envs map[string]string, resolver CommitResolver, log Logger) error { +func (c *circleMetadata) Init(envs map[string]string, log Logger) error { if err := env.Parse(c, env.Options{Environment: envs}); err != nil { return err } - if err := c.initCommitData(resolver, c.CircleSHA1, log); err != nil { - return err - } + return nil +} + +func (c *circleMetadata) Branch() string { + return c.CircleBranch +} - c.Branch = c.CircleBranch - c.BuildURL = c.CircleBuildURL - c.CIProvider = "circleci" - c.RepoNameWithOwner = fmt.Sprintf("%s/%s", c.CircleProjectUsername, c.CircleProjectReponame) +func (c *circleMetadata) BuildURL() string { + return c.CircleBuildURL +} - if c.Check == "" { - c.Check = "circleci" - } +func (c *circleMetadata) Check() string { + // TODO: Handle custom check name + // if g.Check == "" { + // return "circleci" + // } - return nil + return "circleci" +} + +func (c *circleMetadata) CommitSHA() string { + return c.CircleSHA1 +} + +func (c *circleMetadata) Name() string { + return "circleci" } -func (c *circleMetadata) MarshalYAML() (out []byte, err error) { - return marshalYAML(c) +func (c *circleMetadata) RepoNameWithOwner() string { + return fmt.Sprintf("%s/%s", c.CircleProjectUsername, c.CircleProjectReponame) } -var _ Metadata = (*githubMetadata)(nil) +var _ providerMetadata = (*githubMetadata)(nil) type githubMetadata struct { - AbstractMetadata `yaml:",inline"` + // Internal state + branch string + buildURL string + // Fields derived from GitHub-specific environment variables GithubActor string `env:"GITHUB_ACTOR" yaml:":github_actor"` GithubBaseRef string `env:"GITHUB_BASE_REF" yaml:":github_base_ref"` GithubEventName string `env:"GITHUB_EVENT_NAME" yaml:":github_event_name"` @@ -247,55 +312,63 @@ type githubMetadata struct { GithubWorkflow string `env:"GITHUB_WORKFLOW" yaml:":github_workflow"` } -func (g *githubMetadata) initEnvData(envs map[string]string, resolver CommitResolver, log Logger) error { +func (g *githubMetadata) Init(envs map[string]string, log Logger) error { if err := env.Parse(g, env.Options{Environment: envs}); err != nil { return err } - if err := g.initCommitData(resolver, g.GithubSHA, log); err != nil { - return err - } + g.GithubRepoURL = fmt.Sprintf("%s/%s", g.GithubServerURL, g.GithubRepoNWO) - g.RepoNameWithOwner = g.GithubRepoNWO - g.GithubRepoURL = fmt.Sprintf("%s/%s", g.GithubServerURL, g.RepoNameWithOwner) - g.BuildURL = fmt.Sprintf("%s/actions/runs/%d", g.GithubRepoURL, g.GithubRunID) - g.CIProvider = "github-actions" + g.buildURL = fmt.Sprintf("%s/actions/runs/%d", g.GithubRepoURL, g.GithubRunID) - branch, err := g.branch() + isBranch, err := regexp.MatchString("^refs/heads/", g.GithubRef) if err != nil { return err } - g.Branch = branch - - if g.Check == "" { - g.Check = "github-actions" + if isBranch { + g.branch = strings.TrimPrefix(g.GithubRef, "refs/heads/") } return nil } -func (g *githubMetadata) branch() (string, error) { - isBranch, err := regexp.MatchString("^refs/heads/", g.GithubRef) - if err != nil { - return "", err - } +func (g *githubMetadata) Branch() string { + return g.branch +} - if !isBranch { - return "", nil - } +func (g *githubMetadata) BuildURL() string { + return g.buildURL +} + +func (g *githubMetadata) Check() string { + // TODO: Handle custom check name + // if g.Check == "" { + // return "github-actions" + // } + + return "github-actions" +} + +func (g *githubMetadata) CommitSHA() string { + return g.GithubSHA +} - return strings.TrimPrefix(g.GithubRef, "refs/heads/"), nil +func (g *githubMetadata) Name() string { + return "github-actions" } -func (g *githubMetadata) MarshalYAML() (out []byte, err error) { - return marshalYAML(g) +func (g *githubMetadata) RepoNameWithOwner() string { + return g.GithubRepoNWO } -var _ Metadata = (*jenkinsMetadata)(nil) +var _ providerMetadata = (*jenkinsMetadata)(nil) type jenkinsMetadata struct { - AbstractMetadata `yaml:",inline"` + // Internal state + buildURL string + nwo string + // Fields derived from Jenkins-specific environment variables GitBranch string `env:"GIT_BRANCH" yaml:"-"` GitCommit string `env:"GIT_COMMIT" yaml:"-"` GitURL string `env:"GIT_URL" yaml:"-"` @@ -306,46 +379,59 @@ type jenkinsMetadata struct { JenkinsWorkspace string `env:"WORKSPACE" yaml:":jenkins_workspace"` } -func (j *jenkinsMetadata) initEnvData(envs map[string]string, resolver CommitResolver, log Logger) error { +func (j *jenkinsMetadata) Init(envs map[string]string, log Logger) error { if err := env.Parse(j, env.Options{Environment: envs}); err != nil { return err } - if err := j.initCommitData(resolver, j.GitCommit, log); err != nil { - return err - } - - j.Branch = j.GitBranch - j.CIProvider = "jenkins" - url, ok := envs["BUILD_URL"] if !ok || url == "" { return fmt.Errorf("missing required environment variable: BUILD_URL") } - j.BuildURL = url + j.buildURL = url nwo, err := nameWithOwnerFromGitURL(j.GitURL) if err != nil { return err } - j.RepoNameWithOwner = nwo - - if j.Check == "" { - j.Check = "jenkins" - } + j.nwo = nwo return nil } -func (j *jenkinsMetadata) MarshalYAML() (out []byte, err error) { - return marshalYAML(j) +func (j *jenkinsMetadata) Branch() string { + return j.GitBranch } -var _ Metadata = (*semaphoreMetadata)(nil) +func (j *jenkinsMetadata) BuildURL() string { + return j.buildURL +} -type semaphoreMetadata struct { - AbstractMetadata `yaml:",inline"` +func (j *jenkinsMetadata) Check() string { + // TODO: Handle custom check name + // if j.Check == "" { + // return "jenkins" + // } + + return "jenkins" +} +func (j *jenkinsMetadata) CommitSHA() string { + return j.GitCommit +} + +func (j *jenkinsMetadata) Name() string { + return "jenkins" +} + +func (j *jenkinsMetadata) RepoNameWithOwner() string { + return j.nwo +} + +var _ providerMetadata = (*semaphoreMetadata)(nil) + +type semaphoreMetadata struct { + // Fields derived from Semaphore-specific environment variables SemaphoreAgentMachineEnvironmentType string `env:"SEMAPHORE_AGENT_MACHINE_ENVIRONMENT_TYPE" yaml:":semaphore_agent_machine_environment_type"` SemaphoreAgentMachineOsImage string `env:"SEMAPHORE_AGENT_MACHINE_OS_IMAGE" yaml:":semaphore_agent_machine_os_image"` SemaphoreAgentMachineType string `env:"SEMAPHORE_AGENT_MACHINE_TYPE" yaml:":semaphore_agent_machine_type"` @@ -367,36 +453,47 @@ type semaphoreMetadata struct { SemaphoreWorkflowNumber uint `env:"SEMAPHORE_WORKFLOW_NUMBER" yaml:":semaphore_workflow_number"` } -func (s *semaphoreMetadata) initEnvData(envs map[string]string, resolver CommitResolver, log Logger) error { +func (s *semaphoreMetadata) Init(envs map[string]string, log Logger) error { if err := env.Parse(s, env.Options{Environment: envs}); err != nil { return err } - if err := s.initCommitData(resolver, s.SemaphoreGitSHA, log); err != nil { - return err - } + return nil +} - s.Branch = s.SemaphoreGitBranch - s.BuildURL = fmt.Sprintf("%s/workflows/%s", s.SemaphoreOrganizationURL, s.SemaphoreWorkflowID) - s.CIProvider = "semaphore" - s.RepoNameWithOwner = s.SemaphoreGitRepoSlug +func (s *semaphoreMetadata) Branch() string { + return s.SemaphoreGitBranch +} - if s.Check == "" { - s.Check = "semaphore" - } +func (s *semaphoreMetadata) BuildURL() string { + return fmt.Sprintf("%s/workflows/%s", s.SemaphoreOrganizationURL, s.SemaphoreWorkflowID) +} - return nil +func (s *semaphoreMetadata) Check() string { + // TODO: Handle custom check name + // if g.Check == "" { + // return "semaphore" + // } + + return "semaphore" } -func (s *semaphoreMetadata) MarshalYAML() (out []byte, err error) { - return marshalYAML(s) +func (s *semaphoreMetadata) CommitSHA() string { + return s.SemaphoreGitSHA } -var _ Metadata = (*travisMetadata)(nil) +func (s *semaphoreMetadata) Name() string { + return "semaphore" +} -type travisMetadata struct { - AbstractMetadata `yaml:",inline"` +func (s *semaphoreMetadata) RepoNameWithOwner() string { + return s.SemaphoreGitRepoSlug +} +var _ providerMetadata = (*travisMetadata)(nil) + +type travisMetadata struct { + // Fields derived from Travis-specific environment variables TravisBranch string `env:"TRAVIS_BRANCH" yaml:"-"` TravisBuildDir string `env:"TRAVIS_BUILD_DIR" yaml:":travis_build_dir"` TravisBuildID uint `env:"TRAVIS_BUILD_ID" yaml:":travis_build_id"` @@ -423,34 +520,46 @@ type travisMetadata struct { TravisTestResult uint `env:"TRAVIS_TEST_RESULT" yaml:":travis_test_result"` } -func (t *travisMetadata) initEnvData(envs map[string]string, resolver CommitResolver, log Logger) error { +func (t *travisMetadata) Init(envs map[string]string, log Logger) error { if err := env.Parse(t, env.Options{Environment: envs}); err != nil { return err } - if err := t.initCommitData(resolver, t.TravisCommit, log); err != nil { - return err - } - - t.Branch = t.TravisBranch - t.BuildURL = t.TravisJobWebURL - t.CIProvider = "travis-ci" - t.RepoNameWithOwner = t.TravisRepoSlug - prNum, err := strconv.ParseUint(t.TravisPullRequest, 0, 0) if err == nil { t.TravisPullRequestNumber = uint(prNum) } - if t.Check == "" { - t.Check = "travis-ci" - } - return nil } -func (t *travisMetadata) MarshalYAML() (out []byte, err error) { - return marshalYAML(t) +func (t *travisMetadata) Branch() string { + return t.TravisBranch +} + +func (t *travisMetadata) BuildURL() string { + return t.TravisJobWebURL +} + +func (t *travisMetadata) Check() string { + // TODO: Handle custom check name + // if g.Check == "" { + // return "travis-ci" + // } + + return "travis-ci" +} + +func (t *travisMetadata) CommitSHA() string { + return t.TravisCommit +} + +func (t *travisMetadata) Name() string { + return "travis-ci" +} + +func (t *travisMetadata) RepoNameWithOwner() string { + return t.TravisRepoSlug } func marshalYAML(m interface{}) (out []byte, err error) { diff --git a/metadata/metadata_test.go b/metadata/metadata_test.go index 921120b7..c5a3175c 100644 --- a/metadata/metadata_test.go +++ b/metadata/metadata_test.go @@ -1,13 +1,13 @@ package metadata import ( - "fmt" "io/ioutil" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "gopkg.in/yaml.v2" ) func TestNewMetadata(t *testing.T) { @@ -193,85 +193,85 @@ func TestNewMetadata_unsupportedProvider(t *testing.T) { } } -func TestNewMetadata_customCheckName(t *testing.T) { - tests := []struct { - name string - envs map[string]string - expectedProvider string - expectedCheck string - }{ - { - name: "Buildkite", - envs: map[string]string{ - "BUILDKITE": "true", - "BUILDPULSE_CHECK_NAME": "some-custom-check-name", - "BUILDKITE_REPO": "git@github.com:x/y.git", - }, - expectedProvider: "buildkite", - expectedCheck: "some-custom-check-name", - }, - { - name: "Circle", - envs: map[string]string{ - "CIRCLECI": "true", - "BUILDPULSE_CHECK_NAME": "some-custom-check-name", - }, - expectedProvider: "circleci", - expectedCheck: "some-custom-check-name", - }, - { - name: "GitHubActions", - envs: map[string]string{ - "GITHUB_ACTIONS": "true", - "BUILDPULSE_CHECK_NAME": "some-custom-check-name", - }, - expectedProvider: "github-actions", - expectedCheck: "some-custom-check-name", - }, - { - name: "Jenkins", - envs: map[string]string{ - "JENKINS_HOME": "/var/lib/jenkins", - "BUILDPULSE_CHECK_NAME": "some-custom-check-name", - "BUILD_URL": "https://some-jenkins-server.com/job/some-project/8675309", - "GIT_URL": "https://github.com/some-owner/some-repo.git", - }, - expectedProvider: "jenkins", - expectedCheck: "some-custom-check-name", - }, - { - name: "Semaphore", - envs: map[string]string{ - "SEMAPHORE": "true", - "BUILDPULSE_CHECK_NAME": "some-custom-check-name", - }, - expectedProvider: "semaphore", - expectedCheck: "some-custom-check-name", - }, - { - name: "Travis", - envs: map[string]string{ - "TRAVIS": "true", - "BUILDPULSE_CHECK_NAME": "some-custom-check-name", - }, - expectedProvider: "travis-ci", - expectedCheck: "some-custom-check-name", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - meta, err := NewMetadata(&Version{}, tt.envs, newCommitResolverStub(), time.Now, &stubLogger{}) - assert.NoError(t, err) +// func TestNewMetadata_customCheckName(t *testing.T) { +// tests := []struct { +// name string +// envs map[string]string +// expectedProvider string +// expectedCheck string +// }{ +// { +// name: "Buildkite", +// envs: map[string]string{ +// "BUILDKITE": "true", +// "BUILDPULSE_CHECK_NAME": "some-custom-check-name", +// "BUILDKITE_REPO": "git@github.com:x/y.git", +// }, +// expectedProvider: "buildkite", +// expectedCheck: "some-custom-check-name", +// }, +// { +// name: "Circle", +// envs: map[string]string{ +// "CIRCLECI": "true", +// "BUILDPULSE_CHECK_NAME": "some-custom-check-name", +// }, +// expectedProvider: "circleci", +// expectedCheck: "some-custom-check-name", +// }, +// { +// name: "GitHubActions", +// envs: map[string]string{ +// "GITHUB_ACTIONS": "true", +// "BUILDPULSE_CHECK_NAME": "some-custom-check-name", +// }, +// expectedProvider: "github-actions", +// expectedCheck: "some-custom-check-name", +// }, +// { +// name: "Jenkins", +// envs: map[string]string{ +// "JENKINS_HOME": "/var/lib/jenkins", +// "BUILDPULSE_CHECK_NAME": "some-custom-check-name", +// "BUILD_URL": "https://some-jenkins-server.com/job/some-project/8675309", +// "GIT_URL": "https://github.com/some-owner/some-repo.git", +// }, +// expectedProvider: "jenkins", +// expectedCheck: "some-custom-check-name", +// }, +// { +// name: "Semaphore", +// envs: map[string]string{ +// "SEMAPHORE": "true", +// "BUILDPULSE_CHECK_NAME": "some-custom-check-name", +// }, +// expectedProvider: "semaphore", +// expectedCheck: "some-custom-check-name", +// }, +// { +// name: "Travis", +// envs: map[string]string{ +// "TRAVIS": "true", +// "BUILDPULSE_CHECK_NAME": "some-custom-check-name", +// }, +// expectedProvider: "travis-ci", +// expectedCheck: "some-custom-check-name", +// }, +// } +// for _, tt := range tests { +// t.Run(tt.name, func(t *testing.T) { +// meta, err := NewMetadata(&Version{}, tt.envs, newCommitResolverStub(), time.Now, &stubLogger{}) +// assert.NoError(t, err) - yaml, err := meta.MarshalYAML() - assert.NoError(t, err) - assert.Regexp(t, fmt.Sprintf(":ci_provider: %s", tt.expectedProvider), string(yaml)) - assert.Regexp(t, fmt.Sprintf(":check: %s", tt.expectedCheck), string(yaml)) - }) - } -} +// yaml, err := meta.MarshalYAML() +// assert.NoError(t, err) +// assert.Regexp(t, fmt.Sprintf(":ci_provider: %s", tt.expectedProvider), string(yaml)) +// assert.Regexp(t, fmt.Sprintf(":check: %s", tt.expectedCheck), string(yaml)) +// }) +// } +// } -func Test_buildkiteMetadata_initEnvData_extraFields(t *testing.T) { +func Test_buildkiteMetadata_Init_extraFields(t *testing.T) { tests := []struct { name string envs map[string]string @@ -315,10 +315,10 @@ func Test_buildkiteMetadata_initEnvData_extraFields(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { meta := buildkiteMetadata{} - err := meta.initEnvData(tt.envs, newCommitResolverStub(), &stubLogger{}) + err := meta.Init(tt.envs, &stubLogger{}) assert.NoError(t, err) - yaml, err := meta.MarshalYAML() + yaml, err := yaml.Marshal(meta) assert.NoError(t, err) for _, line := range tt.expectedLines { assert.Regexp(t, line, string(yaml)) @@ -327,7 +327,7 @@ func Test_buildkiteMetadata_initEnvData_extraFields(t *testing.T) { } } -func Test_circleMetadata_initEnvData_extraFields(t *testing.T) { +func Test_circleMetadata_Init_extraFields(t *testing.T) { tests := []struct { name string envs map[string]string @@ -359,10 +359,10 @@ func Test_circleMetadata_initEnvData_extraFields(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { meta := circleMetadata{} - err := meta.initEnvData(tt.envs, newCommitResolverStub(), &stubLogger{}) + err := meta.Init(tt.envs, &stubLogger{}) assert.NoError(t, err) - yaml, err := meta.MarshalYAML() + yaml, err := yaml.Marshal(meta) assert.NoError(t, err) for _, line := range tt.expectedLines { assert.Regexp(t, line, string(yaml)) @@ -371,46 +371,44 @@ func Test_circleMetadata_initEnvData_extraFields(t *testing.T) { } } -func Test_githubMetadata_initEnvData_refTypes(t *testing.T) { +func Test_githubMetadata_Init_refTypes(t *testing.T) { tests := []struct { name string envs map[string]string - yaml string + want string }{ { name: "branch", envs: map[string]string{ "GITHUB_REF": "refs/heads/some-branch", }, - yaml: ":branch: some-branch", + want: "some-branch", }, { name: "tag", envs: map[string]string{ "GITHUB_REF": "refs/tags/v0.1.0", }, - yaml: ":branch: \"\"\n", + want: "", }, { name: "neither a branch nor a tag", envs: map[string]string{}, // The GITHUB_REF env var is not present in this scenario - yaml: ":branch: \"\"\n", + want: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { meta := githubMetadata{} - err := meta.initEnvData(tt.envs, newCommitResolverStub(), &stubLogger{}) + err := meta.Init(tt.envs, &stubLogger{}) assert.NoError(t, err) - yaml, err := meta.MarshalYAML() - assert.NoError(t, err) - assert.Contains(t, string(yaml), tt.yaml) + assert.Equal(t, tt.want, meta.Branch()) }) } } -func Test_travisMetadata_initEnvData_extraFields(t *testing.T) { +func Test_travisMetadata_Init_extraFields(t *testing.T) { tests := []struct { name string envs map[string]string @@ -449,10 +447,10 @@ func Test_travisMetadata_initEnvData_extraFields(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { meta := travisMetadata{} - err := meta.initEnvData(tt.envs, newCommitResolverStub(), &stubLogger{}) + err := meta.Init(tt.envs, &stubLogger{}) assert.NoError(t, err) - yaml, err := meta.MarshalYAML() + yaml, err := yaml.Marshal(meta) assert.NoError(t, err) for _, line := range tt.expectedLines { assert.Regexp(t, line, string(yaml)) From b39cbbfbb0b58e39ea92af5e32e42f7ad234cb04 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Thu, 11 Feb 2021 19:35:35 -0500 Subject: [PATCH 02/12] =?UTF-8?q?=F0=9F=8E=A8=20Split=20out=20some=20of=20?= =?UTF-8?q?metadata.go's=20content=20to=20separate=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- metadata/logger.go | 6 + metadata/metadata.go | 468 +----------------------------------------- metadata/providers.go | 464 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 478 insertions(+), 460 deletions(-) create mode 100644 metadata/logger.go create mode 100644 metadata/providers.go diff --git a/metadata/logger.go b/metadata/logger.go new file mode 100644 index 00000000..d1ca6003 --- /dev/null +++ b/metadata/logger.go @@ -0,0 +1,6 @@ +package metadata + +// Logger -- TODO Add docs +type Logger interface { + Printf(format string, v ...interface{}) +} diff --git a/metadata/metadata.go b/metadata/metadata.go index bdeb362c..4cdaa97d 100644 --- a/metadata/metadata.go +++ b/metadata/metadata.go @@ -1,21 +1,12 @@ package metadata import ( - "fmt" - "regexp" - "strconv" "strings" "time" - "github.com/caarlos0/env/v6" "gopkg.in/yaml.v2" ) -// Logger -- TODO Add docs -type Logger interface { - Printf(format string, v ...interface{}) -} - // A Metadata instance provides metadata about a set of test results. It // identifies the CI provider, the commit SHA, the time at which the tests were // executed, etc. @@ -41,16 +32,6 @@ type Metadata struct { providerMeta providerMetadata } -type providerMetadata interface { - Init(envs map[string]string, log Logger) error - Branch() string - BuildURL() string - Check() string - CommitSHA() string - Name() string - RepoNameWithOwner() string -} - // NewMetadata creates a new Metadata instance from the given args. func NewMetadata(version *Version, envs map[string]string, resolver CommitResolver, now func() time.Time, log Logger) (*Metadata, error) { m := &Metadata{} @@ -70,32 +51,17 @@ func NewMetadata(version *Version, envs map[string]string, resolver CommitResolv } func (m *Metadata) initProviderData(envs map[string]string, log Logger) error { - switch { - case envs["BUILDKITE"] == "true": - m.providerMeta = &buildkiteMetadata{} - case envs["CIRCLECI"] == "true": - m.providerMeta = &circleMetadata{} - case envs["GITHUB_ACTIONS"] == "true": - m.providerMeta = &githubMetadata{} - case envs["JENKINS_HOME"] != "": - m.providerMeta = &jenkinsMetadata{} - case envs["SEMAPHORE"] == "true": - m.providerMeta = &semaphoreMetadata{} - case envs["TRAVIS"] == "true": - m.providerMeta = &travisMetadata{} - default: - return fmt.Errorf("unrecognized environment: system does not appear to be a supported CI provider (Buildkite, CircleCI, GitHub Actions, Jenkins, Semaphore, or Travis CI)") - } - - if err := m.providerMeta.Init(envs, log); err != nil { + pm, err := newProviderMetadata(envs, log) + if err != nil { return err } - m.Branch = m.providerMeta.Branch() - m.BuildURL = m.providerMeta.BuildURL() - m.Check = m.providerMeta.Check() - m.CIProvider = m.providerMeta.Name() - m.RepoNameWithOwner = m.providerMeta.RepoNameWithOwner() + m.providerMeta = pm + m.Branch = pm.Branch() + m.BuildURL = pm.BuildURL() + m.Check = pm.Check() + m.CIProvider = pm.Name() + m.RepoNameWithOwner = pm.RepoNameWithOwner() return nil } @@ -155,413 +121,6 @@ func (m *Metadata) MarshalYAML() (out []byte, err error) { return append(topLevel, providerLevel...), nil } -var _ providerMetadata = (*buildkiteMetadata)(nil) - -type buildkiteMetadata struct { - // Internal state - nwo string - - // Fields derived from Buildkite-specific environment variables - BuildkiteBranch string `env:"BUILDKITE_BRANCH" yaml:"-"` - BuildkiteBuildID string `env:"BUILDKITE_BUILD_ID" yaml:":buildkite_build_id"` - BuildkiteBuildNumber uint `env:"BUILDKITE_BUILD_NUMBER" yaml:":buildkite_build_number"` - BuildkiteBuildURL string `env:"BUILDKITE_BUILD_URL" yaml:"-"` - BuildkiteCommit string `env:"BUILDKITE_COMMIT" yaml:"-"` - BuildkiteJobID string `env:"BUILDKITE_JOB_ID" yaml:":buildkite_job_id"` - BuildkiteLabel string `env:"BUILDKITE_LABEL" yaml:":buildkite_label"` - BuildkiteOrganizationSlug string `env:"BUILDKITE_ORGANIZATION_SLUG" yaml:":buildkite_organization_slug"` - BuildkitePipelineID string `env:"BUILDKITE_PIPELINE_ID" yaml:":buildkite_pipeline_id"` - BuildkitePipelineSlug string `env:"BUILDKITE_PIPELINE_SLUG" yaml:":buildkite_pipeline_slug"` - BuildkiteProjectSlug string `env:"BUILDKITE_PROJECT_SLUG" yaml:":buildkite_project_slug"` - BuildkitePullRequest string `env:"BUILDKITE_PULL_REQUEST" yaml:"-"` - BuildkitePullRequestBaseBranch string `env:"BUILDKITE_PULL_REQUEST_BASE_BRANCH" yaml:":buildkite_pull_request_base_branch,omitempty"` - BuildkitePullRequestNumber uint `yaml:":buildkite_pull_request_number,omitempty"` - BuildkitePullRequestRepo string `env:"BUILDKITE_PULL_REQUEST_REPO" yaml:":buildkite_pull_request_repo,omitempty"` - BuildkiteRebuiltFromBuildID string `env:"BUILDKITE_REBUILT_FROM_BUILD_ID" yaml:":buildkite_rebuilt_from_build_id,omitempty"` - BuildkiteRebuiltFromBuildNumber uint `env:"BUILDKITE_REBUILT_FROM_BUILD_NUMBER" yaml:":buildkite_rebuilt_from_build_number,omitempty"` - BuildkiteRepoURL string `env:"BUILDKITE_REPO" yaml:"-"` - BuildkiteRetryCount uint `env:"BUILDKITE_RETRY_COUNT" yaml:":buildkite_retry_count"` - BuildkiteTag string `env:"BUILDKITE_TAG" yaml:":buildkite_tag,omitempty"` -} - -func (b *buildkiteMetadata) Init(envs map[string]string, log Logger) error { - if err := env.Parse(b, env.Options{Environment: envs}); err != nil { - return err - } - - nwo, err := nameWithOwnerFromGitURL(b.BuildkiteRepoURL) - if err != nil { - return err - } - b.nwo = nwo - - prNum, err := strconv.ParseUint(b.BuildkitePullRequest, 0, 0) - if err == nil { - b.BuildkitePullRequestNumber = uint(prNum) - } - - return nil -} - -func (b *buildkiteMetadata) Branch() string { - return b.BuildkiteBranch -} - -func (b *buildkiteMetadata) BuildURL() string { - return b.BuildkiteBuildURL -} - -func (b *buildkiteMetadata) Check() string { - // TODO: Handle custom check name - // if g.Check == "" { - // return "buildkite" - // } - - return "buildkite" -} - -func (b *buildkiteMetadata) CommitSHA() string { - return b.BuildkiteCommit -} - -func (b *buildkiteMetadata) Name() string { - return "buildkite" -} - -func (b *buildkiteMetadata) RepoNameWithOwner() string { - return b.nwo -} - -var _ providerMetadata = (*circleMetadata)(nil) - -type circleMetadata struct { - // Fields derived from Circle-specific environment variables - CircleBranch string `env:"CIRCLE_BRANCH" yaml:"-"` - CircleBuildNumber uint `env:"CIRCLE_BUILD_NUM" yaml:":circle_build_num"` - CircleBuildURL string `env:"CIRCLE_BUILD_URL" yaml:"-"` - CircleJob string `env:"CIRCLE_JOB" yaml:":circle_job"` - CircleProjectReponame string `env:"CIRCLE_PROJECT_REPONAME" yaml:"-"` - CircleProjectUsername string `env:"CIRCLE_PROJECT_USERNAME" yaml:"-"` - CirclePullRequestNumber uint `env:"CIRCLE_PR_NUMBER" yaml:":circle_pr_number,omitempty"` - CirclePullRequestReponame string `env:"CIRCLE_PR_REPONAME" yaml:":circle_pr_reponame,omitempty"` - CirclePullRequestURL string `env:"CIRCLE_PULL_REQUEST" yaml:":circle_pull_request,omitempty"` - CirclePullRequestUsername string `env:"CIRCLE_PR_USERNAME" yaml:":circle_pr_username,omitempty"` - CircleRepoURL string `env:"CIRCLE_REPOSITORY_URL" yaml:":circle_repository_url"` - CircleSHA1 string `env:"CIRCLE_SHA1" yaml:"-"` - CircleTag string `env:"CIRCLE_TAG" yaml:":circle_tag,omitempty"` - CircleUsername string `env:"CIRCLE_USERNAME" yaml:":circle_username"` - CircleWorkflowID string `env:"CIRCLE_WORKFLOW_ID" yaml:":circle_workflow_id"` -} - -func (c *circleMetadata) Init(envs map[string]string, log Logger) error { - if err := env.Parse(c, env.Options{Environment: envs}); err != nil { - return err - } - - return nil -} - -func (c *circleMetadata) Branch() string { - return c.CircleBranch -} - -func (c *circleMetadata) BuildURL() string { - return c.CircleBuildURL -} - -func (c *circleMetadata) Check() string { - // TODO: Handle custom check name - // if g.Check == "" { - // return "circleci" - // } - - return "circleci" -} - -func (c *circleMetadata) CommitSHA() string { - return c.CircleSHA1 -} - -func (c *circleMetadata) Name() string { - return "circleci" -} - -func (c *circleMetadata) RepoNameWithOwner() string { - return fmt.Sprintf("%s/%s", c.CircleProjectUsername, c.CircleProjectReponame) -} - -var _ providerMetadata = (*githubMetadata)(nil) - -type githubMetadata struct { - // Internal state - branch string - buildURL string - - // Fields derived from GitHub-specific environment variables - GithubActor string `env:"GITHUB_ACTOR" yaml:":github_actor"` - GithubBaseRef string `env:"GITHUB_BASE_REF" yaml:":github_base_ref"` - GithubEventName string `env:"GITHUB_EVENT_NAME" yaml:":github_event_name"` - GithubHeadRef string `env:"GITHUB_HEAD_REF" yaml:":github_head_ref"` - GithubRef string `env:"GITHUB_REF" yaml:":github_ref"` - GithubRepoNWO string `env:"GITHUB_REPOSITORY" yaml:"-"` - GithubRepoURL string `yaml:":github_repo_url"` - GithubRunID uint `env:"GITHUB_RUN_ID" yaml:":github_run_id"` - GithubRunNumber uint `env:"GITHUB_RUN_NUMBER" yaml:":github_run_number"` - GithubServerURL string `env:"GITHUB_SERVER_URL" yaml:"-"` - GithubSHA string `env:"GITHUB_SHA" yaml:"-"` - GithubWorkflow string `env:"GITHUB_WORKFLOW" yaml:":github_workflow"` -} - -func (g *githubMetadata) Init(envs map[string]string, log Logger) error { - if err := env.Parse(g, env.Options{Environment: envs}); err != nil { - return err - } - - g.GithubRepoURL = fmt.Sprintf("%s/%s", g.GithubServerURL, g.GithubRepoNWO) - - g.buildURL = fmt.Sprintf("%s/actions/runs/%d", g.GithubRepoURL, g.GithubRunID) - - isBranch, err := regexp.MatchString("^refs/heads/", g.GithubRef) - if err != nil { - return err - } - if isBranch { - g.branch = strings.TrimPrefix(g.GithubRef, "refs/heads/") - } - - return nil -} - -func (g *githubMetadata) Branch() string { - return g.branch -} - -func (g *githubMetadata) BuildURL() string { - return g.buildURL -} - -func (g *githubMetadata) Check() string { - // TODO: Handle custom check name - // if g.Check == "" { - // return "github-actions" - // } - - return "github-actions" -} - -func (g *githubMetadata) CommitSHA() string { - return g.GithubSHA -} - -func (g *githubMetadata) Name() string { - return "github-actions" -} - -func (g *githubMetadata) RepoNameWithOwner() string { - return g.GithubRepoNWO -} - -var _ providerMetadata = (*jenkinsMetadata)(nil) - -type jenkinsMetadata struct { - // Internal state - buildURL string - nwo string - - // Fields derived from Jenkins-specific environment variables - GitBranch string `env:"GIT_BRANCH" yaml:"-"` - GitCommit string `env:"GIT_COMMIT" yaml:"-"` - GitURL string `env:"GIT_URL" yaml:"-"` - JenkinsExecutorNumber uint `env:"EXECUTOR_NUMBER" yaml:":jenkins_executor_number"` - JenkinsJobName string `env:"JOB_NAME" yaml:":jenkins_job_name"` - JenkinsJobURL string `env:"JOB_URL" yaml:":jenkins_job_url"` - JenkinsNodeName string `env:"NODE_NAME" yaml:":jenkins_node_name"` - JenkinsWorkspace string `env:"WORKSPACE" yaml:":jenkins_workspace"` -} - -func (j *jenkinsMetadata) Init(envs map[string]string, log Logger) error { - if err := env.Parse(j, env.Options{Environment: envs}); err != nil { - return err - } - - url, ok := envs["BUILD_URL"] - if !ok || url == "" { - return fmt.Errorf("missing required environment variable: BUILD_URL") - } - j.buildURL = url - - nwo, err := nameWithOwnerFromGitURL(j.GitURL) - if err != nil { - return err - } - j.nwo = nwo - - return nil -} - -func (j *jenkinsMetadata) Branch() string { - return j.GitBranch -} - -func (j *jenkinsMetadata) BuildURL() string { - return j.buildURL -} - -func (j *jenkinsMetadata) Check() string { - // TODO: Handle custom check name - // if j.Check == "" { - // return "jenkins" - // } - - return "jenkins" -} - -func (j *jenkinsMetadata) CommitSHA() string { - return j.GitCommit -} - -func (j *jenkinsMetadata) Name() string { - return "jenkins" -} - -func (j *jenkinsMetadata) RepoNameWithOwner() string { - return j.nwo -} - -var _ providerMetadata = (*semaphoreMetadata)(nil) - -type semaphoreMetadata struct { - // Fields derived from Semaphore-specific environment variables - SemaphoreAgentMachineEnvironmentType string `env:"SEMAPHORE_AGENT_MACHINE_ENVIRONMENT_TYPE" yaml:":semaphore_agent_machine_environment_type"` - SemaphoreAgentMachineOsImage string `env:"SEMAPHORE_AGENT_MACHINE_OS_IMAGE" yaml:":semaphore_agent_machine_os_image"` - SemaphoreAgentMachineType string `env:"SEMAPHORE_AGENT_MACHINE_TYPE" yaml:":semaphore_agent_machine_type"` - SemaphoreGitBranch string `env:"SEMAPHORE_GIT_BRANCH" yaml:"-"` - SemaphoreGitCommitRange string `env:"SEMAPHORE_GIT_COMMIT_RANGE" yaml:":semaphore_git_commit_range"` - SemaphoreGitDir string `env:"SEMAPHORE_GIT_DIR" yaml:":semaphore_git_dir"` - SemaphoreGitRef string `env:"SEMAPHORE_GIT_REF" yaml:":semaphore_git_ref"` - SemaphoreGitRefType string `env:"SEMAPHORE_GIT_REF_TYPE" yaml:":semaphore_git_ref_type"` - SemaphoreGitRepoSlug string `env:"SEMAPHORE_GIT_REPO_SLUG" yaml:"-"` - SemaphoreGitSHA string `env:"SEMAPHORE_GIT_SHA" yaml:"-"` - SemaphoreGitURL string `env:"SEMAPHORE_GIT_URL" yaml:":semaphore_git_url"` - SemaphoreJobID string `env:"SEMAPHORE_JOB_ID" yaml:":semaphore_job_id"` - SemaphoreJobName string `env:"SEMAPHORE_JOB_NAME" yaml:":semaphore_job_name"` - SemaphoreJobResult string `env:"SEMAPHORE_JOB_RESULT" yaml:":semaphore_job_result"` - SemaphoreOrganizationURL string `env:"SEMAPHORE_ORGANIZATION_URL" yaml:":semaphore_organization_url"` - SemaphoreProjectID string `env:"SEMAPHORE_PROJECT_ID" yaml:":semaphore_project_id"` - SemaphoreProjectName string `env:"SEMAPHORE_PROJECT_NAME" yaml:":semaphore_project_name"` - SemaphoreWorkflowID string `env:"SEMAPHORE_WORKFLOW_ID" yaml:":semaphore_workflow_id"` - SemaphoreWorkflowNumber uint `env:"SEMAPHORE_WORKFLOW_NUMBER" yaml:":semaphore_workflow_number"` -} - -func (s *semaphoreMetadata) Init(envs map[string]string, log Logger) error { - if err := env.Parse(s, env.Options{Environment: envs}); err != nil { - return err - } - - return nil -} - -func (s *semaphoreMetadata) Branch() string { - return s.SemaphoreGitBranch -} - -func (s *semaphoreMetadata) BuildURL() string { - return fmt.Sprintf("%s/workflows/%s", s.SemaphoreOrganizationURL, s.SemaphoreWorkflowID) -} - -func (s *semaphoreMetadata) Check() string { - // TODO: Handle custom check name - // if g.Check == "" { - // return "semaphore" - // } - - return "semaphore" -} - -func (s *semaphoreMetadata) CommitSHA() string { - return s.SemaphoreGitSHA -} - -func (s *semaphoreMetadata) Name() string { - return "semaphore" -} - -func (s *semaphoreMetadata) RepoNameWithOwner() string { - return s.SemaphoreGitRepoSlug -} - -var _ providerMetadata = (*travisMetadata)(nil) - -type travisMetadata struct { - // Fields derived from Travis-specific environment variables - TravisBranch string `env:"TRAVIS_BRANCH" yaml:"-"` - TravisBuildDir string `env:"TRAVIS_BUILD_DIR" yaml:":travis_build_dir"` - TravisBuildID uint `env:"TRAVIS_BUILD_ID" yaml:":travis_build_id"` - TravisBuildNumber uint `env:"TRAVIS_BUILD_NUMBER" yaml:":travis_build_number"` - TravisBuildWebURL string `env:"TRAVIS_BUILD_WEB_URL" yaml:":travis_build_web_url"` - TravisCommit string `env:"TRAVIS_COMMIT" yaml:"-"` - TravisCommitRange string `env:"TRAVIS_COMMIT_RANGE" yaml:":travis_commit_range"` - TravisCPUArch string `env:"TRAVIS_CPU_ARCH" yaml:":travis_cpu_arch"` - TravisDist string `env:"TRAVIS_DIST" yaml:":travis_dist"` - TravisEventType string `env:"TRAVIS_EVENT_TYPE" yaml:":travis_event_type"` - TravisJobID uint `env:"TRAVIS_JOB_ID" yaml:":travis_job_id"` - TravisJobName string `env:"TRAVIS_JOB_NAME" yaml:":travis_job_name"` - TravisJobNumber string `env:"TRAVIS_JOB_NUMBER" yaml:":travis_job_number"` - TravisJobWebURL string `env:"TRAVIS_JOB_WEB_URL" yaml:"-"` - TravisOsName string `env:"TRAVIS_OS_NAME" yaml:":travis_os_name"` - TravisPullRequest string `env:"TRAVIS_PULL_REQUEST" yaml:"-"` - TravisPullRequestBranch string `env:"TRAVIS_PULL_REQUEST_BRANCH" yaml:":travis_pull_request_branch,omitempty"` - TravisPullRequestNumber uint `yaml:":travis_pull_request_number,omitempty"` - TravisPullRequestSha string `env:"TRAVIS_PULL_REQUEST_SHA" yaml:":travis_pull_request_sha,omitempty"` - TravisPullRequestSlug string `env:"TRAVIS_PULL_REQUEST_SLUG" yaml:":travis_pull_request_slug,omitempty"` - TravisRepoSlug string `env:"TRAVIS_REPO_SLUG" yaml:"-"` - TravisSudo bool `env:"TRAVIS_SUDO" yaml:":travis_sudo"` - TravisTag string `env:"TRAVIS_TAG" yaml:":travis_tag"` - TravisTestResult uint `env:"TRAVIS_TEST_RESULT" yaml:":travis_test_result"` -} - -func (t *travisMetadata) Init(envs map[string]string, log Logger) error { - if err := env.Parse(t, env.Options{Environment: envs}); err != nil { - return err - } - - prNum, err := strconv.ParseUint(t.TravisPullRequest, 0, 0) - if err == nil { - t.TravisPullRequestNumber = uint(prNum) - } - - return nil -} - -func (t *travisMetadata) Branch() string { - return t.TravisBranch -} - -func (t *travisMetadata) BuildURL() string { - return t.TravisJobWebURL -} - -func (t *travisMetadata) Check() string { - // TODO: Handle custom check name - // if g.Check == "" { - // return "travis-ci" - // } - - return "travis-ci" -} - -func (t *travisMetadata) CommitSHA() string { - return t.TravisCommit -} - -func (t *travisMetadata) Name() string { - return "travis-ci" -} - -func (t *travisMetadata) RepoNameWithOwner() string { - return t.TravisRepoSlug -} - func marshalYAML(m interface{}) (out []byte, err error) { data, err := yaml.Marshal(m) if err != nil { @@ -570,14 +129,3 @@ func marshalYAML(m interface{}) (out []byte, err error) { return data, nil } - -func nameWithOwnerFromGitURL(url string) (string, error) { - re := regexp.MustCompile(`github.com[:/](.*)`) - - matches := re.FindStringSubmatch(url) - if len(matches) != 2 { - return "", fmt.Errorf("unable to extract repository name-with-owner from URL: %s", url) - } - - return strings.TrimSuffix(matches[1], ".git"), nil -} diff --git a/metadata/providers.go b/metadata/providers.go new file mode 100644 index 00000000..51855bb3 --- /dev/null +++ b/metadata/providers.go @@ -0,0 +1,464 @@ +package metadata + +import ( + "fmt" + "regexp" + "strconv" + "strings" + + "github.com/caarlos0/env/v6" +) + +// TODO Doc? +type providerMetadata interface { + Init(envs map[string]string, log Logger) error + Branch() string + BuildURL() string + Check() string + CommitSHA() string + Name() string + RepoNameWithOwner() string +} + +func newProviderMetadata(envs map[string]string, log Logger) (providerMetadata, error) { + var pm providerMetadata + + switch { + case envs["BUILDKITE"] == "true": + pm = &buildkiteMetadata{} + case envs["CIRCLECI"] == "true": + pm = &circleMetadata{} + case envs["GITHUB_ACTIONS"] == "true": + pm = &githubMetadata{} + case envs["JENKINS_HOME"] != "": + pm = &jenkinsMetadata{} + case envs["SEMAPHORE"] == "true": + pm = &semaphoreMetadata{} + case envs["TRAVIS"] == "true": + pm = &travisMetadata{} + default: + return nil, fmt.Errorf("unrecognized environment: system does not appear to be a supported CI provider (Buildkite, CircleCI, GitHub Actions, Jenkins, Semaphore, or Travis CI)") + } + + if err := pm.Init(envs, log); err != nil { + return nil, err + } + + return pm, nil +} + +var _ providerMetadata = (*buildkiteMetadata)(nil) + +type buildkiteMetadata struct { + // Internal state + nwo string + + // Fields derived from Buildkite-specific environment variables + BuildkiteBranch string `env:"BUILDKITE_BRANCH" yaml:"-"` + BuildkiteBuildID string `env:"BUILDKITE_BUILD_ID" yaml:":buildkite_build_id"` + BuildkiteBuildNumber uint `env:"BUILDKITE_BUILD_NUMBER" yaml:":buildkite_build_number"` + BuildkiteBuildURL string `env:"BUILDKITE_BUILD_URL" yaml:"-"` + BuildkiteCommit string `env:"BUILDKITE_COMMIT" yaml:"-"` + BuildkiteJobID string `env:"BUILDKITE_JOB_ID" yaml:":buildkite_job_id"` + BuildkiteLabel string `env:"BUILDKITE_LABEL" yaml:":buildkite_label"` + BuildkiteOrganizationSlug string `env:"BUILDKITE_ORGANIZATION_SLUG" yaml:":buildkite_organization_slug"` + BuildkitePipelineID string `env:"BUILDKITE_PIPELINE_ID" yaml:":buildkite_pipeline_id"` + BuildkitePipelineSlug string `env:"BUILDKITE_PIPELINE_SLUG" yaml:":buildkite_pipeline_slug"` + BuildkiteProjectSlug string `env:"BUILDKITE_PROJECT_SLUG" yaml:":buildkite_project_slug"` + BuildkitePullRequest string `env:"BUILDKITE_PULL_REQUEST" yaml:"-"` + BuildkitePullRequestBaseBranch string `env:"BUILDKITE_PULL_REQUEST_BASE_BRANCH" yaml:":buildkite_pull_request_base_branch,omitempty"` + BuildkitePullRequestNumber uint `yaml:":buildkite_pull_request_number,omitempty"` + BuildkitePullRequestRepo string `env:"BUILDKITE_PULL_REQUEST_REPO" yaml:":buildkite_pull_request_repo,omitempty"` + BuildkiteRebuiltFromBuildID string `env:"BUILDKITE_REBUILT_FROM_BUILD_ID" yaml:":buildkite_rebuilt_from_build_id,omitempty"` + BuildkiteRebuiltFromBuildNumber uint `env:"BUILDKITE_REBUILT_FROM_BUILD_NUMBER" yaml:":buildkite_rebuilt_from_build_number,omitempty"` + BuildkiteRepoURL string `env:"BUILDKITE_REPO" yaml:"-"` + BuildkiteRetryCount uint `env:"BUILDKITE_RETRY_COUNT" yaml:":buildkite_retry_count"` + BuildkiteTag string `env:"BUILDKITE_TAG" yaml:":buildkite_tag,omitempty"` +} + +func (b *buildkiteMetadata) Init(envs map[string]string, log Logger) error { + if err := env.Parse(b, env.Options{Environment: envs}); err != nil { + return err + } + + nwo, err := nameWithOwnerFromGitURL(b.BuildkiteRepoURL) + if err != nil { + return err + } + b.nwo = nwo + + prNum, err := strconv.ParseUint(b.BuildkitePullRequest, 0, 0) + if err == nil { + b.BuildkitePullRequestNumber = uint(prNum) + } + + return nil +} + +func (b *buildkiteMetadata) Branch() string { + return b.BuildkiteBranch +} + +func (b *buildkiteMetadata) BuildURL() string { + return b.BuildkiteBuildURL +} + +func (b *buildkiteMetadata) Check() string { + // TODO: Handle custom check name + // if g.Check == "" { + // return "buildkite" + // } + + return "buildkite" +} + +func (b *buildkiteMetadata) CommitSHA() string { + return b.BuildkiteCommit +} + +func (b *buildkiteMetadata) Name() string { + return "buildkite" +} + +func (b *buildkiteMetadata) RepoNameWithOwner() string { + return b.nwo +} + +type circleMetadata struct { + // Fields derived from Circle-specific environment variables + CircleBranch string `env:"CIRCLE_BRANCH" yaml:"-"` + CircleBuildNumber uint `env:"CIRCLE_BUILD_NUM" yaml:":circle_build_num"` + CircleBuildURL string `env:"CIRCLE_BUILD_URL" yaml:"-"` + CircleJob string `env:"CIRCLE_JOB" yaml:":circle_job"` + CircleProjectReponame string `env:"CIRCLE_PROJECT_REPONAME" yaml:"-"` + CircleProjectUsername string `env:"CIRCLE_PROJECT_USERNAME" yaml:"-"` + CirclePullRequestNumber uint `env:"CIRCLE_PR_NUMBER" yaml:":circle_pr_number,omitempty"` + CirclePullRequestReponame string `env:"CIRCLE_PR_REPONAME" yaml:":circle_pr_reponame,omitempty"` + CirclePullRequestURL string `env:"CIRCLE_PULL_REQUEST" yaml:":circle_pull_request,omitempty"` + CirclePullRequestUsername string `env:"CIRCLE_PR_USERNAME" yaml:":circle_pr_username,omitempty"` + CircleRepoURL string `env:"CIRCLE_REPOSITORY_URL" yaml:":circle_repository_url"` + CircleSHA1 string `env:"CIRCLE_SHA1" yaml:"-"` + CircleTag string `env:"CIRCLE_TAG" yaml:":circle_tag,omitempty"` + CircleUsername string `env:"CIRCLE_USERNAME" yaml:":circle_username"` + CircleWorkflowID string `env:"CIRCLE_WORKFLOW_ID" yaml:":circle_workflow_id"` +} + +func (c *circleMetadata) Init(envs map[string]string, log Logger) error { + if err := env.Parse(c, env.Options{Environment: envs}); err != nil { + return err + } + + return nil +} + +func (c *circleMetadata) Branch() string { + return c.CircleBranch +} + +func (c *circleMetadata) BuildURL() string { + return c.CircleBuildURL +} + +func (c *circleMetadata) Check() string { + // TODO: Handle custom check name + // if g.Check == "" { + // return "circleci" + // } + + return "circleci" +} + +func (c *circleMetadata) CommitSHA() string { + return c.CircleSHA1 +} + +func (c *circleMetadata) Name() string { + return "circleci" +} + +func (c *circleMetadata) RepoNameWithOwner() string { + return fmt.Sprintf("%s/%s", c.CircleProjectUsername, c.CircleProjectReponame) +} + +var _ providerMetadata = (*githubMetadata)(nil) + +type githubMetadata struct { + // Internal state + branch string + buildURL string + + // Fields derived from GitHub-specific environment variables + GithubActor string `env:"GITHUB_ACTOR" yaml:":github_actor"` + GithubBaseRef string `env:"GITHUB_BASE_REF" yaml:":github_base_ref"` + GithubEventName string `env:"GITHUB_EVENT_NAME" yaml:":github_event_name"` + GithubHeadRef string `env:"GITHUB_HEAD_REF" yaml:":github_head_ref"` + GithubRef string `env:"GITHUB_REF" yaml:":github_ref"` + GithubRepoNWO string `env:"GITHUB_REPOSITORY" yaml:"-"` + GithubRepoURL string `yaml:":github_repo_url"` + GithubRunID uint `env:"GITHUB_RUN_ID" yaml:":github_run_id"` + GithubRunNumber uint `env:"GITHUB_RUN_NUMBER" yaml:":github_run_number"` + GithubServerURL string `env:"GITHUB_SERVER_URL" yaml:"-"` + GithubSHA string `env:"GITHUB_SHA" yaml:"-"` + GithubWorkflow string `env:"GITHUB_WORKFLOW" yaml:":github_workflow"` +} + +func (g *githubMetadata) Init(envs map[string]string, log Logger) error { + if err := env.Parse(g, env.Options{Environment: envs}); err != nil { + return err + } + + g.GithubRepoURL = fmt.Sprintf("%s/%s", g.GithubServerURL, g.GithubRepoNWO) + + g.buildURL = fmt.Sprintf("%s/actions/runs/%d", g.GithubRepoURL, g.GithubRunID) + + isBranch, err := regexp.MatchString("^refs/heads/", g.GithubRef) + if err != nil { + return err + } + if isBranch { + g.branch = strings.TrimPrefix(g.GithubRef, "refs/heads/") + } + + return nil +} + +func (g *githubMetadata) Branch() string { + return g.branch +} + +func (g *githubMetadata) BuildURL() string { + return g.buildURL +} + +func (g *githubMetadata) Check() string { + // TODO: Handle custom check name + // if g.Check == "" { + // return "github-actions" + // } + + return "github-actions" +} + +func (g *githubMetadata) CommitSHA() string { + return g.GithubSHA +} + +func (g *githubMetadata) Name() string { + return "github-actions" +} + +func (g *githubMetadata) RepoNameWithOwner() string { + return g.GithubRepoNWO +} + +var _ providerMetadata = (*jenkinsMetadata)(nil) + +type jenkinsMetadata struct { + // Internal state + buildURL string + nwo string + + // Fields derived from Jenkins-specific environment variables + GitBranch string `env:"GIT_BRANCH" yaml:"-"` + GitCommit string `env:"GIT_COMMIT" yaml:"-"` + GitURL string `env:"GIT_URL" yaml:"-"` + JenkinsExecutorNumber uint `env:"EXECUTOR_NUMBER" yaml:":jenkins_executor_number"` + JenkinsJobName string `env:"JOB_NAME" yaml:":jenkins_job_name"` + JenkinsJobURL string `env:"JOB_URL" yaml:":jenkins_job_url"` + JenkinsNodeName string `env:"NODE_NAME" yaml:":jenkins_node_name"` + JenkinsWorkspace string `env:"WORKSPACE" yaml:":jenkins_workspace"` +} + +func (j *jenkinsMetadata) Init(envs map[string]string, log Logger) error { + if err := env.Parse(j, env.Options{Environment: envs}); err != nil { + return err + } + + url, ok := envs["BUILD_URL"] + if !ok || url == "" { + return fmt.Errorf("missing required environment variable: BUILD_URL") + } + j.buildURL = url + + nwo, err := nameWithOwnerFromGitURL(j.GitURL) + if err != nil { + return err + } + j.nwo = nwo + + return nil +} + +func (j *jenkinsMetadata) Branch() string { + return j.GitBranch +} + +func (j *jenkinsMetadata) BuildURL() string { + return j.buildURL +} + +func (j *jenkinsMetadata) Check() string { + // TODO: Handle custom check name + // if j.Check == "" { + // return "jenkins" + // } + + return "jenkins" +} + +func (j *jenkinsMetadata) CommitSHA() string { + return j.GitCommit +} + +func (j *jenkinsMetadata) Name() string { + return "jenkins" +} + +func (j *jenkinsMetadata) RepoNameWithOwner() string { + return j.nwo +} + +var _ providerMetadata = (*semaphoreMetadata)(nil) + +type semaphoreMetadata struct { + // Fields derived from Semaphore-specific environment variables + SemaphoreAgentMachineEnvironmentType string `env:"SEMAPHORE_AGENT_MACHINE_ENVIRONMENT_TYPE" yaml:":semaphore_agent_machine_environment_type"` + SemaphoreAgentMachineOsImage string `env:"SEMAPHORE_AGENT_MACHINE_OS_IMAGE" yaml:":semaphore_agent_machine_os_image"` + SemaphoreAgentMachineType string `env:"SEMAPHORE_AGENT_MACHINE_TYPE" yaml:":semaphore_agent_machine_type"` + SemaphoreGitBranch string `env:"SEMAPHORE_GIT_BRANCH" yaml:"-"` + SemaphoreGitCommitRange string `env:"SEMAPHORE_GIT_COMMIT_RANGE" yaml:":semaphore_git_commit_range"` + SemaphoreGitDir string `env:"SEMAPHORE_GIT_DIR" yaml:":semaphore_git_dir"` + SemaphoreGitRef string `env:"SEMAPHORE_GIT_REF" yaml:":semaphore_git_ref"` + SemaphoreGitRefType string `env:"SEMAPHORE_GIT_REF_TYPE" yaml:":semaphore_git_ref_type"` + SemaphoreGitRepoSlug string `env:"SEMAPHORE_GIT_REPO_SLUG" yaml:"-"` + SemaphoreGitSHA string `env:"SEMAPHORE_GIT_SHA" yaml:"-"` + SemaphoreGitURL string `env:"SEMAPHORE_GIT_URL" yaml:":semaphore_git_url"` + SemaphoreJobID string `env:"SEMAPHORE_JOB_ID" yaml:":semaphore_job_id"` + SemaphoreJobName string `env:"SEMAPHORE_JOB_NAME" yaml:":semaphore_job_name"` + SemaphoreJobResult string `env:"SEMAPHORE_JOB_RESULT" yaml:":semaphore_job_result"` + SemaphoreOrganizationURL string `env:"SEMAPHORE_ORGANIZATION_URL" yaml:":semaphore_organization_url"` + SemaphoreProjectID string `env:"SEMAPHORE_PROJECT_ID" yaml:":semaphore_project_id"` + SemaphoreProjectName string `env:"SEMAPHORE_PROJECT_NAME" yaml:":semaphore_project_name"` + SemaphoreWorkflowID string `env:"SEMAPHORE_WORKFLOW_ID" yaml:":semaphore_workflow_id"` + SemaphoreWorkflowNumber uint `env:"SEMAPHORE_WORKFLOW_NUMBER" yaml:":semaphore_workflow_number"` +} + +func (s *semaphoreMetadata) Init(envs map[string]string, log Logger) error { + if err := env.Parse(s, env.Options{Environment: envs}); err != nil { + return err + } + + return nil +} + +func (s *semaphoreMetadata) Branch() string { + return s.SemaphoreGitBranch +} + +func (s *semaphoreMetadata) BuildURL() string { + return fmt.Sprintf("%s/workflows/%s", s.SemaphoreOrganizationURL, s.SemaphoreWorkflowID) +} + +func (s *semaphoreMetadata) Check() string { + // TODO: Handle custom check name + // if g.Check == "" { + // return "semaphore" + // } + + return "semaphore" +} + +func (s *semaphoreMetadata) CommitSHA() string { + return s.SemaphoreGitSHA +} + +func (s *semaphoreMetadata) Name() string { + return "semaphore" +} + +func (s *semaphoreMetadata) RepoNameWithOwner() string { + return s.SemaphoreGitRepoSlug +} + +var _ providerMetadata = (*travisMetadata)(nil) + +type travisMetadata struct { + // Fields derived from Travis-specific environment variables + TravisBranch string `env:"TRAVIS_BRANCH" yaml:"-"` + TravisBuildDir string `env:"TRAVIS_BUILD_DIR" yaml:":travis_build_dir"` + TravisBuildID uint `env:"TRAVIS_BUILD_ID" yaml:":travis_build_id"` + TravisBuildNumber uint `env:"TRAVIS_BUILD_NUMBER" yaml:":travis_build_number"` + TravisBuildWebURL string `env:"TRAVIS_BUILD_WEB_URL" yaml:":travis_build_web_url"` + TravisCommit string `env:"TRAVIS_COMMIT" yaml:"-"` + TravisCommitRange string `env:"TRAVIS_COMMIT_RANGE" yaml:":travis_commit_range"` + TravisCPUArch string `env:"TRAVIS_CPU_ARCH" yaml:":travis_cpu_arch"` + TravisDist string `env:"TRAVIS_DIST" yaml:":travis_dist"` + TravisEventType string `env:"TRAVIS_EVENT_TYPE" yaml:":travis_event_type"` + TravisJobID uint `env:"TRAVIS_JOB_ID" yaml:":travis_job_id"` + TravisJobName string `env:"TRAVIS_JOB_NAME" yaml:":travis_job_name"` + TravisJobNumber string `env:"TRAVIS_JOB_NUMBER" yaml:":travis_job_number"` + TravisJobWebURL string `env:"TRAVIS_JOB_WEB_URL" yaml:"-"` + TravisOsName string `env:"TRAVIS_OS_NAME" yaml:":travis_os_name"` + TravisPullRequest string `env:"TRAVIS_PULL_REQUEST" yaml:"-"` + TravisPullRequestBranch string `env:"TRAVIS_PULL_REQUEST_BRANCH" yaml:":travis_pull_request_branch,omitempty"` + TravisPullRequestNumber uint `yaml:":travis_pull_request_number,omitempty"` + TravisPullRequestSha string `env:"TRAVIS_PULL_REQUEST_SHA" yaml:":travis_pull_request_sha,omitempty"` + TravisPullRequestSlug string `env:"TRAVIS_PULL_REQUEST_SLUG" yaml:":travis_pull_request_slug,omitempty"` + TravisRepoSlug string `env:"TRAVIS_REPO_SLUG" yaml:"-"` + TravisSudo bool `env:"TRAVIS_SUDO" yaml:":travis_sudo"` + TravisTag string `env:"TRAVIS_TAG" yaml:":travis_tag"` + TravisTestResult uint `env:"TRAVIS_TEST_RESULT" yaml:":travis_test_result"` +} + +func (t *travisMetadata) Init(envs map[string]string, log Logger) error { + if err := env.Parse(t, env.Options{Environment: envs}); err != nil { + return err + } + + prNum, err := strconv.ParseUint(t.TravisPullRequest, 0, 0) + if err == nil { + t.TravisPullRequestNumber = uint(prNum) + } + + return nil +} + +func (t *travisMetadata) Branch() string { + return t.TravisBranch +} + +func (t *travisMetadata) BuildURL() string { + return t.TravisJobWebURL +} + +func (t *travisMetadata) Check() string { + // TODO: Handle custom check name + // if g.Check == "" { + // return "travis-ci" + // } + + return "travis-ci" +} + +func (t *travisMetadata) CommitSHA() string { + return t.TravisCommit +} + +func (t *travisMetadata) Name() string { + return "travis-ci" +} + +func (t *travisMetadata) RepoNameWithOwner() string { + return t.TravisRepoSlug +} + +func nameWithOwnerFromGitURL(url string) (string, error) { + re := regexp.MustCompile(`github.com[:/](.*)`) + + matches := re.FindStringSubmatch(url) + if len(matches) != 2 { + return "", fmt.Errorf("unable to extract repository name-with-owner from URL: %s", url) + } + + return strings.TrimSuffix(matches[1], ".git"), nil +} From 43d88f7bbf1a9e7a19fe8a5702d9c865fe29a6bd Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Thu, 11 Feb 2021 19:36:55 -0500 Subject: [PATCH 03/12] =?UTF-8?q?=F0=9F=8E=A8=20Rename=20field?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- metadata/metadata.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/metadata/metadata.go b/metadata/metadata.go index 4cdaa97d..d500b271 100644 --- a/metadata/metadata.go +++ b/metadata/metadata.go @@ -29,7 +29,7 @@ type Metadata struct { Timestamp time.Time `yaml:":timestamp"` TreeSHA string `yaml:":tree,omitempty"` - providerMeta providerMetadata + providerData providerMetadata } // NewMetadata creates a new Metadata instance from the given args. @@ -40,7 +40,7 @@ func NewMetadata(version *Version, envs map[string]string, resolver CommitResolv return nil, err } - if err := m.initCommitData(resolver, m.providerMeta.CommitSHA(), log); err != nil { + if err := m.initCommitData(resolver, m.providerData.CommitSHA(), log); err != nil { return nil, err } @@ -56,7 +56,7 @@ func (m *Metadata) initProviderData(envs map[string]string, log Logger) error { return err } - m.providerMeta = pm + m.providerData = pm m.Branch = pm.Branch() m.BuildURL = pm.BuildURL() m.Check = pm.Check() @@ -113,7 +113,7 @@ func (m *Metadata) MarshalYAML() (out []byte, err error) { return nil, err } - providerLevel, err := marshalYAML(m.providerMeta) + providerLevel, err := marshalYAML(m.providerData) if err != nil { return nil, err } From 339e5ff76728934cf4f7fc77133da171da11aebe Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Thu, 11 Feb 2021 19:38:42 -0500 Subject: [PATCH 04/12] =?UTF-8?q?=F0=9F=94=A5=20Remove=20obsolete=20fn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- metadata/metadata.go | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/metadata/metadata.go b/metadata/metadata.go index d500b271..7d68b2fe 100644 --- a/metadata/metadata.go +++ b/metadata/metadata.go @@ -108,24 +108,15 @@ func (m *Metadata) initVersionData(version *Version) { // MarshalYAML TODO Add docs func (m *Metadata) MarshalYAML() (out []byte, err error) { - topLevel, err := marshalYAML(m) + topLevel, err := yaml.Marshal(m) if err != nil { return nil, err } - providerLevel, err := marshalYAML(m.providerData) + providerLevel, err := yaml.Marshal(m.providerData) if err != nil { return nil, err } return append(topLevel, providerLevel...), nil } - -func marshalYAML(m interface{}) (out []byte, err error) { - data, err := yaml.Marshal(m) - if err != nil { - return nil, err - } - - return data, nil -} From c7f4aed1f3db63aee645dae310f352fc14d9fe28 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Thu, 11 Feb 2021 19:46:01 -0500 Subject: [PATCH 05/12] =?UTF-8?q?=F0=9F=8E=A8=20Rename=20variables?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- metadata/metadata.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/metadata/metadata.go b/metadata/metadata.go index 7d68b2fe..31887c6f 100644 --- a/metadata/metadata.go +++ b/metadata/metadata.go @@ -108,15 +108,15 @@ func (m *Metadata) initVersionData(version *Version) { // MarshalYAML TODO Add docs func (m *Metadata) MarshalYAML() (out []byte, err error) { - topLevel, err := yaml.Marshal(m) + universalFields, err := yaml.Marshal(m) if err != nil { return nil, err } - providerLevel, err := yaml.Marshal(m.providerData) + providerSpecificFields, err := yaml.Marshal(m.providerData) if err != nil { return nil, err } - return append(topLevel, providerLevel...), nil + return append(universalFields, providerSpecificFields...), nil } From 6cfc8ef43a1b16a3964d6719c97bdf786646980e Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Thu, 11 Feb 2021 19:47:47 -0500 Subject: [PATCH 06/12] =?UTF-8?q?=F0=9F=93=9D=20Add=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/submit.go | 3 +++ metadata/logger.go | 2 +- metadata/metadata.go | 2 +- metadata/providers.go | 3 ++- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cmd/submit.go b/cmd/submit.go index e306619c..443c0eb3 100644 --- a/cmd/submit.go +++ b/cmd/submit.go @@ -28,6 +28,8 @@ type credentials struct { SecretAccessKey string } +// A log object can be passed around for use as a logger. It stores logs +// in-memory and can flush the logs to a string when requested. type log struct { entries []string } @@ -37,6 +39,7 @@ func (l *log) Printf(format string, v ...interface{}) { fmt.Printf(format, v...) } +// Text returns a string concatenation of all of the log's entries. func (l *log) Text() string { return strings.Join(l.entries, "\n") } diff --git a/metadata/logger.go b/metadata/logger.go index d1ca6003..12ec9e18 100644 --- a/metadata/logger.go +++ b/metadata/logger.go @@ -1,6 +1,6 @@ package metadata -// Logger -- TODO Add docs +// A Logger represents a mechanism for logging. 🙃 type Logger interface { Printf(format string, v ...interface{}) } diff --git a/metadata/metadata.go b/metadata/metadata.go index 31887c6f..53a48fd4 100644 --- a/metadata/metadata.go +++ b/metadata/metadata.go @@ -106,7 +106,7 @@ func (m *Metadata) initVersionData(version *Version) { m.ReporterVersion = version.Number } -// MarshalYAML TODO Add docs +// MarshalYAML serializes the metadata into a YAML document. func (m *Metadata) MarshalYAML() (out []byte, err error) { universalFields, err := yaml.Marshal(m) if err != nil { diff --git a/metadata/providers.go b/metadata/providers.go index 51855bb3..600dc5d6 100644 --- a/metadata/providers.go +++ b/metadata/providers.go @@ -9,7 +9,8 @@ import ( "github.com/caarlos0/env/v6" ) -// TODO Doc? +// A providerMetadata instance supplies the metadata for a build taking place on +// a specific CI provider (e.g., CircleCI, GitHub Actions, etc.). type providerMetadata interface { Init(envs map[string]string, log Logger) error Branch() string From 9713565685a60fadd161e5ced6dce5c41a8f221a Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Thu, 11 Feb 2021 20:03:52 -0500 Subject: [PATCH 07/12] =?UTF-8?q?=F0=9F=8E=A8=20DRY=20up=20handling=20of?= =?UTF-8?q?=20custom=20check=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- metadata/metadata.go | 12 +++- metadata/metadata_test.go | 117 +++++++++++++------------------------- metadata/providers.go | 55 ------------------ 3 files changed, 50 insertions(+), 134 deletions(-) diff --git a/metadata/metadata.go b/metadata/metadata.go index 53a48fd4..c62fa7d3 100644 --- a/metadata/metadata.go +++ b/metadata/metadata.go @@ -16,7 +16,7 @@ type Metadata struct { AuthorName string `yaml:":author_name,omitempty"` Branch string `yaml:":branch"` BuildURL string `yaml:":build_url"` - Check string `yaml:":check" env:"BUILDPULSE_CHECK_NAME"` // TODO: Should this env be here or in the providers? + Check string `yaml:":check"` CIProvider string `yaml:":ci_provider"` CommitMessage string `yaml:":commit_message,omitempty"` CommitSHA string `yaml:":commit"` @@ -55,14 +55,20 @@ func (m *Metadata) initProviderData(envs map[string]string, log Logger) error { if err != nil { return err } - m.providerData = pm + m.Branch = pm.Branch() m.BuildURL = pm.BuildURL() - m.Check = pm.Check() m.CIProvider = pm.Name() m.RepoNameWithOwner = pm.RepoNameWithOwner() + check, ok := envs["BUILDPULSE_CHECK_NAME"] + if ok && check != "" { + m.Check = check + } else { + m.Check = pm.Name() + } + return nil } diff --git a/metadata/metadata_test.go b/metadata/metadata_test.go index c5a3175c..2d3d1e70 100644 --- a/metadata/metadata_test.go +++ b/metadata/metadata_test.go @@ -1,6 +1,7 @@ package metadata import ( + "fmt" "io/ioutil" "testing" "time" @@ -193,83 +194,47 @@ func TestNewMetadata_unsupportedProvider(t *testing.T) { } } -// func TestNewMetadata_customCheckName(t *testing.T) { -// tests := []struct { -// name string -// envs map[string]string -// expectedProvider string -// expectedCheck string -// }{ -// { -// name: "Buildkite", -// envs: map[string]string{ -// "BUILDKITE": "true", -// "BUILDPULSE_CHECK_NAME": "some-custom-check-name", -// "BUILDKITE_REPO": "git@github.com:x/y.git", -// }, -// expectedProvider: "buildkite", -// expectedCheck: "some-custom-check-name", -// }, -// { -// name: "Circle", -// envs: map[string]string{ -// "CIRCLECI": "true", -// "BUILDPULSE_CHECK_NAME": "some-custom-check-name", -// }, -// expectedProvider: "circleci", -// expectedCheck: "some-custom-check-name", -// }, -// { -// name: "GitHubActions", -// envs: map[string]string{ -// "GITHUB_ACTIONS": "true", -// "BUILDPULSE_CHECK_NAME": "some-custom-check-name", -// }, -// expectedProvider: "github-actions", -// expectedCheck: "some-custom-check-name", -// }, -// { -// name: "Jenkins", -// envs: map[string]string{ -// "JENKINS_HOME": "/var/lib/jenkins", -// "BUILDPULSE_CHECK_NAME": "some-custom-check-name", -// "BUILD_URL": "https://some-jenkins-server.com/job/some-project/8675309", -// "GIT_URL": "https://github.com/some-owner/some-repo.git", -// }, -// expectedProvider: "jenkins", -// expectedCheck: "some-custom-check-name", -// }, -// { -// name: "Semaphore", -// envs: map[string]string{ -// "SEMAPHORE": "true", -// "BUILDPULSE_CHECK_NAME": "some-custom-check-name", -// }, -// expectedProvider: "semaphore", -// expectedCheck: "some-custom-check-name", -// }, -// { -// name: "Travis", -// envs: map[string]string{ -// "TRAVIS": "true", -// "BUILDPULSE_CHECK_NAME": "some-custom-check-name", -// }, -// expectedProvider: "travis-ci", -// expectedCheck: "some-custom-check-name", -// }, -// } -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// meta, err := NewMetadata(&Version{}, tt.envs, newCommitResolverStub(), time.Now, &stubLogger{}) -// assert.NoError(t, err) +func TestNewMetadata_customCheckName(t *testing.T) { + tests := []struct { + name string + envs map[string]string + expectedCheck string + }{ + { + name: "with custom check name present", + envs: map[string]string{ + "BUILDPULSE_CHECK_NAME": "some-custom-check-name", + "GITHUB_ACTIONS": "true", + }, + expectedCheck: "some-custom-check-name", + }, + { + name: "with custom check name present but empty", + envs: map[string]string{ + "BUILDPULSE_CHECK_NAME": "", + "GITHUB_ACTIONS": "true", + }, + expectedCheck: "github-actions", + }, + { + name: "without custom check name", + envs: map[string]string{ + "GITHUB_ACTIONS": "true", + }, + expectedCheck: "github-actions", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + meta, err := NewMetadata(&Version{}, tt.envs, newCommitResolverStub(), time.Now, &stubLogger{}) + assert.NoError(t, err) -// yaml, err := meta.MarshalYAML() -// assert.NoError(t, err) -// assert.Regexp(t, fmt.Sprintf(":ci_provider: %s", tt.expectedProvider), string(yaml)) -// assert.Regexp(t, fmt.Sprintf(":check: %s", tt.expectedCheck), string(yaml)) -// }) -// } -// } + yaml, err := meta.MarshalYAML() + assert.NoError(t, err) + assert.Regexp(t, fmt.Sprintf(":check: %s", tt.expectedCheck), string(yaml)) + }) + } +} func Test_buildkiteMetadata_Init_extraFields(t *testing.T) { tests := []struct { diff --git a/metadata/providers.go b/metadata/providers.go index 600dc5d6..77bf4616 100644 --- a/metadata/providers.go +++ b/metadata/providers.go @@ -15,7 +15,6 @@ type providerMetadata interface { Init(envs map[string]string, log Logger) error Branch() string BuildURL() string - Check() string CommitSHA() string Name() string RepoNameWithOwner() string @@ -104,15 +103,6 @@ func (b *buildkiteMetadata) BuildURL() string { return b.BuildkiteBuildURL } -func (b *buildkiteMetadata) Check() string { - // TODO: Handle custom check name - // if g.Check == "" { - // return "buildkite" - // } - - return "buildkite" -} - func (b *buildkiteMetadata) CommitSHA() string { return b.BuildkiteCommit } @@ -160,15 +150,6 @@ func (c *circleMetadata) BuildURL() string { return c.CircleBuildURL } -func (c *circleMetadata) Check() string { - // TODO: Handle custom check name - // if g.Check == "" { - // return "circleci" - // } - - return "circleci" -} - func (c *circleMetadata) CommitSHA() string { return c.CircleSHA1 } @@ -231,15 +212,6 @@ func (g *githubMetadata) BuildURL() string { return g.buildURL } -func (g *githubMetadata) Check() string { - // TODO: Handle custom check name - // if g.Check == "" { - // return "github-actions" - // } - - return "github-actions" -} - func (g *githubMetadata) CommitSHA() string { return g.GithubSHA } @@ -298,15 +270,6 @@ func (j *jenkinsMetadata) BuildURL() string { return j.buildURL } -func (j *jenkinsMetadata) Check() string { - // TODO: Handle custom check name - // if j.Check == "" { - // return "jenkins" - // } - - return "jenkins" -} - func (j *jenkinsMetadata) CommitSHA() string { return j.GitCommit } @@ -360,15 +323,6 @@ func (s *semaphoreMetadata) BuildURL() string { return fmt.Sprintf("%s/workflows/%s", s.SemaphoreOrganizationURL, s.SemaphoreWorkflowID) } -func (s *semaphoreMetadata) Check() string { - // TODO: Handle custom check name - // if g.Check == "" { - // return "semaphore" - // } - - return "semaphore" -} - func (s *semaphoreMetadata) CommitSHA() string { return s.SemaphoreGitSHA } @@ -432,15 +386,6 @@ func (t *travisMetadata) BuildURL() string { return t.TravisJobWebURL } -func (t *travisMetadata) Check() string { - // TODO: Handle custom check name - // if g.Check == "" { - // return "travis-ci" - // } - - return "travis-ci" -} - func (t *travisMetadata) CommitSHA() string { return t.TravisCommit } From 41a9bea883f92c37a6ae7e2289d3eeec9a45ae63 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Thu, 11 Feb 2021 20:06:03 -0500 Subject: [PATCH 08/12] =?UTF-8?q?=F0=9F=8E=A8=20Move=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- metadata/metadata_test.go | 214 ----------------------------------- metadata/providers_test.go | 221 +++++++++++++++++++++++++++++++++++++ 2 files changed, 221 insertions(+), 214 deletions(-) create mode 100644 metadata/providers_test.go diff --git a/metadata/metadata_test.go b/metadata/metadata_test.go index 2d3d1e70..f055c0c1 100644 --- a/metadata/metadata_test.go +++ b/metadata/metadata_test.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/yaml.v2" ) func TestNewMetadata(t *testing.T) { @@ -236,219 +235,6 @@ func TestNewMetadata_customCheckName(t *testing.T) { } } -func Test_buildkiteMetadata_Init_extraFields(t *testing.T) { - tests := []struct { - name string - envs map[string]string - expectedLines []string - }{ - { - name: "when rebuilt", - envs: map[string]string{ - "BUILDKITE_REBUILT_FROM_BUILD_ID": "00000000-0000-0000-0000-000000000000", - "BUILDKITE_REBUILT_FROM_BUILD_NUMBER": "42", - "BUILDKITE_REPO": "git@github.com:x/y.git", - }, - expectedLines: []string{ - ":buildkite_rebuilt_from_build_id: 00000000-0000-0000-0000-000000000000", - ":buildkite_rebuilt_from_build_number: 42", - }, - }, - { - name: "with pull request", - envs: map[string]string{ - "BUILDKITE_PULL_REQUEST_BASE_BRANCH": "some-base-branch", - "BUILDKITE_PULL_REQUEST_REPO": "git://github.com/some-forker/some-repo.git", - "BUILDKITE_PULL_REQUEST": "99", - "BUILDKITE_REPO": "git@github.com:x/y.git", - }, - expectedLines: []string{ - ":buildkite_pull_request_base_branch: some-base-branch", - ":buildkite_pull_request_repo: git://github.com/some-forker/some-repo.git", - ":buildkite_pull_request_number: 99", - }, - }, - { - name: "with tag", - envs: map[string]string{ - "BUILDKITE_REPO": "git@github.com:x/y.git", - "BUILDKITE_TAG": "v0.1.0", - }, - expectedLines: []string{":buildkite_tag: v0.1.0"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - meta := buildkiteMetadata{} - err := meta.Init(tt.envs, &stubLogger{}) - assert.NoError(t, err) - - yaml, err := yaml.Marshal(meta) - assert.NoError(t, err) - for _, line := range tt.expectedLines { - assert.Regexp(t, line, string(yaml)) - } - }) - } -} - -func Test_circleMetadata_Init_extraFields(t *testing.T) { - tests := []struct { - name string - envs map[string]string - expectedLines []string - }{ - { - name: "with pull request", - envs: map[string]string{ - "CIRCLE_PR_NUMBER": "42", - "CIRCLE_PR_REPONAME": "some-repo", - "CIRCLE_PR_USERNAME": "some-forker", - "CIRCLE_PULL_REQUEST": "https://github.com/some-owner/some-repo/pull/42", - }, - expectedLines: []string{ - ":circle_pr_number: 42", - ":circle_pr_reponame: some-repo", - ":circle_pr_username: some-forker", - ":circle_pull_request: https://github.com/some-owner/some-repo/pull/42", - }, - }, - { - name: "with tag", - envs: map[string]string{ - "CIRCLE_TAG": "v0.1.0", - }, - expectedLines: []string{":circle_tag: v0.1.0"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - meta := circleMetadata{} - err := meta.Init(tt.envs, &stubLogger{}) - assert.NoError(t, err) - - yaml, err := yaml.Marshal(meta) - assert.NoError(t, err) - for _, line := range tt.expectedLines { - assert.Regexp(t, line, string(yaml)) - } - }) - } -} - -func Test_githubMetadata_Init_refTypes(t *testing.T) { - tests := []struct { - name string - envs map[string]string - want string - }{ - { - name: "branch", - envs: map[string]string{ - "GITHUB_REF": "refs/heads/some-branch", - }, - want: "some-branch", - }, - { - name: "tag", - envs: map[string]string{ - "GITHUB_REF": "refs/tags/v0.1.0", - }, - want: "", - }, - { - name: "neither a branch nor a tag", - envs: map[string]string{}, // The GITHUB_REF env var is not present in this scenario - want: "", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - meta := githubMetadata{} - err := meta.Init(tt.envs, &stubLogger{}) - assert.NoError(t, err) - - assert.Equal(t, tt.want, meta.Branch()) - }) - } -} - -func Test_travisMetadata_Init_extraFields(t *testing.T) { - tests := []struct { - name string - envs map[string]string - expectedLines []string - }{ - { - name: "with job name", - envs: map[string]string{ - "TRAVIS_JOB_NAME": "some-job-name", - }, - expectedLines: []string{":travis_job_name: some-job-name"}, - }, - { - name: "with pull request", - envs: map[string]string{ - "TRAVIS_PULL_REQUEST_BRANCH": "some-branch", - "TRAVIS_PULL_REQUEST_SHA": "eea22cb17a834f39961499af910ec96c82b035f4", - "TRAVIS_PULL_REQUEST_SLUG": "some-forker/some-repo", - "TRAVIS_PULL_REQUEST": "1", - }, - expectedLines: []string{ - ":travis_pull_request_branch: some-branch", - ":travis_pull_request_sha: eea22cb17a834f39961499af910ec96c82b035f4", - ":travis_pull_request_slug: some-forker/some-repo", - ":travis_pull_request_number: 1", - }, - }, - { - name: "with tag", - envs: map[string]string{ - "TRAVIS_TAG": "v0.1.0", - }, - expectedLines: []string{":travis_tag: v0.1.0"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - meta := travisMetadata{} - err := meta.Init(tt.envs, &stubLogger{}) - assert.NoError(t, err) - - yaml, err := yaml.Marshal(meta) - assert.NoError(t, err) - for _, line := range tt.expectedLines { - assert.Regexp(t, line, string(yaml)) - } - }) - } -} - -func Test_nameWithOwnerFromGitURL(t *testing.T) { - tests := []struct { - name string - url string - nwo string - err bool - }{ - {name: "https", url: "https://github.com/some-owner/some-repo.git", nwo: "some-owner/some-repo", err: false}, - {name: "ssh", url: "git@github.com:some-owner/some-repo.git", nwo: "some-owner/some-repo", err: false}, - {name: "malformed", url: "some-malformed-url", nwo: "", err: true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - nwo, err := nameWithOwnerFromGitURL(tt.url) - - if tt.err { - assert.Error(t, err) - } else { - assert.NoError(t, err) - assert.Equal(t, nwo, tt.nwo) - } - }) - } -} - func newCommitResolverStub() CommitResolver { return CommitResolverFunc( func(sha string) (*Commit, error) { diff --git a/metadata/providers_test.go b/metadata/providers_test.go new file mode 100644 index 00000000..86acca94 --- /dev/null +++ b/metadata/providers_test.go @@ -0,0 +1,221 @@ +package metadata + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v2" +) + +func Test_buildkiteMetadata_Init_extraFields(t *testing.T) { + tests := []struct { + name string + envs map[string]string + expectedLines []string + }{ + { + name: "when rebuilt", + envs: map[string]string{ + "BUILDKITE_REBUILT_FROM_BUILD_ID": "00000000-0000-0000-0000-000000000000", + "BUILDKITE_REBUILT_FROM_BUILD_NUMBER": "42", + "BUILDKITE_REPO": "git@github.com:x/y.git", + }, + expectedLines: []string{ + ":buildkite_rebuilt_from_build_id: 00000000-0000-0000-0000-000000000000", + ":buildkite_rebuilt_from_build_number: 42", + }, + }, + { + name: "with pull request", + envs: map[string]string{ + "BUILDKITE_PULL_REQUEST_BASE_BRANCH": "some-base-branch", + "BUILDKITE_PULL_REQUEST_REPO": "git://github.com/some-forker/some-repo.git", + "BUILDKITE_PULL_REQUEST": "99", + "BUILDKITE_REPO": "git@github.com:x/y.git", + }, + expectedLines: []string{ + ":buildkite_pull_request_base_branch: some-base-branch", + ":buildkite_pull_request_repo: git://github.com/some-forker/some-repo.git", + ":buildkite_pull_request_number: 99", + }, + }, + { + name: "with tag", + envs: map[string]string{ + "BUILDKITE_REPO": "git@github.com:x/y.git", + "BUILDKITE_TAG": "v0.1.0", + }, + expectedLines: []string{":buildkite_tag: v0.1.0"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + meta := buildkiteMetadata{} + err := meta.Init(tt.envs, &stubLogger{}) + assert.NoError(t, err) + + yaml, err := yaml.Marshal(meta) + assert.NoError(t, err) + for _, line := range tt.expectedLines { + assert.Regexp(t, line, string(yaml)) + } + }) + } +} + +func Test_circleMetadata_Init_extraFields(t *testing.T) { + tests := []struct { + name string + envs map[string]string + expectedLines []string + }{ + { + name: "with pull request", + envs: map[string]string{ + "CIRCLE_PR_NUMBER": "42", + "CIRCLE_PR_REPONAME": "some-repo", + "CIRCLE_PR_USERNAME": "some-forker", + "CIRCLE_PULL_REQUEST": "https://github.com/some-owner/some-repo/pull/42", + }, + expectedLines: []string{ + ":circle_pr_number: 42", + ":circle_pr_reponame: some-repo", + ":circle_pr_username: some-forker", + ":circle_pull_request: https://github.com/some-owner/some-repo/pull/42", + }, + }, + { + name: "with tag", + envs: map[string]string{ + "CIRCLE_TAG": "v0.1.0", + }, + expectedLines: []string{":circle_tag: v0.1.0"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + meta := circleMetadata{} + err := meta.Init(tt.envs, &stubLogger{}) + assert.NoError(t, err) + + yaml, err := yaml.Marshal(meta) + assert.NoError(t, err) + for _, line := range tt.expectedLines { + assert.Regexp(t, line, string(yaml)) + } + }) + } +} + +func Test_githubMetadata_Init_refTypes(t *testing.T) { + tests := []struct { + name string + envs map[string]string + want string + }{ + { + name: "branch", + envs: map[string]string{ + "GITHUB_REF": "refs/heads/some-branch", + }, + want: "some-branch", + }, + { + name: "tag", + envs: map[string]string{ + "GITHUB_REF": "refs/tags/v0.1.0", + }, + want: "", + }, + { + name: "neither a branch nor a tag", + envs: map[string]string{}, // The GITHUB_REF env var is not present in this scenario + want: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + meta := githubMetadata{} + err := meta.Init(tt.envs, &stubLogger{}) + assert.NoError(t, err) + + assert.Equal(t, tt.want, meta.Branch()) + }) + } +} + +func Test_travisMetadata_Init_extraFields(t *testing.T) { + tests := []struct { + name string + envs map[string]string + expectedLines []string + }{ + { + name: "with job name", + envs: map[string]string{ + "TRAVIS_JOB_NAME": "some-job-name", + }, + expectedLines: []string{":travis_job_name: some-job-name"}, + }, + { + name: "with pull request", + envs: map[string]string{ + "TRAVIS_PULL_REQUEST_BRANCH": "some-branch", + "TRAVIS_PULL_REQUEST_SHA": "eea22cb17a834f39961499af910ec96c82b035f4", + "TRAVIS_PULL_REQUEST_SLUG": "some-forker/some-repo", + "TRAVIS_PULL_REQUEST": "1", + }, + expectedLines: []string{ + ":travis_pull_request_branch: some-branch", + ":travis_pull_request_sha: eea22cb17a834f39961499af910ec96c82b035f4", + ":travis_pull_request_slug: some-forker/some-repo", + ":travis_pull_request_number: 1", + }, + }, + { + name: "with tag", + envs: map[string]string{ + "TRAVIS_TAG": "v0.1.0", + }, + expectedLines: []string{":travis_tag: v0.1.0"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + meta := travisMetadata{} + err := meta.Init(tt.envs, &stubLogger{}) + assert.NoError(t, err) + + yaml, err := yaml.Marshal(meta) + assert.NoError(t, err) + for _, line := range tt.expectedLines { + assert.Regexp(t, line, string(yaml)) + } + }) + } +} + +func Test_nameWithOwnerFromGitURL(t *testing.T) { + tests := []struct { + name string + url string + nwo string + err bool + }{ + {name: "https", url: "https://github.com/some-owner/some-repo.git", nwo: "some-owner/some-repo", err: false}, + {name: "ssh", url: "git@github.com:some-owner/some-repo.git", nwo: "some-owner/some-repo", err: false}, + {name: "malformed", url: "some-malformed-url", nwo: "", err: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + nwo, err := nameWithOwnerFromGitURL(tt.url) + + if tt.err { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, nwo, tt.nwo) + } + }) + } +} From bbd846529ce8252df8147165dd563899e693677c Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Thu, 11 Feb 2021 20:50:33 -0500 Subject: [PATCH 09/12] =?UTF-8?q?=F0=9F=8E=A8=20Add=20newLoggerStub=20func?= =?UTF-8?q?=20for=20symmetry=20with=20newCommitResolverStub?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- metadata/metadata_test.go | 15 +++++++++------ metadata/providers_test.go | 8 ++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/metadata/metadata_test.go b/metadata/metadata_test.go index f055c0c1..7025dcc5 100644 --- a/metadata/metadata_test.go +++ b/metadata/metadata_test.go @@ -176,7 +176,7 @@ func TestNewMetadata(t *testing.T) { }) version := &Version{Number: "v1.2.3", GoOS: "linux"} - meta, err := NewMetadata(version, tt.envs, commitResolverDouble, now, &stubLogger{}) + meta, err := NewMetadata(version, tt.envs, commitResolverDouble, now, newLoggerStub()) assert.NoError(t, err) yaml, err := meta.MarshalYAML() @@ -187,7 +187,7 @@ func TestNewMetadata(t *testing.T) { } func TestNewMetadata_unsupportedProvider(t *testing.T) { - _, err := NewMetadata(&Version{}, map[string]string{}, newCommitResolverStub(), time.Now, &stubLogger{}) + _, err := NewMetadata(&Version{}, map[string]string{}, newCommitResolverStub(), time.Now, newLoggerStub()) if assert.Error(t, err) { assert.Contains(t, err.Error(), "unrecognized environment") } @@ -225,7 +225,7 @@ func TestNewMetadata_customCheckName(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - meta, err := NewMetadata(&Version{}, tt.envs, newCommitResolverStub(), time.Now, &stubLogger{}) + meta, err := NewMetadata(&Version{}, tt.envs, newCommitResolverStub(), time.Now, newLoggerStub()) assert.NoError(t, err) yaml, err := meta.MarshalYAML() @@ -242,7 +242,10 @@ func newCommitResolverStub() CommitResolver { }) } -// TODO: Look for better way to create a no-op logger for testing -type stubLogger struct{} +type loggerStub struct{} -func (s *stubLogger) Printf(format string, v ...interface{}) {} +func (l *loggerStub) Printf(format string, v ...interface{}) {} + +func newLoggerStub() Logger { + return &loggerStub{} +} diff --git a/metadata/providers_test.go b/metadata/providers_test.go index 86acca94..48f90e71 100644 --- a/metadata/providers_test.go +++ b/metadata/providers_test.go @@ -51,7 +51,7 @@ func Test_buildkiteMetadata_Init_extraFields(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { meta := buildkiteMetadata{} - err := meta.Init(tt.envs, &stubLogger{}) + err := meta.Init(tt.envs, newLoggerStub()) assert.NoError(t, err) yaml, err := yaml.Marshal(meta) @@ -95,7 +95,7 @@ func Test_circleMetadata_Init_extraFields(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { meta := circleMetadata{} - err := meta.Init(tt.envs, &stubLogger{}) + err := meta.Init(tt.envs, newLoggerStub()) assert.NoError(t, err) yaml, err := yaml.Marshal(meta) @@ -136,7 +136,7 @@ func Test_githubMetadata_Init_refTypes(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { meta := githubMetadata{} - err := meta.Init(tt.envs, &stubLogger{}) + err := meta.Init(tt.envs, newLoggerStub()) assert.NoError(t, err) assert.Equal(t, tt.want, meta.Branch()) @@ -183,7 +183,7 @@ func Test_travisMetadata_Init_extraFields(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { meta := travisMetadata{} - err := meta.Init(tt.envs, &stubLogger{}) + err := meta.Init(tt.envs, newLoggerStub()) assert.NoError(t, err) yaml, err := yaml.Marshal(meta) From 8c6831c44e7f8947b3caf7f536da8c6f73caf11e Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Fri, 12 Feb 2021 07:54:21 -0500 Subject: [PATCH 10/12] =?UTF-8?q?=F0=9F=93=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/submit.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/submit.go b/cmd/submit.go index 443c0eb3..9d585921 100644 --- a/cmd/submit.go +++ b/cmd/submit.go @@ -29,7 +29,7 @@ type credentials struct { } // A log object can be passed around for use as a logger. It stores logs -// in-memory and can flush the logs to a string when requested. +// in memory and can flush the logs to a string when requested. type log struct { entries []string } From 9bea90d43db52dd15c914490c89cb498c4286d92 Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Fri, 12 Feb 2021 07:54:38 -0500 Subject: [PATCH 11/12] =?UTF-8?q?=F0=9F=94=A5=20Remove=20stray=20Printf=20?= =?UTF-8?q?statement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/submit.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/submit.go b/cmd/submit.go index 9d585921..06fbbbb8 100644 --- a/cmd/submit.go +++ b/cmd/submit.go @@ -36,7 +36,6 @@ type log struct { func (l *log) Printf(format string, v ...interface{}) { l.entries = append(l.entries, fmt.Sprintf(format, v...)) - fmt.Printf(format, v...) } // Text returns a string concatenation of all of the log's entries. From 62b9289669aff46ed49cf937d4510ad22ee2aeef Mon Sep 17 00:00:00 2001 From: Jason Rudolph Date: Fri, 12 Feb 2021 08:09:55 -0500 Subject: [PATCH 12/12] =?UTF-8?q?=F0=9F=8E=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- metadata/providers.go | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/metadata/providers.go b/metadata/providers.go index 77bf4616..f9fb972b 100644 --- a/metadata/providers.go +++ b/metadata/providers.go @@ -50,9 +50,6 @@ func newProviderMetadata(envs map[string]string, log Logger) (providerMetadata, var _ providerMetadata = (*buildkiteMetadata)(nil) type buildkiteMetadata struct { - // Internal state - nwo string - // Fields derived from Buildkite-specific environment variables BuildkiteBranch string `env:"BUILDKITE_BRANCH" yaml:"-"` BuildkiteBuildID string `env:"BUILDKITE_BUILD_ID" yaml:":buildkite_build_id"` @@ -74,6 +71,8 @@ type buildkiteMetadata struct { BuildkiteRepoURL string `env:"BUILDKITE_REPO" yaml:"-"` BuildkiteRetryCount uint `env:"BUILDKITE_RETRY_COUNT" yaml:":buildkite_retry_count"` BuildkiteTag string `env:"BUILDKITE_TAG" yaml:":buildkite_tag,omitempty"` + + nwo string } func (b *buildkiteMetadata) Init(envs map[string]string, log Logger) error { @@ -165,10 +164,6 @@ func (c *circleMetadata) RepoNameWithOwner() string { var _ providerMetadata = (*githubMetadata)(nil) type githubMetadata struct { - // Internal state - branch string - buildURL string - // Fields derived from GitHub-specific environment variables GithubActor string `env:"GITHUB_ACTOR" yaml:":github_actor"` GithubBaseRef string `env:"GITHUB_BASE_REF" yaml:":github_base_ref"` @@ -182,6 +177,9 @@ type githubMetadata struct { GithubServerURL string `env:"GITHUB_SERVER_URL" yaml:"-"` GithubSHA string `env:"GITHUB_SHA" yaml:"-"` GithubWorkflow string `env:"GITHUB_WORKFLOW" yaml:":github_workflow"` + + branch string + buildURL string } func (g *githubMetadata) Init(envs map[string]string, log Logger) error { @@ -227,10 +225,6 @@ func (g *githubMetadata) RepoNameWithOwner() string { var _ providerMetadata = (*jenkinsMetadata)(nil) type jenkinsMetadata struct { - // Internal state - buildURL string - nwo string - // Fields derived from Jenkins-specific environment variables GitBranch string `env:"GIT_BRANCH" yaml:"-"` GitCommit string `env:"GIT_COMMIT" yaml:"-"` @@ -240,6 +234,9 @@ type jenkinsMetadata struct { JenkinsJobURL string `env:"JOB_URL" yaml:":jenkins_job_url"` JenkinsNodeName string `env:"NODE_NAME" yaml:":jenkins_node_name"` JenkinsWorkspace string `env:"WORKSPACE" yaml:":jenkins_workspace"` + + buildURL string + nwo string } func (j *jenkinsMetadata) Init(envs map[string]string, log Logger) error {