mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-10-28 15:51:26 +00:00
feat: build-on-node — git checkouts, remote image builds, image transport (#60)
Git-sourced deploys never materialized a checkout: sourcePath was build_path resolved against the server cwd, so builds consumed the backend's own directory. Remote placement could only run registry-pulled images. - internal/source: per-deployment checkout (init/fetch/checkout) at branch or commit; GIT_ASKPASS keeps tokens out of argv, .git/config, and the build context; dumb-transport and SHA-refusal fallbacks; build_path validated inside the checkout. - BuildManager.PackageContext: produces the same tar context Build ships to the daemon — generated Dockerfile at tar root, .git excluded. Fixes nixpacks passing the Dockerfile path as content. - Agent artifact endpoint: GET /api/agents/artifacts/:id streams build contexts and docker-save tarballs behind agent-token auth; hex-validated ids, per-deployment cleanup, boot-time purge. - Agent commands: build_image (docker build -, --build-arg values via process env, not argv) and load_image (docker load for containr-* rollback tags no registry serves). - Deployment flow: remote git builds package context → artifact → node docker-builds the deployment tag; remote rollbacks of local tags export via SaveImage → artifact → node docker-loads. Verified end-to-end: agent-pinned git service built on the node (build_image → create_container → deployed) and rollback shipped a docker-save tarball (load_image → deployed). Artifact auth returns 401/404 correctly; workspaces and blobs are removed after deploy. Authored By: TDvorak <[email protected]>
This commit is contained in:
1 parent
d57ac72aa5
commit
3cc84553f4
18 files changed
+1020
-51
No files matched your search
@@ -539,12 +539,16 @@ func createBuildContext(sourcePath, dockerfileContent string) (io.ReadCloser, er
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add source files to tar
|
||||
// Add source files to tar. The checkout's .git metadata is never build
|
||||
// context — it bloats the tar and ships clone URLs to remote daemons.
|
||||
err = filepath.Walk(sourcePath, func(file string, fi os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fi.IsDir() {
|
||||
if fi.Name() == ".git" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ package build
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
@@ -92,6 +94,75 @@ func (bm *BuildManager) Build(ctx context.Context, req *types.BuildRequest) (*ty
|
||||
}
|
||||
}
|
||||
|
||||
// PackageContext produces the same build-context tar Build would hand the
|
||||
// local daemon, without building it. Node agents download the tar and run
|
||||
// docker build themselves — the Dockerfile lands at the tar root.
|
||||
func (bm *BuildManager) PackageContext(ctx context.Context, req *types.BuildRequest) (io.ReadCloser, error) {
|
||||
if req.BuildType == "" {
|
||||
detectedType, err := bm.DetectBuildType(ctx, req.SourcePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to detect build type: %w", err)
|
||||
}
|
||||
req.BuildType = string(detectedType)
|
||||
}
|
||||
|
||||
var dockerfileContent string
|
||||
switch BuildType(req.BuildType) {
|
||||
case BuildTypeDockerfile:
|
||||
name, err := bm.dockerfileBuilder.DetectDockerfile(ctx, req.SourcePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to detect Dockerfile: %w", err)
|
||||
}
|
||||
path := filepath.Join(req.SourcePath, name)
|
||||
if optimized, err := bm.dockerfileBuilder.OptimizeDockerfile(ctx, path); err == nil {
|
||||
path = optimized
|
||||
}
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read Dockerfile: %w", err)
|
||||
}
|
||||
dockerfileContent = string(data)
|
||||
case BuildTypeRailpack:
|
||||
if err := bm.railpackBuilder.DetectRailpack(ctx, req.SourcePath); err != nil {
|
||||
return nil, fmt.Errorf("Railpack cannot build this project: %w", err)
|
||||
}
|
||||
content, err := bm.railpackBuilder.generateDockerfile(ctx, req.SourcePath, &RailpackConfig{
|
||||
BuildCmd: req.BuildCommand,
|
||||
StartCmd: req.StartCommand,
|
||||
Env: req.Environment,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate Dockerfile with Railpack: %w", err)
|
||||
}
|
||||
dockerfileContent = content
|
||||
case BuildTypeNixpacks:
|
||||
plan, err := bm.nixpacksBuilder.GeneratePlan(ctx, req.SourcePath, &NixpacksConfig{
|
||||
BuildCmd: req.BuildCommand,
|
||||
StartCmd: req.StartCommand,
|
||||
Env: req.Environment,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate build plan: %w", err)
|
||||
}
|
||||
path, err := bm.nixpacksBuilder.generateDockerfile(plan)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate Dockerfile: %w", err)
|
||||
}
|
||||
defer os.Remove(path)
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read generated Dockerfile: %w", err)
|
||||
}
|
||||
dockerfileContent = string(data)
|
||||
case BuildTypeStatic:
|
||||
dockerfileContent = staticDockerfile
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported build type for remote packaging: %s", req.BuildType)
|
||||
}
|
||||
|
||||
return createBuildContext(req.SourcePath, dockerfileContent)
|
||||
}
|
||||
|
||||
// buildPrebuilt handles prebuilt image deployments
|
||||
func (bm *BuildManager) buildPrebuilt(ctx context.Context, req *types.BuildRequest) (*types.BuildResponse, error) {
|
||||
if req.PrebuiltImage == "" {
|
||||
|
||||
@@ -476,17 +476,22 @@ func (n *NixpacksBuilder) Build(ctx context.Context, req *types.BuildRequest) (*
|
||||
}
|
||||
|
||||
// Create temporary Dockerfile
|
||||
dockerfile, err := n.generateDockerfile(plan)
|
||||
dockerfilePath, err := n.generateDockerfile(plan)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate Dockerfile: %w", err)
|
||||
}
|
||||
defer os.Remove(dockerfile)
|
||||
defer os.Remove(dockerfilePath)
|
||||
|
||||
// Build Docker image
|
||||
imageName := fmt.Sprintf("%s:%s", req.ImageName, req.ImageTag)
|
||||
|
||||
// Create build context tar
|
||||
buildCtx, err := createBuildContext(req.SourcePath, dockerfile)
|
||||
// Create build context tar — createBuildContext takes the Dockerfile
|
||||
// contents, not its path.
|
||||
dockerfileData, err := os.ReadFile(dockerfilePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read generated Dockerfile: %w", err)
|
||||
}
|
||||
buildCtx, err := createBuildContext(req.SourcePath, string(dockerfileData))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create build context: %w", err)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user