Skip to content

Support .yml extension for features files in addition to .yaml #4676

Description

@markphelps

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

  1. 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
  2. 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)
  3. Update error messages and validation to mention both supported extensions

  4. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementCreated by Linear-GitHub Syncv2Flipt v2

    Type

    No type

    Projects

    Status
    Done
    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions