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 pkg/cli/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func serveBuildOptions(cmd *cobra.Command) model.BuildOptions {
UseCogBaseImage: DetermineUseCogBaseImage(cmd),
ProgressOutput: buildProgressOutput,
ExcludeSource: true,
SkipLabels: true,
BaseImageOnly: true,
}
}

Expand Down
7 changes: 7 additions & 0 deletions pkg/config/image_name.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,10 @@ func DockerImageName(projectDir string) string {

return projectName
}

// BaseDockerImageName returns the Docker image name for base images
// used by dev-mode commands (cog serve, predict, run, train).
// These are tagged with "-base" to avoid overwriting images built by "cog build".
func BaseDockerImageName(projectDir string) string {
return DockerImageName(projectDir) + "-base"
}
7 changes: 7 additions & 0 deletions pkg/config/image_name_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,10 @@ func TestDockerImageName(t *testing.T) {
require.Equal(t, "cog-my-great-model", DockerImageName("/home/joe/my great model"))
require.Equal(t, 30, len(DockerImageName("/home/joe/verylongverylongverylongverylongverylongverylongverylong")))
}

func TestBaseDockerImageName(t *testing.T) {
require.Equal(t, "cog-foo-base", BaseDockerImageName("/home/joe/foo"))
require.Equal(t, "cog-foo-base", BaseDockerImageName("/home/joe/Foo"))
require.Equal(t, "cog-foo-base", BaseDockerImageName("/home/joe/cog-foo"))
require.Equal(t, "cog-my-great-model-base", BaseDockerImageName("/home/joe/my great model"))
}
11 changes: 10 additions & 1 deletion pkg/image/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
precompile bool,
excludeSource bool,
skipSchemaValidation bool,
skipLabels bool,
baseImageOnly bool,
annotations map[string]string,
dockerCommand command.Command,
client registry.Client) (string, error) {
Expand Down Expand Up @@ -104,7 +104,7 @@
// schema generation which can handle types that require Python import
// (e.g. package __init__.py modules, pydantic v2 BaseModel subclasses).
var se *schema.SchemaError
if !skipLabels && errors.As(err, &se) && se.Kind == schema.ErrUnresolvableType {

Check failure on line 107 in pkg/image/build.go

View workflow job for this annotation

GitHub Actions / Test Go (macos-latest)

undefined: skipLabels

Check failure on line 107 in pkg/image/build.go

View workflow job for this annotation

GitHub Actions / Test Go (ubuntu-latest)

undefined: skipLabels

Check failure on line 107 in pkg/image/build.go

View workflow job for this annotation

GitHub Actions / Lint Go

undefined: skipLabels
console.Warnf("Static schema generation failed: %s", err)
console.Warn("Falling back to legacy runtime schema generation...")
// leave schemaJSON nil — the post-build legacy path will handle it
Expand Down Expand Up @@ -258,6 +258,15 @@
}
}

// When baseImageOnly is true (cog run/predict/serve/train), skip the expensive
// label-adding phase. This image is for local use only and won't be distributed,
// so we don't need metadata labels, pip freeze, schema bundling, or git info.
if baseImageOnly {
return tmpImageId, nil
}

console.Info("")

// --- Post-build legacy schema generation ---
// For SDK < 0.17.0 (or when static gen was not used), generate the schema
// by running the built image with python -m cog.command.openapi_schema.
Expand Down Expand Up @@ -291,7 +300,7 @@
// label-adding phase. This image is for local use only and won't be distributed,
// so we don't need metadata labels, pip freeze, or git info.
// We still need the schema bundled, so do a minimal second build to add it.
if skipLabels {

Check failure on line 303 in pkg/image/build.go

View workflow job for this annotation

GitHub Actions / Test Go (macos-latest)

undefined: skipLabels

Check failure on line 303 in pkg/image/build.go

View workflow job for this annotation

GitHub Actions / Test Go (ubuntu-latest)

undefined: skipLabels

Check failure on line 303 in pkg/image/build.go

View workflow job for this annotation

GitHub Actions / Lint Go

undefined: skipLabels (typecheck)
if len(schemaJSON) > 0 {
// Use trailing "/" on the destination so Docker creates the .cog/
// directory even in ExcludeSource images where COPY . /src was
Expand Down
2 changes: 1 addition & 1 deletion pkg/model/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (f *DockerfileFactory) Build(ctx context.Context, src *Source, opts BuildOp
opts.Precompile,
opts.ExcludeSource,
opts.SkipSchemaValidation,
opts.SkipLabels,
opts.BaseImageOnly,
opts.Annotations,
f.docker,
f.registry,
Expand Down
20 changes: 14 additions & 6 deletions pkg/model/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,26 @@ type BuildOptions struct {
// a predictor or trainer defined in cog.yaml.
SkipSchemaValidation bool

// SkipLabels skips adding Cog metadata labels to the built image.
// Used by `cog run`, `cog predict`, `cog serve`, and `cog train` where
// the image is for local use only and not being distributed.
SkipLabels bool
// BaseImageOnly builds only the base image without Cog metadata labels.
// Used by dev-mode commands (`cog run`, `cog predict`, `cog serve`,
// `cog train`) where the image is ephemeral and for local use only.
// When true, the default image name gets a "-base" suffix to avoid
// overwriting images built by `cog build`.
BaseImageOnly bool
}

// WithDefaults returns a copy of BuildOptions with defaults applied from Source.
// This fills in sensible defaults for any unset fields.
func (o BuildOptions) WithDefaults(src *Source) BuildOptions {
// Default image name from project directory
// Default image name from project directory.
// Dev-mode builds (BaseImageOnly: serve, predict, run, train) use a
// "-base" suffix so they don't overwrite images built by "cog build".
if o.ImageName == "" {
o.ImageName = config.DockerImageName(src.ProjectDir)
if o.BaseImageOnly {
o.ImageName = config.BaseDockerImageName(src.ProjectDir)
} else {
o.ImageName = config.DockerImageName(src.ProjectDir)
}
}

// Default progress output
Expand Down
26 changes: 26 additions & 0 deletions pkg/model/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,32 @@ func TestBuildOptions_AllFieldsPreserved(t *testing.T) {
require.Equal(t, "/path/to/weights.lock", result.WeightsLockPath)
}

func TestBuildOptions_WithDefaults_DevModeUsesBaseSuffix(t *testing.T) {
src := &Source{
Config: &config.Config{Build: &config.Build{}},
ProjectDir: "/path/to/my-project",
}

// Dev-mode builds (BaseImageOnly=true) should get "-base" suffix
opts := BuildOptions{BaseImageOnly: true}
opts = opts.WithDefaults(src)

require.Equal(t, "cog-my-project-base", opts.ImageName)
}

func TestBuildOptions_WithDefaults_DevModePreservesExplicitImageName(t *testing.T) {
src := &Source{
Config: &config.Config{Build: &config.Build{}},
ProjectDir: "/path/to/my-project",
}

// Explicit image name should be preserved even in dev mode
opts := BuildOptions{ImageName: "my-custom-image", BaseImageOnly: true}
opts = opts.WithDefaults(src)

require.Equal(t, "my-custom-image", opts.ImageName)
}

func TestBuildOptions_WeightsLockPath(t *testing.T) {
opts := BuildOptions{
WeightsLockPath: "/custom/weights.lock",
Expand Down
Loading