Skip to content
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.idea/
.vscode/
**/.env
**/.env*
.DS_Store
Expand Down
27 changes: 3 additions & 24 deletions backend/bootstrap/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import (
"runtime/pprof"

"github.com/diggerhq/digger/backend/config"
"github.com/diggerhq/digger/backend/logging"
"github.com/diggerhq/digger/backend/segment"
"github.com/diggerhq/digger/backend/utils"
pprof_gin "github.com/gin-contrib/pprof"
sloggin "github.com/samber/slog-gin"

"time"

Expand Down Expand Up @@ -98,7 +98,7 @@ func cleanupOldProfiles(dir string, keep int) {

func Bootstrap(templates embed.FS, diggerController controllers.DiggerController) *gin.Engine {
defer segment.CloseClient()
initLogging()
logging.Init()
cfg := config.DiggerConfig

if err := sentry.Init(sentry.ClientOptions{
Expand All @@ -120,7 +120,7 @@ func Bootstrap(templates embed.FS, diggerController controllers.DiggerController

r := gin.Default()

r.Use(sloggin.New(slog.Default().WithGroup("http")))
r.Use(logging.Middleware())

if _, exists := os.LookupEnv("DIGGER_PPROF_DEBUG_ENABLED"); exists {
setupProfiler(r)
Expand Down Expand Up @@ -256,24 +256,3 @@ func Bootstrap(templates embed.FS, diggerController controllers.DiggerController

return r
}

func initLogging() {
logLevel := os.Getenv("DIGGER_LOG_LEVEL")
var level slog.Leveler

if logLevel == "DEBUG" {
level = slog.LevelDebug
} else if logLevel == "WARN" {
level = slog.LevelWarn
} else if logLevel == "ERROR" {
level = slog.LevelError
} else {
level = slog.LevelInfo
}

handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: level,
})
logger := slog.New(handler)
slog.SetDefault(logger)
}
7 changes: 5 additions & 2 deletions backend/controllers/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package controllers

import (
"bytes"
"context"
"encoding/json"
"fmt"
"github.com/diggerhq/digger/libs/digger_config/terragrunt/tac"
Expand All @@ -14,6 +15,7 @@ import (
"path"
"strings"

"github.com/diggerhq/digger/backend/logging"
"github.com/diggerhq/digger/backend/models"
"github.com/diggerhq/digger/backend/utils"
dg_configuration "github.com/diggerhq/digger/libs/digger_config"
Expand Down Expand Up @@ -74,7 +76,8 @@ func (d DiggerController) UpdateRepoCache(c *gin.Context) {
var newAtlantisConfig *tac.AtlantisConfig

// update the cache here, do it async for immediate response
go func() {
go func(ctx context.Context) {
defer logging.InheritRequestLogger(ctx)()
Copy link
Contributor

Choose a reason for hiding this comment

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

The goroutine in UpdateRepoCache inherits the request context using logging.InheritRequestLogger(ctx)(). If the client disconnects and the request context is canceled, the goroutine will continue running but may encounter issues when trying to use the canceled context.

The current implementation correctly inherits the logger from the request context, but it doesn't create a separate context for the goroutine. This means that if the request context is canceled (e.g., when a client disconnects), any operations in the goroutine that depend on that context might fail unexpectedly.

The fix separates the logger cleanup from the context inheritance, ensuring that the goroutine can continue its work even if the original request context is canceled. This is particularly important for long-running operations like cloning a git repository and processing its contents.

Suggested change
defer logging.InheritRequestLogger(ctx)()
// Create a new context that's not tied to the request
loggerCleanup := logging.InheritRequestLogger(ctx)()
defer loggerCleanup
err = git_utils.CloneGitRepoAndDoAction(cloneUrl, branch, "", *token, "", func(dir string) error {
diggerYmlBytes, err := os.ReadFile(path.Join(dir, "digger.yml"))
diggerYmlStr = string(diggerYmlBytes)
Expand All @@ -96,7 +99,7 @@ func (d DiggerController) UpdateRepoCache(c *gin.Context) {
return
}
slog.Info("Successfully updated repo cache", "repoFullName", repoFullName, "orgId", orgId)
}()
}(c.Request.Context())

c.String(http.StatusOK, "successfully submitted cache for processing, check backend logs for progress")
}
Expand Down
28 changes: 20 additions & 8 deletions backend/controllers/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/diggerhq/digger/libs/digger_config/terragrunt/tac"
"github.com/diggerhq/digger/libs/git_utils"
"log/slog"
"math/rand"
"net/http"
Expand All @@ -21,9 +19,13 @@ import (
"strings"
"time"

"github.com/diggerhq/digger/libs/digger_config/terragrunt/tac"
"github.com/diggerhq/digger/libs/git_utils"

"github.com/diggerhq/digger/backend/ci_backends"
config2 "github.com/diggerhq/digger/backend/config"
"github.com/diggerhq/digger/backend/locking"
"github.com/diggerhq/digger/backend/logging"
"github.com/diggerhq/digger/backend/middleware"
"github.com/diggerhq/digger/backend/models"
"github.com/diggerhq/digger/backend/segment"
Expand Down Expand Up @@ -108,7 +110,10 @@ func (d DiggerController) GithubAppWebHook(c *gin.Context) {
"repo", *event.Repo.FullName,
)

go handlePushEvent(gh, event, appId64)
go func(ctx context.Context) {
defer logging.InheritRequestLogger(ctx)()
handlePushEvent(ctx, gh, event, appId64)
}(c.Request.Context())

case *github.IssueCommentEvent:
slog.Info("Processing IssueCommentEvent",
Expand All @@ -122,7 +127,10 @@ func (d DiggerController) GithubAppWebHook(c *gin.Context) {
c.String(http.StatusOK, "OK")
return
}
go handleIssueCommentEvent(gh, event, d.CiBackendProvider, appId64, d.GithubWebhookPostIssueCommentHooks)
go func(ctx context.Context) {
defer logging.InheritRequestLogger(ctx)()
handleIssueCommentEvent(gh, event, d.CiBackendProvider, appId64, d.GithubWebhookPostIssueCommentHooks)
}(c.Request.Context())

case *github.PullRequestEvent:
slog.Info("Processing PullRequestEvent",
Expand All @@ -133,7 +141,10 @@ func (d DiggerController) GithubAppWebHook(c *gin.Context) {
)

// run it as a goroutine to avoid timeouts
go handlePullRequestEvent(gh, event, d.CiBackendProvider, appId64)
go func(ctx context.Context) {
defer logging.InheritRequestLogger(ctx)()
handlePullRequestEvent(gh, event, d.CiBackendProvider, appId64)
}(c.Request.Context())

default:
slog.Debug("Unhandled event type", "eventType", reflect.TypeOf(event))
Expand Down Expand Up @@ -396,7 +407,7 @@ func handleInstallationDeletedEvent(installation *github.InstallationEvent, appI
return nil
}

func handlePushEvent(gh utils.GithubClientProvider, payload *github.PushEvent, appId int64) error {
func handlePushEvent(ctx context.Context, gh utils.GithubClientProvider, payload *github.PushEvent, appId int64) error {
slog.Debug("Handling push event", "appId", appId, "payload", payload)
defer func() {
if r := recover(); r != nil {
Expand Down Expand Up @@ -430,11 +441,12 @@ func handlePushEvent(gh utils.GithubClientProvider, payload *github.PushEvent, a

repoCacheEnabled := os.Getenv("DIGGER_CONFIG_REPO_CACHE_ENABLED")
if repoCacheEnabled == "1" && strings.HasSuffix(ref, defaultBranch) {
go func() {
go func(ctx context.Context) {
defer logging.InheritRequestLogger(ctx)()
if err := sendProcessCacheRequest(repoFullName, defaultBranch, installationId); err != nil {
slog.Error("Failed to process cache request", "error", err, "repoFullName", repoFullName)
}
}()
}(ctx)
}

return nil
Expand Down
41 changes: 26 additions & 15 deletions backend/controllers/projects.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package controllers

import (
"context"
"encoding/json"
"errors"
"fmt"
Expand All @@ -12,6 +13,7 @@ import (
"strings"
"time"

"github.com/diggerhq/digger/backend/logging"
"github.com/diggerhq/digger/backend/middleware"
"github.com/diggerhq/digger/backend/models"
"github.com/diggerhq/digger/backend/services"
Expand Down Expand Up @@ -623,7 +625,6 @@ type SetJobStatusRequest struct {
WorkflowUrl string `json:"workflow_url,omitempty"`
}


func (d DiggerController) SetJobStatusForProject(c *gin.Context) {
jobId := c.Param("jobId")
orgId, exists := c.Get(middleware.ORGANISATION_ID_KEY)
Expand Down Expand Up @@ -679,7 +680,10 @@ func (d DiggerController) SetJobStatusForProject(c *gin.Context) {
slog.Info("Job status updated to created", "jobId", jobId)

// Update PR comment with real-time status
go utils.UpdatePRComment(d.GithubClientProvider, jobId, job, "created")
go func(ctx context.Context) {
defer logging.InheritRequestLogger(ctx)()
utils.UpdatePRComment(d.GithubClientProvider, jobId, job, "created")
}(c.Request.Context())

case "triggered":
job.Status = orchestrator_scheduler.DiggerJobTriggered
Expand All @@ -697,7 +701,10 @@ func (d DiggerController) SetJobStatusForProject(c *gin.Context) {
slog.Info("Job status updated to triggered", "jobId", jobId)

// Update PR comment with real-time status
go utils.UpdatePRComment(d.GithubClientProvider, jobId, job, "triggered")
go func(ctx context.Context) {
defer logging.InheritRequestLogger(ctx)()
utils.UpdatePRComment(d.GithubClientProvider, jobId, job, "triggered")
}(c.Request.Context())

case "started":
job.Status = orchestrator_scheduler.DiggerJobStarted
Expand All @@ -719,7 +726,10 @@ func (d DiggerController) SetJobStatusForProject(c *gin.Context) {
slog.Info("Job status updated to started", "jobId", jobId)

// Update PR comment with real-time status
go utils.UpdatePRComment(d.GithubClientProvider, jobId, job, "started")
go func(ctx context.Context) {
defer logging.InheritRequestLogger(ctx)()
utils.UpdatePRComment(d.GithubClientProvider, jobId, job, "started")
}(c.Request.Context())

case "succeeded":
job.Status = orchestrator_scheduler.DiggerJobSucceeded
Expand Down Expand Up @@ -770,7 +780,7 @@ func (d DiggerController) SetJobStatusForProject(c *gin.Context) {
"batchId", batchId,
)

go func() {
go func(ctx context.Context) {
defer func() {
if r := recover(); r != nil {
stack := string(debug.Stack())
Expand All @@ -783,8 +793,9 @@ func (d DiggerController) SetJobStatusForProject(c *gin.Context) {
)
}
}()
defer logging.InheritRequestLogger(ctx)()

slog.Debug("Starting post-success job processing", "jobId", jobId)
slog.Debug("Starting post-success job processing", "job_id", jobId)

ghClientProvider := d.GithubClientProvider
installationLink, err := models.DB.GetGithubInstallationLinkForOrg(orgId)
Expand Down Expand Up @@ -871,15 +882,18 @@ func (d DiggerController) SetJobStatusForProject(c *gin.Context) {
}

slog.Debug("Successfully processed job completion", "jobId", jobId)
}()
}(c.Request.Context())

// store digger job summary
if request.JobSummary != nil {
models.DB.UpdateDiggerJobSummary(job.DiggerJobID, request.JobSummary.ResourcesCreated, request.JobSummary.ResourcesUpdated, request.JobSummary.ResourcesDeleted)
}

// Update PR comment with real-time status for succeeded job
go utils.UpdatePRComment(d.GithubClientProvider, jobId, job, "succeeded")
go func(ctx context.Context) {
defer logging.InheritRequestLogger(ctx)()
utils.UpdatePRComment(d.GithubClientProvider, jobId, job, "succeeded")
}(c.Request.Context())

case "failed":
job.Status = orchestrator_scheduler.DiggerJobFailed
Expand All @@ -901,7 +915,10 @@ func (d DiggerController) SetJobStatusForProject(c *gin.Context) {
)

// Update PR comment with real-time status for failed job
go utils.UpdatePRComment(d.GithubClientProvider, jobId, job, "failed")
go func(ctx context.Context) {
defer logging.InheritRequestLogger(ctx)()
utils.UpdatePRComment(d.GithubClientProvider, jobId, job, "failed")
}(c.Request.Context())

default:
slog.Warn("Unexpected job status received",
Expand Down Expand Up @@ -1010,12 +1027,6 @@ func (d DiggerController) SetJobStatusForProject(c *gin.Context) {
c.JSON(http.StatusOK, res)
}







func updateWorkflowUrlForJob(githubClientProvider utils.GithubClientProvider, job *models.DiggerJob) error {
if job == nil {
return fmt.Errorf("job is nil")
Expand Down
Loading
Loading