Summary
Currently, Flipt has inconsistent support for .yml vs .yaml file extensions for features files. While some parts of the codebase support both extensions, the namespace discovery and file operations primarily hardcode features.yaml, creating inconsistencies where .yml files may be committed to Git but not properly discovered as namespaces.
Background
This issue was discovered while implementing Git repository initialization for existing features files (#4589). The configuration system and snapshot parsing support both extensions, but namespace discovery only looks for features.yaml.
Current State
✅ What works with .yml files:
- File parsing and snapshot creation (
internal/storage/fs/snapshot.go:200: case ".yaml", ".yml")
- Configuration glob patterns (
internal/storage/fs/config.go:66-68 support both extensions)
- Git operations (files get committed correctly)
❌ What doesn't work with .yml files:
- Namespace discovery hardcodes
features.yaml
- File operations in flags and segments services
- Validation error messages reference
features.yaml
Files That Need Updates
Namespace Discovery
/internal/storage/environments/fs/namespaces.go
- Line 19:
FeaturesFilename = "features.yaml" (hardcoded constant)
- Lines 38, 88, 130, 196: Direct usage of
FeaturesFilename
- Need helper function to try both extensions
Feature File Operations
-
/internal/storage/environments/fs/flipt/common.go
- Line 53:
fs.OpenFile(path.Join(namespace, "features.yaml"), ...)
-
/internal/storage/environments/fs/flipt/flags.go
- Line 149:
fs.OpenFile(path.Join(rs.NamespaceKey, "features.yaml"), ...)
- Line 203:
fs.OpenFile(path.Join(namespace, "features.yaml"), ...)
- Line 236:
validator.Validate("features.yaml", e.buf) (error message)
-
/internal/storage/environments/fs/flipt/segments.go
- Line 142:
fs.OpenFile(path.Join(rs.NamespaceKey, "features.yaml"), ...)
- Line 196:
fs.OpenFile(path.Join(namespace, "features.yaml"), ...)
Proposed Solution
-
Update namespace discovery to check for both extensions in priority order:
- Try
features.yaml first (maintain backward compatibility)
- Fall back to
features.yml if .yaml not found
-
Create helper functions for file operations that:
- Try both extensions when reading existing files
- Preserve existing extension when updating files
- Default to
.yaml for new files (consistency)
-
Update error messages and validation to mention both supported extensions
-
Add comprehensive tests covering:
- Namespace discovery with both extensions
- Mixed environments (some dirs with
.yaml, others with .yml)
- File operations preserving existing extensions
- Git integration with both extensions
Implementation Strategy
// Helper function pattern
func tryOpenFeaturesFile(fs Filesystem, dir string) (io.ReadCloser, string, error) {
for _, filename := range []string{"features.yaml", "features.yml"} {
filePath := path.Join(dir, filename)
fi, err := fs.OpenFile(filePath, os.O_RDONLY, 0644)
if err == nil {
return fi, filename, nil
}
if !errors.Is(err, os.ErrNotExist) {
return nil, "", err
}
}
return nil, "", os.ErrNotExist
}
Benefits
- Consistency: Both extensions work uniformly across all Flipt operations
- User flexibility: Users can choose their preferred YAML extension
- Git integration: Files with either extension are properly discovered and managed
- Backward compatibility: Existing
.yaml files continue to work unchanged
Related Issues
Summary
Currently, Flipt has inconsistent support for
.ymlvs.yamlfile extensions for features files. While some parts of the codebase support both extensions, the namespace discovery and file operations primarily hardcodefeatures.yaml, creating inconsistencies where.ymlfiles may be committed to Git but not properly discovered as namespaces.Background
This issue was discovered while implementing Git repository initialization for existing features files (#4589). The configuration system and snapshot parsing support both extensions, but namespace discovery only looks for
features.yaml.Current State
✅ What works with .yml files:
internal/storage/fs/snapshot.go:200:case ".yaml", ".yml")internal/storage/fs/config.go:66-68support both extensions)❌ What doesn't work with .yml files:
features.yamlfeatures.yamlFiles That Need Updates
Namespace Discovery
/internal/storage/environments/fs/namespaces.goFeaturesFilename = "features.yaml"(hardcoded constant)FeaturesFilenameFeature File Operations
/internal/storage/environments/fs/flipt/common.gofs.OpenFile(path.Join(namespace, "features.yaml"), ...)/internal/storage/environments/fs/flipt/flags.gofs.OpenFile(path.Join(rs.NamespaceKey, "features.yaml"), ...)fs.OpenFile(path.Join(namespace, "features.yaml"), ...)validator.Validate("features.yaml", e.buf)(error message)/internal/storage/environments/fs/flipt/segments.gofs.OpenFile(path.Join(rs.NamespaceKey, "features.yaml"), ...)fs.OpenFile(path.Join(namespace, "features.yaml"), ...)Proposed Solution
Update namespace discovery to check for both extensions in priority order:
features.yamlfirst (maintain backward compatibility)features.ymlif.yamlnot foundCreate helper functions for file operations that:
.yamlfor new files (consistency)Update error messages and validation to mention both supported extensions
Add comprehensive tests covering:
.yaml, others with.yml)Implementation Strategy
Benefits
.yamlfiles continue to work unchangedRelated Issues