Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ensure link to workflow url always present #1892

Merged
merged 3 commits into from
Mar 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/ci_backends/ci_backends.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

type CiBackend interface {
TriggerWorkflow(spec spec.Spec, runName string, vcsToken string) error
GetWorkflowUrl(spec spec.Spec) (string, error)
}

type JenkinsCi struct{}
Expand Down
17 changes: 17 additions & 0 deletions backend/ci_backends/github_actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package ci_backends
import (
"context"
"encoding/json"
"fmt"
"github.com/diggerhq/digger/backend/utils"
orchestrator_scheduler "github.com/diggerhq/digger/libs/scheduler"
"github.com/diggerhq/digger/libs/spec"
"github.com/google/go-github/v61/github"
Expand Down Expand Up @@ -30,3 +32,18 @@ func (g GithubActionCi) TriggerWorkflow(spec spec.Spec, runName string, vcsToken

return err
}

func (g GithubActionCi) GetWorkflowUrl(spec spec.Spec) (string, error) {
if spec.JobId == "" {
log.Printf("Cannot get workflow URL: JobId is empty")
return "", fmt.Errorf("job ID is required to fetch workflow URL")
}

_, workflowRunUrl, err := utils.GetWorkflowIdAndUrlFromDiggerJobId(g.Client, spec.VCS.RepoOwner, spec.VCS.RepoName, spec.JobId)
if err != nil {
log.Printf("Error getting workflow ID from job: %v", err)
return "", err
} else {
return workflowRunUrl, nil
}
}
33 changes: 18 additions & 15 deletions backend/controllers/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,21 +364,6 @@ func (d DiggerController) SetJobStatusForProject(c *gin.Context) {
return
}

client, _, err := utils.GetGithubClient(d.GithubClientProvider, job.Batch.GithubInstallationId, job.Batch.RepoFullName)
if err != nil {
log.Printf("Error Creating github client: %v", err)
} else {
_, workflowRunUrl, err := utils.GetWorkflowIdAndUrlFromDiggerJobId(client, job.Batch.RepoOwner, job.Batch.RepoName, job.DiggerJobID)
if err != nil {
log.Printf("Error getting workflow ID from job: %v", err)
} else {
job.WorkflowRunUrl = &workflowRunUrl
err = models.DB.UpdateDiggerJob(job)
if err != nil {
log.Printf("Error updating digger job: %v", err)
}
}
}
case "succeeded":
job.Status = orchestrator_scheduler.DiggerJobSucceeded
job.TerraformOutput = request.TerraformOutput
Expand Down Expand Up @@ -463,6 +448,24 @@ func (d DiggerController) SetJobStatusForProject(c *gin.Context) {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error saving job"})
return
}

// attempt to update workflow run url
client, _, err := utils.GetGithubClient(d.GithubClientProvider, job.Batch.GithubInstallationId, job.Batch.RepoFullName)
if err != nil {
log.Printf("Error Creating github client: %v", err)
} else {
_, workflowRunUrl, err := utils.GetWorkflowIdAndUrlFromDiggerJobId(client, job.Batch.RepoOwner, job.Batch.RepoName, job.DiggerJobID)
if err != nil {
log.Printf("Error getting workflow ID from job: %v", err)
} else if workflowRunUrl != "#" && workflowRunUrl != "" {
job.WorkflowRunUrl = &workflowRunUrl
err = models.DB.UpdateDiggerJob(job)
if err != nil {
log.Printf("Error updating digger job: %v", err)
}
}
}

job.StatusUpdatedAt = request.Timestamp
err = models.DB.GormDB.Save(&job).Error
if err != nil {
Expand Down
48 changes: 48 additions & 0 deletions backend/services/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ import (
"github.com/diggerhq/digger/backend/models"
"github.com/diggerhq/digger/backend/utils"
orchestrator_scheduler "github.com/diggerhq/digger/libs/scheduler"
"github.com/diggerhq/digger/libs/spec"
"github.com/google/go-github/v61/github"
"github.com/google/uuid"
"log"
"runtime/debug"
"time"
)

func DiggerJobCompleted(client *github.Client, batchId *uuid.UUID, parentJob *models.DiggerJob, repoFullName string, repoOwner string, repoName string, workflowFileName string, gh utils.GithubClientProvider) error {
Expand Down Expand Up @@ -131,5 +134,50 @@ func TriggerJob(gh utils.GithubClientProvider, ciBackend ci_backends.CiBackend,
return err
}

go UpdateWorkflowUrlForJob(job, ciBackend, spec)

return nil
}

// This is meant to run asyncronously since it queries for job url
func UpdateWorkflowUrlForJob(job *models.DiggerJob, ciBackend ci_backends.CiBackend, spec *spec.Spec) {
defer func() {
if r := recover(); r != nil {
log.Printf("Recovered from panic in UpdateWorkflowUrlForJob handler: %v", r)
log.Printf("\n=== PANIC RECOVERED ===\n")
log.Printf("Error: %v\n", r)
log.Printf("Stack Trace:\n%s", string(debug.Stack()))
log.Printf("=== END PANIC ===\n")
}
}()

batch := job.Batch
// for now we only perform this update for github
if batch.VCS != models.DiggerVCSGithub {
return
}
for n := 0; n < 30; n++ {
time.Sleep(1 * time.Second)
workflowUrl, err := ciBackend.GetWorkflowUrl(*spec)
if err != nil {
log.Printf("DiggerJobId %v: error while attempting to fetch workflow url: %v", job.DiggerJobID, err)
} else {
if workflowUrl == "#" || workflowUrl == "" {
log.Printf("DiggerJobId %v: got blank workflow url as response, ignoring", job.DiggerJobID)
} else {
job.WorkflowRunUrl = &workflowUrl
err = models.DB.UpdateDiggerJob(job)
if err != nil {
log.Printf("DiggerJobId %v: Error updating digger job: %v", job.DiggerJobID, err)
continue
} else {
log.Printf("DiggerJobId %v: successfully updated workflow run url to: %v for DiggerJobID: %v", job.DiggerJobID, workflowUrl, job.DiggerJobID)
}

return
}
}
}

// if we get to here its highly likely that the workflow job entirely failed to start for some reason
}
4 changes: 4 additions & 0 deletions backend/tasks/runs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ func (m MockCiBackend) TriggerWorkflow(spec spec.Spec, runName string, vcsToken
return nil
}

func (m MockCiBackend) GetWorkflowUrl(spec spec.Spec) (string, error) {
return "", nil
}

func setupSuite(tb testing.TB) (func(tb testing.TB), *models.Database) {
log.Println("setup suite")

Expand Down
1 change: 0 additions & 1 deletion backend/utils/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,6 @@ func GetWorkflowIdAndUrlFromDiggerJobId(client *github.Client, repoOwner string,
}

for _, workflowRun := range runs.WorkflowRuns {
println(*workflowRun.ID)
workflowjobs, _, err := client.Actions.ListWorkflowJobs(context.Background(), repoOwner, repoName, *workflowRun.ID, nil)
if err != nil {
return 0, "#", fmt.Errorf("error listing workflow jobs for run %v %v", workflowRun.ID, err)
Expand Down
7 changes: 7 additions & 0 deletions ee/backend/ci_backends/bitbucket_pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,10 @@ func (bbp BitbucketPipelineCI) TriggerWorkflow(spec spec.Spec, runName string, v
_, err = bbp.Client.TriggerPipeline(bbp.Branch, variables)
return err
}

// GetWorkflowUrl fetch workflow url after triggering a job
// since some CI don't return url automatically we split it out to become a
// followup method
func (bbp BitbucketPipelineCI) GetWorkflowUrl(spec spec.Spec) (string, error) {
return "", nil
}
4 changes: 4 additions & 0 deletions ee/backend/ci_backends/buildkite.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@ func (b BuildkiteCi) TriggerWorkflow(spec spec.Spec, runName string, vcsToken st
return err

}

func (b BuildkiteCi) GetWorkflowUrl(spec spec.Spec) (string, error) {
return "", nil
}
Comment on lines +35 to +37
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Placeholder implementation needs completion.

This is currently a stub implementation of the GetWorkflowUrl method for Buildkite CI integration. As this PR is a work in progress, ensure that the actual implementation is completed before finalizing to properly retrieve Buildkite workflow URLs.


🏁 Script executed:

#!/bin/bash
# Check if there are any other implementations of GetWorkflowUrl in the codebase that might serve as a reference
rg "func \(.+\) GetWorkflowUrl" -A 10 --type go | grep -v "return \"\", nil"

Length of output: 1926


Attention: Complete Buildkite Workflow URL Implementation

The current implementation of BuildkiteCi.GetWorkflowUrl in ee/backend/ci_backends/buildkite.go (lines 35–37) remains a stub that simply returns an empty string. Unlike the more detailed implementation in the GitHub Actions CI integration—which retrieves the URL using external API calls—the Buildkite implementation still needs to fetch the actual workflow URL and handle errors appropriately.

  • Location to address: ee/backend/ci_backends/buildkite.go, lines 35–37.
  • Action required: Replace the placeholder code with a complete implementation that retrieves Buildkite workflow URLs, following best practices similar to those seen in other CI integrations.

4 changes: 4 additions & 0 deletions ee/backend/ci_backends/gitlab_pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,7 @@ func (gl GitlabPipelineCI) TriggerWorkflow(spec spec.Spec, runName string, vcsTo

return err
}

func (g GitlabPipelineCI) GetWorkflowUrl(spec spec.Spec) (string, error) {
return "", nil
}
11 changes: 11 additions & 0 deletions ee/backend/controllers/bitbucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"log"
"net/http"
"os"
"runtime/debug"
"strconv"
"strings"
)
Expand Down Expand Up @@ -139,6 +140,16 @@ func (ee DiggerEEController) BitbucketWebhookHandler(c *gin.Context) {
}

func handleIssueCommentEventBB(bitbucketProvider utils.BitbucketProvider, payload *BitbucketCommentCreatedEvent, ciBackendProvider ci_backends.CiBackendProvider, organisationId uint, vcsConnectionId *uint, bbAccessToken string) error {
defer func() {
if r := recover(); r != nil {
log.Printf("Recovered from panic in handleIssueCommentEventBB handler: %v", r)
log.Printf("\n=== PANIC RECOVERED ===\n")
log.Printf("Error: %v\n", r)
log.Printf("Stack Trace:\n%s", string(debug.Stack()))
log.Printf("=== END PANIC ===\n")
}
}()

repoFullName := payload.Repository.FullName
repoOwner := payload.Repository.Owner.Username
repoName := payload.Repository.Name
Expand Down
1 change: 1 addition & 0 deletions next/ci_backends/ci_backends.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

type CiBackend interface {
TriggerWorkflow(spec spec.Spec, runName string, vcsToken string) error
GetWorkflowUrl(spec spec.Spec) (string, error)
}

type JenkinsCi struct{}
Expand Down
4 changes: 4 additions & 0 deletions next/ci_backends/github_actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ func (g GithubActionCi) TriggerWorkflow(spec spec.Spec, runName string, vcsToken

return err
}

func (g GithubActionCi) GetWorkflowUrl(spec spec.Spec) (string, error) {
return "", nil
}
Loading