chore: fix build dependencies and frontend config

Migrate Docker SDK from github.com/docker/docker (+incompatible)
to github.com/moby/moby/client v0.3.0 + moby/moby/api v1.54.0
(proper Go modules). Adapt all container/image/network operations
to the new moby API (Filters, ContainerListOptions, PullResponse,
InspectResult, etc.). Add AsyncTriggerDeploy runDeploy method.

Fix SvelteKit build: disable prerender, set strict=false for SPA,
bump vite-plugin-svelte to v5 for vite 6 compat.

Add .dockerignore to exclude .git, node_modules, plans.
This commit is contained in:
2026-03-28 13:13:45 +03:00
parent 179be231c2
commit 652229c67f
9 changed files with 301 additions and 82 deletions
+37 -25
View File
@@ -7,11 +7,10 @@ import (
"strconv"
"strings"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/network"
"github.com/docker/go-connections/nat"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/mount"
"github.com/moby/moby/api/types/network"
"github.com/moby/moby/client"
)
// ContainerConfig holds all parameters needed to create a managed container.
@@ -69,13 +68,16 @@ func ContainerName(project, stage, tag string) string {
// It returns the container ID on success.
func (c *Client) CreateContainer(ctx context.Context, cfg ContainerConfig) (string, error) {
// Build port bindings: each exposed port maps to a random host port.
exposedPorts := nat.PortSet{}
portBindings := nat.PortMap{}
exposedPorts := network.PortSet{}
portBindings := network.PortMap{}
for _, p := range cfg.ExposedPorts {
port := nat.Port(p)
port, err := network.ParsePort(p)
if err != nil {
return "", fmt.Errorf("parse port %s: %w", p, err)
}
exposedPorts[port] = struct{}{}
portBindings[port] = []nat.PortBinding{
{HostIP: "0.0.0.0", HostPort: ""}, // empty HostPort = auto-assign
portBindings[port] = []network.PortBinding{
{HostPort: ""}, // empty HostPort = auto-assign
}
}
@@ -113,7 +115,12 @@ func (c *Client) CreateContainer(ctx context.Context, cfg ContainerConfig) (stri
}
}
resp, err := c.api.ContainerCreate(ctx, containerCfg, hostCfg, networkCfg, nil, cfg.Name)
resp, err := c.api.ContainerCreate(ctx, client.ContainerCreateOptions{
Config: containerCfg,
HostConfig: hostCfg,
NetworkingConfig: networkCfg,
Name: cfg.Name,
})
if err != nil {
return "", fmt.Errorf("create container %s: %w", cfg.Name, err)
}
@@ -123,7 +130,7 @@ func (c *Client) CreateContainer(ctx context.Context, cfg ContainerConfig) (stri
// StartContainer starts a stopped container.
func (c *Client) StartContainer(ctx context.Context, containerID string) error {
if err := c.api.ContainerStart(ctx, containerID, container.StartOptions{}); err != nil {
if _, err := c.api.ContainerStart(ctx, containerID, client.ContainerStartOptions{}); err != nil {
return fmt.Errorf("start container %s: %w", containerID, err)
}
return nil
@@ -132,12 +139,12 @@ func (c *Client) StartContainer(ctx context.Context, containerID string) error {
// StopContainer gracefully stops a running container with the given timeout in seconds.
// A nil timeout uses the Docker default (10 seconds).
func (c *Client) StopContainer(ctx context.Context, containerID string, timeoutSeconds int) error {
opts := container.StopOptions{}
opts := client.ContainerStopOptions{}
if timeoutSeconds > 0 {
opts.Timeout = &timeoutSeconds
}
if err := c.api.ContainerStop(ctx, containerID, opts); err != nil {
if _, err := c.api.ContainerStop(ctx, containerID, opts); err != nil {
return fmt.Errorf("stop container %s: %w", containerID, err)
}
return nil
@@ -146,12 +153,12 @@ func (c *Client) StopContainer(ctx context.Context, containerID string, timeoutS
// RemoveContainer removes a container. If force is true, a running container
// will be killed before removal.
func (c *Client) RemoveContainer(ctx context.Context, containerID string, force bool) error {
opts := container.RemoveOptions{
opts := client.ContainerRemoveOptions{
Force: force,
RemoveVolumes: true,
}
if err := c.api.ContainerRemove(ctx, containerID, opts); err != nil {
if _, err := c.api.ContainerRemove(ctx, containerID, opts); err != nil {
return fmt.Errorf("remove container %s: %w", containerID, err)
}
return nil
@@ -159,12 +166,12 @@ func (c *Client) RemoveContainer(ctx context.Context, containerID string, force
// RestartContainer restarts a container with the given timeout in seconds.
func (c *Client) RestartContainer(ctx context.Context, containerID string, timeoutSeconds int) error {
opts := container.StopOptions{}
opts := client.ContainerRestartOptions{}
if timeoutSeconds > 0 {
opts.Timeout = &timeoutSeconds
}
if err := c.api.ContainerRestart(ctx, containerID, opts); err != nil {
if _, err := c.api.ContainerRestart(ctx, containerID, opts); err != nil {
return fmt.Errorf("restart container %s: %w", containerID, err)
}
return nil
@@ -187,7 +194,7 @@ type ManagedContainer struct {
// Pass nil or an empty map to list all docker-watcher managed containers.
// Label filters are key=value pairs applied as Docker label filters.
func (c *Client) ListContainers(ctx context.Context, labelFilters map[string]string) ([]ManagedContainer, error) {
filterArgs := filters.NewArgs()
filterArgs := make(client.Filters)
// Always filter by the docker-watcher project label to only return managed containers.
filterArgs.Add("label", LabelProject)
@@ -200,7 +207,7 @@ func (c *Client) ListContainers(ctx context.Context, labelFilters map[string]str
}
}
containers, err := c.api.ContainerList(ctx, container.ListOptions{
listResult, err := c.api.ContainerList(ctx, client.ContainerListOptions{
All: true,
Filters: filterArgs,
})
@@ -208,8 +215,8 @@ func (c *Client) ListContainers(ctx context.Context, labelFilters map[string]str
return nil, fmt.Errorf("list containers: %w", err)
}
result := make([]ManagedContainer, 0, len(containers))
for _, ctr := range containers {
result := make([]ManagedContainer, 0, len(listResult.Items))
for _, ctr := range listResult.Items {
name := ""
if len(ctr.Names) > 0 {
// Docker prefixes names with "/".
@@ -228,7 +235,7 @@ func (c *Client) ListContainers(ctx context.Context, labelFilters map[string]str
Name: name,
Image: ctr.Image,
Status: ctr.Status,
State: ctr.State,
State: string(ctr.State),
Project: ctr.Labels[LabelProject],
Stage: ctr.Labels[LabelStage],
InstanceID: ctr.Labels[LabelInstanceID],
@@ -242,12 +249,17 @@ func (c *Client) ListContainers(ctx context.Context, labelFilters map[string]str
// InspectContainerPort returns the host port mapped to a given container port.
// This is useful after starting a container with auto-assigned ports.
func (c *Client) InspectContainerPort(ctx context.Context, containerID string, containerPort string) (uint16, error) {
inspect, err := c.api.ContainerInspect(ctx, containerID)
inspectResult, err := c.api.ContainerInspect(ctx, containerID, client.ContainerInspectOptions{})
if err != nil {
return 0, fmt.Errorf("inspect container %s: %w", containerID, err)
}
inspect := inspectResult.Container
port, err := network.ParsePort(containerPort)
if err != nil {
return 0, fmt.Errorf("parse container port %s: %w", containerPort, err)
}
port := nat.Port(containerPort)
bindings, ok := inspect.NetworkSettings.Ports[port]
if !ok || len(bindings) == 0 {
return 0, fmt.Errorf("container %s: no binding for port %s", containerID, containerPort)