@@ -19,29 +19,42 @@ require "./admiral_patch"
1919# Load squarectl
2020require " ./squarectl/**"
2121
22+ # Top-level namespace and configuration holder.
23+ #
24+ # The parsed configuration is kept in class-level state (`@@config`,
25+ # `@@environments`, `@@environment_all`) so that any component can reach it via
26+ # `Squarectl.config` without threading it through. Because this state is global,
27+ # specs must call `reset_config!` between examples (see `spec/spec_helper.cr`).
2228module Squarectl
2329 VERSION = {{ ` shards version #{ __DIR__ } ` .chomp.stringify }}
2430 GIT_REF = {{ ` git log -n 1 --format="%H" | head -c 8` .chomp.stringify }}
2531
2632 @@environment_all : Squarectl ::Config ::SquarectlEnvironment ?
2733
34+ # Human-readable version string, e.g. `1.6.0 (7347781a)`.
2835 def self.version
2936 " #{ VERSION } (#{ GIT_REF } )"
3037 end
3138
39+ # Reads the config file, renders it as a Crinja (Jinja) template with the
40+ # current process environment exposed as `ENV`, then parses the result as YAML.
41+ # This is why `{{ ENV.FOO }}` works inside `squarectl.yml`.
3242 def self.load_config (config_path )
3343 config_file = File .read(config_path)
3444 rendered = Crinja .render(config_file, {" ENV" => env_vars_to_hash})
3545 self .config = Squarectl ::Config ::SquarectlConfig .from_yaml(rendered)
3646 self .environments = config.environments
3747 end
3848
49+ # Clears the cached config so a fresh one can be loaded. Used by the test suite.
3950 def self.reset_config !
4051 @@config = nil
4152 @@environments = nil
4253 @@environment_all = nil
4354 end
4455
56+ # Snapshot of the process environment as a plain hash, injected into the
57+ # Crinja template context.
4558 def self.env_vars_to_hash
4659 hash = Hash (String , String ).new
4760 ENV .each { |k , v | hash[k] = v }
@@ -64,10 +77,16 @@ module Squarectl
6477 @@environments || [] of Squarectl ::Config ::SquarectlEnvironment
6578 end
6679
80+ # The special `all` environment holding global defaults that `TaskFactory`
81+ # merges into every other environment. Returns `nil` when not defined.
6782 def self.environment_all
6883 @@environment_all ||= environments.not_nil!.find { |e | e.name == " all" } # ameba:disable Lint/NotNil
6984 end
7085
86+ # Resolves the environment selected on the command line for the given target.
87+ # Matching is substring-based (`name.includes?`), so `prod` matches `production`.
88+ # Raises on an unknown target/environment, and forbids any non-`compose` target
89+ # on the `development` environment.
7190 def self.find_environment (environment, target)
7291 raise " Target not found: #{ target } " if ! %w[compose swarm kubernetes] .includes?(target)
7392 env = environments.not_nil!.find(& .name.includes?(environment)) # ameba:disable Lint/NotNil
@@ -76,6 +95,8 @@ module Squarectl
7695 env
7796 end
7897
98+ # Whether the config requests Compose v1 (`docker-compose`) instead of v2
99+ # (`docker compose`). Drives the command prefix in `Commands::Compose`.
79100 def self.compose_v1 ?
80101 config.compose[" version" ] == 1
81102 end
@@ -84,28 +105,40 @@ module Squarectl
84105 config.app
85106 end
86107
108+ # Project directory conventions, all relative to the current working directory:
109+ # root_dir/ # the project (cwd)
110+ # root_dir/kubernetes/<env>/ # generated Kubernetes manifests
111+ # root_dir/squarectl/ # data_dir: base.yml lives here
112+ # root_dir/squarectl/targets/ # per-target compose files (<target>/common.yml, <target>/<env>.yml)
113+ # root_dir/squarectl/targets/common/ # extra compose files referenced by config
87114 def self.root_dir
88115 Path .new(Dir .current)
89116 end
90117
118+ # :ditto:
91119 def self.kubernetes_dir
92120 root_dir.join(" kubernetes" )
93121 end
94122
123+ # :ditto:
95124 def self.data_dir
96125 root_dir.join(" squarectl" )
97126 end
98127
128+ # :ditto:
99129 def self.targets_dir
100130 data_dir.join(" targets" )
101131 end
102132
133+ # :ditto:
103134 def self.targets_common_dir
104135 targets_dir.join(" common" )
105136 end
106137end
107138
108- # Start the CLI
139+ # Start the CLI, unless we are running under the spec suite (which requires the
140+ # module without wanting the command dispatcher to fire). Any uncaught exception
141+ # is reported as a message and turned into a non-zero exit code.
109142unless Crystal .env.test?
110143 begin
111144 Squarectl ::CLI .run
0 commit comments