seijaku 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +16 -2
- data/docs/1 - basic.md +10 -0
- data/docs/2 - variables.md +24 -0
- data/docs/3 - ssh.md +78 -0
- data/lib/seijaku/executors/bash.rb +27 -0
- data/lib/seijaku/executors/sh.rb +27 -0
- data/lib/seijaku/executors/ssh.rb +43 -0
- data/lib/seijaku/payload.rb +33 -0
- data/lib/seijaku/ssh/group.rb +15 -0
- data/lib/seijaku/ssh/host.rb +15 -0
- data/lib/seijaku/step.rb +50 -0
- data/lib/seijaku/task.rb +32 -0
- data/lib/seijaku/variable.rb +24 -0
- data/lib/seijaku/version.rb +1 -1
- data/lib/seijaku.rb +4 -0
- data/seijaku.gemspec +3 -5
- metadata +57 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 63fdce33cce3f7b4496f7ba0968a069554f351f5b1ea37d84c0dc20b7cfb3d49
|
4
|
+
data.tar.gz: 01fc22c0d064e13bbf514633545ec4f947176a531bf57fe479294d816f60154b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ffc15482a72317f1617f65374e2de9dd86008d7d4faa64dc3a34fe9dc9e5f940f77982f2a009a82c54613b1226135e3b8a58a2a37d126e6b0a6b105d4de7b0f6
|
7
|
+
data.tar.gz: 3bdfd31aac02d9bdd5be33b5f77f05327ac4eaa05af82654b00d9fb4c30382be07c0879ae1ac91deaaf36468e934a6f0696b7e6223659332de25a2ed22f31fca
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@ Seijaku is a program that allows you to execute shell commands listed in YAML pa
|
|
6
6
|
|
7
7
|
Payloads are YAML files that describe the various tasks Seijaku will have to perform (in order). Each task contains one or more steps.
|
8
8
|
|
9
|
-
A step is a shell command to be executed. Seijaku currently supports the following shells: bash and
|
9
|
+
A step is a shell command to be executed. Seijaku currently supports the following shells: bash, sh and ssh.
|
10
10
|
|
11
11
|
Each task can have "pre" and "post" tasks, for example to create and delete folders, or install and uninstall software needed to run a task.
|
12
12
|
|
@@ -17,6 +17,11 @@ A step sometimes needs variables in order to be performed correctly: Seijaku sup
|
|
17
17
|
```yaml
|
18
18
|
name: my payload
|
19
19
|
|
20
|
+
ssh:
|
21
|
+
- host: my-host
|
22
|
+
user: user
|
23
|
+
port: 22
|
24
|
+
|
20
25
|
variables:
|
21
26
|
MY_VARIABLE: a static variable
|
22
27
|
MY_ENV_VARIABLE: $MY_ENV_VARIABLE
|
@@ -37,6 +42,11 @@ tasks:
|
|
37
42
|
soft_fail: true
|
38
43
|
post:
|
39
44
|
- sh: "do something after"
|
45
|
+
|
46
|
+
- name: task with SSH executor
|
47
|
+
host: my-host
|
48
|
+
steps:
|
49
|
+
- ssh: echo "executed on host"
|
40
50
|
```
|
41
51
|
|
42
52
|
## Installation
|
@@ -46,11 +56,15 @@ tasks:
|
|
46
56
|
* Ruby 2.5+
|
47
57
|
* Rubygem
|
48
58
|
|
49
|
-
|
50
59
|
Install Seijaku using Gem:
|
51
60
|
|
52
61
|
```bash
|
53
62
|
gem install seijaku
|
54
63
|
```
|
55
64
|
|
65
|
+
## Usage
|
56
66
|
|
67
|
+
```bash
|
68
|
+
seijaku -h
|
69
|
+
seijaku -f ./my-payload.yaml
|
70
|
+
```
|
data/docs/1 - basic.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# Variables
|
2
|
+
|
3
|
+
Steps can call environment variables, listed in the `variables` dictionary.
|
4
|
+
|
5
|
+
Variable values can either be given in the payload (static) or be defined from the executive shell.
|
6
|
+
|
7
|
+
```yaml
|
8
|
+
name: payload with variables
|
9
|
+
|
10
|
+
variables:
|
11
|
+
ENV_VARIABLE: value
|
12
|
+
PROVIDED_ENV_VARIABLE: $PROVIDED_ENV_VARIABLE
|
13
|
+
|
14
|
+
tasks:
|
15
|
+
- name: do something useful
|
16
|
+
steps:
|
17
|
+
- sh: echo "sh executor: $ENV_VARIABLE"
|
18
|
+
- bash: echo "bash executor: $PROVIDED_ENV_VARIABLE"
|
19
|
+
```
|
20
|
+
|
21
|
+
```bash
|
22
|
+
export PROVIDED_ENV_VARIABLE=value
|
23
|
+
seijaku -f ./docs/variables.yaml
|
24
|
+
```
|
data/docs/3 - ssh.md
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
# SSH
|
2
|
+
|
3
|
+
Since version `0.1.0`, Seijaku implements a SSH step executor. Only one SSH host can be set per task.
|
4
|
+
|
5
|
+
|
6
|
+
```yaml
|
7
|
+
name: payload with SSH executor
|
8
|
+
|
9
|
+
ssh:
|
10
|
+
- host: my-host
|
11
|
+
port: 22
|
12
|
+
user: ubuntu
|
13
|
+
|
14
|
+
tasks:
|
15
|
+
- name: do something useful
|
16
|
+
host: my-host
|
17
|
+
steps:
|
18
|
+
- ssh: echo "connection test"
|
19
|
+
```
|
20
|
+
|
21
|
+
It is recommended to run `ssh-add` before running Seijaku, as each step would require you to enter your password.
|
22
|
+
|
23
|
+
## Variables
|
24
|
+
|
25
|
+
Variables can be set from the Seijaku executive host and ran in the SSH host context:
|
26
|
+
|
27
|
+
```yaml
|
28
|
+
name: payload with SSH executor (variables)
|
29
|
+
|
30
|
+
ssh:
|
31
|
+
- host: my-host
|
32
|
+
port: 22
|
33
|
+
user: ubuntu
|
34
|
+
|
35
|
+
variables:
|
36
|
+
ENV_VARIABLE: value
|
37
|
+
PROVIDED_ENV_VARIABLE: $PROVIDED_ENV_VARIABLE
|
38
|
+
|
39
|
+
tasks:
|
40
|
+
- name: do something useful
|
41
|
+
host: my-host
|
42
|
+
steps:
|
43
|
+
- ssh: echo "connection test"
|
44
|
+
- ssh: echo "$ENV_VARIABLE"
|
45
|
+
- ssh: echo "$PROVIDED_ENV_VARIABLE"
|
46
|
+
```
|
47
|
+
|
48
|
+
## Bastion / Jump host / Proxy
|
49
|
+
|
50
|
+
You sometimes need to jump over a publicly exposed server to reach another one. Seijaku supports SSH Proxy, with a maximum of 1 hop.
|
51
|
+
|
52
|
+
```yaml
|
53
|
+
name: payload with SSH executor (bastion)
|
54
|
+
|
55
|
+
ssh:
|
56
|
+
- host: bastion-host
|
57
|
+
port: 22
|
58
|
+
user: bastion_user
|
59
|
+
|
60
|
+
- host: my-host
|
61
|
+
port: 22
|
62
|
+
user: ubuntu
|
63
|
+
bastion: bastion-host
|
64
|
+
|
65
|
+
variables:
|
66
|
+
ENV_VARIABLE: value
|
67
|
+
PROVIDED_ENV_VARIABLE: $PROVIDED_ENV_VARIABLE
|
68
|
+
|
69
|
+
tasks:
|
70
|
+
- name: do something useful
|
71
|
+
host: my-host
|
72
|
+
steps:
|
73
|
+
- ssh: echo "connection test"
|
74
|
+
- ssh: echo "$ENV_VARIABLE"
|
75
|
+
- ssh: echo "$PROVIDED_ENV_VARIABLE"
|
76
|
+
```
|
77
|
+
|
78
|
+
The `ssh.[].host` must be a reachable address (domain name or IP).
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "open3"
|
4
|
+
|
5
|
+
module Seijaku
|
6
|
+
# executes `bash` commands
|
7
|
+
class BashExecutor
|
8
|
+
def initialize(raw, variables)
|
9
|
+
@command = ["bash", "-c", "'#{raw}'"]
|
10
|
+
@variables = variables
|
11
|
+
end
|
12
|
+
|
13
|
+
def run!
|
14
|
+
stdout, stderr, exit_status = Open3.capture3(
|
15
|
+
@variables,
|
16
|
+
@command.join(" ")
|
17
|
+
)
|
18
|
+
|
19
|
+
{
|
20
|
+
stdout: stdout.chomp,
|
21
|
+
stderr: stderr.chomp,
|
22
|
+
exit_status: exit_status.exitstatus,
|
23
|
+
command: @command.join(" ")
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "open3"
|
4
|
+
|
5
|
+
module Seijaku
|
6
|
+
# Execute `sh` commands
|
7
|
+
class SHExecutor
|
8
|
+
def initialize(raw, variables)
|
9
|
+
@command = ["sh", "-c", "'#{raw}'"]
|
10
|
+
@variables = variables
|
11
|
+
end
|
12
|
+
|
13
|
+
def run!
|
14
|
+
stdout, stderr, exit_status = Open3.capture3(
|
15
|
+
@variables,
|
16
|
+
@command.join(" ")
|
17
|
+
)
|
18
|
+
|
19
|
+
{
|
20
|
+
stdout: stdout.chomp,
|
21
|
+
stderr: stderr.chomp,
|
22
|
+
exit_status: exit_status.exitstatus,
|
23
|
+
command: @command.join(" ")
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "net/ssh"
|
4
|
+
require "net/ssh/proxy/jump"
|
5
|
+
|
6
|
+
module Seijaku
|
7
|
+
# SSHExecutor connects to SSH host and runs command
|
8
|
+
class SSHExecutor
|
9
|
+
def initialize(raw, variables, task, ssh_hosts)
|
10
|
+
@command = ["sh", "-c", "'#{raw}'"]
|
11
|
+
@hosts = ssh_hosts
|
12
|
+
@variables = variables
|
13
|
+
@task = task
|
14
|
+
@ssh_hosts = ssh_hosts
|
15
|
+
|
16
|
+
raise SSHExecutorError, "no ssh host defined in payload", [] if ssh_hosts.nil?
|
17
|
+
end
|
18
|
+
|
19
|
+
def run!
|
20
|
+
machine = @ssh_hosts.hosts[@task.host]
|
21
|
+
result = { command: @command.join(" "), stdout: nil, stderr: nil }
|
22
|
+
status = {}
|
23
|
+
options = machine.bastion ? { proxy: bastion_setup } : {}
|
24
|
+
ssh = Net::SSH.start(machine.host, machine.user, port: machine.port, **options)
|
25
|
+
ssh.exec!(@command.join(" "), status: status) do |_ch, stream, data|
|
26
|
+
result[:stdout] = data.chomp if stream == :stdout
|
27
|
+
result[:stderr] = data.chomp unless stream == :stdout
|
28
|
+
end
|
29
|
+
ssh.close
|
30
|
+
|
31
|
+
result[:exit_status] = status[:exit_code]
|
32
|
+
result
|
33
|
+
end
|
34
|
+
|
35
|
+
def bastion_setup
|
36
|
+
bastion_name = @ssh_hosts.hosts[@task.host].bastion
|
37
|
+
bastion_host = @ssh_hosts.hosts[bastion_name]
|
38
|
+
|
39
|
+
connect_str = "#{bastion_host.user}@#{bastion_host.host}:#{bastion_host.port}"
|
40
|
+
Net::SSH::Proxy::Jump.new(connect_str)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Seijaku
|
4
|
+
# Payload refers to the payload YAML file submitted by the user.
|
5
|
+
# it includes tasks, steps, variables and name.
|
6
|
+
class Payload
|
7
|
+
def initialize(payload, logger)
|
8
|
+
@name = payload.fetch("name")
|
9
|
+
@variables = get_variables(payload.fetch("variables"))
|
10
|
+
@ssh_hosts = SSHGroup.new(payload.fetch("ssh", []))
|
11
|
+
@tasks = payload.fetch("tasks").map do |task|
|
12
|
+
Task.new(task, @variables, logger, @ssh_hosts)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute!
|
17
|
+
@tasks.each(&:execute!)
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_variables(payload_variables)
|
21
|
+
sorted_variables = {}
|
22
|
+
variables = payload_variables.map do |var|
|
23
|
+
Variable.new(var)
|
24
|
+
end
|
25
|
+
|
26
|
+
variables.each do |var|
|
27
|
+
sorted_variables.merge!({ var.key => var.value })
|
28
|
+
end
|
29
|
+
|
30
|
+
sorted_variables
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Seijaku
|
4
|
+
# Host of SSHGroup
|
5
|
+
class Host
|
6
|
+
attr_reader :host, :port, :user, :bastion
|
7
|
+
|
8
|
+
def initialize(host)
|
9
|
+
@host = host.fetch("host", "localhost")
|
10
|
+
@port = host.fetch("port", 22)
|
11
|
+
@bastion = host.fetch("bastion", nil)
|
12
|
+
@user = host.fetch("user", "root")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/seijaku/step.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Seijaku
|
4
|
+
# A step is a bash or sh command that must be executed.
|
5
|
+
# steps execution is always fifo.
|
6
|
+
class Step
|
7
|
+
attr_reader :command, :pipeline
|
8
|
+
|
9
|
+
def initialize(step, variables, pipeline, logger, task, ssh_hosts = nil)
|
10
|
+
@sh = step.fetch("sh", nil)
|
11
|
+
@bash = step.fetch("bash", nil)
|
12
|
+
@ssh = step.fetch("ssh", nil)
|
13
|
+
@soft_fail = step.fetch("soft_fail", false)
|
14
|
+
@output = step.fetch("output", false)
|
15
|
+
@variables = variables
|
16
|
+
@pipeline = pipeline
|
17
|
+
@logger = logger
|
18
|
+
@task = task
|
19
|
+
@ssh_hosts = ssh_hosts
|
20
|
+
|
21
|
+
@command = (@sh || @bash) || @ssh
|
22
|
+
end
|
23
|
+
|
24
|
+
def execute!
|
25
|
+
result = SHExecutor.new(@sh, @variables).run! if @sh
|
26
|
+
result = BashExecutor.new(@bash, @variables).run! if @bash
|
27
|
+
result = SSHExecutor.new(@ssh, @variables, @task, @ssh_hosts).run!
|
28
|
+
|
29
|
+
if result[:exit_status] != 0
|
30
|
+
logger_output(result)
|
31
|
+
exit(1) unless @soft_fail
|
32
|
+
end
|
33
|
+
|
34
|
+
return unless @output
|
35
|
+
|
36
|
+
%i[stdout stderr].each do |stream|
|
37
|
+
puts format("%<stream>s:\t %<result>s", stream: stream, result: result[stream])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def logger_output(result)
|
42
|
+
@logger.info <<~OUTPUT
|
43
|
+
command: `#{result[:command]}`
|
44
|
+
exit_code: #{result[:exit_status]}
|
45
|
+
stdout: #{result[:stdout]}
|
46
|
+
stderr: #{result[:stderr]}"
|
47
|
+
OUTPUT
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/seijaku/task.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Seijaku
|
4
|
+
# Task is composed of a name, an array of Step (Pre, Post)
|
5
|
+
class Task
|
6
|
+
attr_reader :host
|
7
|
+
|
8
|
+
def initialize(task, variables, logger, ssh_hosts = nil)
|
9
|
+
@name = task.fetch("name", nil)
|
10
|
+
@host = task.fetch("host", nil)
|
11
|
+
@steps = task.fetch("steps", []).map { |step| Step.new(step, variables, :steps, logger, self, ssh_hosts) }
|
12
|
+
@pre_steps = task.fetch("pre", []).map { |step| Step.new(step, variables, :pre, logger, self, ssh_hosts) }
|
13
|
+
@post_steps = task.fetch("post", []).map { |step| Step.new(step, variables, :post, logger, self, ssh_hosts) }
|
14
|
+
@logger = logger
|
15
|
+
|
16
|
+
raise TaskError, "no name set in task", [] if @name.nil?
|
17
|
+
end
|
18
|
+
|
19
|
+
def execute!
|
20
|
+
[@pre_steps, @steps, @post_steps].each do |pipeline_steps|
|
21
|
+
pipeline_steps.each do |step|
|
22
|
+
@logger.info(format("[%<name>s:%<pipeline>s] running %<command>s",
|
23
|
+
name: @name,
|
24
|
+
pipeline: step.pipeline,
|
25
|
+
command: step.command))
|
26
|
+
|
27
|
+
step.execute!
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Seijaku
|
4
|
+
# variable
|
5
|
+
class Variable
|
6
|
+
attr_reader :key, :value
|
7
|
+
|
8
|
+
def initialize(variable)
|
9
|
+
@key = variable.first
|
10
|
+
|
11
|
+
value = variable.last
|
12
|
+
@value = value
|
13
|
+
|
14
|
+
return unless value.start_with?("$")
|
15
|
+
|
16
|
+
env_key = value.split("$").last
|
17
|
+
env_value = ENV.fetch(env_key, nil)
|
18
|
+
|
19
|
+
raise VariableError, "no value set for #{env_key}", [] if env_value.nil?
|
20
|
+
|
21
|
+
@value = env_value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/seijaku/version.rb
CHANGED
data/lib/seijaku.rb
CHANGED
@@ -5,12 +5,16 @@ require_relative "seijaku/payload"
|
|
5
5
|
require_relative "seijaku/variable"
|
6
6
|
require_relative "seijaku/task"
|
7
7
|
require_relative "seijaku/step"
|
8
|
+
require_relative "seijaku/ssh/group"
|
9
|
+
require_relative "seijaku/ssh/host"
|
8
10
|
|
9
11
|
require_relative "seijaku/executors/sh"
|
10
12
|
require_relative "seijaku/executors/bash"
|
13
|
+
require_relative "seijaku/executors/ssh"
|
11
14
|
|
12
15
|
module Seijaku
|
13
16
|
class Error < StandardError; end
|
14
17
|
class VariableError < Error; end
|
15
18
|
class TaskError < Error; end
|
19
|
+
class SSHExecutorError < Error; end
|
16
20
|
end
|
data/seijaku.gemspec
CHANGED
@@ -32,9 +32,7 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.executables = %w[seijaku]
|
33
33
|
spec.require_paths = ["lib"]
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
# For more information and examples about making a new gem, check out our
|
39
|
-
# guide at: https://bundler.io/guides/creating_gem.html
|
35
|
+
spec.add_dependency "bcrypt_pbkdf", "~> 1.1"
|
36
|
+
spec.add_dependency "ed25519", "~> 1.3"
|
37
|
+
spec.add_dependency "net-ssh", "~> 7.2"
|
40
38
|
end
|
metadata
CHANGED
@@ -1,15 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: seijaku
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kohlrabbit
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-12-
|
12
|
-
dependencies:
|
11
|
+
date: 2023-12-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bcrypt_pbkdf
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: ed25519
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.3'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: net-ssh
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '7.2'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '7.2'
|
13
55
|
description: CLI tool ingesting YAML files to execute tasks on different shells with
|
14
56
|
pre and post routines.
|
15
57
|
email:
|
@@ -26,7 +68,19 @@ files:
|
|
26
68
|
- README.md
|
27
69
|
- Rakefile
|
28
70
|
- bin/seijaku
|
71
|
+
- docs/1 - basic.md
|
72
|
+
- docs/2 - variables.md
|
73
|
+
- docs/3 - ssh.md
|
29
74
|
- lib/seijaku.rb
|
75
|
+
- lib/seijaku/executors/bash.rb
|
76
|
+
- lib/seijaku/executors/sh.rb
|
77
|
+
- lib/seijaku/executors/ssh.rb
|
78
|
+
- lib/seijaku/payload.rb
|
79
|
+
- lib/seijaku/ssh/group.rb
|
80
|
+
- lib/seijaku/ssh/host.rb
|
81
|
+
- lib/seijaku/step.rb
|
82
|
+
- lib/seijaku/task.rb
|
83
|
+
- lib/seijaku/variable.rb
|
30
84
|
- lib/seijaku/version.rb
|
31
85
|
- seijaku.gemspec
|
32
86
|
- sig/seijaku.rbs
|