Module: Kdeploy::DSL

Included in:
CLI
Defined in:
lib/kdeploy/dsl.rb

Overview

Domain-specific language for defining hosts, roles, and tasks

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.extended(base) ⇒ Object



34
35
36
37
38
# File 'lib/kdeploy/dsl.rb', line 34

def self.extended(base)
  base.instance_variable_set(:@kdeploy_hosts, {})
  base.instance_variable_set(:@kdeploy_tasks, {})
  base.instance_variable_set(:@kdeploy_roles, {})
end

.included(base) ⇒ Object



27
28
29
30
31
32
# File 'lib/kdeploy/dsl.rb', line 27

def self.included(base)
  # Support `include Kdeploy::DSL` by promoting the DSL methods to class methods.
  # This keeps tests and external integrations simpler while preserving the
  # primary usage pattern (CLI uses `extend DSL`).
  base.extend(self)
end

Instance Method Details

#add_explicit_hosts(task, hosts) ⇒ Object



248
249
250
251
252
# File 'lib/kdeploy/dsl.rb', line 248

def add_explicit_hosts(task, hosts)
  task[:hosts]&.each do |host|
    hosts.add(host) if kdeploy_hosts.key?(host)
  end
end

#add_role_hosts(task, hosts) ⇒ Object



254
255
256
257
258
259
260
261
262
263
# File 'lib/kdeploy/dsl.rb', line 254

def add_role_hosts(task, hosts)
  task[:roles]&.each do |role|
    role_hosts = kdeploy_roles[role]
    next unless role_hosts

    role_hosts.each do |host|
      hosts.add(host) if kdeploy_hosts.key?(host)
    end
  end
end

#assign_task(task_name, on: nil, roles: nil) ⇒ Object

Assign task to roles or hosts after task definition

Raises:

  • (ArgumentError)


83
84
85
86
87
88
89
# File 'lib/kdeploy/dsl.rb', line 83

def assign_task(task_name, on: nil, roles: nil)
  task = kdeploy_tasks[task_name.to_sym]
  raise ArgumentError, "Task #{task_name} not found" unless task

  task[:hosts] = normalize_hosts_option(on) if on
  task[:roles] = normalize_roles_option(roles) if roles
end

#build_package_command(name, version, platform) ⇒ Object



265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/kdeploy/dsl.rb', line 265

def build_package_command(name, version, platform)
  n = Shellwords.escape(name.to_s)
  case platform.to_sym
  when :yum, :rpm
    if version
      "yum install -y #{n}-#{Shellwords.escape(version.to_s)}"
    else
      "yum install -y #{n}"
    end
  else
    base = "apt-get update && apt-get install -y #{n}"
    version ? "#{base}=#{Shellwords.escape(version.to_s)}" : base
  end
end

#create_task_block(block) ⇒ Object



136
137
138
139
140
141
142
# File 'lib/kdeploy/dsl.rb', line 136

def create_task_block(block)
  lambda {
    @kdeploy_commands = []
    instance_eval(&block)
    @kdeploy_commands
  }
end

#directory(path, mode: nil) ⇒ Object

确保远程目录存在。支持 mode 参数。



221
222
223
224
225
226
227
228
# File 'lib/kdeploy/dsl.rb', line 221

def directory(path, mode: nil)
  @kdeploy_commands ||= []
  cmd = "mkdir -p #{Shellwords.escape(path.to_s)}"
  @kdeploy_commands << { type: :run, command: cmd, sudo: true }
  return unless mode

  @kdeploy_commands << { type: :run, command: "chmod #{mode} #{Shellwords.escape(path.to_s)}", sudo: true }
end

#file(destination, source:) ⇒ Object

上传本地文件到远程路径。



215
216
217
218
# File 'lib/kdeploy/dsl.rb', line 215

def file(destination, source:)
  @kdeploy_commands ||= []
  upload(source, destination)
end

#get_task_hosts(task_name) ⇒ Object



234
235
236
237
238
239
240
241
242
# File 'lib/kdeploy/dsl.rb', line 234

def get_task_hosts(task_name)
  task = kdeploy_tasks[task_name]
  return kdeploy_hosts.keys if task_empty?(task)

  hosts = Set.new
  add_explicit_hosts(task, hosts)
  add_role_hosts(task, hosts)
  hosts.to_a
end

#host(name, **options) ⇒ Object



66
67
68
# File 'lib/kdeploy/dsl.rb', line 66

def host(name, **options)
  kdeploy_hosts[name] = options.merge(name: name)
end

#hostsObject

Stable read accessors for tests/integrations. Keep these as aliases so internal storage can evolve without breaking callers.



42
43
44
# File 'lib/kdeploy/dsl.rb', line 42

def hosts
  kdeploy_hosts
end

#include_tasks(file_path, roles: nil, on: nil) ⇒ Object

Include task file and automatically assign all tasks to specified roles or hosts



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/kdeploy/dsl.rb', line 92

def include_tasks(file_path, roles: nil, on: nil)
  # Resolve relative paths based on the caller's file location
  unless File.absolute_path?(file_path)
    caller_file = caller_locations(1, 1).first.path
    base_dir = File.dirname(File.expand_path(caller_file))
    file_path = File.expand_path(file_path, base_dir)
  end

  # Store tasks before loading
  tasks_before = kdeploy_tasks.keys

  # Load the task file
  module_eval(File.read(file_path), file_path)

  # Get newly added tasks
  tasks_after = kdeploy_tasks.keys
  new_tasks = tasks_after - tasks_before

  # Assign roles/hosts to all new tasks (only if task doesn't already have hosts/roles)
  new_tasks.each do |task_name|
    task = kdeploy_tasks[task_name]
    # Only assign if task doesn't already have hosts or roles defined
    next if task[:hosts] || task[:roles]

    assign_task(task_name, roles: roles, on: on) if roles || on
  end
end

#inventory(&block) ⇒ Object



230
231
232
# File 'lib/kdeploy/dsl.rb', line 230

def inventory(&block)
  instance_eval(&block) if block_given?
end

#kdeploy_hostsObject



54
55
56
# File 'lib/kdeploy/dsl.rb', line 54

def kdeploy_hosts
  @kdeploy_hosts ||= {}
end

#kdeploy_rolesObject



62
63
64
# File 'lib/kdeploy/dsl.rb', line 62

def kdeploy_roles
  @kdeploy_roles ||= {}
end

#kdeploy_tasksObject



58
59
60
# File 'lib/kdeploy/dsl.rb', line 58

def kdeploy_tasks
  @kdeploy_tasks ||= {}
end

#normalize_hosts_option(on) ⇒ Object



120
121
122
123
124
125
126
# File 'lib/kdeploy/dsl.rb', line 120

def normalize_hosts_option(on)
  return on if on.is_a?(Array)

  return [on] if on

  nil
end

#normalize_roles_option(roles) ⇒ Object



128
129
130
131
132
133
134
# File 'lib/kdeploy/dsl.rb', line 128

def normalize_roles_option(roles)
  return roles if roles.is_a?(Array)

  return [roles] if roles

  nil
end

#package(name, version: nil, platform: :apt) ⇒ Object

安装系统包。默认 apt 平台;支持 platform: :yum。



181
182
183
184
185
# File 'lib/kdeploy/dsl.rb', line 181

def package(name, version: nil, platform: :apt)
  @kdeploy_commands ||= []
  cmd = build_package_command(name, version, platform)
  @kdeploy_commands << { type: :run, command: cmd, sudo: true }
end

#role(name, hosts) ⇒ Object



70
71
72
# File 'lib/kdeploy/dsl.rb', line 70

def role(name, hosts)
  kdeploy_roles[name] = hosts
end

#rolesObject



50
51
52
# File 'lib/kdeploy/dsl.rb', line 50

def roles
  kdeploy_roles
end

#run(command, sudo: nil) ⇒ Object



144
145
146
147
# File 'lib/kdeploy/dsl.rb', line 144

def run(command, sudo: nil)
  @kdeploy_commands ||= []
  @kdeploy_commands << { type: :run, command: command, sudo: sudo }
end

#service(name, action: :start) ⇒ Object

管理系统服务(systemd)。action 支持 :start, :stop, :restart, :reload, :enable, :disable。



188
189
190
191
192
193
194
195
# File 'lib/kdeploy/dsl.rb', line 188

def service(name, action: :start)
  @kdeploy_commands ||= []
  actions = Array(action)
  actions.each do |a|
    cmd = "systemctl #{a} #{Shellwords.escape(name.to_s)}"
    @kdeploy_commands << { type: :run, command: cmd, sudo: true }
  end
end

#sync(source, destination, ignore: [], delete: false, exclude: []) ⇒ Object



164
165
166
167
168
169
170
171
172
173
174
# File 'lib/kdeploy/dsl.rb', line 164

def sync(source, destination, ignore: [], delete: false, exclude: [])
  @kdeploy_commands ||= []
  @kdeploy_commands << {
    type: :sync,
    source: source,
    destination: destination,
    ignore: Array(ignore),
    exclude: Array(exclude),
    delete: delete
  }
end

#task(name, on: nil, roles: nil, &block) ⇒ Object



74
75
76
77
78
79
80
# File 'lib/kdeploy/dsl.rb', line 74

def task(name, on: nil, roles: nil, &block)
  kdeploy_tasks[name] = {
    hosts: normalize_hosts_option(on),
    roles: normalize_roles_option(roles),
    block: create_task_block(block)
  }
end

#task_empty?(task) ⇒ Boolean

Returns:

  • (Boolean)


244
245
246
# File 'lib/kdeploy/dsl.rb', line 244

def task_empty?(task)
  !task || (!task[:hosts] && !task[:roles])
end

#tasksObject



46
47
48
# File 'lib/kdeploy/dsl.rb', line 46

def tasks
  kdeploy_tasks
end

#template(destination, source: nil, variables: nil, &block) ⇒ Object

部署 ERB 模板到远程路径。支持 block 或关键字参数。



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/kdeploy/dsl.rb', line 198

def template(destination, source: nil, variables: nil, &block)
  @kdeploy_commands ||= []
  if block
    opts = TemplateOptions.new
    opts.instance_eval(&block)
    src = opts.source || raise(ArgumentError, 'template requires source')
    vars = opts.variables || {}
  else
    raise ArgumentError, 'template requires source' unless source

    src = source
    vars = variables || {}
  end
  upload_template(src, destination, vars)
end

#upload(source, destination) ⇒ Object



149
150
151
152
# File 'lib/kdeploy/dsl.rb', line 149

def upload(source, destination)
  @kdeploy_commands ||= []
  @kdeploy_commands << { type: :upload, source: source, destination: destination }
end

#upload_template(source, destination, variables = {}) ⇒ Object



154
155
156
157
158
159
160
161
162
# File 'lib/kdeploy/dsl.rb', line 154

def upload_template(source, destination, variables = {})
  @kdeploy_commands ||= []
  @kdeploy_commands << {
    type: :upload_template,
    source: source,
    destination: destination,
    variables: variables
  }
end