Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion architecture/05-build-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ Resolution follows a 3-tier priority for each wheel:
| 2. Auto-detect `dist/coglet-*.whl` | Dev builds only |
| 3. Default | Install from PyPI |

Local wheel files are copied into `.cog/tmp/` in the Docker build context, then `COPY`'d and `pip install`'d in the Dockerfile.
Local wheel files are copied into `.cog/build/` and referenced via the `cog_build` named build context, then `COPY --from=cog_build`'d and `pip install`'d in the Dockerfile.

---

Expand Down
3 changes: 1 addition & 2 deletions architecture/06-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,7 @@ Commands delegate to packages under `pkg/`:

**Utilities:**

- `pkg/dockercontext/` -- Docker build context directory management
- `pkg/dockerignore/` -- `.dockerignore` parsing
- `pkg/dotcog/` -- `.cog/` project state directory (path accessors, advisory lock, cleanup)
- `pkg/requirements/` -- `requirements.txt` parsing
- `pkg/env/` -- `R8_*` environment variable config
- `pkg/update/` -- CLI version update checker
Expand Down
6 changes: 0 additions & 6 deletions examples/managed-weights/.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,5 @@
# to the docker daemon on every build.
weights/

# Packed layer cache written by `cog weights build`. Do NOT exclude all of
# .cog/ — `cog build` stages the SDK + coglet wheels and CA cert under
# .cog/tmp/ and references them from its generated Dockerfile, so excluding
# the whole directory breaks the image build.
.cog/weights-cache/

# Git metadata doesn't belong in the image.
.git/
3 changes: 0 additions & 3 deletions examples/resnet/.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,5 @@
# image index — they must NOT be baked into the model image by `cog build`.
weights/

# Packed layer cache written by `cog weights build`.
.cog/weights-cache/

# Git metadata doesn't belong in the image.
.git/
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ require (
github.com/stretchr/testify v1.11.1
github.com/testcontainers/testcontainers-go v0.41.0
github.com/testcontainers/testcontainers-go/modules/registry v0.41.0
github.com/tonistiigi/fsutil v0.0.0-20251211185533-a2aa163d723f
github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0
github.com/vincent-petithory/dataurl v1.0.0
github.com/xeipuuv/gojsonschema v1.2.0
Expand Down Expand Up @@ -122,7 +123,6 @@ require (
github.com/stretchr/objx v0.5.2 // indirect
github.com/tklauser/go-sysconf v0.3.16 // indirect
github.com/tklauser/numcpus v0.11.0 // indirect
github.com/tonistiigi/fsutil v0.0.0-20251211185533-a2aa163d723f // indirect
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect
github.com/vbatts/tar-split v0.12.2 // indirect
Expand Down
20 changes: 0 additions & 20 deletions integration-tests/tests/bad_dockerignore.txtar

This file was deleted.

4 changes: 2 additions & 2 deletions pkg/cli/baseimage.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/replicate/cog/pkg/config"
"github.com/replicate/cog/pkg/docker"
"github.com/replicate/cog/pkg/docker/command"
"github.com/replicate/cog/pkg/dockercontext"

"github.com/replicate/cog/pkg/dockerfile"
"github.com/replicate/cog/pkg/global"
"github.com/replicate/cog/pkg/registry"
Expand Down Expand Up @@ -187,7 +187,7 @@ func newBaseImageBuildCommand() *cobra.Command {
NoCache: buildNoCache,
ProgressOutput: buildProgressOutput,
Epoch: &config.BuildSourceEpochTimestamp,
ContextDir: dockercontext.StandardBuildDirectory,
ContextDir: ".",
}
if _, err := dockerClient.ImageBuild(ctx, buildOpts); err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions pkg/cli/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ func buildCommand(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
defer src.Close()

if err := weights.CheckDrift(src.ProjectDir, src.Config.Weights); err != nil {
return err
Expand Down
47 changes: 11 additions & 36 deletions pkg/cli/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ package cli

import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/spf13/cobra"

"github.com/replicate/cog/pkg/config"
"github.com/replicate/cog/pkg/docker"
"github.com/replicate/cog/pkg/dockerfile"
"github.com/replicate/cog/pkg/model"
"github.com/replicate/cog/pkg/registry"
"github.com/replicate/cog/pkg/util/console"
)
Expand Down Expand Up @@ -38,52 +38,27 @@ func newDebugCommand() *cobra.Command {
func cmdDockerfile(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()

// Find the root project directory
rootDir, err := config.GetProjectDir(configFilename)
src, err := model.NewSource(configFilename)
if err != nil {
return err
}
defer src.Close()

configPath := filepath.Join(rootDir, configFilename)

f, err := os.Open(configPath)
if err != nil {
return &config.ParseError{Filename: configFilename, Err: err}
}

result, err := config.Load(f, rootDir)
dockerClient, err := docker.NewClient(ctx)
if err != nil {
_ = f.Close()
return err
}

_ = f.Close()

var (
cfg = result.Config
projectDir = result.RootDir
)

// Display any deprecation warnings
for _, w := range result.Warnings {
console.Warnf("%s", w.Error())
}

dockerClient, err := docker.NewClient(ctx)
buildDir, err := src.DotCog.TempPath("build")
if err != nil {
return err
}

client := registry.NewRegistryClient()
generator, err := dockerfile.NewGenerator(cfg, projectDir, configFilename, dockerClient, client, true)
generator, err := dockerfile.NewStandardGenerator(src.Config, src.ProjectDir, buildDir, src.ConfigFilename, dockerClient, client, true)
if err != nil {
return fmt.Errorf("Error creating Dockerfile generator: %w", err)
}
defer func() {
if err := generator.Cleanup(); err != nil {
console.Warnf("Error cleaning up after build: %v", err)
}
}()

generator.SetUseCudaBaseImage(buildUseCudaBaseImage)
useCogBaseImage := DetermineUseCogBaseImage(cmd)
Expand All @@ -93,17 +68,17 @@ func cmdDockerfile(cmd *cobra.Command, args []string) error {

if buildSeparateWeights {
if imageName == "" {
imageName = config.DockerImageName(projectDir)
imageName = config.DockerImageName(src.ProjectDir)
}

weightsDockerfile, RunnerDockerfile, dockerignore, err := generator.GenerateModelBaseWithSeparateWeights(ctx, imageName)
weightsDockerfile, runnerDockerfile, weightsExclude, err := generator.GenerateModelBaseWithSeparateWeights(ctx, imageName)
if err != nil {
return err
}

console.Output(fmt.Sprintf("=== Weights Dockerfile contents:\n%s\n===\n", weightsDockerfile))
console.Output(fmt.Sprintf("=== Runner Dockerfile contents:\n%s\n===\n", RunnerDockerfile))
console.Output(fmt.Sprintf("=== DockerIgnore contents:\n%s===\n", dockerignore))
console.Output(fmt.Sprintf("=== Runner Dockerfile contents:\n%s\n===\n", runnerDockerfile))
console.Output(fmt.Sprintf("=== Weights exclude patterns:\n%s\n===\n", strings.Join(weightsExclude, "\n")))
} else {
dockerfile, err := generator.GenerateDockerfileWithoutSeparateWeights(ctx)
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions pkg/cli/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func execCmd(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
defer src.Close()

resolver := model.NewResolver(dockerClient, registry.NewRegistryClient())

Expand Down
1 change: 1 addition & 0 deletions pkg/cli/predict.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ func cmdPredict(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
defer src.Close()

if err := weights.CheckDrift(src.ProjectDir, src.Config.Weights); err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions pkg/cli/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func push(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
defer src.Close()

if err := weights.CheckDrift(src.ProjectDir, src.Config.Weights); err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions pkg/cli/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func cmdServe(cmd *cobra.Command, arg []string) error {
if err != nil {
return err
}
defer src.Close()

console.Info("Building Docker image from environment in cog.yaml...")
console.Info("")
Expand Down
1 change: 1 addition & 0 deletions pkg/cli/train.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ func cmdTrain(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
defer src.Close()

if err := weights.CheckDrift(src.ProjectDir, src.Config.Weights); err != nil {
return err
Expand Down
28 changes: 14 additions & 14 deletions pkg/cli/weights.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ func weightsImportCommand(cmd *cobra.Command, args []string, dryRun, verbose boo
if err != nil {
return fmt.Errorf("failed to read config: %w", err)
}
defer src.Close()

cfg := src.Config

Expand Down Expand Up @@ -127,22 +128,21 @@ func weightsImportCommand(cmd *cobra.Command, args []string, dryRun, verbose boo

console.Infof("Building %d weight(s)...", len(weightSpecs))

var artifacts []*model.WeightArtifact
if err := lockfile.WithLock(ctx, lockPath, func() error {
var buildErr error
artifacts, buildErr = buildWeightArtifactsFromPlans(ctx, builder, weightSpecs, plans)
if buildErr != nil {
return buildErr
}
// Prune using the full config so orphans clear even when only
// some weights are being imported.
if err := lockfile.PruneLockfile(lockPath, config.WeightNames(cfg.Weights)); err != nil {
return fmt.Errorf("prune lockfile: %w", err)
}
return nil
}); err != nil {
release, err := src.DotCog.Lock(ctx)
if err != nil {
return err
}
defer release()

artifacts, err := buildWeightArtifactsFromPlans(ctx, builder, weightSpecs, plans)
if err != nil {
return err
}
// Prune using the full config so orphans clear even when only
// some weights are being imported.
if err := lockfile.PruneLockfile(lockPath, config.WeightNames(cfg.Weights)); err != nil {
return fmt.Errorf("prune lockfile: %w", err)
}

for _, wa := range artifacts {
console.Infof(" %s -> %s (%d layer(s), %s)",
Expand Down
1 change: 1 addition & 0 deletions pkg/cli/weights_pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ func weightsPullCommand(cmd *cobra.Command, args []string, verbose bool, imageOv
if err != nil {
return fmt.Errorf("failed to read config: %w", err)
}
defer src.Close()

if len(src.Config.Weights) == 0 {
return fmt.Errorf("no weights defined in %s", configFilename)
Expand Down
1 change: 1 addition & 0 deletions pkg/cli/weights_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ func weightsStatusCommand(cmd *cobra.Command, jsonOutput, verbose bool) error {
if err != nil {
return fmt.Errorf("failed to read config: %w", err)
}
defer src.Close()

// Load lockfile — missing is fine (weights may not be built yet), but
// a present-but-corrupt file gets a warning so it doesn't fail silently.
Expand Down
52 changes: 46 additions & 6 deletions pkg/docker/buildkit.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/moby/buildkit/session/auth"
"github.com/moby/buildkit/session/secrets/secretsprovider"
"github.com/moby/buildkit/util/progress/progressui"
"github.com/tonistiigi/fsutil"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand Down Expand Up @@ -73,15 +74,50 @@ func solveOptFromImageOptions(buildDir string, opts command.ImageBuildOptions) (
frontendAttrs["build-arg:SOURCE_DATE_EPOCH"] = fmt.Sprintf("%d", *opts.Epoch)
}

// Use WorkingDir as context if ContextDir is relative to ensure consistency with CLI client
localMounts := make(map[string]fsutil.FS)

// Dockerfile mount — filtered to only the Dockerfile so that other
// files in the same directory (e.g. when DockerfileDir points at
// .cog/build/ alongside wheels and requirements) are not exposed
// through this mount.
dockerfileFS, err := fsutil.NewFS(filepath.Dir(dockerfilePath))
if err != nil {
return buildkitclient.SolveOpt{}, fmt.Errorf("create dockerfile fs: %w", err)
}
dockerfileFS, err = fsutil.NewFilterFS(dockerfileFS, &fsutil.FilterOpt{
IncludePatterns: []string{filepath.Base(dockerfilePath)},
})
if err != nil {
return buildkitclient.SolveOpt{}, fmt.Errorf("filter dockerfile fs: %w", err)
}
localMounts["dockerfile"] = dockerfileFS

// Context mount — optionally filtered by ExcludePatterns so callers
// can prevent large directories (e.g. .cog/) from being sent to the
// daemon without mutating .dockerignore on disk.

contextDir := opts.ContextDir

// Use WorkingDir as context if ContextDir is relative to ensure consistency with CLI client
if opts.WorkingDir != "" && !filepath.IsAbs(opts.ContextDir) {
contextDir = filepath.Join(opts.WorkingDir, opts.ContextDir)
}

localDirs := map[string]string{
"dockerfile": filepath.Dir(dockerfilePath),
"context": contextDir,
if contextDir != "" {
var contextFS fsutil.FS
contextFS, err = fsutil.NewFS(contextDir)
if err != nil {
return buildkitclient.SolveOpt{}, fmt.Errorf("create context fs: %w", err)
}
if len(opts.ExcludePatterns) > 0 {
contextFS, err = fsutil.NewFilterFS(contextFS, &fsutil.FilterOpt{
ExcludePatterns: opts.ExcludePatterns,
})
if err != nil {
return buildkitclient.SolveOpt{}, fmt.Errorf("create filtered context fs: %w", err)
}
}
localMounts["context"] = contextFS
}

// Add user-supplied build contexts, but don't overwrite 'dockerfile' or 'context'
Expand All @@ -90,7 +126,11 @@ func solveOptFromImageOptions(buildDir string, opts command.ImageBuildOptions) (
console.Warnf("build context name collision: %q", name)
continue
}
localDirs[name] = dir
bcFS, fsErr := fsutil.NewFS(dir)
if fsErr != nil {
return buildkitclient.SolveOpt{}, fmt.Errorf("create build context fs %q: %w", name, fsErr)
}
localMounts[name] = bcFS
// Tell the dockerfile frontend about this build context
frontendAttrs["context:"+name] = "local:" + name
}
Expand All @@ -108,7 +148,7 @@ func solveOptFromImageOptions(buildDir string, opts command.ImageBuildOptions) (
solveOpts := buildkitclient.SolveOpt{
Frontend: "dockerfile.v0",
FrontendAttrs: frontendAttrs,
LocalDirs: localDirs,
LocalMounts: localMounts,
// Docker Engine's worker only supports three exporters.
// "moby" exporter works best for cog, since we want to keep images in
// Docker Engine's image store. The others are exporting images to somewhere else.
Expand Down
Loading