Class: StandardHealth::EnvSpec

Inherits:
Object
  • Object
show all
Defined in:
lib/standard_health/env_spec.rb

Overview

DSL for declaring required and recommended environment variables.

Example:

StandardHealth::EnvSpec.define do
mode_alias :deployed, %w[staging preview production]
mode_alias :live, %w[production]

required :SECRET_KEY_BASE
required :APP_ENVIRONMENT, in: %w[staging production]
recommended :SENTRY_DSN, description: "Error tracking DSN"
forbidden :DEMO_MODE_ENABLED, in: :live
required :CSP_REPORT_ONLY, in: :live, expected_value: "false"

group "Singpass / MyInfo" do
  required :MYINFO_CLIENT_ID
  required :MYINFO_PRIVATE_JWKS,
    in: :live,
    unless: -> { ENV["MYINFO_MOCK_MODE"].present? }
end
end

Each entry has:

- `name` (Symbol)
- `level` (:required | :recommended | :forbidden)
- `in:` (Array<String>, Symbol, or nil) — when an Array, the entry only
applies while `APP_ENVIRONMENT` matches one of these modes. A Symbol
is resolved against `mode_alias` declarations at audit time. When
omitted, the entry applies to every mode.
- `description:` (String, optional) — human-readable hint surfaced
verbatim by `/diagnostics/env`.
- `consumed_by:` (String or Array<String>, optional) — pointer(s) to
where the value is read in the host app. Surfaced verbatim. When
`audit` is called with `root:`, each path is checked for an
`ENV[...]` / `ENV.fetch(...)` reference to the var; the result is
reported as `consumer:` in the audit row
(`:present`, `:file_missing`, or `:not_referenced`).
- `if:` / `unless:` (Proc, optional) — predicates evaluated at audit
time. When `unless:` returns truthy or `if:` returns falsy the entry
is reported with `status: :not_applicable`.
- `group` (String, optional) — set implicitly by enclosing `group`
block; surfaced verbatim.
- `deprecated:` (Boolean, optional) — flag for staged removal. When
true, the audit row includes `deprecated: true`.
- `sunset_on:` (String/Date, optional) — target removal date.
Surfaced verbatim in audit rows.
- `replacement:` (String, optional) — what to use instead. Surfaced
in audit rows.
- `expected_value:` (String/Symbol/Numeric/Regexp/Array, optional) —
asserts the *value*, not just presence. A present value that does
not match reports `status: :mismatch`. An Array means "any of".
Surfaced in audit rows as `expected_value:` so an operator can see
what was demanded; the actual value is deliberately NEVER surfaced
(env values are frequently secrets).

Defined Under Namespace

Classes: Entry, UnknownModeAlias

Constant Summary collapse

FORBIDDEN_LEVEL =

Levels whose absence is fine and whose presence is the failure.

:forbidden
VIOLATION_STATUSES =

Statuses that mean the environment is wrong, as opposed to merely advisory (:should_set) or inapplicable (:not_applicable).

Shared by DiagnosticsController#audit_status and the opt-in Checks::EnvSpecAudit, so the doctor endpoint and the health check cannot drift apart on what counts as a violation.

%i[missing forbidden mismatch].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeEnvSpec

Returns a new instance of EnvSpec.



103
104
105
106
107
# File 'lib/standard_health/env_spec.rb', line 103

def initialize
  @entries = []
  @mode_aliases = {}
  @group_stack = []
end

Instance Attribute Details

#entriesArray<Entry> (readonly)

Returns:



93
94
95
# File 'lib/standard_health/env_spec.rb', line 93

def entries
  @entries
end

#mode_aliasesHash{Symbol => Array<String>} (readonly)

Returns:

  • (Hash{Symbol => Array<String>})


96
97
98
# File 'lib/standard_health/env_spec.rb', line 96

def mode_aliases
  @mode_aliases
end

Class Method Details

.define(&block) ⇒ Object

Build a spec via the DSL.



99
100
101
# File 'lib/standard_health/env_spec.rb', line 99

def self.define(&block)
  new.tap { |spec| spec.instance_eval(&block) if block }
end

Instance Method Details

#audit(env_hash, mode:, root: nil) ⇒ Array<Hash>

Run the audit against an env-like hash.

Parameters:

  • env_hash (Hash{String, Symbol => String})

    e.g. ENV.to_h

  • mode (String, Symbol)

    current APP_ENVIRONMENT value

  • root (String, Pathname, nil) (defaults to: nil)

    host app root for resolving consumed_by paths. When given, each row gains a consumer: field: :present (file exists and references the var), :file_missing (path does not exist), :not_referenced (file exists but no ENV[...] / ENV.fetch(...) match for the var). When the entry has no consumed_by or root is nil, the field is omitted.

Returns:

  • (Array<Hash>)

    one row per applicable entry. Each row has at least name, level, status, mode. When an entry is suppressed by an if:/unless: predicate, status is :not_applicable and a reason field explains why. description, consumed_by, group, deprecated, sunset_on, replacement, expected_value, and consumer are included when set on the entry / computed during audit.



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/standard_health/env_spec.rb', line 193

def audit(env_hash, mode:, root: nil)
  mode_str = mode.to_s
  env = stringify(env_hash)

  @entries.each_with_object([]) do |entry, out|
    next unless mode_applies?(entry, mode_str)

    row = base_row(entry, mode_str)

    suppression = predicate_suppression(entry)
    if suppression
      row[:status] = :not_applicable
      row[:reason] = suppression
    else
      value = env[entry.name.to_s]
      row[:status] = classify(entry, value)
    end

    consumer_status = consumer_state(entry, root)
    row[:consumer] = consumer_status if consumer_status

    out << row
  end
end

#forbidden(name, **opts) ⇒ Object

Declare an env var that must NOT be set (in the applicable modes). Inverts the presence rule: absent is :ok, present is :forbidden.

This is the level for dangerous ops toggles — demo modes, auth bypasses, bootstrap flags — that are legitimate on staging and must never survive to production:

forbidden :DEMO_MODE_ENABLED, in: :live,
description: "Demo surfaces; unset before promoting"

expected_value: is meaningless here (the assertion is "absent"), so combining them is a declaration error rather than a silently ignored option. Raised at define time — i.e. host boot, well off the health path — so it can never surface as a 500 from /ready.



165
166
167
168
169
170
171
172
173
174
# File 'lib/standard_health/env_spec.rb', line 165

def forbidden(name, **opts)
  if opts.key?(:expected_value)
    raise ArgumentError,
          "`forbidden` asserts absence, so expected_value: is meaningless " \
          "(got #{opts[:expected_value].inspect} for #{name}). Use " \
          "`required ..., expected_value:` to assert a value instead."
  end

  add(FORBIDDEN_LEVEL, name, **opts)
end

#group(label, &block) ⇒ Object

Group subsequent declarations under a label. Pure metadata — propagated to audit rows as group: and otherwise inert. Nested group blocks are supported; the innermost label is the one that propagates to enclosed entries.

Parameters:

  • label (String)


124
125
126
127
128
129
130
131
# File 'lib/standard_health/env_spec.rb', line 124

def group(label, &block)
  raise ArgumentError, "group requires a block" unless block

  @group_stack.push(label.to_s)
  block.call
ensure
  @group_stack.pop
end

#mode_alias(name, modes) ⇒ Object

Declare a mode alias usable in in:. Re-declaring overrides the previous value (last writer wins) so layered specs are easy to compose.

Parameters:

  • name (Symbol)
  • modes (Array<String>)


114
115
116
# File 'lib/standard_health/env_spec.rb', line 114

def mode_alias(name, modes)
  @mode_aliases[name.to_sym] = Array(modes).map(&:to_s)
end

Declare a recommended env var. A missing value never fails the audit; it surfaces as :should_set.



147
148
149
# File 'lib/standard_health/env_spec.rb', line 147

def recommended(name, **opts)
  add(:recommended, name, **opts)
end

#required(name, **opts) ⇒ Object

Declare a required env var.

Parameters:

  • name (Symbol, String)
  • in (Array<String>, Symbol, nil)

    limit applicability to these modes

  • description (String, nil)
  • consumed_by (String, Array<String>, nil)
  • if (Proc, nil)
  • unless (Proc, nil)


141
142
143
# File 'lib/standard_health/env_spec.rb', line 141

def required(name, **opts)
  add(:required, name, **opts)
end