Skip to content

Commit 8038474

Browse files
Add --commit flag to report command (#285)
- Allow specifying commit SHA directly, skipping git detection - Useful for CI environments where git is not available - Falls back to existing git/env var resolution when not set Co-authored-by: Sourya Vatsyayan <sourya@deepsource.io>
1 parent bd0f271 commit 8038474

File tree

4 files changed

+68
-5
lines changed

4 files changed

+68
-5
lines changed

command/report/report.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
type ReportOptions struct {
2121
Analyzer string
2222
AnalyzerType string
23+
CommitOID string
2324
Key string
2425
Value string
2526
ValueFile string
@@ -110,6 +111,8 @@ func NewCmdReportWithDeps(deps *container.Container) *cobra.Command {
110111

111112
cmd.Flags().StringVar(&opts.AnalyzerType, "analyzer-type", "", "type of the analyzer (example: community)")
112113

114+
cmd.Flags().StringVar(&opts.CommitOID, "commit", "", "commit SHA to report against (skips git detection)")
115+
113116
cmd.Flags().StringVar(&opts.Key, "key", "", "shortcode of the language (example: go)")
114117

115118
cmd.Flags().StringVar(&opts.Value, "value", "", "value of the artifact")
@@ -165,6 +168,7 @@ func (opts *ReportOptions) Run(ctx context.Context, svc *reportsvc.Service, outp
165168
result, err := svc.Report(ctx, reportsvc.Options{
166169
Analyzer: opts.Analyzer,
167170
AnalyzerType: opts.AnalyzerType,
171+
CommitOID: opts.CommitOID,
168172
Key: opts.Key,
169173
Value: opts.Value,
170174
ValueFile: opts.ValueFile,
@@ -193,7 +197,7 @@ func setReportUsageFunc(cmd *cobra.Command) {
193197
title string
194198
flags []string
195199
}{
196-
{"Artifact", []string{"analyzer", "analyzer-type", "key", "value", "value-file"}},
200+
{"Artifact", []string{"analyzer", "analyzer-type", "commit", "key", "value", "value-file"}},
197201
{"Authentication", []string{"use-oidc", "oidc-provider", "oidc-request-token", "oidc-request-url", "host"}},
198202
{"Output", []string{"output"}},
199203
{"General", []string{"skip-verify", "help"}},

internal/services/report/service.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,15 @@ func (s *Service) Report(ctx context.Context, opts Options) (*Result, error) {
8888
return nil, uerr
8989
}
9090

91-
headCommitOID, warning, err := s.git.GetHead(currentDir)
92-
if err != nil {
93-
s.capture(err)
94-
return nil, errors.New("Unable to get commit OID HEAD. Make sure you are running the CLI from a git repository")
91+
var headCommitOID, warning string
92+
if opts.CommitOID != "" {
93+
headCommitOID = opts.CommitOID
94+
} else {
95+
headCommitOID, warning, err = s.git.GetHead(currentDir)
96+
if err != nil {
97+
s.capture(err)
98+
return nil, errors.New("Unable to get commit OID HEAD. Make sure you are running the CLI from a git repository or use --commit flag")
99+
}
95100
}
96101

97102
artifactValue, err := s.resolveArtifactValue(opts)

internal/services/report/service_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,59 @@ func TestReportCreateArtifactEmptyError(t *testing.T) {
369369
assert.Contains(t, err.Error(), "raw response")
370370
}
371371

372+
func TestReportExplicitCommitSkipsGit(t *testing.T) {
373+
tempDir := t.TempDir()
374+
artifactPath := filepath.Join(tempDir, "coverage.xml")
375+
assert.NoError(t, os.WriteFile(artifactPath, []byte("<coverage/>"), 0o644))
376+
377+
var capturedCommit string
378+
httpClient := &mockHTTPClient{DoFunc: func(req *http.Request) (*http.Response, error) {
379+
body, _ := io.ReadAll(req.Body)
380+
_ = req.Body.Close()
381+
if bytes.Contains(body, []byte("ArtifactMetadataInput")) {
382+
payload := `{"data":{"__type":{"inputFields":[]}}}`
383+
return httpResponse(200, payload), nil
384+
}
385+
if bytes.Contains(body, []byte("createArtifact")) {
386+
var q ReportQuery
387+
_ = json.Unmarshal(body, &q)
388+
capturedCommit = q.Variables.Input.CommitOID
389+
payload := `{"data":{"createArtifact":{"ok":true,"message":"ok","error":""}}}`
390+
return httpResponse(200, payload), nil
391+
}
392+
return httpResponse(400, `{"error":"unexpected"}`), nil
393+
}}
394+
395+
// Git client returns an error — simulates no git repo available
396+
git := adapters.NewMockGitClient()
397+
git.SetError(errors.New("not a git repository"))
398+
env := adapters.NewMockEnvironment()
399+
env.Set("DEEPSOURCE_DSN", "https://token@localhost:8080")
400+
401+
svc := NewService(ServiceDeps{
402+
GitClient: git,
403+
HTTPClient: httpClient,
404+
FileSystem: adapters.NewOSFileSystem(),
405+
Environment: env,
406+
Sentry: adapters.NewNoOpSentry(),
407+
Output: adapters.NewBufferOutput(),
408+
Workdir: func() (string, error) { return tempDir, nil },
409+
})
410+
411+
result, err := svc.Report(context.Background(), Options{
412+
Analyzer: "test-coverage",
413+
Key: "python",
414+
ValueFile: artifactPath,
415+
CommitOID: "deadbeef1234567890abcdef1234567890abcdef",
416+
})
417+
418+
assert.NoError(t, err)
419+
if assert.NotNil(t, result) {
420+
assert.Equal(t, "ok", result.Message)
421+
}
422+
assert.Equal(t, "deadbeef1234567890abcdef1234567890abcdef", capturedCommit)
423+
}
424+
372425
func TestCaptureSkipsUserErrors(t *testing.T) {
373426
captured := false
374427
mockSentry := &captureSentry{onCapture: func(_ error) { captured = true }}

internal/services/report/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ type QueryResponse struct {
3636
type Options struct {
3737
Analyzer string
3838
AnalyzerType string
39+
CommitOID string
3940
Key string
4041
Value string
4142
ValueFile string

0 commit comments

Comments
 (0)