Class: Clacky::DeployTools::SetDeployVariables
- Inherits:
-
Object
- Object
- Clacky::DeployTools::SetDeployVariables
- Defined in:
- lib/clacky/default_skills/deploy/tools/set_deploy_variables.rb
Overview
Set environment variables on a Railway service via ‘railway variables –set`. Uses RAILWAY_TOKEN passed through environment — no clackycli wrapper needed.
Supports both normal key=value pairs and Railway inter-service references like ${postgres{postgres.DATABASE_PUBLIC_URL} (pass raw_value: true to skip escaping).
Constant Summary collapse
- SENSITIVE_PATTERNS =
[ /password/i, /secret/i, /api_key/i, /token/i, /credential/i, /private_key/i ].freeze
- BATCH_SIZE =
Maximum number of variables to set in a single batch call
20- MAX_RETRIES =
Retry config for transient failures
3- RETRY_DELAY =
seconds
2
Class Method Summary collapse
-
.execute(service_name:, variables:, platform_token:, raw_value: false) ⇒ Hash
Set one or more environment variables on a Railway service.
-
.set_batch(env, service_name, pairs, raw_value: false) ⇒ Hash
Set a batch of variables in a single railway command call.
-
.set_one(env, service_name, key, value, raw_value: false) ⇒ Hash
Set a single variable.
-
.set_one_with_retry(env, service_name, key, value, raw_value: false) ⇒ Hash
Set a single variable with retry logic for transient network errors.
Class Method Details
.execute(service_name:, variables:, platform_token:, raw_value: false) ⇒ Hash
Set one or more environment variables on a Railway service. Batches all variables into a single ‘railway variables` call to minimize network connections and avoid SSL reset issues.
}
40 41 42 43 44 45 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 75 76 77 78 79 80 |
# File 'lib/clacky/default_skills/deploy/tools/set_deploy_variables.rb', line 40 def self.execute(service_name:, variables:, platform_token:, raw_value: false) if service_name.nil? || service_name.strip.empty? return { success: false, error: "service_name is required" } end env = ENV.to_h.merge("RAILWAY_TOKEN" => platform_token) # Log all variables being set variables.each do |key, value| log_value = sensitive?(key) ? "******" : value puts " Setting #{key}=#{log_value}" end # Split into batches to avoid command line length limits set_vars = [] error_list = [] var_pairs = variables.map { |k, v| [k.to_s, v.to_s] } var_pairs.each_slice(BATCH_SIZE) do |batch| result = set_batch(env, service_name, batch, raw_value: raw_value) if result[:success] set_vars.concat(batch.map(&:first)) else # Retry logic: attempt individual vars if batch fails batch.each do |key, value| individual = set_one_with_retry(env, service_name, key, value, raw_value: raw_value) if individual[:success] set_vars << key else error_list << { key: key, error: individual[:error] } end end end end { success: error_list.empty?, set_variables: set_vars, errors: error_list } end |
.set_batch(env, service_name, pairs, raw_value: false) ⇒ Hash
Set a batch of variables in a single railway command call.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/clacky/default_skills/deploy/tools/set_deploy_variables.rb', line 88 def self.set_batch(env, service_name, pairs, raw_value: false) # Each --set argument is passed as a separate array element set_flags = pairs.flat_map { |key, value| ["--set", "#{key}=#{value}"] } cmd = ["railway", "variables", "--service", service_name, "--skip-deploys"] + set_flags # Debug: print the full command being executed puts " [DEBUG] Executing Railway CLI command:" puts " [DEBUG] Array form: #{cmd.inspect}" puts " [DEBUG] Shell form: #{cmd.join(' ')}" puts " [DEBUG] with RAILWAY_TOKEN=#{env['RAILWAY_TOKEN']}" if env['RAILWAY_TOKEN'] $stdout.flush # Use system() instead of Open3.capture3 to avoid stdin/stdout blocking issues # system() inherits the current process's stdin/stdout/stderr directly require 'timeout' begin success = Timeout.timeout(30) do # Close stdin, suppress stdout, but keep stderr visible system(env, *cmd, in: :close, out: File::NULL) end rescue Timeout::Error return { success: false, error: "Railway CLI command timed out after 30 seconds" } end if success { success: true } else { success: false, error: "railway variables command failed (exit code: #{$?.exitstatus})" } end end |
.set_one(env, service_name, key, value, raw_value: false) ⇒ Hash
Set a single variable. Builds the ‘railway variables –set` command.
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/clacky/default_skills/deploy/tools/set_deploy_variables.rb', line 144 def self.set_one(env, service_name, key, value, raw_value: false) assignment = "#{key}=#{value}" cmd = [ "railway", "variables", "--service", service_name, "--skip-deploys", "--set", assignment ] # Debug: print the full command being executed puts " [DEBUG] Executing single var Railway CLI command:" puts " [DEBUG] Array form: #{cmd.inspect}" puts " [DEBUG] Shell form: #{cmd.join(' ')}" puts " [DEBUG] with RAILWAY_TOKEN=#{env['RAILWAY_TOKEN']}" if env['RAILWAY_TOKEN'] $stdout.flush # Use system() instead of Open3.capture3 to avoid stdin/stdout blocking issues require 'timeout' begin success = Timeout.timeout(30) do # Close stdin, suppress stdout, but keep stderr visible system(env, *cmd, in: :close, out: File::NULL) end rescue Timeout::Error return { success: false, error: "Railway CLI command timed out after 30 seconds" } end if success { success: true } else { success: false, error: "railway variables command failed (exit code: #{$?.exitstatus})" } end end |
.set_one_with_retry(env, service_name, key, value, raw_value: false) ⇒ Hash
Set a single variable with retry logic for transient network errors.
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/clacky/default_skills/deploy/tools/set_deploy_variables.rb', line 123 def self.set_one_with_retry(env, service_name, key, value, raw_value: false) last_error = nil MAX_RETRIES.times do |attempt| result = set_one(env, service_name, key, value, raw_value: raw_value) return result if result[:success] last_error = result[:error] # Only retry on connection/SSL errors break unless last_error.to_s =~ /connection|ssl|reset|timeout|network/i puts " ⚠️ Retrying #{key} (attempt #{attempt + 2}/#{MAX_RETRIES})..." if attempt < MAX_RETRIES - 1 sleep RETRY_DELAY end { success: false, error: last_error } end |