Skip to content

Commit 7c59ddf

Browse files
refactor: own deployment contracts and configuration rendering in CLI (#766)
* fix: adopt core v0.3.42's coordinate contract Core replaced the cell-contract wrapper with codefly/coordinate/v1: the schema string changed, the `cell` field is gone, and the parser keeps KnownFields(true), so the superseded cell/v1 and cell/v2 descriptors now fail at parse rather than importing a wrong shape. `--cell-contract` stays as the former spelling for one release. Passing both spellings is refused rather than resolved to one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: deep-copy the environment service config and repin the matrix Core v0.3.42 adds Environment.ServiceConfig, the home for a coordinate's resolved non-secret values. cloneEnvironment enumerates the fields it copies, so a new pointer field is shared until it is named: concurrent flows would hand each other the same map and one flow's override would surface in another. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep an absent coordinate from stamping a dangling provenance label The coordinate is an optional provenance label in core's contract, and the cell identifier that used to sit beside it in these two messages is gone, so a producer that omits it left "imported from coordinate contract on <time>" in the user's workspace.codefly.yaml. cloneEnvironment guarded the services map on length rather than nil, so an explicitly empty map stayed a shared header and the first flow to add a service would contaminate the other. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: state what core v0.3.42 changed, not what a review concluded Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: copy every empty environment map and check the whole type graph cloneEnvironment guarded its maps on length, so an explicitly empty map stayed a shared header between flows. This is reachable from an ordinary workspace: "managed-services: {}" decodes to a non-nil empty map, and the first flow to add an entry contaminates the other. ManagedServices, ServiceSecrets.Services and the per-service RemoteKeys all had it. The canary that exists to catch exactly this only enumerated top-level Environment fields, so marking a field deep-copied stopped any inspection of what it contained -- EnvironmentServiceConfigMapping was never examined at all. It now walks the type graph and names each path, so a field added to a nested contract type fails it too. The two remaining length guards are slices, not maps. A zero-length slice can only alias through append when it carries spare capacity; yaml decodes an empty sequence at cap 0 and the sole allocation here takes an exact length, so neither is reachable. contractFlag now reports which spelling it read, so an empty --cell-contract no longer reports --coordinate-contract as required. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: pin the released core v0.3.42 tag Core's version-tag workflow cut v0.3.42 from version/info.codefly.yaml at 34ad95991f33, the commit this branch was built and tested against as a pseudo-version. The extracted modules are byte-identical and the go.sum go.mod hash is unchanged, so this relabels the dependency rather than moving it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs: finish renaming the contract in prose Three descriptions still called it a cell contract after the type, flag and command help moved to codefly/coordinate/v1, leaving the rename visibly half-applied in the command list and the publish guidance. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: fail environment import when readiness validation refuses the workspace PostImportValidate had no error return, so `codefly environment import` printed "Workspace is NOT ready" and still exited 0 — a CI step gating on the exit status proceeded on a workspace the CLI had just refused. Give the seam an error return and propagate it. The merged file is still written before validation runs, so the printed diagnostics can be read against it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: own deployment contracts and configuration rendering in CLI * fix(gitops): validate secret delivery and bind declared service identities The renderer trusted container names, unscoped ServiceAccounts and unchanged secretKeyRefs even when an overlay redirected the ExternalSecret. Bind through CODEFLY__SERVICE, validate namespace-scoped identity and the complete effective secret delivery specification, and pin Core's lossless YAML repair. Add regressions for scalar spellings, agent-owned container names, secret redirects, identity namespaces and preserving published output on rejection. Remove unrelated DNS-label and zero-port policies; retain the narrowing checks required by the moved code's G115 gate. * fix(ci): qualify runnable lifecycle with a protocol-advertising agent The prior source pin returns a nil AgentContract and is correctly rejected before Builder.Load. Consume the owner fix from runnable-python#11, tested through the real create/build/relocate/invoke lifecycle, without weakening host admission or changing required checks. --------- Co-authored-by: Antoine Toussaint <312536060+antoinetoussaint-byte@users.noreply.github.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent c4817dd commit 7c59ddf

108 files changed

Lines changed: 5545 additions & 755 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/runnable-native.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
2525
with:
2626
repository: codefly-dev/runnable-python
27-
ref: 207c50f5b11fc0d439e06eadf916ada7d8b2c2d7
27+
ref: cee8544982ae9e4589848eef806a1016d6744801
2828
path: .qualification-agent
2929
# The pinned agent requires Go 1.27.1; CLI's 1.27.0 minimum is lower.
3030
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0

‎AGENTS.md‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ new runbook (and its skill) whenever you do a multi-step operational task a seco
6767
### Extending the CLI
6868
- **Add a new command** (Cobra wiring, help, MCP exposure) → [docs/runbooks/add-a-command.md](docs/runbooks/add-a-command.md)
6969
- **Rebuild the CLI and agents from local source** → [docs/runbooks/update-agents.md](docs/runbooks/update-agents.md)
70-
- **Point a workspace at a cell** (import explicit `codefly/cell/v2` environment declarations) → [docs/commands.md#codefly-environment](docs/commands.md)
70+
- **Point a workspace at a coordinate** (import explicit `codefly/coordinate/v1` environment declarations) → [docs/commands.md#codefly-environment](docs/commands.md)
7171
- **Export a module's API contracts** → [docs/commands.md#generate-contracts](docs/commands.md#generate-contracts)
7272

7373
### Reference (deep dives, not step-by-step)
@@ -160,6 +160,11 @@ relative paths.
160160

161161
## Key Patterns & Rules
162162

163+
- **Deployment declarations belong to CLI.** `pkg/environments` owns the coordinate
164+
parser, cluster/registry/namespace, GitOps, ingress, quotas, service injection and
165+
identity attachments. Core owns runtime configuration and secrets; pass `Runtime()`
166+
to agents. Producers emit the CLI contract, never private inventories to translate.
167+
See [docs/configuration-contract.md](docs/configuration-contract.md).
163168
- **CLI ↔ Agent communication is ALWAYS gRPC.** Never import agent code, never call agent
164169
functions. The agent runs as a separate process. Clients: `runtimev0.RuntimeClient`,
165170
`builderv0.BuilderClient`, `agentv0.AgentClient`, `codev0.CodeClient`.

‎cmd/build/cache_test.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package build
22

33
import (
4+
"testing"
5+
46
"github.com/spf13/cobra"
57
"github.com/stretchr/testify/require"
6-
"testing"
78
)
89

910
func TestBuildCommandsExposeCachePolicy(t *testing.T) {

‎cmd/build/module.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/codefly-dev/cli/cmd/common"
88
"github.com/codefly-dev/cli/pkg/builder"
99
"github.com/codefly-dev/cli/pkg/cli"
10+
"github.com/codefly-dev/cli/pkg/environments"
1011
"github.com/codefly-dev/cli/pkg/orchestration"
1112
"github.com/codefly-dev/core/resources"
1213
"github.com/codefly-dev/core/services"
@@ -73,7 +74,7 @@ var ModuleCmd = &cobra.Command{
7374
},
7475
}
7576

76-
func buildOneService(ctx context.Context, workspace *resources.Workspace, module *resources.Module, name string, env *resources.Environment) error {
77+
func buildOneService(ctx context.Context, workspace *resources.Workspace, module *resources.Module, name string, env *environments.Environment) error {
7778
w := wool.Get(ctx).In("buildModule.buildOneService", wool.NameField(name))
7879

7980
service, err := module.LoadServiceFromName(ctx, name)

‎cmd/ci/cache_test.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package ci
22

33
import (
4+
"testing"
5+
46
"github.com/spf13/cobra"
57
"github.com/stretchr/testify/require"
6-
"testing"
78
)
89

910
func TestCIBuildPathsExposeCachePolicy(t *testing.T) {

‎cmd/ci/replay_test.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ import (
44
"context"
55
"encoding/json"
66
"errors"
7-
"github.com/codefly-dev/core/resources"
87
"net"
98
"os"
109
"path/filepath"
1110
"reflect"
1211
"strings"
1312
"testing"
13+
14+
"github.com/codefly-dev/core/resources"
1415
)
1516

1617
func TestReplayPreservesSelectionAndRejectsAlterations(t *testing.T) {

‎cmd/ci/test.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/codefly-dev/cli/cmd/common"
99
"github.com/codefly-dev/cli/pkg/cli"
10+
"github.com/codefly-dev/cli/pkg/environments"
1011
"github.com/codefly-dev/cli/pkg/orchestration"
1112
runtimev0 "github.com/codefly-dev/core/generated/go/codefly/services/runtime/v0"
1213
"github.com/codefly-dev/core/resources"
@@ -37,7 +38,7 @@ func testFlowOwner(flow *orchestration.Flow) flowStopper {
3738
return flow
3839
}
3940

40-
func testEnvironment(workspace *resources.Workspace, disposable bool) (*resources.Environment, error) {
41+
func testEnvironment(workspace *resources.Workspace, disposable bool) (*environments.Environment, error) {
4142
env, err := orchestration.SelectEnvironment(workspace, orchestration.LocalEnvironmentName)
4243
if err != nil {
4344
return nil, err

‎cmd/ci/test_environment_test.go‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
package ci
22

33
import (
4-
"github.com/codefly-dev/core/resources"
54
"testing"
5+
6+
"github.com/codefly-dev/cli/pkg/environments"
7+
"github.com/codefly-dev/core/resources"
68
)
79

810
func TestDisposableTestEnvironmentCannotReuseFixtureState(t *testing.T) {
9-
declared := &resources.Environment{Name: "local", NamingScope: "retained-fixture", Fixture: "controlled"}
10-
workspace := &resources.Workspace{Environments: []*resources.Environment{declared}}
11+
declared := &environments.Environment{Name: "local", NamingScope: "retained-fixture", Fixture: "controlled"}
12+
workspace := &resources.Workspace{Environments: []*resources.Environment{declared.Runtime()}}
1113
first, err := testEnvironment(workspace, true)
1214
if err != nil {
1315
t.Fatal(err)
@@ -16,7 +18,7 @@ func TestDisposableTestEnvironmentCannotReuseFixtureState(t *testing.T) {
1618
if err != nil {
1719
t.Fatal(err)
1820
}
19-
for _, env := range []*resources.Environment{first, second} {
21+
for _, env := range []*environments.Environment{first, second} {
2022
if env.NamingScope == "" || env.NamingScope == declared.NamingScope {
2123
t.Fatalf("disposable flow could reach retained fixture: %q", env.NamingScope)
2224
}

‎cmd/common/build_cache_test.go‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
package common
22

33
import (
4+
"testing"
5+
46
"github.com/spf13/cobra"
57
"github.com/stretchr/testify/require"
6-
"testing"
78
)
89

910
func TestBuildCacheFlagsValidateAndPreserveReadOnlyPolicy(t *testing.T) {

‎cmd/composition/target.go‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ package composition
22

33
import (
44
"encoding/json"
5-
"fmt"
65

76
"github.com/codefly-dev/cli/pkg/deployments"
7+
"github.com/codefly-dev/cli/pkg/environments"
88
"github.com/codefly-dev/core/resources"
99
"github.com/spf13/cobra"
1010
)
@@ -24,9 +24,9 @@ func newTargetCommand(workspace *string) *cobra.Command {
2424
if err != nil {
2525
return err
2626
}
27-
env := current.FindEnvironment(args[0])
28-
if env == nil {
29-
return fmt.Errorf("environment %q is not declared in %s", args[0], resources.WorkspaceConfigurationName)
27+
env, err := environments.Select(current, args[0])
28+
if err != nil {
29+
return err
3030
}
3131
var inspected *deployments.KubernetesTargetInspection
3232
if cmd.Flags().Changed("expected-identity") {

0 commit comments

Comments
 (0)