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
11 changes: 8 additions & 3 deletions cmd/environment/configuration_profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"regexp"
"strings"

"github.com/codefly-dev/cli/pkg/run/fixture"

"github.com/codefly-dev/cli/pkg/environments"
"github.com/codefly-dev/core/configurations"
"github.com/codefly-dev/core/shared"
Expand Down Expand Up @@ -76,7 +78,7 @@ func materializeConfigurationProfile(ctx context.Context, directory string, env
}

// validateConfigurationSourceLayer refuses a source layer that is not an ordinary
// directory of flat .env inputs: a symlink, a derivation from the destination, an
// directory of flat .env inputs (and at most the module envelope): a symlink, a derivation from the destination, an
// environment-specific runnable binding, or a secret file holding a value rather
// than an empty placeholder.
func validateConfigurationSourceLayer(layer, destination string) error {
Expand All @@ -92,8 +94,11 @@ func validateConfigurationSourceLayer(layer, destination string) error {
return err
}
for _, entry := range entries {
if !strings.HasSuffix(entry.Name(), ".env") && entry.Name() != configurations.ProfileDerivationFile {
return fmt.Errorf("configuration-source supports only flat .env inputs and profile derivation metadata: %s", entry.Name())
// The module envelope is the one single-file declaration a source profile
// may carry: a cell deriving from it is delivered that ceiling (fixture.EnvelopeFile).
if !strings.HasSuffix(entry.Name(), ".env") && entry.Name() != configurations.ProfileDerivationFile &&
entry.Name() != fixture.EnvelopeFile {
return fmt.Errorf("configuration-source supports only flat .env inputs, the module envelope and profile derivation metadata: %s", entry.Name())
}
info, infoErr := entry.Info()
if infoErr != nil || !info.Mode().IsRegular() {
Expand Down
14 changes: 14 additions & 0 deletions cmd/environment/configuration_profile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,17 @@ func TestProfileImportRefusesAuthoredOrSymlinkedFiles(t *testing.T) {
})
}
}

// A source profile may carry the module envelope: every cell deriving from it is
// delivered that ceiling (fixture.EnvelopeFile), so importing a cell onto a
// profile that states one must not refuse it as an unvalidated input.
func TestProfileImportAcceptsTheSourceProfilesModuleEnvelope(t *testing.T) {
root := profileSource(t)
require.NoError(t, os.WriteFile(filepath.Join(root, "configurations", "hosted", "module-envelope.json"), []byte(`{"principals":{}}`+"\n"), 0o644))
require.NoError(t, materializeConfigurationProfile(t.Context(), root, profileEnvironment("blue"), false))
_, err := os.Stat(filepath.Join(root, "configurations", "blue", configurations.ProfileDerivationFile))
require.NoError(t, err)
// It is accepted by its exact name only.
require.NoError(t, os.WriteFile(filepath.Join(root, "configurations", "hosted", "module-envelope.backup.json"), []byte("{}\n"), 0o644))
require.ErrorContains(t, materializeConfigurationProfile(t.Context(), root, profileEnvironment("green"), false), "only flat .env inputs")
}
3 changes: 2 additions & 1 deletion docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -2467,7 +2467,8 @@ An imported environment may declare `configuration-source: hosted` and select
in `configurations/blue/` deriving from `hosted`, using Core's profile inheritance.
The profile chain selects the first directory at each location; inheritance adds
shared groups and per-profile values. Import validates every inherited source:
only flat `.env` inputs and profile derivation metadata are accepted; secret
only flat `.env` inputs, the module envelope (`module-envelope.json`, which every
environment deriving from the profile inherits) and profile derivation metadata are accepted; secret
files contain empty placeholders, symlinks are refused, and sources may
not contain environment-specific runnable bindings. Dry-run writes nothing.
Import refuses authored destination files and retires only its own earlier copied
Expand Down
6 changes: 4 additions & 2 deletions pkg/run/fixture/fixture.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,14 +284,16 @@ func composedDirsOf(workspace *resources.Workspace) []string {
// workspace holding no directory for it states no envelope for it, rather
// than lending the envelope it states for another environment.
func grantsPath(workspaceDir, profile string) (string, error) {
path, _, err := configurations.ProfileFile(context.Background(), workspaceDir, "configurations", []string{profile}, envelopeFile)
path, _, err := configurations.ProfileFile(context.Background(), workspaceDir, "configurations", []string{profile}, EnvelopeFile)
if err != nil {
return "", fmt.Errorf("resolve the module envelope of configuration profile %q in %s: %w", profile, workspaceDir, err)
}
return path, nil
}

const envelopeFile = "module-envelope.json"
// EnvelopeFile is the module envelope's file name in a configuration profile
// directory: a single-file declaration a derived profile inherits (grantsPath).
const EnvelopeFile = "module-envelope.json"

// composedWorkspacesOf is every workspace the composition inherits from, the
// deepest first, so a base's base is read before the base.
Expand Down
Loading