791cd4d6af
Build / build (push) Successful in 12m20s
Rebrand the project as Tinyforge to reflect its evolution from a Docker container watcher into a self-hosted mini CI/deployment platform. Rename covers: Go module path, Docker labels, DB/config filenames, JWT issuer, Dockerfile binary, docker-compose, CI workflows, frontend i18n, README with static sites docs, and all code comments.
92 lines
2.4 KiB
Go
92 lines
2.4 KiB
Go
package webhook
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log/slog"
|
|
"strings"
|
|
|
|
"github.com/alexei/tinyforge/internal/docker"
|
|
"github.com/alexei/tinyforge/internal/store"
|
|
)
|
|
|
|
// AutoCreateProject creates a new project and a default "dev" stage from an
|
|
// unknown image. It inspects the Docker image to extract defaults (EXPOSE port,
|
|
// healthcheck, labels).
|
|
//
|
|
// The auto-created project uses:
|
|
// - Name: derived from image name (e.g. "web-app-launcher")
|
|
// - Image: full owner/name path
|
|
// - Port: first EXPOSE port from the image, or 0 if none
|
|
// - Healthcheck: from image HEALTHCHECK instruction, if present
|
|
// - A single "dev" stage with auto_deploy=true and tag_pattern="*"
|
|
func AutoCreateProject(
|
|
ctx context.Context,
|
|
st *store.Store,
|
|
inspector ImageInspector,
|
|
parsed ParsedImage,
|
|
) (store.Project, store.Stage, error) {
|
|
// Build the full image ref for inspection (registry/owner/name:tag).
|
|
imageRef := buildImageRef(parsed)
|
|
|
|
var port int
|
|
var healthcheck string
|
|
|
|
// Attempt to inspect the image for metadata. If inspection fails (image
|
|
// not pulled locally), proceed with zero defaults.
|
|
if inspector != nil {
|
|
info, err := inspector.InspectImage(ctx, imageRef)
|
|
if err != nil {
|
|
slog.Warn("webhook: image inspection failed, using defaults", "image", imageRef, "error", err)
|
|
} else {
|
|
port = docker.ExtractPort(info.ExposedPorts)
|
|
healthcheck = info.Healthcheck
|
|
}
|
|
}
|
|
|
|
project, err := st.CreateProject(store.Project{
|
|
Name: parsed.Name,
|
|
Registry: parsed.Registry,
|
|
Image: parsed.FullName(),
|
|
Port: port,
|
|
Healthcheck: healthcheck,
|
|
Env: "{}",
|
|
Volumes: "{}",
|
|
})
|
|
if err != nil {
|
|
return store.Project{}, store.Stage{}, fmt.Errorf("create project: %w", err)
|
|
}
|
|
|
|
stage, err := st.CreateStage(store.Stage{
|
|
ProjectID: project.ID,
|
|
Name: "dev",
|
|
TagPattern: "*",
|
|
AutoDeploy: true,
|
|
MaxInstances: 1,
|
|
})
|
|
if err != nil {
|
|
return store.Project{}, store.Stage{}, fmt.Errorf("create default stage: %w", err)
|
|
}
|
|
|
|
return project, stage, nil
|
|
}
|
|
|
|
// buildImageRef reconstructs a pullable image reference from parsed components.
|
|
func buildImageRef(parsed ParsedImage) string {
|
|
var parts []string
|
|
if parsed.Registry != "" {
|
|
parts = append(parts, parsed.Registry)
|
|
}
|
|
if parsed.Owner != "" {
|
|
parts = append(parts, parsed.Owner)
|
|
}
|
|
parts = append(parts, parsed.Name)
|
|
|
|
ref := strings.Join(parts, "/")
|
|
if parsed.Tag != "" {
|
|
ref += ":" + parsed.Tag
|
|
}
|
|
return ref
|
|
}
|
|
|