Module: EnvSpec::Init
- Defined in:
- lib/envspec/init.rb
Overview
Generates an initial env.spec by scanning a repo for env var usages.
Constant Summary collapse
- HEADER =
<<~HEADER # env.spec — generated by `envspec init` %s # # This is a starting point. Review and adjust: # 1. Move secrets (passwords, tokens, keys) under [secrets] # 2. Add types: str (default) | int | bool | dsn | enum(a,b,c) # 3. Mark optional vars with ? # 4. Add defaults for configmap vars: KEY: type = value # 5. Group per-env vars under [env: production], [env: staging], etc # # Discovered usages are listed as comments above each key. # Confirm or remove before committing. # # Validate: envspec lint env.spec # See docs: https://github.com/repleadfy/envspec HEADER
Class Method Summary collapse
- .render(results, root) ⇒ Object
- .render_key(name, usages, inference, root) ⇒ Object
- .run(root: Dir.pwd, force: false, output: "env.spec") ⇒ Object
Class Method Details
.render(results, root) ⇒ Object
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/envspec/init.rb', line 46 def self.render(results, root) out = [] out << format(HEADER, Time.now.strftime("%Y-%m-%d")) out << "" configmap_keys = [] secret_keys = [] results.keys.sort.each do |name| usages = results[name] inference = Heuristics.infer(name, usages) block = render_key(name, usages, inference, root) if inference[:secret] secret_keys << block else configmap_keys << block end end out.concat(configmap_keys) unless configmap_keys.empty? unless secret_keys.empty? out << "" out << "[secrets]" out.concat(secret_keys) end out.join("\n").gsub(/\n{3,}/, "\n\n").strip + "\n" end |
.render_key(name, usages, inference, root) ⇒ Object
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/envspec/init.rb', line 76 def self.render_key(name, usages, inference, root) lines = [] usages.first(3).each do |u| rel = u[:file].sub(/\A#{Regexp.escape(root)}\/?/, "") lines << "# Found in: #{rel}:#{u[:line]}" end lines << "# (and #{usages.size - 3} more usages)" if usages.size > 3 hints = [] hints << "name suggests #{inference[:type]}" if inference[:type] != :str hints << "name suggests secret" if inference[:secret] && Heuristics::SECRET_NAME_RE.match?(name) && inference[:type] != :dsn hints << "uses fetch with default" if inference[:optional] && inference[:default] lines << "# Heuristic: #{hints.join(', ')}" unless hints.empty? decl = name.dup decl << "?" if inference[:optional] type = inference[:type] decl << ": #{type}" if type != :str || (inference[:default] && type == :str) decl << " = #{inference[:default]}" if inference[:default] && !inference[:secret] lines << decl lines << "" lines.join("\n") end |
.run(root: Dir.pwd, force: false, output: "env.spec") ⇒ Object
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/envspec/init.rb', line 24 def self.run(root: Dir.pwd, force: false, output: "env.spec") out_path = File.(output, root) if File.exist?(out_path) && !force return { ok: false, reason: :exists, path: out_path } end t0 = Time.now results = Scanner.scan(root) elapsed = Time.now - t0 content = render(results, root) File.write(out_path, content) { ok: true, path: out_path, keys: results.size, files: results.values.flatten.map { |u| u[:file] }.uniq.size, elapsed: elapsed, } end |