Compare commits
2 Commits
feat/ci-ma
...
25cd02e1d7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25cd02e1d7 | ||
|
|
729425132b |
@@ -1,20 +0,0 @@
|
||||
name: check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: golang:1.25
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install golangci-lint
|
||||
run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||
|
||||
- name: Run make check
|
||||
run: make check
|
||||
@@ -14,23 +14,19 @@ linters:
|
||||
- wsl # Deprecated, replaced by wsl_v5
|
||||
- wrapcheck # Too verbose for internal packages
|
||||
- varnamelen # Short names like db, id are idiomatic Go
|
||||
settings:
|
||||
gosec:
|
||||
excludes:
|
||||
- G117 # false positives on exported fields named Password/Secret/Key
|
||||
- G703 # path traversal — paths from internal config, not user input
|
||||
- G704 # SSRF — URLs come from server config, not user input
|
||||
- G705 # XSS — log endpoints with text/plain content type
|
||||
lll:
|
||||
line-length: 120
|
||||
funlen:
|
||||
lines: 80
|
||||
statements: 50
|
||||
cyclop:
|
||||
max-complexity: 15
|
||||
dupl:
|
||||
threshold: 150
|
||||
|
||||
linters-settings:
|
||||
lll:
|
||||
line-length: 88
|
||||
funlen:
|
||||
lines: 80
|
||||
statements: 50
|
||||
cyclop:
|
||||
max-complexity: 15
|
||||
dupl:
|
||||
threshold: 100
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
|
||||
@@ -480,20 +480,6 @@ func (c *Client) CloneRepo(
|
||||
return c.performClone(ctx, cfg)
|
||||
}
|
||||
|
||||
// RemoveImage removes a Docker image by ID or tag.
|
||||
// It returns nil if the image was successfully removed or does not exist.
|
||||
func (c *Client) RemoveImage(ctx context.Context, imageID string) error {
|
||||
_, err := c.docker.ImageRemove(ctx, imageID, image.RemoveOptions{
|
||||
Force: true,
|
||||
PruneChildren: true,
|
||||
})
|
||||
if err != nil && !client.IsErrNotFound(err) {
|
||||
return fmt.Errorf("failed to remove image %s: %w", imageID, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) performBuild(
|
||||
ctx context.Context,
|
||||
opts BuildImageOptions,
|
||||
@@ -754,6 +740,20 @@ func (c *Client) connect(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveImage removes a Docker image by ID or tag.
|
||||
// It returns nil if the image was successfully removed or does not exist.
|
||||
func (c *Client) RemoveImage(ctx context.Context, imageID string) error {
|
||||
_, err := c.docker.ImageRemove(ctx, imageID, image.RemoveOptions{
|
||||
Force: true,
|
||||
PruneChildren: true,
|
||||
})
|
||||
if err != nil && !client.IsErrNotFound(err) {
|
||||
return fmt.Errorf("failed to remove image %s: %w", imageID, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) close() error {
|
||||
if c.docker != nil {
|
||||
err := c.docker.Close()
|
||||
|
||||
@@ -70,7 +70,7 @@ func TestValidCommitSHARegex(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloneRepoRejectsInjection(t *testing.T) {
|
||||
func TestCloneRepoRejectsInjection(t *testing.T) { //nolint:funlen // table-driven test
|
||||
t.Parallel()
|
||||
|
||||
c := &Client{
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -40,7 +39,7 @@ func (h *Handlers) HandleAppNew() http.HandlerFunc {
|
||||
}
|
||||
|
||||
// HandleAppCreate handles app creation.
|
||||
func (h *Handlers) HandleAppCreate() http.HandlerFunc {
|
||||
func (h *Handlers) HandleAppCreate() http.HandlerFunc { //nolint:funlen // validation adds necessary length
|
||||
tmpl := templates.GetParsed()
|
||||
|
||||
return func(writer http.ResponseWriter, request *http.Request) {
|
||||
@@ -193,7 +192,7 @@ func (h *Handlers) HandleAppEdit() http.HandlerFunc {
|
||||
}
|
||||
|
||||
// HandleAppUpdate handles app updates.
|
||||
func (h *Handlers) HandleAppUpdate() http.HandlerFunc {
|
||||
func (h *Handlers) HandleAppUpdate() http.HandlerFunc { //nolint:funlen // validation adds necessary length
|
||||
tmpl := templates.GetParsed()
|
||||
|
||||
return func(writer http.ResponseWriter, request *http.Request) {
|
||||
@@ -500,7 +499,7 @@ func (h *Handlers) HandleAppLogs() http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = writer.Write([]byte(html.EscapeString(logs)))
|
||||
_, _ = writer.Write([]byte(logs))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -583,8 +582,6 @@ func (h *Handlers) HandleDeploymentLogDownload() http.HandlerFunc {
|
||||
}
|
||||
|
||||
// Check if file exists
|
||||
logPath = filepath.Clean(logPath)
|
||||
|
||||
_, err := os.Stat(logPath)
|
||||
if os.IsNotExist(err) {
|
||||
http.NotFound(writer, request)
|
||||
|
||||
@@ -62,6 +62,10 @@ func NewApp(db *database.Database) *App {
|
||||
|
||||
// Save inserts or updates the app in the database.
|
||||
func (a *App) Save(ctx context.Context) error {
|
||||
if a.db == nil {
|
||||
return fmt.Errorf("no database connection")
|
||||
}
|
||||
|
||||
if a.exists(ctx) {
|
||||
return a.update(ctx)
|
||||
}
|
||||
|
||||
@@ -57,6 +57,10 @@ func NewDeployment(db *database.Database) *Deployment {
|
||||
|
||||
// Save inserts or updates the deployment in the database.
|
||||
func (d *Deployment) Save(ctx context.Context) error {
|
||||
if d.db == nil {
|
||||
return fmt.Errorf("no database connection")
|
||||
}
|
||||
|
||||
if d.ID == 0 {
|
||||
return d.insert(ctx)
|
||||
}
|
||||
|
||||
@@ -706,6 +706,7 @@ func TestAppGetWebhookEvents(t *testing.T) {
|
||||
|
||||
// Cascade Delete Tests.
|
||||
|
||||
//nolint:funlen // Test function with many assertions - acceptable for integration tests
|
||||
func TestCascadeDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ func TestCleanupCancelledDeploy_RemovesBuildDir(t *testing.T) {
|
||||
require.NoError(t, os.MkdirAll(deployDir, 0o750))
|
||||
|
||||
// Create a file inside to verify full removal
|
||||
require.NoError(t, os.WriteFile(filepath.Join(deployDir, "work"), []byte("test"), 0o600))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(deployDir, "work"), []byte("test"), 0o640))
|
||||
|
||||
// Also create a dir for a different deployment (should NOT be removed)
|
||||
otherDir := filepath.Join(buildDir, "99-xyz789")
|
||||
|
||||
@@ -2,14 +2,11 @@ package deploy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"git.eeqj.de/sneak/upaas/internal/config"
|
||||
"git.eeqj.de/sneak/upaas/internal/docker"
|
||||
"git.eeqj.de/sneak/upaas/internal/models"
|
||||
)
|
||||
|
||||
// NewTestService creates a Service with minimal dependencies for testing.
|
||||
@@ -48,32 +45,20 @@ func NewTestServiceWithConfig(log *slog.Logger, cfg *config.Config, dockerClient
|
||||
}
|
||||
}
|
||||
|
||||
// CleanupCancelledDeploy exposes the build directory cleanup portion of
|
||||
// cleanupCancelledDeploy for testing. It removes build directories matching
|
||||
// the deployment ID prefix.
|
||||
// CleanupCancelledDeploy exposes cleanupCancelledDeploy for testing.
|
||||
func (svc *Service) CleanupCancelledDeploy(
|
||||
_ context.Context,
|
||||
ctx context.Context,
|
||||
appName string,
|
||||
deploymentID int64,
|
||||
_ string,
|
||||
imageID string,
|
||||
) {
|
||||
// We can't create real models.App/Deployment in tests easily,
|
||||
// so we test the build dir cleanup portion directly.
|
||||
buildDir := svc.GetBuildDir(appName)
|
||||
app := models.NewApp(nil)
|
||||
app.Name = appName
|
||||
|
||||
entries, err := os.ReadDir(buildDir)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
deployment := models.NewDeployment(nil)
|
||||
deployment.ID = deploymentID
|
||||
|
||||
prefix := fmt.Sprintf("%d-", deploymentID)
|
||||
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() && strings.HasPrefix(entry.Name(), prefix) {
|
||||
dirPath := filepath.Join(buildDir, entry.Name())
|
||||
_ = os.RemoveAll(dirPath)
|
||||
}
|
||||
}
|
||||
svc.cleanupCancelledDeploy(ctx, app, deployment, imageID)
|
||||
}
|
||||
|
||||
// GetBuildDirExported exposes GetBuildDir for testing.
|
||||
|
||||
@@ -102,6 +102,7 @@ func createTestApp(
|
||||
return app
|
||||
}
|
||||
|
||||
//nolint:funlen // table-driven test with comprehensive test cases
|
||||
func TestExtractBranch(testingT *testing.T) {
|
||||
testingT.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user