Fix profile loading with ConfigKit history
This commit is contained in:
@@ -46,16 +46,6 @@ func loadPipelineWithConfigKit(path string, opts PipelineLoadOptions) (*Pipeline
|
||||
if err != nil {
|
||||
return nil, describePipelineLoadError(err)
|
||||
}
|
||||
if profile != "" {
|
||||
base, err := loadPipelineConfigKitResult(path, "")
|
||||
if err != nil {
|
||||
return nil, describePipelineLoadError(err)
|
||||
}
|
||||
baseConfig := base.Config
|
||||
if err := rejectDuplicateBaseDefinitions(&baseConfig, base.Metadata(), base.Sources()); err != nil {
|
||||
return nil, fmt.Errorf("load pipeline config: %w", err)
|
||||
}
|
||||
}
|
||||
cfg := result.Config
|
||||
metadata, err := pipelineMetadataFromConfigKit(&cfg, result, opts)
|
||||
if err != nil {
|
||||
@@ -266,7 +256,7 @@ func validatePipelineConfigKit(cfg *PipelineConfig, validation *configkit.Valida
|
||||
func pipelineMetadataFromConfigKit(cfg *PipelineConfig, result *configkit.Result[PipelineConfig], opts PipelineLoadOptions) (*pipelineResolutionMetadata, error) {
|
||||
sources := result.Sources()
|
||||
metadata := result.Metadata()
|
||||
if err := rejectDuplicateBaseDefinitions(cfg, metadata, sources); err != nil {
|
||||
if err := rejectDuplicateBaseDefinitions(metadata, sources); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resolved := &pipelineResolutionMetadata{
|
||||
@@ -345,34 +335,30 @@ func ownershipSources(origin configkit.Origin, pathsByID map[string]string) []st
|
||||
return sources
|
||||
}
|
||||
|
||||
func rejectDuplicateBaseDefinitions(cfg *PipelineConfig, metadata configkit.Metadata, sources []configkit.SourceInfo) error {
|
||||
func rejectDuplicateBaseDefinitions(metadata configkit.Metadata, sources []configkit.SourceInfo) error {
|
||||
pathsByID := make(map[string]string, len(sources))
|
||||
for _, source := range sources {
|
||||
pathsByID[source.ID] = source.Path
|
||||
}
|
||||
var declarations []struct {
|
||||
kind string
|
||||
key string
|
||||
path configkit.Path
|
||||
}
|
||||
if cfg.Scriptorium != nil {
|
||||
for key := range cfg.Scriptorium.Artifacts {
|
||||
declarations = append(declarations, struct {
|
||||
kind, key string
|
||||
path configkit.Path
|
||||
}{"artifact", key, configkit.Keys("scriptorium", "artifacts", key)})
|
||||
// Enumerate retained declarations, not just surviving effective keys. Loading
|
||||
// the base separately would also resolve credentials removed by the profile.
|
||||
// ConfigKit returns canonical JSON Pointers in deterministic order.
|
||||
for _, path := range metadata.HistoricalPaths() {
|
||||
parts := strings.Split(path.String(), "/")
|
||||
if len(parts) != 4 || parts[1] != "scriptorium" {
|
||||
continue
|
||||
}
|
||||
for key := range cfg.Scriptorium.ArtifactFamilies {
|
||||
declarations = append(declarations, struct {
|
||||
kind, key string
|
||||
path configkit.Path
|
||||
}{"artifact family", key, configkit.Keys("scriptorium", "artifact_families", key)})
|
||||
var kind string
|
||||
switch parts[2] {
|
||||
case "artifacts":
|
||||
kind = "artifact"
|
||||
case "artifact_families":
|
||||
kind = "artifact family"
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
sort.Slice(declarations, func(i, j int) bool { return declarations[i].path.String() < declarations[j].path.String() })
|
||||
for _, declaration := range declarations {
|
||||
seen := map[string]struct{}{}
|
||||
for _, change := range metadata.History(declaration.path) {
|
||||
for _, change := range metadata.History(path) {
|
||||
if change.Origin.Layer != configkit.LayerBase {
|
||||
continue
|
||||
}
|
||||
@@ -386,7 +372,8 @@ func rejectDuplicateBaseDefinitions(cfg *PipelineConfig, metadata configkit.Meta
|
||||
claims = append(claims, pathsByID[sourceID])
|
||||
}
|
||||
sort.Strings(claims)
|
||||
return fmt.Errorf("pipeline Scriptorium %s %q is declared by multiple base files: %s", declaration.kind, declaration.key, strings.Join(claims, ", "))
|
||||
key := strings.NewReplacer("~1", "/", "~0", "~").Replace(parts[3])
|
||||
return fmt.Errorf("pipeline Scriptorium %s %q is declared by multiple base files: %s", kind, key, strings.Join(claims, ", "))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -88,6 +88,35 @@ whisperx:
|
||||
}
|
||||
}
|
||||
|
||||
func TestPipelineProfileDoesNotRequireUnusedBaseCredentials(t *testing.T) {
|
||||
const production = "NARRATIO_PROFILE_UNUSED_PROD_KEY"
|
||||
const testingKey = "NARRATIO_PROFILE_TEST_KEY"
|
||||
t.Setenv(production, "")
|
||||
if err := os.Unsetenv(production); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Setenv(testingKey, "synthetic-test-key")
|
||||
for _, credential := range []string{testingKey, "null"} {
|
||||
t.Run(credential, func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
root := writePipelineFixture(t, dir, "pipeline.yml", "whisperx:\n transcribe_url: https://transcription.example.test/transcribe\naudita:\n llm_api_key: "+production+"\n")
|
||||
writePipelineFixture(t, dir, "profiles/testing.yml", "audita:\n llm_api_key: "+credential+"\n")
|
||||
profile := "testing"
|
||||
cfg, err := LoadPipelineWithOptions(root, PipelineLoadOptions{Profile: &profile})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if credential == "null" {
|
||||
if cfg.Audita.LLMAPIKey != nil {
|
||||
t.Fatal("profile did not remove the base credential")
|
||||
}
|
||||
} else if cfg.Audita.LLMAPIKey == nil || cfg.Audita.LLMAPIKey.EnvName() != testingKey || !cfg.Audita.LLMAPIKey.IsResolved() {
|
||||
t.Fatal("profile credential was not resolved")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPipelineConfigKitAllowsOneBaseDeclarationWithProfileOverride(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
writePipelineFixture(t, dir, "pipeline.yml", `configkit:
|
||||
|
||||
Reference in New Issue
Block a user