-
Notifications
You must be signed in to change notification settings - Fork 561
projects refresh background service #2011
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
Changes from all commits
ee391d5
dbc686e
681e3ca
c405121
fc510a7
3a911ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,19 @@ | ||
# See https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/ | ||
|
||
name: Deploy pro-backend | ||
name: Deploy projects-refresh-service | ||
on: | ||
push: | ||
branches: | ||
- develop | ||
- pro | ||
jobs: | ||
deploy: | ||
name: Deploy app | ||
name: Deploy projects-refresh-service app | ||
runs-on: ubuntu-latest | ||
concurrency: deploy-group # optional: ensure only one action runs at a time | ||
concurrency: projects-refresh # optional: ensure only one action runs at a time | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: superfly/flyctl-actions/setup-flyctl@master | ||
- run: flyctl deploy --remote-only --config fly-pro.toml | ||
- run: flyctl deploy --remote-only --config fly-projects-refresh-service.toml --image-label latest | ||
|
||
env: | ||
FLY_API_TOKEN: ${{ secrets.FLYIO_PRO_TOKEN }} | ||
FLY_API_TOKEN: ${{ secrets.FLYIO_PROJECTS_REFRESH_SVC_TOKEN }} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,28 @@ | ||||||
FROM golang:1.24.0 as builder | ||||||
ARG COMMIT_SHA | ||||||
RUN echo "commit sha: ${COMMIT_SHA}" | ||||||
|
||||||
# Set the working directory | ||||||
WORKDIR $GOPATH/src/github.com/diggerhq/digger | ||||||
|
||||||
# Copy all required source, blacklist files that are not required through `.dockerignore` | ||||||
COPY . . | ||||||
|
||||||
RUN go build -ldflags="-X 'main.Version=${COMMIT_SHA}'" -o projects_refresh_exe ./background/projects-refresh-service | ||||||
|
||||||
# Multi-stage build will just copy the binary to an alpine image. | ||||||
FROM ubuntu:24.04 as runner | ||||||
ARG COMMIT_SHA | ||||||
WORKDIR /app | ||||||
|
||||||
RUN apt-get update && apt-get install -y ca-certificates curl && apt-get install -y git && apt-get clean all | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Add --no-install-recommends to apt-get install to reduce image size (consistent with other Dockerfiles)
Suggested change
|
||||||
RUN update-ca-certificates | ||||||
|
||||||
RUN echo "commit sha: ${COMMIT_SHA}" | ||||||
|
||||||
# Copy the binary to the corresponding folder | ||||||
COPY --from=builder /go/src/github.com/diggerhq/digger/projects_refresh_exe /app/projects_refresh_exe | ||||||
RUN chmod +x projects_refresh_exe | ||||||
|
||||||
# Run the binary | ||||||
ENTRYPOINT ["./projects_refresh_exe"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ package controllers | |
|
||
import ( | ||
"fmt" | ||
"github.com/diggerhq/digger/libs/git_utils" | ||
"log/slog" | ||
"net/http" | ||
"os" | ||
|
@@ -67,7 +68,7 @@ func (d DiggerController) UpdateRepoCache(c *gin.Context) { | |
|
||
// update the cache here, do it async for immediate response | ||
go func() { | ||
err = utils.CloneGitRepoAndDoAction(cloneUrl, branch, "", *token, "", func(dir string) error { | ||
err = git_utils.CloneGitRepoAndDoAction(cloneUrl, branch, "", *token, "", func(dir string) error { | ||
diggerYmlBytes, err := os.ReadFile(path.Join(dir, "digger.yml")) | ||
diggerYmlStr = string(diggerYmlBytes) | ||
config, _, _, err = dg_configuration.LoadDiggerConfig(dir, true, nil) | ||
Comment on lines
72
to
74
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: consider adding error handling for os.ReadFile separately from LoadDiggerConfig to provide more specific error messages |
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1642,6 +1642,7 @@ func (db *Database) RefreshProjectsFromRepo(orgId string, config configuration.D | |
|
||
err = db.GormDB.Transaction(func(tx *gorm.DB) error { | ||
for _, dc := range config.Projects { | ||
slog.Debug("refreshing for project", "name", dc.Name, "dir", dc.Dir) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Keep debugging context consistent with other log messages in the file. Consider adding 'projectDirectory' to the structured log fields. |
||
projectName := dc.Name | ||
projectDirectory := dc.Dir | ||
p, err := db.GetProjectByName(orgId, repoFullName, projectName) | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,107 @@ | ||||||
package service_clients | ||||||
|
||||||
import ( | ||||||
"bytes" | ||||||
"encoding/json" | ||||||
"fmt" | ||||||
"io" | ||||||
"log/slog" | ||||||
"net/http" | ||||||
"os" | ||||||
"time" | ||||||
) | ||||||
|
||||||
type MachineConfig struct { | ||||||
Name string `json:"name"` | ||||||
Config struct { | ||||||
Image string `json:"image"` | ||||||
Env struct { | ||||||
CloneUrl string `json:"DIGGER_GITHUB_REPO_CLONE_URL"` | ||||||
Branch string `json:"DIGGER_GITHUB_REPO_CLONE_BRANCH"` | ||||||
GithubToken string `json:"DIGGER_GITHUB_TOKEN"` | ||||||
RepoFullName string `json:"DIGGER_REPO_FULL_NAME"` | ||||||
OrgId string `json:"DIGGER_ORG_ID"` | ||||||
} `json:"env"` | ||||||
Guest struct { | ||||||
CPUs int `json:"cpus"` | ||||||
CPUKind string `json:"cpu_kind"` | ||||||
MemoryMB int `json:"memory_mb"` | ||||||
} `json:"guest"` | ||||||
AutoDestroy bool `json:"auto_destroy"` | ||||||
} `json:"config"` | ||||||
} | ||||||
|
||||||
type MachineResponse struct { | ||||||
ID string `json:"id"` | ||||||
} | ||||||
|
||||||
type QueuedResponse struct { | ||||||
Queued string `json:"queued"` | ||||||
} | ||||||
|
||||||
func TriggerProjectsRefreshService(cloneUrl string, branch string, githubToken string, repoFullName string, orgId string) (*MachineResponse, error) { | ||||||
|
||||||
slog.Debug("awaiting machine fetch") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Debug log message 'awaiting machine fetch' is misleading since no fetching is happening yet |
||||||
|
||||||
// Prepare machine configuration | ||||||
machineConfig := MachineConfig{ | ||||||
Name: fmt.Sprintf("hello-%d", time.Now().UnixMilli()), | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Machine name 'hello-' prefix is non-descriptive. Consider 'projects-refresh-' for better identification in logs/metrics |
||||||
} | ||||||
|
||||||
machineConfig.Config.Image = "registry.fly.io/projects-refresh-service:latest" | ||||||
machineConfig.Config.Env.CloneUrl = cloneUrl | ||||||
machineConfig.Config.Env.Branch = branch | ||||||
machineConfig.Config.Env.GithubToken = githubToken | ||||||
machineConfig.Config.Env.RepoFullName = repoFullName | ||||||
machineConfig.Config.Env.OrgId = orgId | ||||||
|
||||||
machineConfig.Config.Guest.CPUs = 1 | ||||||
machineConfig.Config.Guest.CPUKind = "shared" | ||||||
machineConfig.Config.Guest.MemoryMB = 256 | ||||||
machineConfig.Config.AutoDestroy = true | ||||||
|
||||||
// Marshal JSON payload | ||||||
payload, err := json.Marshal(machineConfig) | ||||||
if err != nil { | ||||||
slog.Error("Error creating machine config", "error", err) | ||||||
return nil, err | ||||||
} | ||||||
|
||||||
// Create HTTP request | ||||||
apiURL := fmt.Sprintf("https://api.machines.dev/v1/apps/%s/machines", os.Getenv("DIGGER_PROJECTS_SVC_APP_NAME")) | ||||||
req2, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(payload)) | ||||||
if err != nil { | ||||||
slog.Error("Error creating request", "error", err) | ||||||
return nil, err | ||||||
} | ||||||
|
||||||
// Set headers | ||||||
req2.Header.Set("Authorization", "Bearer "+os.Getenv("FLY_PROJECTS_SVC_API_TOKEN")) | ||||||
req2.Header.Set("Content-Type", "application/json") | ||||||
|
||||||
// Make HTTP request | ||||||
client := &http.Client{} | ||||||
resp, err := client.Do(req2) | ||||||
if err != nil { | ||||||
slog.Error("Error making request", "error", err) | ||||||
return nil, err | ||||||
} | ||||||
defer resp.Body.Close() | ||||||
|
||||||
if resp.StatusCode != 200 { | ||||||
body, err2 := io.ReadAll(resp.Body) | ||||||
slog.Error("Error triggering projects refresh service", "statusCode", resp.StatusCode, "body", body, "readyErr", err2) | ||||||
return nil, fmt.Errorf("error triggering projects refresh service") | ||||||
} | ||||||
|
||||||
// Parse response | ||||||
var machineResp MachineResponse | ||||||
if err := json.NewDecoder(resp.Body).Decode(&machineResp); err != nil { | ||||||
slog.Error("Error parsing response", "error", err) | ||||||
return nil, err | ||||||
} | ||||||
|
||||||
slog.Debug("triggered projects refresh service", "machineId", machineResp.ID, "statusCode", resp.StatusCode) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Log level inconsistency. Consider ERROR for failures and INFO for successful operations instead of DEBUG
Suggested change
|
||||||
|
||||||
return &MachineResponse{ID: machineResp.ID}, nil | ||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,8 +3,8 @@ package services | |
import ( | ||
"fmt" | ||
"github.com/diggerhq/digger/backend/models" | ||
"github.com/diggerhq/digger/backend/service_clients" | ||
utils3 "github.com/diggerhq/digger/backend/utils" | ||
dg_configuration "github.com/diggerhq/digger/libs/digger_config" | ||
"log/slog" | ||
"strconv" | ||
"strings" | ||
|
@@ -41,17 +41,12 @@ func LoadProjectsFromGithubRepo(gh utils3.GithubClientProvider, installationId s | |
return fmt.Errorf("error getting github service") | ||
} | ||
|
||
err = utils3.CloneGitRepoAndDoAction(cloneUrl, branch, "", *token, "", func(dir string) error { | ||
config, err := dg_configuration.LoadDiggerConfigYaml(dir, true, nil) | ||
if err != nil { | ||
slog.Error("failed to load digger.yml: %v", "error", err) | ||
return fmt.Errorf("error loading digger.yml %v", err) | ||
} | ||
models.DB.RefreshProjectsFromRepo(strconv.Itoa(int(link.OrganisationId)), *config, repoFullName) | ||
return nil | ||
}) | ||
resp, err := service_clients.TriggerProjectsRefreshService(cloneUrl, branch, *token, repoFullName, strconv.Itoa(int(link.OrganisationId))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Add debug log before triggering refresh service for better traceability |
||
if err != nil { | ||
return fmt.Errorf("error while cloning repo: %v", err) | ||
return fmt.Errorf("error triggering projects refresh service: %v", err) | ||
} | ||
if resp == nil { | ||
return fmt.Errorf("error triggering projects refresh service: response nil") | ||
} | ||
|
||
return nil | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
package utils | ||
|
||
import ( | ||
"github.com/diggerhq/digger/libs/git_utils" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. style: Consider grouping standard library imports (os, testing) together, separate from third-party imports, following Go conventions |
||
"os" | ||
motatoes marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"testing" | ||
|
||
|
@@ -9,14 +10,14 @@ import ( | |
|
||
func TestGithubCloneWithInvalidTokenThrowsErr(t *testing.T) { | ||
f := func(d string) error { return nil } | ||
err := CloneGitRepoAndDoAction("https://github.com/diggerhq/private-repo", "main", "", "invalid-token", "", f) | ||
err := git_utils.CloneGitRepoAndDoAction("https://github.com/diggerhq/private-repo", "main", "", "invalid-token", "", f) | ||
assert.NotNil(t, err) | ||
} | ||
|
||
func TestGithubCloneWithPublicRepoThrowsNoError(t *testing.T) { | ||
token := os.Getenv("GITHUB_PAT_TOKEN") | ||
f := func(d string) error { return nil } | ||
err := CloneGitRepoAndDoAction("https://github.com/diggerhq/digger", "develop", "", token, "", f) | ||
err := git_utils.CloneGitRepoAndDoAction("https://github.com/diggerhq/digger", "develop", "", token, "", f) | ||
assert.Nil(t, err) | ||
} | ||
|
||
|
@@ -27,13 +28,13 @@ func TestGithubCloneWithPrivateRepoAndValidTokenThrowsNoError(t *testing.T) { | |
return | ||
} | ||
f := func(d string) error { return nil } | ||
err := CloneGitRepoAndDoAction("https://github.com/diggerhq/infra-gcp", "main", "", token, "", f) | ||
err := git_utils.CloneGitRepoAndDoAction("https://github.com/diggerhq/infra-gcp", "main", "", token, "", f) | ||
assert.Nil(t, err) | ||
} | ||
|
||
func TestGithubCloneWithInvalidBranchThrowsError(t *testing.T) { | ||
token := os.Getenv("GITHUB_PAT_TOKEN") | ||
f := func(d string) error { return nil } | ||
err := CloneGitRepoAndDoAction("https://github.com/diggerhq/digger", "not-a-branch", "", token, "", f) | ||
err := git_utils.CloneGitRepoAndDoAction("https://github.com/diggerhq/digger", "not-a-branch", "", token, "", f) | ||
assert.NotNil(t, err) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Add 'go mod download' before build step to ensure dependencies are properly cached