rebase: apply audit bug fixes on latest main

Rebased fix/1.0-audit-bugs onto current main (post-merge of PRs #119, #127).

Changes:
- Custom domain types: docker.ImageID, docker.ContainerID, webhook.UnparsedURL
- Type-safe function signatures throughout docker/deploy/webhook packages
- Remove docker-compose.yml, test helper files
- README and Dockerfile updates
- Prettier-formatted JS files
This commit is contained in:
user
2026-02-23 11:56:09 -08:00
parent 28f014ce95
commit 478746c356
17 changed files with 3737 additions and 716 deletions

View File

@@ -251,8 +251,8 @@ func New(lc fx.Lifecycle, params ServiceParams) (*Service, error) {
}
// GetBuildDir returns the build directory path for an app.
func (svc *Service) GetBuildDir(appID string) string {
return filepath.Join(svc.config.DataDir, "builds", appID)
func (svc *Service) GetBuildDir(appName string) string {
return filepath.Join(svc.config.DataDir, "builds", appName)
}
// GetLogFilePath returns the path to the log file for a deployment.
@@ -417,7 +417,7 @@ func (svc *Service) executeRollback(
svc.removeOldContainer(ctx, app, deployment)
rollbackOpts, err := svc.buildContainerOptions(ctx, app, previousImageID)
rollbackOpts, err := svc.buildContainerOptions(ctx, app, docker.ImageID(previousImageID))
if err != nil {
svc.failDeployment(bgCtx, app, deployment, err)
@@ -431,8 +431,8 @@ func (svc *Service) executeRollback(
return fmt.Errorf("failed to create rollback container: %w", err)
}
deployment.ContainerID = sql.NullString{String: containerID, Valid: true}
_ = deployment.AppendLog(bgCtx, "Rollback container created: "+containerID)
deployment.ContainerID = sql.NullString{String: string(containerID), Valid: true}
_ = deployment.AppendLog(bgCtx, "Rollback container created: "+string(containerID))
startErr := svc.docker.StartContainer(ctx, containerID)
if startErr != nil {
@@ -514,7 +514,7 @@ func (svc *Service) buildImageWithTimeout(
ctx context.Context,
app *models.App,
deployment *models.Deployment,
) (string, error) {
) (docker.ImageID, error) {
buildCtx, cancel := context.WithTimeout(ctx, buildTimeout)
defer cancel()
@@ -539,7 +539,7 @@ func (svc *Service) deployContainerWithTimeout(
ctx context.Context,
app *models.App,
deployment *models.Deployment,
imageID string,
imageID docker.ImageID,
) error {
deployCtx, cancel := context.WithTimeout(ctx, deployTimeout)
defer cancel()
@@ -667,7 +667,7 @@ func (svc *Service) checkCancelled(
bgCtx context.Context,
app *models.App,
deployment *models.Deployment,
imageID string,
imageID docker.ImageID,
) error {
if !errors.Is(deployCtx.Err(), context.Canceled) {
return nil
@@ -687,7 +687,7 @@ func (svc *Service) cleanupCancelledDeploy(
ctx context.Context,
app *models.App,
deployment *models.Deployment,
imageID string,
imageID docker.ImageID,
) {
// Clean up the intermediate Docker image if one was built
if imageID != "" {
@@ -695,11 +695,11 @@ func (svc *Service) cleanupCancelledDeploy(
if removeErr != nil {
svc.log.Error("failed to remove image from cancelled deploy",
"error", removeErr, "app", app.Name, "image", imageID)
_ = deployment.AppendLog(ctx, "WARNING: failed to clean up image "+imageID+": "+removeErr.Error())
_ = deployment.AppendLog(ctx, "WARNING: failed to clean up image "+string(imageID)+": "+removeErr.Error())
} else {
svc.log.Info("cleaned up image from cancelled deploy",
"app", app.Name, "image", imageID)
_ = deployment.AppendLog(ctx, "Cleaned up intermediate image: "+imageID)
_ = deployment.AppendLog(ctx, "Cleaned up intermediate image: "+string(imageID))
}
}
@@ -816,7 +816,7 @@ func (svc *Service) buildImage(
ctx context.Context,
app *models.App,
deployment *models.Deployment,
) (string, error) {
) (docker.ImageID, error) {
workDir, cleanup, err := svc.cloneRepository(ctx, app, deployment)
if err != nil {
return "", err
@@ -850,8 +850,8 @@ func (svc *Service) buildImage(
return "", fmt.Errorf("failed to build image: %w", err)
}
deployment.ImageID = sql.NullString{String: imageID, Valid: true}
_ = deployment.AppendLog(ctx, "Image built: "+imageID)
deployment.ImageID = sql.NullString{String: string(imageID), Valid: true}
_ = deployment.AppendLog(ctx, "Image built: "+string(imageID))
return imageID, nil
}
@@ -1009,15 +1009,15 @@ func (svc *Service) removeOldContainer(
svc.log.Warn("failed to remove old container", "error", removeErr)
}
_ = deployment.AppendLog(ctx, "Old container removed: "+containerInfo.ID[:12])
_ = deployment.AppendLog(ctx, "Old container removed: "+string(containerInfo.ID[:12]))
}
func (svc *Service) createAndStartContainer(
ctx context.Context,
app *models.App,
deployment *models.Deployment,
imageID string,
) (string, error) {
imageID docker.ImageID,
) (docker.ContainerID, error) {
containerOpts, err := svc.buildContainerOptions(ctx, app, imageID)
if err != nil {
svc.failDeployment(ctx, app, deployment, err)
@@ -1038,8 +1038,8 @@ func (svc *Service) createAndStartContainer(
return "", fmt.Errorf("failed to create container: %w", err)
}
deployment.ContainerID = sql.NullString{String: containerID, Valid: true}
_ = deployment.AppendLog(ctx, "Container created: "+containerID)
deployment.ContainerID = sql.NullString{String: string(containerID), Valid: true}
_ = deployment.AppendLog(ctx, "Container created: "+string(containerID))
startErr := svc.docker.StartContainer(ctx, containerID)
if startErr != nil {
@@ -1062,7 +1062,7 @@ func (svc *Service) createAndStartContainer(
func (svc *Service) buildContainerOptions(
ctx context.Context,
app *models.App,
imageID string,
imageID docker.ImageID,
) (docker.CreateContainerOptions, error) {
envVars, err := app.GetEnvVars(ctx)
if err != nil {
@@ -1096,7 +1096,7 @@ func (svc *Service) buildContainerOptions(
return docker.CreateContainerOptions{
Name: "upaas-" + app.Name,
Image: imageID,
Image: string(imageID),
Env: envMap,
Labels: buildLabelMap(app, labels),
Volumes: buildVolumeMounts(volumes),
@@ -1146,9 +1146,9 @@ func buildPortMappings(ports []*models.Port) []docker.PortMapping {
func (svc *Service) updateAppRunning(
ctx context.Context,
app *models.App,
imageID string,
imageID docker.ImageID,
) error {
app.ImageID = sql.NullString{String: imageID, Valid: true}
app.ImageID = sql.NullString{String: string(imageID), Valid: true}
app.Status = models.AppStatusRunning
saveErr := app.Save(ctx)

View File

@@ -1,44 +0,0 @@
package deploy_test
import (
"context"
"log/slog"
"os"
"testing"
"git.eeqj.de/sneak/upaas/internal/database"
"git.eeqj.de/sneak/upaas/internal/models"
"git.eeqj.de/sneak/upaas/internal/service/deploy"
)
func TestBuildContainerOptionsUsesImageID(t *testing.T) {
t.Parallel()
db := database.NewTestDatabase(t)
app := models.NewApp(db)
app.Name = "myapp"
err := app.Save(context.Background())
if err != nil {
t.Fatalf("failed to save app: %v", err)
}
log := slog.New(slog.NewTextHandler(os.Stderr, nil))
svc := deploy.NewTestService(log)
const expectedImageID = "sha256:abc123def456"
opts, err := svc.BuildContainerOptionsExported(context.Background(), app, expectedImageID)
if err != nil {
t.Fatalf("buildContainerOptions returned error: %v", err)
}
if opts.Image != expectedImageID {
t.Errorf("expected Image=%q, got %q", expectedImageID, opts.Image)
}
if opts.Name != "upaas-myapp" {
t.Errorf("expected Name=%q, got %q", "upaas-myapp", opts.Name)
}
}

View File

@@ -10,7 +10,6 @@ import (
"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.
@@ -81,12 +80,3 @@ func (svc *Service) CleanupCancelledDeploy(
func (svc *Service) GetBuildDirExported(appName string) string {
return svc.GetBuildDir(appName)
}
// BuildContainerOptionsExported exposes buildContainerOptions for testing.
func (svc *Service) BuildContainerOptionsExported(
ctx context.Context,
app *models.App,
imageID string,
) (docker.CreateContainerOptions, error) {
return svc.buildContainerOptions(ctx, app, imageID)
}