minfra-cli 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.dockerignore +12 -0
- data/.gitignore +16 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +2 -0
- data/Dockerfile +12 -0
- data/bin/build +20 -0
- data/bin/console +16 -0
- data/bin/container_exec +9 -0
- data/bin/run_tests +74 -0
- data/bin/setup.sh +22 -0
- data/exe/minfra +6 -0
- data/lib/deep_merge.rb +149 -0
- data/lib/hash.rb +28 -0
- data/lib/minfra/cli/ask.rb +43 -0
- data/lib/minfra/cli/command.rb +35 -0
- data/lib/minfra/cli/commands/dev.rb +54 -0
- data/lib/minfra/cli/commands/kube.rb +279 -0
- data/lib/minfra/cli/commands/project/branch.rb +17 -0
- data/lib/minfra/cli/commands/project/tag.rb +40 -0
- data/lib/minfra/cli/commands/project.rb +113 -0
- data/lib/minfra/cli/commands/setup.rb +49 -0
- data/lib/minfra/cli/commands/stack/app_template.rb +65 -0
- data/lib/minfra/cli/commands/stack/client_template.rb +36 -0
- data/lib/minfra/cli/commands/stack/kube_stack_template.rb +94 -0
- data/lib/minfra/cli/commands/stack.rb +120 -0
- data/lib/minfra/cli/commands/tag.rb +86 -0
- data/lib/minfra/cli/common.rb +41 -0
- data/lib/minfra/cli/config.rb +111 -0
- data/lib/minfra/cli/document.rb +19 -0
- data/lib/minfra/cli/hook.rb +65 -0
- data/lib/minfra/cli/logging.rb +26 -0
- data/lib/minfra/cli/main_command.rb +32 -0
- data/lib/minfra/cli/plugins.rb +34 -0
- data/lib/minfra/cli/runner.rb +59 -0
- data/lib/minfra/cli/templater.rb +63 -0
- data/lib/minfra/cli/version.rb +5 -0
- data/lib/minfra/cli.rb +80 -0
- data/lib/orchparty/ast.rb +53 -0
- data/lib/orchparty/cli.rb +69 -0
- data/lib/orchparty/context.rb +22 -0
- data/lib/orchparty/dsl_parser.rb +229 -0
- data/lib/orchparty/dsl_parser_kubernetes.rb +361 -0
- data/lib/orchparty/kubernetes_application.rb +305 -0
- data/lib/orchparty/plugin.rb +24 -0
- data/lib/orchparty/plugins/env.rb +41 -0
- data/lib/orchparty/transformations/all.rb +18 -0
- data/lib/orchparty/transformations/mixin.rb +73 -0
- data/lib/orchparty/transformations/remove_internal.rb +16 -0
- data/lib/orchparty/transformations/sort.rb +10 -0
- data/lib/orchparty/transformations/variable.rb +56 -0
- data/lib/orchparty/transformations.rb +24 -0
- data/lib/orchparty/version.rb +3 -0
- data/lib/orchparty.rb +59 -0
- data/minfra-cli.gemspec +40 -0
- data/project.json +7 -0
- data/templates/kind.yaml.erb +33 -0
- data/templates/kube_config.yaml.erb +7 -0
- data/templates/minfra_config.json.erb +26 -0
- metadata +196 -0
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require_relative 'stack/app_template'
|
3
|
+
require_relative 'stack/client_template'
|
4
|
+
require_relative 'stack/kube_stack_template'
|
5
|
+
|
6
|
+
module Minfra
|
7
|
+
module Cli
|
8
|
+
class Stack < Command
|
9
|
+
|
10
|
+
desc "describe","get information about a stack"
|
11
|
+
option :environment, aliases: ['-e']
|
12
|
+
def describe
|
13
|
+
pp @minfra_config.describe(options["environment"])
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "dashboard <stack_name>", "openening a dashboard for a stack"
|
17
|
+
option :environment, aliases: ['-e']
|
18
|
+
option :deployment, aliases: ['-d']
|
19
|
+
option :cluster, aliases: ['-c']
|
20
|
+
def dashboard(stack_name='all')
|
21
|
+
kube.dashboard(stack_name, options[:environment], options[:deployment], options[:cluster])
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "deploy <stack_name> '<message> (optional)'", "deploy a complete stack"
|
25
|
+
option :environment, aliases: ['-e']
|
26
|
+
# option :deployment, aliases: ['-d']
|
27
|
+
option :cluster, aliases: ['-c']
|
28
|
+
option :dev, type: :boolean # currently, about to be changed
|
29
|
+
option :explain, type: :boolean
|
30
|
+
option :install, type: :boolean
|
31
|
+
option :test, type: :boolean
|
32
|
+
option :opts
|
33
|
+
def deploy(stack_name, message='')
|
34
|
+
kube.deploy(stack_name, message)
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "rollback <extraargs>", "rollback a deployment"
|
38
|
+
option :environment, aliases: ['-e']
|
39
|
+
option :deployment, aliases: ['-d']
|
40
|
+
option :cluster, aliases: ['-c']
|
41
|
+
def rollback(*args)
|
42
|
+
STDERR.puts "needs implementation"
|
43
|
+
exit 1
|
44
|
+
#kube.rollback(stack_name, options[:environment], options[:deployment], options[:cluster], args)
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "destroy", "remove the whole stack"
|
48
|
+
option :environment, aliases: ['-e']
|
49
|
+
option :cluster, aliases: ['-c']
|
50
|
+
def destroy(stack_name)
|
51
|
+
kube.destroy(stack_name)
|
52
|
+
end
|
53
|
+
|
54
|
+
desc "list", "list all stacks in an environment"
|
55
|
+
option :environment, aliases: ['-e']
|
56
|
+
option :cluster, aliases: ['-c']
|
57
|
+
def list
|
58
|
+
kube.list
|
59
|
+
end
|
60
|
+
|
61
|
+
desc "app", "show the app a stack provides"
|
62
|
+
option :environment, aliases: ['-e']
|
63
|
+
def app(stack_name)
|
64
|
+
cluster=nil
|
65
|
+
deployment=nil
|
66
|
+
template = Minfra::Cli::StackM::AppTemplate.new(stack_name, minfra_config)
|
67
|
+
template.read
|
68
|
+
puts "Template: #{template.app_path}\n#{template.to_s}"
|
69
|
+
apps = AppResource.all(filter: {identifier: template.app.identifier} ).data
|
70
|
+
if apps.empty?
|
71
|
+
puts "Auth: app uninstalled"
|
72
|
+
atts = {
|
73
|
+
identifier: template.app.identifier,
|
74
|
+
name: template.app.name,
|
75
|
+
short_name: template.app.short_name,
|
76
|
+
description: template.app.description,
|
77
|
+
native: template.app.native,
|
78
|
+
start_url: template.app.start_url,
|
79
|
+
public: template.app.public
|
80
|
+
}
|
81
|
+
app_res=AppResource.build( {data: {attributes: atts, type: 'apps'}} )
|
82
|
+
app_res.save
|
83
|
+
app = app_res.data
|
84
|
+
else
|
85
|
+
app = apps.first
|
86
|
+
end
|
87
|
+
puts "Auth: app installed #{app.id}"
|
88
|
+
|
89
|
+
clients = OauthClientResource.all(filter: {app_id: app.id}).data
|
90
|
+
if clients.empty?
|
91
|
+
puts "Auth: client not registered"
|
92
|
+
atts= {redirect_uris: template.client.redirect_uris.map { |r| "#{app.start_url}#{r}" },
|
93
|
+
native: template.client.native, ppid: template.client.ppid, name: template.client.name, app_id: app.id}
|
94
|
+
client_res=OauthClientResource.build( {data: {attributes: atts, type: 'oauth_clients'}} )
|
95
|
+
client_res.save
|
96
|
+
client=client_res.data
|
97
|
+
else
|
98
|
+
client = clients.first
|
99
|
+
end
|
100
|
+
puts "Auth: client registered #{client.id}"
|
101
|
+
|
102
|
+
client_template=Minfra::Cli::StackM::ClientTemplate.new(stack_name, client.name, minfra_config)
|
103
|
+
unless client_template.exist?
|
104
|
+
File.write(client_template.path.to_s, { name: client.name, identifier: client.identifier, secret: client.secret }.to_json)
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
# TODO: create app configuration
|
109
|
+
# TODO: create provider
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
def kube
|
114
|
+
Kube.new(options, @minfra_config)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
Minfra::Cli.register("stack", "dealing wit stacks", Minfra::Cli::Stack)
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Minfra
|
4
|
+
module Cli
|
5
|
+
class Tag
|
6
|
+
include Minfra::Cli::Logging
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@now = Time.now.utc
|
10
|
+
@format = '%Y_%m_%dT%H_%M_%SZ'
|
11
|
+
@tags_folder = '.tags'
|
12
|
+
end
|
13
|
+
|
14
|
+
def tag_current_commit_for_deploy(message, format)
|
15
|
+
@format = format if format
|
16
|
+
|
17
|
+
info 'Creating tag.'
|
18
|
+
debug "Using .tags folder..."
|
19
|
+
write_tag_folder_file(message)
|
20
|
+
run_cmd(cmd_add_tag_info("#{@tags_folder}/#{tag_name}"), :system)
|
21
|
+
run_cmd(cmd_create_tag_commit, :system)
|
22
|
+
run_cmd(cmd_tag_commit(message), :system)
|
23
|
+
info 'Pushing tag to remote.'
|
24
|
+
run_cmd(cmd_push, :system)
|
25
|
+
run_cmd(cmd_push_tag, :system)
|
26
|
+
end
|
27
|
+
|
28
|
+
def ensure_commit_is_pushed
|
29
|
+
info 'Checking that the current commit is present on the remote.'
|
30
|
+
output = run_cmd(cmd_ensure_commit_is_pushed)
|
31
|
+
|
32
|
+
if output.empty?
|
33
|
+
exit_error "The current commit is not present on the remote.\n" \
|
34
|
+
'Please push your changes to origin and try again.'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def cmd_ensure_commit_is_pushed
|
39
|
+
'git branch -r --contains $(git rev-list --max-count=1 HEAD)'
|
40
|
+
end
|
41
|
+
|
42
|
+
def cmd_add_tag_info(file)
|
43
|
+
"git add #{file}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def cmd_create_tag_commit
|
47
|
+
"git commit -m '#{tag_name}'"
|
48
|
+
end
|
49
|
+
|
50
|
+
def cmd_tag_commit(message)
|
51
|
+
"git tag -a #{tag_name} -m '#{message}'"
|
52
|
+
end
|
53
|
+
|
54
|
+
def cmd_push
|
55
|
+
"git push"
|
56
|
+
end
|
57
|
+
|
58
|
+
def cmd_push_tag
|
59
|
+
"git push origin #{tag_name}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def git_current_branch
|
63
|
+
`git rev-parse --abbrev-ref HEAD`.strip
|
64
|
+
end
|
65
|
+
|
66
|
+
#TBD: this should be more flexible
|
67
|
+
def tag_name
|
68
|
+
"#{git_current_branch}-REL-#{@now.strftime(@format)}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def write_tag_folder_file(message)
|
72
|
+
File.write("#{@tags_folder}/#{tag_name}", "#{message}\n")
|
73
|
+
end
|
74
|
+
|
75
|
+
def write_tag_file(_message)
|
76
|
+
File.write(@tags_file.to_s, "#{tag_name}\n")
|
77
|
+
end
|
78
|
+
|
79
|
+
def run_cmd(cmd, _how = :system)
|
80
|
+
Runner.run(cmd)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
#Minfra::Cli.register("tag", "creating tags", Minfra::Cli::Tag)
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module Minfra
|
4
|
+
module Cli
|
5
|
+
module Common
|
6
|
+
|
7
|
+
def exit_error(msg)
|
8
|
+
STDERR.puts("ERROR: #{msg}" )
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
|
12
|
+
def run_cmd(cmd, type = :non_system, silence: false)
|
13
|
+
puts cmd unless silence
|
14
|
+
case type
|
15
|
+
when :exec
|
16
|
+
Kernel.exec(cmd)
|
17
|
+
when :bash
|
18
|
+
res = system(%{bash -c "#{Array.new(cmd).join(' && ')}"})
|
19
|
+
exit_error("failed!") unless res
|
20
|
+
nil # no output!
|
21
|
+
when :system
|
22
|
+
res = system(cmd)
|
23
|
+
exit_error("failed!") unless res
|
24
|
+
nil # no output!
|
25
|
+
else
|
26
|
+
`#{cmd}` # with output!
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def parse_cmd(cmd, silence: false)
|
31
|
+
reply = JSON.parse(run_cmd(cmd, silence: silence))
|
32
|
+
rescue JSON::ParserError, TypeError
|
33
|
+
error "ERROR: #{$ERROR_INFO.message}"
|
34
|
+
error reply
|
35
|
+
error "command was: #{cmd}"
|
36
|
+
exit 1
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'hashie/mash'
|
3
|
+
require 'json'
|
4
|
+
require_relative 'templater'
|
5
|
+
module Minfra
|
6
|
+
module Cli
|
7
|
+
# responsible the read the config file(s) and add a small abstraction layer on top of it
|
8
|
+
class Config
|
9
|
+
include Logging
|
10
|
+
|
11
|
+
class ConfigNotFoundError < StandardError
|
12
|
+
end
|
13
|
+
class EnvironmentNotFoundError < StandardError
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :base_path
|
17
|
+
attr_reader :config_path
|
18
|
+
attr_reader :stacks_path
|
19
|
+
attr_reader :me_path
|
20
|
+
attr_reader :kube_path
|
21
|
+
attr_reader :kube_config_path
|
22
|
+
attr_reader :kind_config_path
|
23
|
+
|
24
|
+
attr_reader :orch_env
|
25
|
+
attr_reader :orch_env_config
|
26
|
+
attr_reader :config
|
27
|
+
attr_reader :project
|
28
|
+
|
29
|
+
def self.load(orch_env, base_path_str = nil)
|
30
|
+
new(base_path_str).load(orch_env)
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(base_path_str=nil)
|
34
|
+
init!(base_path_str)
|
35
|
+
end
|
36
|
+
|
37
|
+
def init!(base_path_str=nil)
|
38
|
+
debug( "Config: initializing" )
|
39
|
+
@base_path = Pathname.new(base_path_str || ENV["MINFRA_PATH"]).expand_path
|
40
|
+
@me_path = @base_path.join('me')
|
41
|
+
@project_config_path=@base_path.join("config","project.json")
|
42
|
+
@config_path = @me_path.join('config.json')
|
43
|
+
@stacks_path = @base_path.join('stacks')
|
44
|
+
@kube_path=@me_path.join('kube')
|
45
|
+
@kube_config_path=@kube_path.join('config')
|
46
|
+
@kind_config_path=@me_path.join("kind.yaml.erb")
|
47
|
+
@project_minfrarc_path = @base_path.join("config",'minfrarc.rb')
|
48
|
+
require @project_minfrarc_path if @project_minfrarc_path.exist?
|
49
|
+
@me_minfrarc_path = @me_path.join('minfrarc.rb')
|
50
|
+
require @me_minfrarc_path if @me_minfrarc_path.exist?
|
51
|
+
if config_path.exist?
|
52
|
+
@config = Hashie::Mash.new(JSON.parse(Minfra::Cli::Templater.render(File.read(config_path),{})))
|
53
|
+
else
|
54
|
+
warn("personal minfra configuration file '#{config_path}' not found, you might have to run 'minfra setup dev'")
|
55
|
+
@config = Hashie::Mash.new({})
|
56
|
+
end
|
57
|
+
@project = Hashie::Mash.new(JSON.parse(Minfra::Cli::Templater.render(File.read(@project_config_path),{})))
|
58
|
+
end
|
59
|
+
|
60
|
+
def load(orch_env)
|
61
|
+
debug( "loading config env: #{orch_env} #{@orch_env}" )
|
62
|
+
return self if defined?(@orch_env)
|
63
|
+
@orch_env = orch_env
|
64
|
+
@orch_env_config = @config.environments[@orch_env] || raise(EnvironmentNotFoundError.new("Configuration for orchestration environment '#{@orch_env}' not found. Available orechstration environments: #{@config.environments.keys.inspect}"))
|
65
|
+
@project= @project.
|
66
|
+
deep_merge(@project.environments[@orch_env]).
|
67
|
+
deep_merge(@config).
|
68
|
+
deep_merge(@orch_env_config)
|
69
|
+
@orch_env_config['env']=@orch_env
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
def name
|
74
|
+
@project.name
|
75
|
+
end
|
76
|
+
|
77
|
+
def describe(environment)
|
78
|
+
{
|
79
|
+
env: {
|
80
|
+
minfra_name: ENV["MINFRA_NAME"],
|
81
|
+
minfra_path: ENV["MINFRA_PATH"],
|
82
|
+
},
|
83
|
+
base_path: base_path.to_s,
|
84
|
+
me_path: me_path.to_s,
|
85
|
+
kube_path: kube_path.to_s,
|
86
|
+
config_path: config_path.to_s,
|
87
|
+
config: @config.to_h,
|
88
|
+
env_config: @orch_env_config.to_h,
|
89
|
+
project: @project
|
90
|
+
}
|
91
|
+
end
|
92
|
+
def dev?
|
93
|
+
@orch_env=='dev'
|
94
|
+
end
|
95
|
+
|
96
|
+
def email
|
97
|
+
@config.identity.email
|
98
|
+
end
|
99
|
+
|
100
|
+
def api_key
|
101
|
+
@project.account_api_key
|
102
|
+
end
|
103
|
+
|
104
|
+
def endpoint(name)
|
105
|
+
Hashie::Mash.new({"api_key": api_key}).deep_merge(@project.endpoints[name])
|
106
|
+
rescue
|
107
|
+
raise("endpoint #{name} is undefinded please add <env>:endpoints:#{name} to you #{config_path} file ")
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Minfra
|
2
|
+
module Cli
|
3
|
+
class Document
|
4
|
+
def self.document(config, message)
|
5
|
+
new(config).document(message)
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(config)
|
9
|
+
@config = config
|
10
|
+
end
|
11
|
+
|
12
|
+
def document(message)
|
13
|
+
return true if @config.dev?
|
14
|
+
puts "TBD: calling documentation hooks"
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# no support for around hooks yet
|
2
|
+
|
3
|
+
module Minfra
|
4
|
+
module Cli
|
5
|
+
module Hook
|
6
|
+
class Hook
|
7
|
+
def initialize(type, name, block)
|
8
|
+
@type=type
|
9
|
+
@name=name
|
10
|
+
@block=block
|
11
|
+
end
|
12
|
+
|
13
|
+
def match?(type, name)
|
14
|
+
@type == type && @name == name
|
15
|
+
end
|
16
|
+
|
17
|
+
def exec (obj)
|
18
|
+
obj.instance_eval(&@block)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
class Hooker
|
24
|
+
def initialize(klass)
|
25
|
+
@klass=klass
|
26
|
+
@hooks=[]
|
27
|
+
end
|
28
|
+
|
29
|
+
def register_before(name, block)
|
30
|
+
@hooks << Hook.new(:before, name, block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def register_after(name, block)
|
34
|
+
@hooks << Hook.new(:after, name, block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def call(obj, name, &block)
|
38
|
+
@hooks.select do |h| h.match?(:before, name) end.each do |h| h.exec(obj) end
|
39
|
+
obj.instance_eval(&block)
|
40
|
+
@hooks.select do |h| h.match?(:after, name) end.each do |h| h.exec(obj) end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.included(klass)
|
45
|
+
klass.extend(ClassMethods)
|
46
|
+
end
|
47
|
+
|
48
|
+
module ClassMethods
|
49
|
+
def hooks
|
50
|
+
@hooker||=Hooker.new(self)
|
51
|
+
end
|
52
|
+
def after_hook(name,&block)
|
53
|
+
hooks.register_after(name, block)
|
54
|
+
end
|
55
|
+
def before_hook(name, &block)
|
56
|
+
hooks.register_before(name, block)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def with_hook(hook_name, &block)
|
61
|
+
self.class.hooks.call(self, hook_name, &block)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Minfra
|
2
|
+
module Cli
|
3
|
+
module Logging
|
4
|
+
def error(str)
|
5
|
+
STDERR.puts "Error: #{str}"
|
6
|
+
end
|
7
|
+
|
8
|
+
def exit_error(str)
|
9
|
+
error str
|
10
|
+
exit 1
|
11
|
+
end
|
12
|
+
|
13
|
+
def info(str)
|
14
|
+
STDOUT.puts str
|
15
|
+
end
|
16
|
+
|
17
|
+
def debug(str)
|
18
|
+
STDOUT.puts "Debug: #{str}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def deprecated(comment)
|
22
|
+
puts "DEPRECATED: #{comment}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Minfra
|
2
|
+
module Cli
|
3
|
+
class Main < Command
|
4
|
+
|
5
|
+
desc 'kube', 'kubectl wrapper and other features'
|
6
|
+
long_desc '
|
7
|
+
'
|
8
|
+
option :environment, required: false, aliases: ['-e']
|
9
|
+
option :cluster
|
10
|
+
def kube(*args)
|
11
|
+
kube.kubectl_command(args)
|
12
|
+
end
|
13
|
+
|
14
|
+
# tbd: move this to project
|
15
|
+
desc 'tag', 'tag current commit for deployment - triggers CI'
|
16
|
+
option :message, default: 'release', aliases: ['-m']
|
17
|
+
option :format, required: false, aliases: ['-f']
|
18
|
+
def tag
|
19
|
+
Tag.new.tag_current_commit_for_deploy(options[:message], options[:format])
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'version', 'prints version of the cli'
|
23
|
+
def version
|
24
|
+
puts Minfra::Cli::VERSION
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.exit_on_failure?
|
28
|
+
true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Minfra
|
2
|
+
module Cli
|
3
|
+
class Plugins
|
4
|
+
def self.load
|
5
|
+
[Pathname.new(ENV["MINFRA_PATH"]).join("config","minfra_plugins.json"),
|
6
|
+
Pathname.new(ENV["MINFRA_PATH"]).join("me","minfra_plugins.json")].each do |file|
|
7
|
+
|
8
|
+
next unless File.exist?(file)
|
9
|
+
|
10
|
+
plugins=JSON.parse(File.read(file))
|
11
|
+
plugins["plugins"].each do |spec|
|
12
|
+
opts=spec["opts"] || {}
|
13
|
+
opts.merge(require: false)
|
14
|
+
if opts["path"]
|
15
|
+
begin
|
16
|
+
$LOAD_PATH.unshift opts["path"]+"/lib"
|
17
|
+
require spec["name"]
|
18
|
+
rescue Gem::Requirement::BadRequirementError
|
19
|
+
STDERR.puts("Can't load plugin: #{spec["name"]}")
|
20
|
+
end
|
21
|
+
else
|
22
|
+
begin
|
23
|
+
Gem::Specification.find_by_name(spec["name"])
|
24
|
+
gem spec["name"], spec["version"]
|
25
|
+
rescue Gem::MissingSpecError
|
26
|
+
STDERR.puts("Can't load plugin: #{spec["name"]}, #{spec["version"]}; run 'minfra plugin setup'")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
module Minfra
|
4
|
+
module Cli
|
5
|
+
class Runner
|
6
|
+
class Result
|
7
|
+
attr_reader :stdout, :stderr
|
8
|
+
def initialize(stdout,stderr, status)
|
9
|
+
@stderr=stderr
|
10
|
+
@stdout=stdout
|
11
|
+
@status=status
|
12
|
+
end
|
13
|
+
|
14
|
+
def success?
|
15
|
+
@status.success?
|
16
|
+
end
|
17
|
+
|
18
|
+
def error?
|
19
|
+
!success?
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
@stdout.to_s
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
include Logging
|
28
|
+
def self.run(cmd, **args)
|
29
|
+
new(cmd, **args).run
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :exit_on_error
|
33
|
+
def initialize(cmd, exit_on_error: true)
|
34
|
+
@cmd=cmd
|
35
|
+
@exit_on_error = exit_on_error
|
36
|
+
end
|
37
|
+
|
38
|
+
def run
|
39
|
+
debug(@cmd)
|
40
|
+
res=nil
|
41
|
+
begin
|
42
|
+
res=Result.new(*Open3.capture3(@cmd))
|
43
|
+
rescue
|
44
|
+
end
|
45
|
+
if res&.error?
|
46
|
+
STDERR.puts "command failed: #{@cmd}"
|
47
|
+
STDERR.puts res.stdout
|
48
|
+
STDERR.puts res.stderr
|
49
|
+
end
|
50
|
+
if exit_on_error && res&.error?
|
51
|
+
STDERR.puts "exiting on error"
|
52
|
+
exit 1
|
53
|
+
end
|
54
|
+
res
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Minfra
|
4
|
+
module Cli
|
5
|
+
class Templater # not threadsafe!
|
6
|
+
def self.read(path, params: {}, fallback: nil)
|
7
|
+
p=Pathname.new(path)
|
8
|
+
if p.exist?
|
9
|
+
content=File.read(path)
|
10
|
+
else
|
11
|
+
if fallback
|
12
|
+
content=fallback
|
13
|
+
else
|
14
|
+
raise "file #{path} not found"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
render(template, params)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.render(template, params)
|
21
|
+
new(template).render(params)
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(template)
|
25
|
+
@erb = ERB.new(template)
|
26
|
+
@check_mode=false
|
27
|
+
@check_missing=[]
|
28
|
+
end
|
29
|
+
|
30
|
+
def missing?
|
31
|
+
!check_missing.empty?
|
32
|
+
end
|
33
|
+
|
34
|
+
def check_missing(&block)
|
35
|
+
begin
|
36
|
+
@check_mode = true
|
37
|
+
@check_block = block
|
38
|
+
@check_missing = []
|
39
|
+
@erb.result(binding)
|
40
|
+
ensure
|
41
|
+
@check_block = nil
|
42
|
+
@check_mode = false
|
43
|
+
end
|
44
|
+
@check_missing
|
45
|
+
end
|
46
|
+
|
47
|
+
def render(params)
|
48
|
+
@erb.result_with_hash(params)
|
49
|
+
end
|
50
|
+
|
51
|
+
def method_missing(name)
|
52
|
+
if @check_mode
|
53
|
+
if @check_block
|
54
|
+
@check_block.call(name)
|
55
|
+
end
|
56
|
+
@check_missing << name
|
57
|
+
else
|
58
|
+
super
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|