Class: EnvSpec

Inherits:
Object
  • Object
show all
Defined in:
lib/envspec/parser.rb,
lib/envspec/cli.rb,
lib/envspec/init.rb,
lib/envspec/entry.rb,
lib/envspec/errors.rb,
lib/envspec/scanner.rb,
lib/envspec/version.rb,
lib/envspec/cli/init.rb,
lib/envspec/cli/lint.rb,
lib/envspec/cli/check.rb,
lib/envspec/validator.rb,
lib/envspec/heuristics.rb,
lib/envspec/cli/command.rb

Overview

Parses env.spec files – declarative contract for ConfigMap and Secret keys an app expects.

Syntax (v5):

KEY                       # str, required, configmap, shared (all envs)
KEY?                      # optional
KEY: int                  # typed (str|int|bool|dsn|enum(a,b))
KEY: str = default        # with default (configmap only)

[secrets]                 # MODIFIER: type flips to secret (scope unchanged)
[env: production]         # SELECTOR: scope -> production, type RESETS to configmap
[env: production, staging]# multi-env scope

Output:

configmap = { "*" => { "KEY" => Entry }, "production" => {...} }
secrets   = { "*" => { "DB_PASS" => Entry }, "production" => {...} }

Defined Under Namespace

Modules: Heuristics, Init, Scanner Classes: CLI, Entry, Error, ParseError, ValidationError

Constant Summary collapse

SHARED =
"*".freeze
VALID_TYPES =
%i[str int bool dsn enum].freeze
ENV_NAME_RE =
/\A[A-Za-z0-9_-]+\z/.freeze
VERSION =
"0.1.2".freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeEnvSpec

Returns a new instance of EnvSpec.



32
33
34
35
# File 'lib/envspec/parser.rb', line 32

def initialize
  @configmap = {}
  @secrets   = {}
end

Instance Attribute Details

#configmapObject (readonly)

Returns the value of attribute configmap.



22
23
24
# File 'lib/envspec/parser.rb', line 22

def configmap
  @configmap
end

#secretsObject (readonly)

Returns the value of attribute secrets.



22
23
24
# File 'lib/envspec/parser.rb', line 22

def secrets
  @secrets
end

Class Method Details

.parse(text) ⇒ Object



24
25
26
# File 'lib/envspec/parser.rb', line 24

def self.parse(text)
  new.tap { |spec| spec.send(:_parse, text) }
end

.parse_file(path) ⇒ Object



28
29
30
# File 'lib/envspec/parser.rb', line 28

def self.parse_file(path)
  parse(File.read(path))
end

Instance Method Details

#configmap_for(env) ⇒ Object

Returns merged keys (shared + given env) for configmap.



43
44
45
# File 'lib/envspec/parser.rb', line 43

def configmap_for(env)
  merge_scope(@configmap, env)
end

#envsObject

Returns array of env names declared in the spec (excluding “*”).



38
39
40
# File 'lib/envspec/parser.rb', line 38

def envs
  (@configmap.keys + @secrets.keys).uniq.reject { |e| e == SHARED }.sort
end

#secrets_for(env) ⇒ Object



47
48
49
# File 'lib/envspec/parser.rb', line 47

def secrets_for(env)
  merge_scope(@secrets, env)
end

#validate(env_hash, env: SHARED, strict: false) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/envspec/validator.rb', line 19

def validate(env_hash, env: SHARED, strict: false)
  problems = []

  keys = configmap_for(env).merge(secrets_for(env))

  keys.each do |name, entry|
    raw = env_hash[name]
    if raw.nil? || raw.empty?
      next if entry.optional || !entry.default.nil?
      problems << "missing required env var '#{name}' (declared at line #{entry.line})"
      next
    end

    err = type_problem(name, raw, entry)
    problems << err if err
  end

  if strict
    known = keys.keys
    env_hash.each_key do |k|
      next unless k =~ /\A[A-Z][A-Z0-9_]*\z/
      problems << "unknown env var '#{k}' (not declared in spec)" unless known.include?(k)
    end
  end

  problems
end

#validate!(env_hash, env: SHARED, strict: false) ⇒ Object

Validates an env hash (or ENV) against a parsed spec.

Checks:

- required keys are present
- typed values parse cleanly (int, bool, dsn, enum)
- reports unknown keys as warnings (extras), not errors

Usage:

spec.validate!(ENV.to_h, env: "production")

Raises EnvSpec::ValidationError with a list of all problems found.

Raises:



13
14
15
16
17
# File 'lib/envspec/validator.rb', line 13

def validate!(env_hash, env: SHARED, strict: false)
  problems = validate(env_hash, env: env, strict: strict)
  raise ValidationError.new(problems) unless problems.empty?
  true
end