Skip to content
Merged
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
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,9 @@ into `templates/factory/code/pyproject.toml.tmpl` yourself.
(`RECIPE_INVENTORY_SCOPE_TREE`), the Dockerfile-only one claims just what it
emitted. Never rewrite `pyproject.toml` or `uv.lock` to make a path resolve.
- **The runtime image is pinned.** `docker-image` rejects a bare name and
`:latest`. The default `codeflydev/python` companion is built by core and
repinned on release — prefer it.
`:latest`. The default `ghcr.io/codefly-dev/python` companion is built from
core and pinned by the digest of its multi-platform index — prefer it. Its tag
must equal the linked core's companion version (test-enforced).
- **A user's `flake.nix` is never overwritten.** The embedded flake is written
only when the source dir has none.
- **Release needs cgo.** The binary links core's tree-sitter grammars, so
Expand Down
35 changes: 27 additions & 8 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,39 @@ type Settings struct {

// RuntimeImage overrides the default codefly-built runtime image.
// Format: "name:tag". Plain "name" and ":latest" are rejected —
// pinning is enforced. Leave empty to use codeflydev/python:<ver>
// pinning is enforced. Leave empty to use ghcr.io/codefly-dev/python:<ver>
// (recommended; companion is rebuilt + pinned on every codefly
// release). Field named RuntimeImage (not DockerImage) to avoid
// colliding with services.Base.DockerImage(req).
RuntimeImage string `yaml:"docker-image"`
}

// runtimeImage is the codefly-built Python runtime companion —
// python:3.13.1-alpine3.21 + codefly CLI + uv 0.5.29. Built from
// core/companions/python/. Users can override via the Python settings
// (DockerImage field) but NOT recommended — the companion image is
// the mode-consistent default and gets the same tool set as every
// other codefly-built image.
var runtimeImage = &resources.DockerImage{Name: "codeflydev/python", Tag: "0.0.1"}
// runtimeImage is the codefly-built Python runtime companion, built from
// core/companions/python/ and published by the CLI to ghcr.io/codefly-dev as
// a linux/amd64 + linux/arm64 image index. Users can override it through the
// Python settings (docker-image) but should not: the companion is the
// mode-consistent default and carries the same tool set as every other
// codefly-built image.
//
// It is pinned by the digest of the multi-platform INDEX, never of one
// platform's manifest: the build recipe is rendered for every target platform
// and each resolves its own entry from the index. The legacy
// docker.io/codeflydev/python:0.0.1 this replaces was a single amd64 manifest,
// so every linux/arm64 image build ran amd64 binaries under emulation and died
// on its first RUN ("Invalid ELF image for this architecture").
//
// Tag is the companion version of the core this agent links
// (core/companions/python/info.codefly.yaml); a test holds the two together,
// so bumping core without re-pinning here fails the build. Re-pin the digest
// from the registry when the tag moves:
//
// docker buildx imagetools inspect ghcr.io/codefly-dev/python:<tag>
var runtimeImage = &resources.DockerImage{
Repository: resources.ImageRegistry,
Name: "python",
Tag: "0.0.5",
Digest: "sha256:67d6fe825765fc30f94131ee551e3800f5b6fa4e07094b47d036904387cb75a7",
}

// uvVersion is the uv the emitted build recipe resolves its lockfile with.
//
Expand Down
33 changes: 33 additions & 0 deletions runtime_image_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package main

import (
"context"
"regexp"
"testing"

"github.com/codefly-dev/core/companions/python"
"github.com/codefly-dev/core/resources"
"github.com/stretchr/testify/require"
)

// The default runtime image is the Python companion of the core this agent
// links, from the registry codefly publishes to. A core bump that moves the
// companion version fails here until the pin, and its digest, move with it.
func TestRuntimeImageIsTheLinkedCoreCompanion(t *testing.T) {
companion, err := python.CompanionImage(context.Background())
require.NoError(t, err)
require.Equal(t, companion.Repository, runtimeImage.Repository)
require.Equal(t, companion.Name, runtimeImage.Name)
require.Equal(t, companion.Tag, runtimeImage.Tag,
"re-pin runtimeImage (tag and index digest) to core's python companion")
}

// A tag alone lets the registry move the image under a built recipe, and a
// platform manifest digest would pin every target platform to one
// architecture. The pin is a digest; that it names the multi-platform index is
// what the comment on runtimeImage records and a reviewer checks.
func TestRuntimeImageIsDigestPinned(t *testing.T) {
require.Regexp(t, regexp.MustCompile(`^sha256:[0-9a-f]{64}$`), runtimeImage.Digest)
require.Equal(t, resources.ImageRegistry+"/python@"+runtimeImage.Digest, runtimeImage.FullName(),
"the recipe's FROM must resolve by digest")
}
Loading