diff --git a/README.md b/README.md index c0590cd5..70602494 100644 --- a/README.md +++ b/README.md @@ -56,13 +56,14 @@ To use `test-reporter` with another CI provider, the following environment varia | `REPOSITORY_NAME` | Name of the repository | The following are flags that can be set. Make sure to **set flags after CLI args**. -| Environment Variable | Required | Description | -|----------------------|-----------------------------------|----------------------------------------------| -| `account-id` | ✓ | BuildPulse account ID (see dashboard) | -| `repository-id` | ✓ | BuildPulse repository ID (see dashboard) | -| `repository-dir` | Only if `tree` not set | Path to repository directory | -| `tree` | Only if `repository-dir` not set | Git tree SHA | -| `coverage-files` | Only if using BuildPulse Coverage | **Space-separated** paths to coverage files. | +| Flag | Required | Description | +|----------------------|-----------------------------------|-------------------------------------------------| +| `account-id` | ✓ | BuildPulse account ID (see dashboard) | +| `repository-id` | ✓ | BuildPulse repository ID (see dashboard) | +| `repository-dir` | Only if `tree` not set | Path to repository directory | +| `tree` | Only if `repository-dir` not set | Git tree SHA | +| `coverage-files` | Only if using BuildPulse Coverage | **Space-separated** paths to coverage files. | +| `tags` | | **Space-separated** tags to apply to the build. | Example: ``` diff --git a/cmd/test-reporter/main.go b/cmd/test-reporter/main.go index 270b8328..adfa50e7 100644 --- a/cmd/test-reporter/main.go +++ b/cmd/test-reporter/main.go @@ -30,6 +30,7 @@ FLAGS --repository-dir Path to local git clone of the repository (default: ".") --tree SHA-1 hash of the git tree that produced the test results (for use only if a local git clone does not exist) --coverage-files Paths to coverage files or directories containing coverage files (space-separated) + --tags Tags to apply to the build (space-separated) ENVIRONMENT VARIABLES Set the following environment variables: diff --git a/internal/cmd/submit/submit.go b/internal/cmd/submit/submit.go index 230e15ed..84257654 100644 --- a/internal/cmd/submit/submit.go +++ b/internal/cmd/submit/submit.go @@ -71,6 +71,7 @@ type Submit struct { paths []string coveragePathsString string coveragePaths []string + tagsString string bucket string accountID uint64 repositoryID uint64 @@ -97,6 +98,7 @@ func NewSubmit(version *metadata.Version, log logger.Logger) *Submit { s.fs.StringVar(&s.tree, "tree", "", "SHA-1 hash of git tree") s.fs.StringVar(&s.coveragePathsString, "coverage-files", "", "Paths to coverage files (space-separated)") s.fs.BoolVar(&s.disableCoverageAutoDiscovery, "disable-coverage-auto", false, "Disables coverage file autodiscovery") + s.fs.StringVar(&s.tagsString, "tags", "", "Tags to apply to the build (space-separated)") s.fs.SetOutput(io.Discard) // Disable automatic writing to STDERR s.logger.Printf("Current version: %s", s.version.String()) @@ -156,6 +158,8 @@ func (s *Submit) Init(args []string, envs map[string]string, commitResolverFacto if len(s.coveragePathsString) > 0 { s.coveragePaths = strings.Split(s.coveragePathsString, " ") + } else { + s.coveragePaths = []string{} } id, ok := envs["BUILDPULSE_ACCESS_KEY_ID"] @@ -242,7 +246,8 @@ func (s *Submit) bundle() (string, error) { ////////////////////////////////////////////////////////////////////////////// s.logger.Printf("Gathering metadata to describe the build") - meta, err := metadata.NewMetadata(s.version, s.envs, s.commitResolver, time.Now, s.logger) + tags := strings.Split(s.tagsString, " ") + meta, err := metadata.NewMetadata(s.version, s.envs, tags, s.commitResolver, time.Now, s.logger) if err != nil { return "", err } diff --git a/internal/cmd/submit/submit_test.go b/internal/cmd/submit/submit_test.go index 7585672f..60464be3 100644 --- a/internal/cmd/submit/submit_test.go +++ b/internal/cmd/submit/submit_test.go @@ -44,6 +44,7 @@ func TestSubmit_Init(t *testing.T) { assert.Equal(t, exampleEnv, s.envs) assert.Equal(t, ".", s.repositoryPath) assert.Equal(t, "Repository", s.commitResolver.Source()) + assert.Equal(t, s.coveragePaths, []string{}) }) t.Run("WithCoveragePathString", func(t *testing.T) { @@ -78,6 +79,22 @@ func TestSubmit_Init(t *testing.T) { assert.True(t, s.disableCoverageAutoDiscovery) }) + t.Run("WithTagsString", func(t *testing.T) { + s := NewSubmit(&metadata.Version{}, logger.New()) + err := s.Init([]string{"testdata/example-reports-dir/example-*.xml", "--account-id", "42", "--repository-id", "8675309", "--tags", "tag1 tag2"}, exampleEnv, new(stubCommitResolverFactory)) + require.NoError(t, err) + assert.ElementsMatch(t, []string{"testdata/example-reports-dir/example-1.xml"}, s.paths) + assert.EqualValues(t, 42, s.accountID) + assert.EqualValues(t, 8675309, s.repositoryID) + assert.Equal(t, "buildpulse-uploads", s.bucket) + assert.Equal(t, "some-access-key-id", s.credentials.AccessKeyID) + assert.Equal(t, "some-secret-access-key", s.credentials.SecretAccessKey) + assert.Equal(t, exampleEnv, s.envs) + assert.Equal(t, ".", s.repositoryPath) + assert.Equal(t, "Repository", s.commitResolver.Source()) + assert.Equal(t, s.tagsString, "tag1 tag2") + }) + t.Run("WithMultiplePathArgs", func(t *testing.T) { s := NewSubmit(&metadata.Version{}, logger.New()) err := s.Init( @@ -609,6 +626,40 @@ func Test_bundle(t *testing.T) { require.NoError(t, err) assert.Contains(t, string(logdata), "Gathering metadata to describe the build") }) + + t.Run("bundle with tags", func(t *testing.T) { + envs := map[string]string{ + "GITHUB_ACTIONS": "true", + "GITHUB_SHA": "aaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbb", + } + + log := logger.New() + s := &Submit{ + logger: log, + version: &metadata.Version{Number: "v1.2.3"}, + commitResolver: metadata.NewStaticCommitResolver(&metadata.Commit{TreeSHA: "ccccccccccccccccccccdddddddddddddddddddd"}, log), + envs: envs, + paths: []string{"testdata/example-reports-dir/example-1.xml"}, + bucket: "buildpulse-uploads", + accountID: 42, + repositoryID: 8675309, + tagsString: "tag1 tag2", + } + + path, err := s.bundle() + require.NoError(t, err) + + unzipDir := t.TempDir() + err = archiver.Unarchive(path, unzipDir) + require.NoError(t, err) + + // Verify buildpulse.yml is present and contains expected content + yaml, err := os.ReadFile(filepath.Join(unzipDir, "buildpulse.yml")) + require.NoError(t, err) + + assert.Contains(t, string(yaml), "- tag1") + assert.Contains(t, string(yaml), "- tag2") + }) } func Test_upload(t *testing.T) { diff --git a/internal/metadata/metadata.go b/internal/metadata/metadata.go index 7e9361da..714e513b 100644 --- a/internal/metadata/metadata.go +++ b/internal/metadata/metadata.go @@ -28,6 +28,7 @@ type Metadata struct { RepoNameWithOwner string `yaml:":repo_name_with_owner"` ReporterOS string `yaml:":reporter_os"` ReporterVersion string `yaml:":reporter_version"` + Tags []string `yaml:":tags,omitempty"` Timestamp time.Time `yaml:":timestamp"` TreeSHA string `yaml:":tree,omitempty"` @@ -36,7 +37,7 @@ type Metadata struct { } // NewMetadata creates a new Metadata instance from the given args. -func NewMetadata(version *Version, envs map[string]string, resolver CommitResolver, now func() time.Time, logger logger.Logger) (*Metadata, error) { +func NewMetadata(version *Version, envs map[string]string, tags []string, resolver CommitResolver, now func() time.Time, logger logger.Logger) (*Metadata, error) { m := &Metadata{logger: logger} if err := m.initProviderData(envs); err != nil { @@ -50,6 +51,8 @@ func NewMetadata(version *Version, envs map[string]string, resolver CommitResolv m.initTimestamp(now) m.initVersionData(version) + m.Tags = tags + return m, nil } diff --git a/internal/metadata/metadata_test.go b/internal/metadata/metadata_test.go index a042b192..8733bbfa 100644 --- a/internal/metadata/metadata_test.go +++ b/internal/metadata/metadata_test.go @@ -15,6 +15,7 @@ func TestNewMetadata(t *testing.T) { tests := []struct { name string envs map[string]string + tags []string fixture string }{ { @@ -192,7 +193,7 @@ func TestNewMetadata(t *testing.T) { ) version := &Version{Number: "v1.2.3", GoOS: "linux"} - meta, err := NewMetadata(version, tt.envs, commitResolver, now, logger.New()) + meta, err := NewMetadata(version, tt.envs, tt.tags, commitResolver, now, logger.New()) assert.NoError(t, err) yaml, err := meta.MarshalYAML() @@ -203,7 +204,7 @@ func TestNewMetadata(t *testing.T) { } func TestNewMetadata_unsupportedProvider(t *testing.T) { - _, err := NewMetadata(&Version{}, map[string]string{}, newCommitResolverStub(), time.Now, logger.New()) + _, err := NewMetadata(&Version{}, map[string]string{}, []string{}, newCommitResolverStub(), time.Now, logger.New()) if assert.Error(t, err) { assert.Contains(t, err.Error(), "missing required environment variables") } @@ -241,7 +242,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, logger.New()) + meta, err := NewMetadata(&Version{}, tt.envs, []string{}, newCommitResolverStub(), time.Now, logger.New()) assert.NoError(t, err) yaml, err := meta.MarshalYAML() @@ -254,3 +255,71 @@ func TestNewMetadata_customCheckName(t *testing.T) { func newCommitResolverStub() CommitResolver { return NewStaticCommitResolver(&Commit{}, logger.New()) } + +func TestNewMetadata_appliesTags(t *testing.T) { + tests := []struct { + name string + envs map[string]string + tags []string + fixture string + }{ + { + name: "GitHubActions", + envs: map[string]string{ + "GITHUB_ACTIONS": "true", + "GITHUB_ACTOR": "some-user", + "GITHUB_BASE_REF": "refs/heads/main", + "GITHUB_EVENT_NAME": "push", + "GITHUB_HEAD_REF": "refs/heads/some-feature", + "GITHUB_REF": "refs/heads/some-feature", + "GITHUB_REPOSITORY": "some-owner/some-repo", + "GITHUB_RUN_ATTEMPT": "1", + "GITHUB_RUN_ID": "8675309", + "GITHUB_RUN_NUMBER": "42", + "GITHUB_SERVER_URL": "https://github.com", + "GITHUB_SHA": "1f192ff735f887dd7a25229b2ece0422d17931f5", + "GITHUB_WORKFLOW": "build", + }, + fixture: "./testdata/github_tags.yml", + tags: []string{"tag1", "tag2"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + now := func() time.Time { + return time.Date(2020, 7, 11, 1, 2, 3, 0, time.UTC) + } + + expected, err := os.ReadFile(tt.fixture) + require.NoError(t, err) + + authoredAt, err := time.Parse(time.RFC3339, "2020-07-09T04:05:06-05:00") + require.NoError(t, err) + + committedAt, err := time.Parse(time.RFC3339, "2020-07-10T07:08:09+13:00") + require.NoError(t, err) + + commitResolver := NewStaticCommitResolver( + &Commit{ + AuthoredAt: authoredAt, + AuthorEmail: "some-author@example.com", + AuthorName: "Some Author", + CommittedAt: committedAt, + CommitterEmail: "some-committer@example.com", + CommitterName: "Some Committer", + Message: "Some message", + TreeSHA: "0da9df599c02da5e7f5058b7108dcd5e1929a0fe", + }, + logger.New(), + ) + + version := &Version{Number: "v1.2.3", GoOS: "linux"} + meta, err := NewMetadata(version, tt.envs, tt.tags, commitResolver, now, logger.New()) + assert.NoError(t, err) + + yaml, err := meta.MarshalYAML() + assert.NoError(t, err) + assert.Equal(t, string(expected), string(yaml)) + }) + } +} diff --git a/internal/metadata/testdata/github_tags.yml b/internal/metadata/testdata/github_tags.yml new file mode 100644 index 00000000..61899a9d --- /dev/null +++ b/internal/metadata/testdata/github_tags.yml @@ -0,0 +1,31 @@ +:authored_at: 2020-07-09T04:05:06-05:00 +:author_email: some-author@example.com +:author_name: Some Author +:branch: some-feature +:build_url: https://github.com/some-owner/some-repo/actions/runs/8675309/attempts/1 +:check: github-actions +:ci_provider: github-actions +:commit_message: Some message +:commit_metadata_source: Static +:commit: 1f192ff735f887dd7a25229b2ece0422d17931f5 +:committed_at: 2020-07-10T07:08:09+13:00 +:committer_email: some-committer@example.com +:committer_name: Some Committer +:repo_name_with_owner: some-owner/some-repo +:reporter_os: linux +:reporter_version: v1.2.3 +:tags: + - tag1 + - tag2 +:timestamp: 2020-07-11T01:02:03Z +:tree: 0da9df599c02da5e7f5058b7108dcd5e1929a0fe +:github_actor: some-user +:github_base_ref: refs/heads/main +:github_event_name: push +:github_head_ref: refs/heads/some-feature +:github_ref: refs/heads/some-feature +:github_repo_url: https://github.com/some-owner/some-repo +:github_run_attempt: 1 +:github_run_id: 8675309 +:github_run_number: 42 +:github_workflow: build