rework: address review feedback on PR #126
Changes per sneak's review: - Delete docker-compose.yml, add example stanza to README - Define custom domain types: ImageID, ContainerID, UnparsedURL - Use custom types in all function signatures throughout codebase - Restore imageID parameter (as domain.ImageID) in deploy pipeline - buildContainerOptions now takes ImageID directly instead of constructing image tag from deploymentID - Fix pre-existing JS formatting (prettier) make check passes with zero failures.
This commit is contained in:
@@ -20,6 +20,7 @@ import (
|
||||
"git.eeqj.de/sneak/upaas/internal/config"
|
||||
"git.eeqj.de/sneak/upaas/internal/database"
|
||||
"git.eeqj.de/sneak/upaas/internal/docker"
|
||||
"git.eeqj.de/sneak/upaas/internal/domain"
|
||||
"git.eeqj.de/sneak/upaas/internal/logger"
|
||||
"git.eeqj.de/sneak/upaas/internal/models"
|
||||
"git.eeqj.de/sneak/upaas/internal/service/notify"
|
||||
@@ -417,7 +418,7 @@ func (svc *Service) executeRollback(
|
||||
|
||||
svc.removeOldContainer(ctx, app, deployment)
|
||||
|
||||
rollbackOpts, err := svc.buildContainerOptions(ctx, app, previousImageID)
|
||||
rollbackOpts, err := svc.buildContainerOptions(ctx, app, domain.ImageID(previousImageID))
|
||||
if err != nil {
|
||||
svc.failDeployment(bgCtx, app, deployment, err)
|
||||
|
||||
@@ -431,8 +432,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 +515,7 @@ func (svc *Service) buildImageWithTimeout(
|
||||
ctx context.Context,
|
||||
app *models.App,
|
||||
deployment *models.Deployment,
|
||||
) (string, error) {
|
||||
) (domain.ImageID, error) {
|
||||
buildCtx, cancel := context.WithTimeout(ctx, buildTimeout)
|
||||
defer cancel()
|
||||
|
||||
@@ -539,7 +540,7 @@ func (svc *Service) deployContainerWithTimeout(
|
||||
ctx context.Context,
|
||||
app *models.App,
|
||||
deployment *models.Deployment,
|
||||
imageID string,
|
||||
imageID domain.ImageID,
|
||||
) error {
|
||||
deployCtx, cancel := context.WithTimeout(ctx, deployTimeout)
|
||||
defer cancel()
|
||||
@@ -667,7 +668,7 @@ func (svc *Service) checkCancelled(
|
||||
bgCtx context.Context,
|
||||
app *models.App,
|
||||
deployment *models.Deployment,
|
||||
imageID string,
|
||||
imageID domain.ImageID,
|
||||
) error {
|
||||
if !errors.Is(deployCtx.Err(), context.Canceled) {
|
||||
return nil
|
||||
@@ -687,7 +688,7 @@ func (svc *Service) cleanupCancelledDeploy(
|
||||
ctx context.Context,
|
||||
app *models.App,
|
||||
deployment *models.Deployment,
|
||||
imageID string,
|
||||
imageID domain.ImageID,
|
||||
) {
|
||||
// Clean up the intermediate Docker image if one was built
|
||||
if imageID != "" {
|
||||
@@ -695,11 +696,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 +817,7 @@ func (svc *Service) buildImage(
|
||||
ctx context.Context,
|
||||
app *models.App,
|
||||
deployment *models.Deployment,
|
||||
) (string, error) {
|
||||
) (domain.ImageID, error) {
|
||||
workDir, cleanup, err := svc.cloneRepository(ctx, app, deployment)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -850,8 +851,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 +1010,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 domain.ImageID,
|
||||
) (domain.ContainerID, error) {
|
||||
containerOpts, err := svc.buildContainerOptions(ctx, app, imageID)
|
||||
if err != nil {
|
||||
svc.failDeployment(ctx, app, deployment, err)
|
||||
@@ -1038,8 +1039,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 +1063,7 @@ func (svc *Service) createAndStartContainer(
|
||||
func (svc *Service) buildContainerOptions(
|
||||
ctx context.Context,
|
||||
app *models.App,
|
||||
imageID string,
|
||||
imageID domain.ImageID,
|
||||
) (docker.CreateContainerOptions, error) {
|
||||
envVars, err := app.GetEnvVars(ctx)
|
||||
if err != nil {
|
||||
@@ -1096,7 +1097,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 +1147,9 @@ func buildPortMappings(ports []*models.Port) []docker.PortMapping {
|
||||
func (svc *Service) updateAppRunning(
|
||||
ctx context.Context,
|
||||
app *models.App,
|
||||
imageID string,
|
||||
imageID domain.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)
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"git.eeqj.de/sneak/upaas/internal/database"
|
||||
"git.eeqj.de/sneak/upaas/internal/domain"
|
||||
"git.eeqj.de/sneak/upaas/internal/models"
|
||||
"git.eeqj.de/sneak/upaas/internal/service/deploy"
|
||||
)
|
||||
@@ -27,14 +28,14 @@ func TestBuildContainerOptionsUsesImageID(t *testing.T) {
|
||||
log := slog.New(slog.NewTextHandler(os.Stderr, nil))
|
||||
svc := deploy.NewTestService(log)
|
||||
|
||||
const expectedImageID = "sha256:abc123def456"
|
||||
const expectedImageID = domain.ImageID("sha256:abc123def456")
|
||||
|
||||
opts, err := svc.BuildContainerOptionsExported(context.Background(), app, expectedImageID)
|
||||
if err != nil {
|
||||
t.Fatalf("buildContainerOptions returned error: %v", err)
|
||||
}
|
||||
|
||||
if opts.Image != expectedImageID {
|
||||
if opts.Image != string(expectedImageID) {
|
||||
t.Errorf("expected Image=%q, got %q", expectedImageID, opts.Image)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"git.eeqj.de/sneak/upaas/internal/config"
|
||||
"git.eeqj.de/sneak/upaas/internal/docker"
|
||||
"git.eeqj.de/sneak/upaas/internal/domain"
|
||||
"git.eeqj.de/sneak/upaas/internal/models"
|
||||
)
|
||||
|
||||
@@ -86,7 +87,7 @@ func (svc *Service) GetBuildDirExported(appName string) string {
|
||||
func (svc *Service) BuildContainerOptionsExported(
|
||||
ctx context.Context,
|
||||
app *models.App,
|
||||
imageID string,
|
||||
imageID domain.ImageID,
|
||||
) (docker.CreateContainerOptions, error) {
|
||||
return svc.buildContainerOptions(ctx, app, imageID)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"go.uber.org/fx"
|
||||
|
||||
"git.eeqj.de/sneak/upaas/internal/database"
|
||||
"git.eeqj.de/sneak/upaas/internal/domain"
|
||||
"git.eeqj.de/sneak/upaas/internal/logger"
|
||||
"git.eeqj.de/sneak/upaas/internal/models"
|
||||
"git.eeqj.de/sneak/upaas/internal/service/deploy"
|
||||
@@ -47,24 +48,24 @@ func New(_ fx.Lifecycle, params ServiceParams) (*Service, error) {
|
||||
//
|
||||
//nolint:tagliatelle // Field names match Gitea API (snake_case)
|
||||
type GiteaPushPayload struct {
|
||||
Ref string `json:"ref"`
|
||||
Before string `json:"before"`
|
||||
After string `json:"after"`
|
||||
CompareURL string `json:"compare_url"`
|
||||
Ref string `json:"ref"`
|
||||
Before string `json:"before"`
|
||||
After string `json:"after"`
|
||||
CompareURL domain.UnparsedURL `json:"compare_url"`
|
||||
Repository struct {
|
||||
FullName string `json:"full_name"`
|
||||
CloneURL string `json:"clone_url"`
|
||||
SSHURL string `json:"ssh_url"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
FullName string `json:"full_name"`
|
||||
CloneURL domain.UnparsedURL `json:"clone_url"`
|
||||
SSHURL string `json:"ssh_url"`
|
||||
HTMLURL domain.UnparsedURL `json:"html_url"`
|
||||
} `json:"repository"`
|
||||
Pusher struct {
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
} `json:"pusher"`
|
||||
Commits []struct {
|
||||
ID string `json:"id"`
|
||||
URL string `json:"url"`
|
||||
Message string `json:"message"`
|
||||
ID string `json:"id"`
|
||||
URL domain.UnparsedURL `json:"url"`
|
||||
Message string `json:"message"`
|
||||
Author struct {
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
@@ -104,7 +105,7 @@ func (svc *Service) HandleWebhook(
|
||||
event.EventType = eventType
|
||||
event.Branch = branch
|
||||
event.CommitSHA = sql.NullString{String: commitSHA, Valid: commitSHA != ""}
|
||||
event.CommitURL = sql.NullString{String: commitURL, Valid: commitURL != ""}
|
||||
event.CommitURL = sql.NullString{String: string(commitURL), Valid: commitURL != ""}
|
||||
event.Payload = sql.NullString{String: string(payload), Valid: true}
|
||||
event.Matched = matched
|
||||
event.Processed = false
|
||||
@@ -168,7 +169,7 @@ func extractBranch(ref string) string {
|
||||
|
||||
// extractCommitURL extracts the commit URL from the webhook payload.
|
||||
// Prefers the URL from the head commit, falls back to constructing from repo URL.
|
||||
func extractCommitURL(payload GiteaPushPayload) string {
|
||||
func extractCommitURL(payload GiteaPushPayload) domain.UnparsedURL {
|
||||
// Try to find the URL from the head commit (matching After SHA)
|
||||
for _, commit := range payload.Commits {
|
||||
if commit.ID == payload.After && commit.URL != "" {
|
||||
@@ -178,7 +179,7 @@ func extractCommitURL(payload GiteaPushPayload) string {
|
||||
|
||||
// Fall back to constructing URL from repo HTML URL
|
||||
if payload.Repository.HTMLURL != "" && payload.After != "" {
|
||||
return payload.Repository.HTMLURL + "/commit/" + payload.After
|
||||
return domain.UnparsedURL(string(payload.Repository.HTMLURL) + "/commit/" + payload.After)
|
||||
}
|
||||
|
||||
return ""
|
||||
|
||||
Reference in New Issue
Block a user