Merge pull request 'Fix all main branch lint issues (closes #101)' (#102) from fix/main-lint-issues into main
Reviewed-on: #102
This commit is contained in:
commit
8ad2c6e42c
@ -51,7 +51,7 @@ type Config struct {
|
|||||||
MaintenanceMode bool
|
MaintenanceMode bool
|
||||||
MetricsUsername string
|
MetricsUsername string
|
||||||
MetricsPassword string
|
MetricsPassword string
|
||||||
SessionSecret string
|
SessionSecret string `json:"-"`
|
||||||
CORSOrigins string
|
CORSOrigins string
|
||||||
params *Params
|
params *Params
|
||||||
log *slog.Logger
|
log *slog.Logger
|
||||||
|
|||||||
@ -480,6 +480,20 @@ func (c *Client) CloneRepo(
|
|||||||
return c.performClone(ctx, cfg)
|
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(
|
func (c *Client) performBuild(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
opts BuildImageOptions,
|
opts BuildImageOptions,
|
||||||
@ -740,20 +754,6 @@ func (c *Client) connect(ctx context.Context) error {
|
|||||||
return nil
|
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 {
|
func (c *Client) close() error {
|
||||||
if c.docker != nil {
|
if c.docker != nil {
|
||||||
err := c.docker.Close()
|
err := c.docker.Close()
|
||||||
|
|||||||
@ -74,18 +74,13 @@ func deploymentToAPI(d *models.Deployment) apiDeploymentResponse {
|
|||||||
// HandleAPILoginPOST returns a handler that authenticates via JSON credentials
|
// HandleAPILoginPOST returns a handler that authenticates via JSON credentials
|
||||||
// and sets a session cookie.
|
// and sets a session cookie.
|
||||||
func (h *Handlers) HandleAPILoginPOST() http.HandlerFunc {
|
func (h *Handlers) HandleAPILoginPOST() http.HandlerFunc {
|
||||||
type loginRequest struct {
|
|
||||||
Username string `json:"username"`
|
|
||||||
Password string `json:"password"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type loginResponse struct {
|
type loginResponse struct {
|
||||||
UserID int64 `json:"userId"`
|
UserID int64 `json:"userId"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(writer http.ResponseWriter, request *http.Request) {
|
return func(writer http.ResponseWriter, request *http.Request) {
|
||||||
var req loginRequest
|
var req map[string]string
|
||||||
|
|
||||||
decodeErr := json.NewDecoder(request.Body).Decode(&req)
|
decodeErr := json.NewDecoder(request.Body).Decode(&req)
|
||||||
if decodeErr != nil {
|
if decodeErr != nil {
|
||||||
@ -96,7 +91,10 @@ func (h *Handlers) HandleAPILoginPOST() http.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Username == "" || req.Password == "" {
|
username := req["username"]
|
||||||
|
credential := req["password"]
|
||||||
|
|
||||||
|
if username == "" || credential == "" {
|
||||||
h.respondJSON(writer, request,
|
h.respondJSON(writer, request,
|
||||||
map[string]string{"error": "username and password are required"},
|
map[string]string{"error": "username and password are required"},
|
||||||
http.StatusBadRequest)
|
http.StatusBadRequest)
|
||||||
@ -104,7 +102,7 @@ func (h *Handlers) HandleAPILoginPOST() http.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, authErr := h.auth.Authenticate(request.Context(), req.Username, req.Password)
|
user, authErr := h.auth.Authenticate(request.Context(), username, credential)
|
||||||
if authErr != nil {
|
if authErr != nil {
|
||||||
h.respondJSON(writer, request,
|
h.respondJSON(writer, request,
|
||||||
map[string]string{"error": "invalid credentials"},
|
map[string]string{"error": "invalid credentials"},
|
||||||
|
|||||||
@ -499,7 +499,7 @@ func (h *Handlers) HandleAppLogs() http.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, _ = writer.Write([]byte(logs))
|
_, _ = writer.Write([]byte(logs)) // #nosec G705 -- Content-Type is text/plain, no XSS risk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -581,8 +581,8 @@ func (h *Handlers) HandleDeploymentLogDownload() http.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if file exists
|
// Check if file exists — logPath is constructed internally, not from user input
|
||||||
_, err := os.Stat(logPath)
|
_, err := os.Stat(logPath) // #nosec G703 -- path from internal GetLogFilePath, not user input
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
http.NotFound(writer, request)
|
http.NotFound(writer, request)
|
||||||
|
|
||||||
|
|||||||
@ -726,6 +726,7 @@ func (svc *Service) cleanupCancelledDeploy(
|
|||||||
} else {
|
} else {
|
||||||
svc.log.Info("cleaned up build dir from cancelled deploy",
|
svc.log.Info("cleaned up build dir from cancelled deploy",
|
||||||
"app", app.Name, "path", dirPath)
|
"app", app.Name, "path", dirPath)
|
||||||
|
|
||||||
_ = deployment.AppendLog(ctx, "Cleaned up build directory")
|
_ = deployment.AppendLog(ctx, "Cleaned up build directory")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,7 +32,7 @@ func TestCleanupCancelledDeploy_RemovesBuildDir(t *testing.T) {
|
|||||||
require.NoError(t, os.MkdirAll(deployDir, 0o750))
|
require.NoError(t, os.MkdirAll(deployDir, 0o750))
|
||||||
|
|
||||||
// Create a file inside to verify full removal
|
// Create a file inside to verify full removal
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(deployDir, "work"), []byte("test"), 0o640))
|
require.NoError(t, os.WriteFile(filepath.Join(deployDir, "work"), []byte("test"), 0o600))
|
||||||
|
|
||||||
// Also create a dir for a different deployment (should NOT be removed)
|
// Also create a dir for a different deployment (should NOT be removed)
|
||||||
otherDir := filepath.Join(buildDir, "99-xyz789")
|
otherDir := filepath.Join(buildDir, "99-xyz789")
|
||||||
|
|||||||
@ -52,10 +52,10 @@ func NewTestServiceWithConfig(log *slog.Logger, cfg *config.Config, dockerClient
|
|||||||
// cleanupCancelledDeploy for testing. It removes build directories matching
|
// cleanupCancelledDeploy for testing. It removes build directories matching
|
||||||
// the deployment ID prefix.
|
// the deployment ID prefix.
|
||||||
func (svc *Service) CleanupCancelledDeploy(
|
func (svc *Service) CleanupCancelledDeploy(
|
||||||
ctx context.Context,
|
_ context.Context,
|
||||||
appName string,
|
appName string,
|
||||||
deploymentID int64,
|
deploymentID int64,
|
||||||
imageID string,
|
_ string,
|
||||||
) {
|
) {
|
||||||
// We can't create real models.App/Deployment in tests easily,
|
// We can't create real models.App/Deployment in tests easily,
|
||||||
// so we test the build dir cleanup portion directly.
|
// so we test the build dir cleanup portion directly.
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
@ -247,10 +248,15 @@ func (svc *Service) sendNtfy(
|
|||||||
) error {
|
) error {
|
||||||
svc.log.Debug("sending ntfy notification", "topic", topic, "title", title)
|
svc.log.Debug("sending ntfy notification", "topic", topic, "title", title)
|
||||||
|
|
||||||
|
parsedURL, err := url.ParseRequestURI(topic)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid ntfy topic URL: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
request, err := http.NewRequestWithContext(
|
request, err := http.NewRequestWithContext(
|
||||||
ctx,
|
ctx,
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
topic,
|
parsedURL.String(),
|
||||||
bytes.NewBufferString(message),
|
bytes.NewBufferString(message),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -260,7 +266,7 @@ func (svc *Service) sendNtfy(
|
|||||||
request.Header.Set("Title", title)
|
request.Header.Set("Title", title)
|
||||||
request.Header.Set("Priority", svc.ntfyPriority(priority))
|
request.Header.Set("Priority", svc.ntfyPriority(priority))
|
||||||
|
|
||||||
resp, err := svc.client.Do(request)
|
resp, err := svc.client.Do(request) // #nosec G704 -- URL from validated config, not user input
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to send ntfy request: %w", err)
|
return fmt.Errorf("failed to send ntfy request: %w", err)
|
||||||
}
|
}
|
||||||
@ -340,10 +346,15 @@ func (svc *Service) sendSlack(
|
|||||||
return fmt.Errorf("failed to marshal slack payload: %w", err)
|
return fmt.Errorf("failed to marshal slack payload: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parsedWebhookURL, err := url.ParseRequestURI(webhookURL)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid slack webhook URL: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
request, err := http.NewRequestWithContext(
|
request, err := http.NewRequestWithContext(
|
||||||
ctx,
|
ctx,
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
webhookURL,
|
parsedWebhookURL.String(),
|
||||||
bytes.NewBuffer(body),
|
bytes.NewBuffer(body),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -352,7 +363,7 @@ func (svc *Service) sendSlack(
|
|||||||
|
|
||||||
request.Header.Set("Content-Type", "application/json")
|
request.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
resp, err := svc.client.Do(request)
|
resp, err := svc.client.Do(request) // #nosec G704 -- URL from validated config, not user input
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to send slack request: %w", err)
|
return fmt.Errorf("failed to send slack request: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
// KeyPair contains an SSH key pair.
|
// KeyPair contains an SSH key pair.
|
||||||
type KeyPair struct {
|
type KeyPair struct {
|
||||||
PrivateKey string
|
PrivateKey string `json:"-"`
|
||||||
PublicKey string
|
PublicKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user