seijaku 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|