Skip to content

Commit 2236b23

Browse files
authored
fix: add linters and improve code quality (#480)
* chore: add golangci-lint configuration file * fix: group imports with local prefixes * chore: update golangci-lint configuration and streamline linting commands * refactor: replace hardcoded file permissions with constants from utils * chore: add additional linters to golangci-lint configuration * refactor: lint * refactor: lint * refactor: use request context in WebhookHandler * chore: add additional linters to golangci-lint configuration * refactor: add errname linter and improve error handling in GetDeployConfigFromYAML * refactor: update golangci-lint command to use GO_BIN variable * refactor: update wsl linter configuration and clean up code formatting
1 parent 9c32048 commit 2236b23

30 files changed

+227
-94
lines changed

.golangci.yaml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
version: "2"
2+
linters:
3+
default: standard
4+
enable:
5+
- asciicheck
6+
- bodyclose
7+
- contextcheck
8+
- copyloopvar
9+
- dupword
10+
- durationcheck
11+
- errname
12+
- gosec
13+
- misspell
14+
- nilerr
15+
- unconvert
16+
- godot
17+
- gocritic
18+
- makezero
19+
- perfsprint
20+
- wsl_v5
21+
settings:
22+
wsl_v5:
23+
allow-first-in-block: true
24+
allow-whole-block: false
25+
branch-max-lines: 2
26+
formatters:
27+
enable:
28+
- gofmt
29+
- gofumpt
30+
- goimports
31+
settings:
32+
# gofmt:
33+
# extra-rules: true
34+
goimports:
35+
local-prefixes:
36+
- github.com/kimdre/doco-cd
37+
issues:
38+
max-issues-per-linter: 0
39+
max-same-issues: 0
40+
run:
41+
timeout: 5m

Makefile

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,8 @@ build:
3030
mkdir -p $(BINARY_DIR)
3131
go build -o $(BINARY_DIR) ./...
3232

33-
lint:
34-
golangci-lint run --fix ./...
35-
36-
fmt:
37-
go fmt ./...
38-
-go tool gofumpt -l -w .
39-
-go tool goimports -l -w .
40-
-go tool wsl -test=true -fix ./...
41-
-go tool perfsprint -fix ./...
33+
lint fmt:
34+
${GO_BIN}/golangci-lint run --fix ./...
4235

4336
update:
4437
git pull origin main

cmd/doco-cd/http_handler.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121

2222
"github.com/docker/cli/cli/command"
2323
"github.com/google/uuid"
24+
2425
"github.com/kimdre/doco-cd/internal/config"
2526
"github.com/kimdre/doco-cd/internal/docker"
2627
"github.com/kimdre/doco-cd/internal/git"
@@ -46,7 +47,7 @@ func onError(w http.ResponseWriter, log *slog.Logger, errMsg string, details any
4647
statusCode)
4748
}
4849

49-
// getRepoName extracts the repository name from the clone URL
50+
// getRepoName extracts the repository name from the clone URL.
5051
func getRepoName(cloneURL string) string {
5152
repoName := strings.SplitAfter(cloneURL, "://")[1]
5253

@@ -57,7 +58,7 @@ func getRepoName(cloneURL string) string {
5758
return strings.TrimSuffix(repoName, ".git")
5859
}
5960

60-
// HandleEvent handles the incoming webhook event
61+
// HandleEvent handles the incoming webhook event.
6162
func HandleEvent(ctx context.Context, jobLog *slog.Logger, w http.ResponseWriter, appConfig *config.AppConfig, dataMountPoint container.MountPoint, payload webhook.ParsedPayload, customTarget, jobID string, dockerCli command.Cli, dockerClient *client.Client) {
6263
startTime := time.Now()
6364
repoName := getRepoName(payload.CloneURL)
@@ -83,6 +84,7 @@ func HandleEvent(ctx context.Context, jobLog *slog.Logger, w http.ResponseWriter
8384

8485
if appConfig.GitAccessToken == "" {
8586
onError(w, jobLog, "missing access token for private repository", "", jobID, http.StatusInternalServerError)
87+
8688
return
8789
}
8890

@@ -95,18 +97,21 @@ func HandleEvent(ctx context.Context, jobLog *slog.Logger, w http.ResponseWriter
9597
// Validate payload.FullName to prevent directory traversal
9698
if strings.Contains(payload.FullName, "..") {
9799
onError(w, jobLog.With(slog.String("repository", payload.FullName)), "invalid repository name", "", jobID, http.StatusBadRequest)
100+
98101
return
99102
}
100103

101104
internalRepoPath, err := utils.VerifyAndSanitizePath(filepath.Join(dataMountPoint.Destination, repoName), dataMountPoint.Destination) // Path inside the container
102105
if err != nil {
103106
onError(w, jobLog.With(logger.ErrAttr(err)), "failed to verify and sanitize internal filesystem path", err.Error(), jobID, http.StatusBadRequest)
107+
104108
return
105109
}
106110

107111
externalRepoPath, err := utils.VerifyAndSanitizePath(filepath.Join(dataMountPoint.Destination, repoName), dataMountPoint.Destination) // Path on the host
108112
if err != nil {
109113
onError(w, jobLog.With(logger.ErrAttr(err)), "failed to verify and sanitize external filesystem path", err.Error(), jobID, http.StatusBadRequest)
114+
110115
return
111116
}
112117

@@ -120,10 +125,12 @@ func HandleEvent(ctx context.Context, jobLog *slog.Logger, w http.ResponseWriter
120125
_, err = git.UpdateRepository(internalRepoPath, payload.Ref, appConfig.SkipTLSVerification, appConfig.HttpProxy)
121126
if err != nil {
122127
onError(w, jobLog.With(logger.ErrAttr(err)), "failed to checkout repository", err.Error(), jobID, http.StatusInternalServerError)
128+
123129
return
124130
}
125131
} else {
126132
onError(w, jobLog.With(logger.ErrAttr(err)), "failed to clone repository", err.Error(), jobID, http.StatusInternalServerError)
133+
127134
return
128135
}
129136
} else {
@@ -139,6 +146,7 @@ func HandleEvent(ctx context.Context, jobLog *slog.Logger, w http.ResponseWriter
139146
jobLog.Warn(err.Error())
140147
} else {
141148
onError(w, jobLog.With(logger.ErrAttr(err)), "failed to get deploy configuration", err.Error(), jobID, http.StatusInternalServerError)
149+
142150
return
143151
}
144152
}
@@ -154,12 +162,14 @@ func HandleEvent(ctx context.Context, jobLog *slog.Logger, w http.ResponseWriter
154162
internalRepoPath, err = utils.VerifyAndSanitizePath(filepath.Join(dataMountPoint.Destination, repoName), dataMountPoint.Destination) // Path inside the container
155163
if err != nil {
156164
onError(w, subJobLog.With(logger.ErrAttr(err)), "invalid repository name", err.Error(), jobID, http.StatusBadRequest)
165+
157166
return
158167
}
159168

160169
externalRepoPath, err = utils.VerifyAndSanitizePath(filepath.Join(dataMountPoint.Source, repoName), dataMountPoint.Source) // Path on the host
161170
if err != nil {
162171
onError(w, subJobLog.With(logger.ErrAttr(err)), "invalid repository name", err.Error(), jobID, http.StatusBadRequest)
172+
163173
return
164174
}
165175

@@ -182,6 +192,7 @@ func HandleEvent(ctx context.Context, jobLog *slog.Logger, w http.ResponseWriter
182192
_, err = git.CloneRepository(internalRepoPath, cloneUrl, deployConfig.Reference, appConfig.SkipTLSVerification, appConfig.HttpProxy)
183193
if err != nil && !errors.Is(err, git.ErrRepositoryAlreadyExists) {
184194
onError(w, subJobLog.With(logger.ErrAttr(err)), "failed to clone remote repository", err.Error(), jobID, http.StatusInternalServerError)
195+
185196
return
186197
}
187198

@@ -193,12 +204,14 @@ func HandleEvent(ctx context.Context, jobLog *slog.Logger, w http.ResponseWriter
193204
repo, err := git.UpdateRepository(internalRepoPath, deployConfig.Reference, appConfig.SkipTLSVerification, appConfig.HttpProxy)
194205
if err != nil {
195206
onError(w, subJobLog.With(logger.ErrAttr(err)), "failed to checkout repository", err.Error(), jobID, http.StatusInternalServerError)
207+
196208
return
197209
}
198210

199211
latestCommit, err := git.GetLatestCommit(repo, deployConfig.Reference)
200212
if err != nil {
201213
onError(w, subJobLog.With(logger.ErrAttr(err)), "failed to get latest commit", err.Error(), jobID, http.StatusInternalServerError)
214+
202215
return
203216
}
204217

@@ -209,12 +222,14 @@ func HandleEvent(ctx context.Context, jobLog *slog.Logger, w http.ResponseWriter
209222
containers, err := docker.GetLabeledContainers(ctx, dockerClient, api.ProjectLabel, deployConfig.Name)
210223
if err != nil {
211224
onError(w, subJobLog.With(logger.ErrAttr(err)), "failed to retrieve containers", err.Error(), jobID, http.StatusInternalServerError)
225+
212226
return
213227
}
214228

215229
// If no containers are found, skip the destruction step
216230
if len(containers) == 0 {
217231
subJobLog.Debug("no containers found for stack, skipping...")
232+
218233
continue
219234
}
220235

@@ -251,6 +266,7 @@ func HandleEvent(ctx context.Context, jobLog *slog.Logger, w http.ResponseWriter
251266
err = docker.DestroyStack(subJobLog, &ctx, &dockerCli, deployConfig)
252267
if err != nil {
253268
onError(w, subJobLog.With(logger.ErrAttr(err)), "failed to destroy stack", err.Error(), jobID, http.StatusInternalServerError)
269+
254270
return
255271
}
256272

@@ -263,6 +279,7 @@ func HandleEvent(ctx context.Context, jobLog *slog.Logger, w http.ResponseWriter
263279
subDirs, err := os.ReadDir(parentDir)
264280
if err != nil {
265281
onError(w, subJobLog.With(logger.ErrAttr(err)), "failed to read parent directory", err.Error(), jobID, http.StatusInternalServerError)
282+
266283
return
267284
}
268285

@@ -274,13 +291,15 @@ func HandleEvent(ctx context.Context, jobLog *slog.Logger, w http.ResponseWriter
274291
err = os.RemoveAll(internalRepoPath)
275292
if err != nil {
276293
onError(w, subJobLog.With(logger.ErrAttr(err)), "failed to remove deployment directory", err.Error(), jobID, http.StatusInternalServerError)
294+
277295
return
278296
}
279297
} else {
280298
// Remove the parent directory if it has only one subdirectory
281299
err = os.RemoveAll(parentDir)
282300
if err != nil {
283301
onError(w, subJobLog.With(logger.ErrAttr(err)), "failed to remove deployment directory", err.Error(), jobID, http.StatusInternalServerError)
302+
284303
return
285304
}
286305

@@ -292,6 +311,7 @@ func HandleEvent(ctx context.Context, jobLog *slog.Logger, w http.ResponseWriter
292311
containers, err := docker.GetLabeledContainers(ctx, dockerClient, api.ProjectLabel, deployConfig.Name)
293312
if err != nil {
294313
onError(w, subJobLog.With(logger.ErrAttr(err)), "failed to retrieve containers", err.Error(), jobID, http.StatusInternalServerError)
314+
295315
return
296316
}
297317

@@ -303,6 +323,7 @@ func HandleEvent(ctx context.Context, jobLog *slog.Logger, w http.ResponseWriter
303323
name, ok := cont.Labels[docker.DocoCDLabels.Repository.Name]
304324
if !ok || name != payload.FullName {
305325
correctRepo = false
326+
306327
break
307328
}
308329

@@ -343,6 +364,7 @@ func HandleEvent(ctx context.Context, jobLog *slog.Logger, w http.ResponseWriter
343364
err = docker.DeployStack(subJobLog, internalRepoPath, externalRepoPath, &ctx, &dockerCli, &payload, deployConfig, latestCommit, Version, false)
344365
if err != nil {
345366
onError(w, subJobLog.With(logger.ErrAttr(err)), "deployment failed", err.Error(), jobID, http.StatusInternalServerError)
367+
346368
return
347369
}
348370
}
@@ -355,7 +377,7 @@ func HandleEvent(ctx context.Context, jobLog *slog.Logger, w http.ResponseWriter
355377
}
356378

357379
func (h *handlerData) WebhookHandler(w http.ResponseWriter, r *http.Request) {
358-
ctx := context.Background()
380+
ctx := r.Context()
359381

360382
customTarget := r.PathValue("customTarget")
361383

@@ -405,6 +427,7 @@ func (h *handlerData) HealthCheckHandler(w http.ResponseWriter, _ *http.Request)
405427
err := docker.VerifySocketConnection()
406428
if err != nil {
407429
onError(w, h.log.With(logger.ErrAttr(err)), docker.ErrDockerSocketConnectionFailed.Error(), err.Error(), "", http.StatusServiceUnavailable)
430+
408431
return
409432
}
410433

cmd/doco-cd/http_handler_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const (
3030
githubPayloadFile = "testdata/github_payload.json"
3131
)
3232

33-
// Make http call to HealthCheckHandler
33+
// Make http call to HealthCheckHandler.
3434
func TestHandlerData_HealthCheckHandler(t *testing.T) {
3535
expectedResponse := fmt.Sprintln(`{"details":"healthy"}`)
3636
expectedStatusCode := http.StatusOK
@@ -207,7 +207,7 @@ func TestHandlerData_WebhookHandler(t *testing.T) {
207207
testContainerPort := testContainer.NetworkSettings.Ports["80/tcp"][0].HostPort
208208
testURL := "http://localhost:" + testContainerPort
209209

210-
resp, err := http.Get(testURL)
210+
resp, err := http.Get(testURL) // #nosec G107
211211
if err != nil {
212212
t.Fatalf("Failed to make GET request to test container: %v", err)
213213
}
@@ -233,7 +233,7 @@ func TestHandlerData_WebhookHandler(t *testing.T) {
233233

234234
indexFile := path.Join(repoDir, "test", "index.html")
235235

236-
fileContent, err := os.ReadFile(indexFile)
236+
fileContent, err := os.ReadFile(indexFile) // #nosec G304
237237
if err != nil {
238238
t.Fatalf("Failed to read index.html file: %v", err)
239239
}

cmd/doco-cd/main.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,16 @@ import (
1111
"regexp"
1212
"strings"
1313
"sync"
14+
"time"
15+
16+
"github.com/kimdre/doco-cd/internal/utils"
1417

1518
"github.com/docker/docker/api/types/container"
1619

1720
"github.com/go-git/go-git/v5/plumbing/transport"
1821

1922
"github.com/docker/docker/client"
23+
2024
"github.com/kimdre/doco-cd/internal/config"
2125
"github.com/kimdre/doco-cd/internal/docker"
2226
"github.com/kimdre/doco-cd/internal/logger"
@@ -33,7 +37,7 @@ var (
3337
errMsg string
3438
)
3539

36-
// getAppContainerID retrieves the application container ID from the cpuset file
40+
// getAppContainerID retrieves the application container ID from the cpuset file.
3741
func getAppContainerID() (string, error) {
3842
const (
3943
cgroupMounts = "/proc/self/mountinfo"
@@ -91,12 +95,12 @@ func GetProxyUrlRedacted(proxyUrl string) string {
9195
}
9296

9397
// CreateMountpointSymlink creates the Symlink for the data mount point to reflect the data volume path in the container.
94-
// Required so that the docker cli client is able to read/parse certain files (like .env files) in docker.LoadCompose
98+
// Required so that the docker cli client is able to read/parse certain files (like .env files) in docker.LoadCompose.
9599
func CreateMountpointSymlink(m container.MountPoint) error {
96100
// prepare the symlink parent directory
97101
symlinkParentDir := path.Dir(m.Source)
98102

99-
err := os.MkdirAll(symlinkParentDir, 0o755)
103+
err := os.MkdirAll(symlinkParentDir, utils.PermDir)
100104
if err != nil {
101105
return fmt.Errorf("failed to create parent directory %s: %w", symlinkParentDir, err)
102106
}
@@ -170,6 +174,7 @@ func main() {
170174
dockerCli, err := docker.CreateDockerCli(c.DockerQuietDeploy, !c.SkipTLSVerification)
171175
if err != nil {
172176
log.Critical("failed to create docker client", logger.ErrAttr(err))
177+
173178
return
174179
}
175180
defer func(client client.APIClient) {
@@ -198,6 +203,7 @@ func main() {
198203
appContainerID, err := getAppContainerID()
199204
if err != nil {
200205
log.Critical("failed to retrieve application container id", logger.ErrAttr(err))
206+
201207
return
202208
}
203209

@@ -225,6 +231,7 @@ func main() {
225231
err = CreateMountpointSymlink(dataMountPoint)
226232
if err != nil {
227233
log.Critical(fmt.Sprintf("failed to create symlink for %s mount point", dataMountPoint.Destination), logger.ErrAttr(err))
234+
228235
return
229236
}
230237

@@ -255,6 +262,7 @@ func main() {
255262
err = StartPoll(&h, pollConfig, &wg)
256263
if err != nil {
257264
log.Critical("failed to scheduling polling jobs", logger.ErrAttr(err))
265+
258266
return
259267
}
260268
}
@@ -267,7 +275,7 @@ func main() {
267275
log.Error("failed to retrieve doco-cd containers", logger.ErrAttr(err))
268276
}
269277

270-
if len(containers) <= 0 {
278+
if len(containers) == 0 {
271279
log.Debug("no containers found that are managed by doco-cd", slog.Int("count", len(containers)))
272280
} else {
273281
log.Debug("retrieved containers successfully", slog.Int("count", len(containers)))
@@ -280,8 +288,9 @@ func main() {
280288
))
281289

282290
dir := cont.Labels[docker.DocoCDLabels.Deployment.WorkingDir]
283-
if len(dir) <= 0 {
291+
if len(dir) == 0 {
284292
log.Error(fmt.Sprintf("failed to retrieve container %v working directory", cont.ID))
293+
285294
continue
286295
}
287296

@@ -303,7 +312,12 @@ func main() {
303312
}()
304313
}
305314

306-
err = http.ListenAndServe(fmt.Sprintf(":%d", c.HttpPort), nil)
315+
server := &http.Server{
316+
Addr: fmt.Sprintf(":%d", c.HttpPort),
317+
ReadHeaderTimeout: 3 * time.Second,
318+
}
319+
320+
err = server.ListenAndServe()
307321
if err != nil {
308322
log.Error(fmt.Sprintf("failed to listen on port: %v", c.HttpPort), logger.ErrAttr(err))
309323
}

0 commit comments

Comments
 (0)