Add github actions sync resolver (#539)

* feat: add GHA env resolver

* refactor: expose sync reporter resolver constructors

* fix: use os.LookupEnv for better GHA detection

* fix typo

* use environment sync resolver

* test: add test cases & fix naming

* Update pkg/reporter/sync_test.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Sahil Bansal <bansalsahil315@gmail.com>

* modify sync resolver tests

* fix tests failing in ci/cd

---------

Signed-off-by: Sahil Bansal <bansalsahil315@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Sahil Bansal 2025-07-21 09:11:24 +05:30 committed by GitHub
parent 075627f53f
commit 3d6d8ed036
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 136 additions and 1 deletions

View File

@ -0,0 +1,53 @@
package reporter
import (
"os"
controltowerv1pb "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/controltower/v1"
controltowerv1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/services/controltower/v1"
)
type githubActionResolver struct{}
func GithubActionsSyncReporterResolver() SyncReporterEnvResolver {
return &githubActionResolver{}
}
var _ SyncReporterEnvResolver = &githubActionResolver{}
func (g *githubActionResolver) GetProjectSource() controltowerv1pb.Project_Source {
return controltowerv1pb.Project_SOURCE_GITHUB
}
func (g *githubActionResolver) GetProjectURL() string {
return os.Getenv("GITHUB_SERVER_URL") + "/" + os.Getenv("GITHUB_REPOSITORY")
}
func (g *githubActionResolver) GitRef() string {
return os.Getenv("GITHUB_REF")
}
func (g *githubActionResolver) GitSha() string {
return os.Getenv("GITHUB_SHA")
}
func (g *githubActionResolver) Trigger() controltowerv1.ToolTrigger {
switch eventName := os.Getenv("GITHUB_EVENT_NAME"); eventName {
case "push":
return controltowerv1.ToolTrigger_TOOL_TRIGGER_PUSH
case "pull_request", "pull_request_target":
return controltowerv1.ToolTrigger_TOOL_TRIGGER_PULL_REQUEST
case "create":
// In GitHub Actions, 'create' event with ref_type=tag indicates a tag was created
if os.Getenv("GITHUB_REF_TYPE") == "tag" {
return controltowerv1.ToolTrigger_TOOL_TRIGGER_TAG
}
return controltowerv1.ToolTrigger_TOOL_TRIGGER_UNSPECIFIED
case "schedule":
return controltowerv1.ToolTrigger_TOOL_TRIGGER_SCHEDULED
case "workflow_dispatch", "repository_dispatch":
return controltowerv1.ToolTrigger_TOOL_TRIGGER_MANUAL
default:
return controltowerv1.ToolTrigger_TOOL_TRIGGER_UNSPECIFIED
}
}

View File

@ -3,6 +3,7 @@ package reporter
import (
"context"
"fmt"
"os"
"strings"
"sync"
@ -187,6 +188,15 @@ type syncReporter struct {
// Verify syncReporter implements the Reporter interface
var _ Reporter = (*syncReporter)(nil)
func NewSyncReporterEnvironmentResolver() SyncReporterEnvResolver {
// The `GITHUB_ACTIONS` environment variable is always set to true when GitHub Actions is running the workflow
if _, exists := os.LookupEnv("GITHUB_ACTIONS"); exists {
return GithubActionsSyncReporterResolver()
}
return DefaultSyncReporterEnvResolver()
}
// NewSyncReporter creates a new sync reporter.
func NewSyncReporter(config SyncReporterConfig, envResolver SyncReporterEnvResolver, callbacks SyncReporterCallbacks) (*syncReporter, error) {
if config.ClientConnection == nil {

View File

@ -3,6 +3,7 @@ package reporter
import (
"context"
"errors"
"os"
"testing"
controltowerv1pb "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/controltower/v1"
@ -111,6 +112,77 @@ func mockDependencyGraph(pkg *models.Package) {
pkg.Manifest.DependencyGraph = dg
}
func TestNewSyncReporterEnvironmentResolver(t *testing.T) {
tests := []struct {
name string
setupEnv func(t *testing.T)
expectedProjectSource controltowerv1pb.Project_Source
expectedProjectUrl string
expectedTrigger controltowerv1.ToolTrigger
expectedGitRef string
expectedGitSha string
}{
{
name: "should return GitHub Actions values when GITHUB_ACTIONS is set",
setupEnv: func(t *testing.T) {
t.Setenv("GITHUB_ACTIONS", "true")
t.Setenv("GITHUB_SERVER_URL", "https://github.com")
t.Setenv("GITHUB_REPOSITORY", "safedep/vet")
t.Setenv("GITHUB_EVENT_NAME", "push")
t.Setenv("GITHUB_REF", "refs/heads/main")
t.Setenv("GITHUB_SHA", "abc123")
},
expectedProjectSource: controltowerv1pb.Project_SOURCE_GITHUB,
expectedTrigger: controltowerv1.ToolTrigger_TOOL_TRIGGER_PUSH,
expectedProjectUrl: "https://github.com/safedep/vet",
expectedGitRef: "refs/heads/main",
expectedGitSha: "abc123",
},
{
name: "should return default values when GITHUB_ACTIONS is not set",
setupEnv: func(t *testing.T) {
// No environment setup needed
os.Unsetenv("GITHUB_ACTIONS")
os.Unsetenv("GITHUB_REPOSITORY")
os.Unsetenv("GITHUB_SERVER_URL")
os.Unsetenv("GITHUB_REF")
os.Unsetenv("GITHUB_SHA")
os.Unsetenv("GITHUB_EVENT_NAME")
},
expectedProjectSource: controltowerv1pb.Project_SOURCE_UNSPECIFIED,
expectedTrigger: controltowerv1.ToolTrigger_TOOL_TRIGGER_MANUAL,
expectedProjectUrl: "",
expectedGitRef: "",
expectedGitSha: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Setup test environment
tt.setupEnv(t)
// Get the resolver
resolver := NewSyncReporterEnvironmentResolver()
source := resolver.GetProjectSource()
assert.Equal(t, tt.expectedProjectSource, source, "unexpected project source")
url := resolver.GetProjectURL()
assert.Equal(t, tt.expectedProjectUrl, url, "unexpected project URL")
trigger := resolver.Trigger()
assert.Equal(t, tt.expectedTrigger, trigger, "unexpected trigger")
gitRef := resolver.GitRef()
assert.Equal(t, tt.expectedGitRef, gitRef, "unexpected git ref")
gitSha := resolver.GitSha()
assert.Equal(t, tt.expectedGitSha, gitSha, "unexpected git sha")
})
}
}
func TestSyncPackage(t *testing.T) {
tests := []struct {
name string

View File

@ -679,7 +679,7 @@ func internalStartScan() error {
ProjectVersion: syncReportStream,
EnableMultiProjectSync: syncEnableMultiProject,
ClientConnection: clientConn,
}, reporter.DefaultSyncReporterEnvResolver(), reporter.SyncReporterCallbacks{
}, reporter.NewSyncReporterEnvironmentResolver(), reporter.SyncReporterCallbacks{
OnSyncStart: func() {
ui.PrintMsg("🌐 Syncing data to SafeDep Cloud...")
},