diff --git a/pkg/orchestrator/gitHubActions.go b/pkg/orchestrator/gitHubActions.go index ba737c456f..ee68d92443 100644 --- a/pkg/orchestrator/gitHubActions.go +++ b/pkg/orchestrator/gitHubActions.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "io" + "regexp" "strconv" "strings" "sync" @@ -26,7 +27,6 @@ type GitHubActionsConfigProvider struct { runData run jobs []job jobsFetched bool - currentJob job } type run struct { @@ -180,18 +180,21 @@ func (g *GitHubActionsConfigProvider) GetReference() string { return getEnv("GITHUB_REF", "n/a") } -// GetBuildURL returns the builds URL. For example, https://github.com/SAP/jenkins-library/actions/runs/5815297487 +// GetBuildURL returns the builds URL. The URL should point to the pipeline (not to the stage) +// that is currently being executed. For example, https://github.com/SAP/jenkins-library/actions/runs/5815297487 func (g *GitHubActionsConfigProvider) GetBuildURL() string { return g.GetRepoURL() + "/actions/runs/" + g.GetBuildID() } -// GetJobURL returns the current job HTML URL (not API URL). -// For example, https://github.com/SAP/jenkins-library/actions/runs/123456/jobs/7654321 +// GetJobURL returns the job URL. The URL should point to project’s pipelines. +// For example, https://github.com/SAP/jenkins-library/actions/workflows/workflow-file-name.yaml func (g *GitHubActionsConfigProvider) GetJobURL() string { - // We need to query the GitHub API here because the environment variable GITHUB_JOB returns - // the name of the job, not a numeric ID (which we need to form the URL) - g.guessCurrentJob() - return g.currentJob.HtmlURL + fileName := workflowFileName() + if fileName == "" { + return "" + } + + return g.GetRepoURL() + "/actions/workflows/" + fileName } // GetJobName returns the current workflow name. For example, "Piper workflow" @@ -301,32 +304,6 @@ func convertJobs(jobs []*github.WorkflowJob) []job { return result } -func (g *GitHubActionsConfigProvider) guessCurrentJob() { - // check if the current job has already been guessed - if g.currentJob.ID != 0 { - return - } - - // fetch jobs if they haven't been fetched yet - if err := g.fetchJobs(); err != nil { - log.Entry().Errorf("failed to fetch jobs: %s", err) - g.jobs = []job{} - return - } - - targetJobName := getEnv("GITHUB_JOB", "unknown") - log.Entry().Debugf("looking for job '%s' in jobs list: %v", targetJobName, g.jobs) - for _, j := range g.jobs { - // j.Name may be something like "piper / Init / Init" - // but GITHUB_JOB env may contain only "Init" - if strings.HasSuffix(j.Name, targetJobName) { - log.Entry().Debugf("current job id: %d", j.ID) - g.currentJob = j - return - } - } -} - func (g *GitHubActionsConfigProvider) runIdInt64() (int64, error) { strRunId := g.GetBuildID() runId, err := strconv.ParseInt(strRunId, 10, 64) @@ -347,3 +324,15 @@ func getOwnerAndRepoNames() (string, string) { return s[0], s[1] } + +func workflowFileName() string { + workflowRef := getEnv("GITHUB_WORKFLOW_REF", "") + re := regexp.MustCompile(`\.github/workflows/([a-zA-Z0-9_-]+\.(yml|yaml))`) + matches := re.FindStringSubmatch(workflowRef) + if len(matches) > 1 { + return matches[1] + } + + log.Entry().Debugf("unable to determine workflow file name from GITHUB_WORKFLOW_REF: %s", workflowRef) + return "" +} diff --git a/pkg/orchestrator/gitHubActions_test.go b/pkg/orchestrator/gitHubActions_test.go index c8aa8e632e..6a18c5c9a8 100644 --- a/pkg/orchestrator/gitHubActions_test.go +++ b/pkg/orchestrator/gitHubActions_test.go @@ -104,50 +104,6 @@ func TestGitHubActionsConfigProvider_GetPullRequestConfig(t *testing.T) { } } -func TestGitHubActionsConfigProvider_guessCurrentJob(t *testing.T) { - tests := []struct { - name string - jobs []job - jobsFetched bool - targetJobName string - wantJob job - }{ - { - name: "job found", - jobs: []job{{Name: "Job1"}, {Name: "Job2"}, {Name: "Job3"}}, - jobsFetched: true, - targetJobName: "Job2", - wantJob: job{Name: "Job2"}, - }, - { - name: "job found", - jobs: []job{{Name: "Piper / Job1"}, {Name: "Piper / Job2"}, {Name: "Piper / Job3"}}, - jobsFetched: true, - targetJobName: "Job2", - wantJob: job{Name: "Piper / Job2"}, - }, - { - name: "job not found", - jobs: []job{{Name: "Job1"}, {Name: "Job2"}, {Name: "Job3"}}, - jobsFetched: true, - targetJobName: "Job123", - wantJob: job{}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := &GitHubActionsConfigProvider{ - jobs: tt.jobs, - jobsFetched: tt.jobsFetched, - } - _ = os.Setenv("GITHUB_JOB", tt.targetJobName) - g.guessCurrentJob() - - assert.Equal(t, tt.wantJob, g.currentJob) - }) - } -} - func TestGitHubActionsConfigProvider_fetchRunData(t *testing.T) { // data respJson := map[string]interface{}{ @@ -325,6 +281,7 @@ func TestGitHubActionsConfigProvider_Others(t *testing.T) { _ = os.Setenv("GITHUB_API_URL", "https://api.github.com") _ = os.Setenv("GITHUB_SERVER_URL", "https://github.com") _ = os.Setenv("GITHUB_REPOSITORY", "SAP/jenkins-library") + _ = os.Setenv("GITHUB_WORKFLOW_REF", "SAP/jenkins-library/.github/workflows/piper.yml@refs/heads/main") p := GitHubActionsConfigProvider{} startedAt, _ := time.Parse(time.RFC3339, "2023-08-11T07:28:24Z") @@ -333,7 +290,6 @@ func TestGitHubActionsConfigProvider_Others(t *testing.T) { Status: "", StartedAt: startedAt, } - p.currentJob = job{ID: 111, Name: "job1", HtmlURL: "https://github.com/SAP/jenkins-library/actions/runs/123456/jobs/7654321"} assert.Equal(t, "n/a", p.OrchestratorVersion()) assert.Equal(t, "GitHubActions", p.OrchestratorType()) @@ -344,10 +300,42 @@ func TestGitHubActionsConfigProvider_Others(t *testing.T) { assert.Equal(t, "main", p.GetBranch()) assert.Equal(t, "refs/pull/42/merge", p.GetReference()) assert.Equal(t, "https://github.com/SAP/jenkins-library/actions/runs/11111", p.GetBuildURL()) - assert.Equal(t, "https://github.com/SAP/jenkins-library/actions/runs/123456/jobs/7654321", p.GetJobURL()) + assert.Equal(t, "https://github.com/SAP/jenkins-library/actions/workflows/piper.yml", p.GetJobURL()) assert.Equal(t, "Piper workflow", p.GetJobName()) assert.Equal(t, "ffac537e6cbbf934b08745a378932722df287a53", p.GetCommit()) assert.Equal(t, "https://api.github.com/repos/SAP/jenkins-library/actions", actionsURL()) assert.True(t, p.IsPullRequest()) assert.True(t, isGitHubActions()) } + +func TestWorkflowFileName(t *testing.T) { + defer resetEnv(os.Environ()) + os.Clearenv() + + tests := []struct { + name, workflowRef, want string + }{ + { + name: "valid file name (yaml)", + workflowRef: "owner/repo/.github/workflows/test-workflow.yaml@refs/heads/main", + want: "test-workflow.yaml", + }, + { + name: "valid file name (yml)", + workflowRef: "owner/repo/.github/workflows/test-workflow.yml@refs/heads/main", + want: "test-workflow.yml", + }, + { + name: "invalid file name", + workflowRef: "owner/repo/.github/workflows/test-workflow@refs/heads/main", + want: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _ = os.Setenv("GITHUB_WORKFLOW_REF", tt.workflowRef) + result := workflowFileName() + assert.Equal(t, tt.want, result) + }) + } +} diff --git a/pkg/telemetry/telemetry.go b/pkg/telemetry/telemetry.go index c3f0c34f10..e262010be0 100644 --- a/pkg/telemetry/telemetry.go +++ b/pkg/telemetry/telemetry.go @@ -4,15 +4,14 @@ import ( "crypto/sha1" "encoding/json" "fmt" - "github.com/SAP/jenkins-library/pkg/orchestrator" - "strconv" - "time" - "net/http" "net/url" + "strconv" + "time" piperhttp "github.com/SAP/jenkins-library/pkg/http" "github.com/SAP/jenkins-library/pkg/log" + "github.com/SAP/jenkins-library/pkg/orchestrator" ) // eventType @@ -81,8 +80,8 @@ func (t *Telemetry) Initialize(telemetryDisabled bool, stepName string) { EventType: eventType, StepName: stepName, SiteID: t.SiteID, - PipelineURLHash: t.getPipelineURLHash(), // http://server:port/jenkins/job/foo/ - BuildURLHash: t.getBuildURLHash(), // http://server:port/jenkins/job/foo/15/ + PipelineURLHash: t.getPipelineURLHash(), // URL (hashed value) which points to the project’s pipelines + BuildURLHash: t.getBuildURLHash(), // URL (hashed value) which points to the pipeline that is currently running } t.baseMetaData = baseMetaData }