qb 0.1.36 → 0.1.37
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/ansible.cfg +1 -1
- data/bin/rake +3 -0
- data/dev/scratch/ansible_module/defaults/main.yml +2 -0
- data/dev/scratch/ansible_module/library/test +22 -0
- data/dev/scratch/ansible_module/meta/main.yml +8 -0
- data/dev/scratch/ansible_module/meta/qb.yml +44 -0
- data/dev/scratch/ansible_module/tasks/main.yml +9 -0
- data/dev/scratch/stdio/defaults/main.yml +4 -0
- data/dev/scratch/stdio/library/test +34 -0
- data/dev/scratch/stdio/meta/main.yml +8 -0
- data/dev/scratch/stdio/meta/qb.yml +55 -0
- data/dev/scratch/stdio/tasks/main.yml +5 -0
- data/exe/qb +14 -1
- data/lib/qb.rb +4 -9
- data/lib/qb/ansible_module.rb +65 -0
- data/lib/qb/options.rb +4 -5
- data/lib/qb/role.rb +63 -14
- data/lib/qb/util.rb +54 -0
- data/lib/qb/util/stdio.rb +86 -0
- data/lib/qb/version.rb +1 -1
- data/qb.gemspec +1 -0
- data/roles/qb.git_submodule_update/.qb-options.yml +3 -0
- data/roles/qb.git_submodule_update/README.md +5 -0
- data/roles/qb.git_submodule_update/defaults/main.yml +3 -0
- data/roles/qb.git_submodule_update/library/git_submodule_update +167 -0
- data/roles/qb.git_submodule_update/meta/main.yml +8 -0
- data/roles/qb.git_submodule_update/meta/qb.yml +44 -0
- data/roles/qb.git_submodule_update/tasks/main.yml +11 -0
- data/roles/qb.role/defaults/main.yml +1 -0
- data/roles/qb.role/meta/qb.yml +5 -0
- data/roles/qb.role/tasks/main.yml +11 -0
- data/roles/qb.role/templates/README.md.j2 +4 -0
- metadata +44 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cafbcb81c0a8179b326824b5b89203534604398c
|
4
|
+
data.tar.gz: 46823f3b27926e4c5652f4fac6b2932d3d74250a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b6d08dab932b56a417c31fd56dd3584d8cf6874f6f467dcd22e575806fe39e8894fa447905f18258023be19a0f2c0b17389d1bdb941397058b63f567180fdb8c
|
7
|
+
data.tar.gz: 3167ccdd9f411767e2302c58adf8d697e428c7515d5c30502e645fe4c43a2d707d34e99fe2c77850c45e77b0607f33dff6bde49744de9ea855ecc48cea16daa0
|
data/ansible.cfg
CHANGED
data/bin/rake
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# WANT_JSON
|
3
|
+
|
4
|
+
# init bundler in dev env
|
5
|
+
if ENV['QB_DEV_ENV']
|
6
|
+
ENV.each {|k, v|
|
7
|
+
if k.start_with? 'QB_DEV_ENV_'
|
8
|
+
ENV[k.sub('QB_DEV_ENV_', '')] = v
|
9
|
+
end
|
10
|
+
}
|
11
|
+
require 'bundler/setup'
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'qb'
|
15
|
+
|
16
|
+
class Test < QB::AnsibleModule
|
17
|
+
def main
|
18
|
+
changed! blah: "blow me: #{ @args['x'] }"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Test.new.run
|
@@ -0,0 +1,44 @@
|
|
1
|
+
---
|
2
|
+
# meta/qb.yml file for ansible_module
|
3
|
+
#
|
4
|
+
# qb settings for this role. see README.md for more info.
|
5
|
+
#
|
6
|
+
|
7
|
+
# description of the role to show in it's help output.
|
8
|
+
description: null
|
9
|
+
|
10
|
+
# prefix for role variables
|
11
|
+
var_prefix: null
|
12
|
+
|
13
|
+
# how to get a default for `dir` if it's not provided as the only
|
14
|
+
# positional argument. if a positional argument is provided it will
|
15
|
+
# override the method defined here.
|
16
|
+
#
|
17
|
+
# options:
|
18
|
+
#
|
19
|
+
# - null
|
20
|
+
# - require the value on the command line.
|
21
|
+
# - git_root
|
22
|
+
# - use the git root fof the directory that the `qb` command is invoked
|
23
|
+
# from. useful for 'project-centric' commands so they can be invoked
|
24
|
+
# from anywhere in the repo.
|
25
|
+
# - cwd
|
26
|
+
# - use the directory the `qb` command is invoked form.
|
27
|
+
# - {exe: PATH}
|
28
|
+
# - invoke an execuable, passing a JSON serialization of the options
|
29
|
+
# mapping their CLI names to values. path can be relative to role
|
30
|
+
# directory.
|
31
|
+
default_dir: cwd
|
32
|
+
|
33
|
+
# default user to become for play
|
34
|
+
default_user: null
|
35
|
+
|
36
|
+
# set to false to not save options in .qb-options.yml files
|
37
|
+
save_options: false
|
38
|
+
|
39
|
+
options: []
|
40
|
+
# - name: example
|
41
|
+
# description: an example of a variable.
|
42
|
+
# required: false
|
43
|
+
# type: boolean # boolean (default) | string
|
44
|
+
# short: e
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# WANT_JSON
|
3
|
+
|
4
|
+
# init bundler in dev env
|
5
|
+
if ENV['QB_DEV_ENV']
|
6
|
+
ENV.each {|k, v|
|
7
|
+
if k.start_with? 'QB_DEV_ENV_'
|
8
|
+
ENV[k.sub('QB_DEV_ENV_', '')] = v
|
9
|
+
end
|
10
|
+
}
|
11
|
+
require 'bundler/setup'
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'qb'
|
15
|
+
require 'pp'
|
16
|
+
|
17
|
+
class Test < QB::AnsibleModule
|
18
|
+
def main
|
19
|
+
QB.debug args: @args
|
20
|
+
|
21
|
+
if @args['count']
|
22
|
+
(1..10).each {|i|
|
23
|
+
puts i
|
24
|
+
sleep 1
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
raise "HERE" if @args['raise']
|
29
|
+
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Test.new.run
|
@@ -0,0 +1,55 @@
|
|
1
|
+
---
|
2
|
+
# meta/qb.yml file for stdio
|
3
|
+
#
|
4
|
+
# qb settings for this role. see README.md for more info.
|
5
|
+
#
|
6
|
+
|
7
|
+
# description of the role to show in it's help output.
|
8
|
+
description: null
|
9
|
+
|
10
|
+
# prefix for role variables
|
11
|
+
var_prefix: null
|
12
|
+
|
13
|
+
# how to get a default for `dir` if it's not provided as the only
|
14
|
+
# positional argument. if a positional argument is provided it will
|
15
|
+
# override the method defined here.
|
16
|
+
#
|
17
|
+
# options:
|
18
|
+
#
|
19
|
+
# - null
|
20
|
+
# - require the value on the command line.
|
21
|
+
# - git_root
|
22
|
+
# - use the git root fof the directory that the `qb` command is invoked
|
23
|
+
# from. useful for 'project-centric' commands so they can be invoked
|
24
|
+
# from anywhere in the repo.
|
25
|
+
# - cwd
|
26
|
+
# - use the directory the `qb` command is invoked form.
|
27
|
+
# - {exe: PATH}
|
28
|
+
# - invoke an execuable, passing a JSON serialization of the options
|
29
|
+
# mapping their CLI names to values. path can be relative to role
|
30
|
+
# directory.
|
31
|
+
default_dir: cwd
|
32
|
+
|
33
|
+
# default user to become for play
|
34
|
+
default_user: null
|
35
|
+
|
36
|
+
# set to false to not save options in .qb-options.yml files
|
37
|
+
save_options: false
|
38
|
+
|
39
|
+
options:
|
40
|
+
# - name: example
|
41
|
+
# description: an example of a variable.
|
42
|
+
# required: false
|
43
|
+
# type: boolean # boolean (default) | string
|
44
|
+
# short: e
|
45
|
+
- name: raise
|
46
|
+
description: raise an error in main.
|
47
|
+
required: false
|
48
|
+
type: boolean
|
49
|
+
short: r
|
50
|
+
|
51
|
+
- name: count
|
52
|
+
description: count to 10 on stdout with 1 sec sleeps between
|
53
|
+
required: false
|
54
|
+
type: boolean
|
55
|
+
short: c
|
data/exe/qb
CHANGED
@@ -42,7 +42,7 @@ end
|
|
42
42
|
|
43
43
|
def set_debug! args
|
44
44
|
if DEBUG_ARGS.any? {|arg| args.include? arg}
|
45
|
-
|
45
|
+
ENV['QB_DEBUG'] = 'true'
|
46
46
|
debug "ON"
|
47
47
|
DEBUG_ARGS.each {|arg| args.delete arg}
|
48
48
|
end
|
@@ -78,11 +78,15 @@ def with_clean_env &block
|
|
78
78
|
].include?(k)
|
79
79
|
}
|
80
80
|
|
81
|
+
qb_env = ENV.select {|k, v| k.start_with? 'QB_'}
|
82
|
+
|
81
83
|
Bundler.with_clean_env do
|
82
84
|
# now that we're in a clean env, copy the Bundler env vars into
|
83
85
|
# 'QB_DEV_ENV_<NAME>' vars.
|
84
86
|
dev_env.each {|k, v| ENV["QB_DEV_ENV_#{ k }"] = v}
|
85
87
|
|
88
|
+
qb_env.each {|k, v| ENV[k] = v}
|
89
|
+
|
86
90
|
# and set QB_DEV_ENV=true
|
87
91
|
ENV['QB_DEV_ENV'] = 'true'
|
88
92
|
|
@@ -352,8 +356,17 @@ def main args
|
|
352
356
|
|
353
357
|
puts "COMMAND: #{ cmd }"
|
354
358
|
|
359
|
+
# boot up stdio services so that ansible modules can stream to our
|
360
|
+
# stdout and stderr to print stuff (including debug lines) in real-time
|
361
|
+
stdio_services = {'out' => $stdout, 'err' => $stderr}.map do |name, dest|
|
362
|
+
QB::Util::STDIO::Service.new(name, dest).tap {|s| s.open! }
|
363
|
+
end
|
364
|
+
|
355
365
|
status = Cmds.stream cmd
|
356
366
|
|
367
|
+
# close the stdio services
|
368
|
+
stdio_services.each {|s| s.close! }
|
369
|
+
|
357
370
|
if status != 0
|
358
371
|
puts "ERROR ansible-playbook failed."
|
359
372
|
end
|
data/lib/qb.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'nrser/extras'
|
2
2
|
|
3
3
|
require "qb/version"
|
4
|
+
require "qb/util"
|
5
|
+
require 'qb/util/stdio'
|
6
|
+
require "qb/ansible_module"
|
4
7
|
|
5
8
|
module QB
|
6
9
|
ROOT = (Pathname.new(__FILE__).dirname + '..').expand_path
|
@@ -10,16 +13,8 @@ module QB
|
|
10
13
|
class Error < StandardError
|
11
14
|
end
|
12
15
|
|
13
|
-
# TODO this should be in an instance that is run instead of module global
|
14
|
-
# hack for now
|
15
|
-
@@debug = false
|
16
|
-
|
17
|
-
def self.debug= bool
|
18
|
-
@@debug = !!bool
|
19
|
-
end
|
20
|
-
|
21
16
|
def self.debug *args
|
22
|
-
return unless
|
17
|
+
return unless ENV['QB_DEBUG'] && args.length > 0
|
23
18
|
|
24
19
|
header = 'DEBUG'
|
25
20
|
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module QB
|
4
|
+
class AnsibleModule
|
5
|
+
def self.stringify_keys hash
|
6
|
+
hash.map {|k, v| [k.to_s, v]}.to_h
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@changed = false
|
11
|
+
@input_file = ARGV[0]
|
12
|
+
@input = File.read @input_file
|
13
|
+
@args = JSON.load @input
|
14
|
+
@facts = {}
|
15
|
+
|
16
|
+
# if QB_STDIO_ env vars are set send stdout and stderr
|
17
|
+
# to those sockets to print in the parent process
|
18
|
+
|
19
|
+
if ENV['QB_STDIO_OUT']
|
20
|
+
$stdout = UNIXSocket.new ENV['QB_STDIO_OUT']
|
21
|
+
end
|
22
|
+
|
23
|
+
if ENV['QB_STDIO_ERR']
|
24
|
+
$stderr = UNIXSocket.new ENV['QB_STDIO_ERR']
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def run
|
29
|
+
result = main
|
30
|
+
|
31
|
+
case result
|
32
|
+
when nil
|
33
|
+
# pass
|
34
|
+
when Hash
|
35
|
+
@facts.merge! result
|
36
|
+
else
|
37
|
+
raise "result of #main should be nil or Hash, found #{ result.inspect }"
|
38
|
+
end
|
39
|
+
|
40
|
+
done
|
41
|
+
end
|
42
|
+
|
43
|
+
def changed! facts = {}
|
44
|
+
@changed = true
|
45
|
+
@facts.merge! facts
|
46
|
+
done
|
47
|
+
end
|
48
|
+
|
49
|
+
def done
|
50
|
+
exit_json changed: @changed,
|
51
|
+
ansible_facts: self.class.stringify_keys(@facts)
|
52
|
+
end
|
53
|
+
|
54
|
+
def exit_json hash
|
55
|
+
# print JSON response to process' actual STDOUT (instead of $stdout,
|
56
|
+
# which may be pointing to the qb parent process)
|
57
|
+
STDOUT.print JSON.dump(self.class.stringify_keys(hash))
|
58
|
+
exit 0
|
59
|
+
end
|
60
|
+
|
61
|
+
def fail msg
|
62
|
+
exit_json failed: true, msg: msg
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end # QB
|
data/lib/qb/options.rb
CHANGED
@@ -161,7 +161,7 @@ module QB
|
|
161
161
|
|
162
162
|
qb_options = {
|
163
163
|
'hosts' => ['localhost'],
|
164
|
-
'facts' =>
|
164
|
+
'facts' => true,
|
165
165
|
}
|
166
166
|
|
167
167
|
if role.meta['default_user']
|
@@ -212,11 +212,10 @@ module QB
|
|
212
212
|
end
|
213
213
|
|
214
214
|
opts.on(
|
215
|
-
'-
|
216
|
-
'
|
217
|
-
"gather facts (often un-needed)",
|
215
|
+
'--NO-FACTS',
|
216
|
+
"don't gather facts",
|
218
217
|
) do |value|
|
219
|
-
qb_options['facts'] =
|
218
|
+
qb_options['facts'] = false
|
220
219
|
end
|
221
220
|
|
222
221
|
add opts, role_options, role
|
data/lib/qb/role.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'cmds'
|
3
|
+
require 'parseconfig'
|
3
4
|
|
4
5
|
module QB
|
5
6
|
class Role
|
@@ -46,17 +47,44 @@ module QB
|
|
46
47
|
['qb.yml', 'qb'].any? {|filename| pathname.join('meta', filename).file?}
|
47
48
|
end
|
48
49
|
|
49
|
-
#
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
50
|
+
# get role paths from ansible.cfg if it exists in a directory.
|
51
|
+
#
|
52
|
+
# @param dir [Pathname] directory to look for ansible.cfg in.
|
53
|
+
#
|
54
|
+
# @return [Array<String>] role paths
|
55
|
+
#
|
56
|
+
def self.cfg_roles_path dir
|
57
|
+
path = dir.join 'ansible.cfg'
|
58
|
+
|
59
|
+
if path.file?
|
60
|
+
config = ParseConfig.new path.to_s
|
61
|
+
config['defaults']['roles_path'].split(':').map {|path|
|
62
|
+
QB::Util.resolve dir, path
|
63
|
+
}
|
64
|
+
else
|
65
|
+
[]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# @param dir [Pathname] dir to include.
|
70
|
+
def self.roles_paths dir
|
71
|
+
cfg_roles_path(dir) + [
|
72
|
+
dir.join('roles'),
|
73
|
+
dir.join('roles', 'tmp')
|
57
74
|
]
|
58
75
|
end
|
59
76
|
|
77
|
+
# @return [Array<Pathname>] places to look for role dirs.
|
78
|
+
def self.search_path
|
79
|
+
[QB::ROLES_DIR] + [
|
80
|
+
QB::Util.resolve,
|
81
|
+
QB::Util.resolve('ansible'),
|
82
|
+
QB::Util.resolve('dev'),
|
83
|
+
].map {|dir|
|
84
|
+
roles_paths dir
|
85
|
+
}.flatten
|
86
|
+
end
|
87
|
+
|
60
88
|
# array of QB::Role found in search path.
|
61
89
|
def self.available
|
62
90
|
search_path.
|
@@ -69,6 +97,8 @@ module QB
|
|
69
97
|
search_dir.children.select {|child| role_dir? child }
|
70
98
|
}.
|
71
99
|
flatten.
|
100
|
+
# should allow uniq to remove dups
|
101
|
+
map {|role_dir| role_dir.realpath }.
|
72
102
|
# needed when qb is run from the qb repo since QB::ROLES_DIR and
|
73
103
|
# ./roles are the same dir
|
74
104
|
uniq.
|
@@ -79,8 +109,10 @@ module QB
|
|
79
109
|
|
80
110
|
# get an array of QB::Role that match an input string
|
81
111
|
def self.matches input
|
112
|
+
available = self.available
|
113
|
+
|
82
114
|
available.each {|role|
|
83
|
-
# exact match to
|
115
|
+
# exact match to relative path
|
84
116
|
return [role] if role.rel_path.to_s == input
|
85
117
|
}.each {|role|
|
86
118
|
# exact match to full name
|
@@ -88,12 +120,29 @@ module QB
|
|
88
120
|
}.each {|role|
|
89
121
|
# exact match without the namespace prefix ('qb.' or similar)
|
90
122
|
return [role] if role.namespaceless == input
|
91
|
-
}.select {|role|
|
92
|
-
# select any that have that string in them
|
93
|
-
role.rel_path.to_s.include? input
|
94
|
-
}.tap {|matches|
|
95
|
-
QB.debug "role matches" => matches
|
96
123
|
}
|
124
|
+
|
125
|
+
# see if we prefix match any full names
|
126
|
+
name_prefix_matches = available.select {|role|
|
127
|
+
role.name.start_with? input
|
128
|
+
}
|
129
|
+
return name_prefix_matches unless name_prefix_matches.empty?
|
130
|
+
|
131
|
+
# see if we prefix match any name
|
132
|
+
namespaceless_prefix_matches = available.select {|role|
|
133
|
+
role.namespaceless.start_with? input
|
134
|
+
}
|
135
|
+
unless namespaceless_prefix_matches.empty?
|
136
|
+
return namespaceless_prefix_matches
|
137
|
+
end
|
138
|
+
|
139
|
+
# see if we word match any names
|
140
|
+
name_word_matches = available.select {|role|
|
141
|
+
QB::Util.words_start_with? role.name, input
|
142
|
+
}
|
143
|
+
return name_word_matches unless name_word_matches.empty?
|
144
|
+
|
145
|
+
[]
|
97
146
|
end
|
98
147
|
|
99
148
|
# find exactly one matching role for the input string or raise.
|
data/lib/qb/util.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
module QB
|
2
|
+
module Util
|
3
|
+
# split a string into 'words' for word-based matching
|
4
|
+
def self.words string
|
5
|
+
string.split(/[\W_-]+/).reject {|w| w.empty?}
|
6
|
+
end # .words
|
7
|
+
|
8
|
+
# see if words from an input match words
|
9
|
+
def self.words_start_with? full_string, input
|
10
|
+
# QB.debug "does #{ input } match #{ full_string }?"
|
11
|
+
|
12
|
+
input_words = words input
|
13
|
+
full_string_words = words full_string
|
14
|
+
|
15
|
+
full_string_words.each_with_index {|word, start_index|
|
16
|
+
# compute the end index in full_string_words
|
17
|
+
end_index = start_index + input_words.length - 1
|
18
|
+
|
19
|
+
# short-circuit if can't match (more input words than full words left)
|
20
|
+
if end_index >= full_string_words.length
|
21
|
+
return false
|
22
|
+
end
|
23
|
+
|
24
|
+
# create the slice to test against
|
25
|
+
slice = full_string_words[start_index..end_index]
|
26
|
+
|
27
|
+
# see if every word in the slice starts with the corresponding word
|
28
|
+
# in the input
|
29
|
+
if slice.zip(input_words).all? {|full_word, input_word|
|
30
|
+
full_word.start_with? input_word
|
31
|
+
}
|
32
|
+
# got a match!
|
33
|
+
return true
|
34
|
+
end
|
35
|
+
}
|
36
|
+
|
37
|
+
# no match
|
38
|
+
false
|
39
|
+
end # .match_words?
|
40
|
+
|
41
|
+
# @return [Pathname] absolute resolved path.
|
42
|
+
def self.resolve *segments
|
43
|
+
joined = Pathname.new ''
|
44
|
+
|
45
|
+
([Dir.pwd] + segments).reverse.each_with_index {|segment, index|
|
46
|
+
joined = Pathname.new(segment).join joined
|
47
|
+
return joined if joined.absolute?
|
48
|
+
}
|
49
|
+
|
50
|
+
# shouldn't ever happen
|
51
|
+
raise "resolution failed: #{ segments.inspect }"
|
52
|
+
end
|
53
|
+
end # Util
|
54
|
+
end # QB
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'socket'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'nrser'
|
6
|
+
|
7
|
+
using NRSER
|
8
|
+
|
9
|
+
module QB; end
|
10
|
+
module QB::Util; end
|
11
|
+
|
12
|
+
module QB::Util::STDIO
|
13
|
+
SOCKET_DIR = Pathname.new('/').join 'tmp', 'qb-stdio'
|
14
|
+
|
15
|
+
# STDIO as a service exposed on a UNIX socket so that modules can stream
|
16
|
+
# their output to it, which is in turn printed to the console `qb` is running
|
17
|
+
# in.
|
18
|
+
class Service
|
19
|
+
def initialize name, dest
|
20
|
+
@name = name
|
21
|
+
@dest = dest
|
22
|
+
@thread = nil
|
23
|
+
@server = nil
|
24
|
+
@socket = nil
|
25
|
+
@env_key = "QB_STDIO_#{ name.upcase }"
|
26
|
+
|
27
|
+
unless SOCKET_DIR.exist?
|
28
|
+
FileUtils.mkdir SOCKET_DIR
|
29
|
+
end
|
30
|
+
|
31
|
+
@path = SOCKET_DIR.join "#{ name }.#{ SecureRandom.uuid }.sock"
|
32
|
+
|
33
|
+
@debug_header = "#{ name }@#{ @path.to_s }"
|
34
|
+
end
|
35
|
+
|
36
|
+
def debug *args
|
37
|
+
QB.debug "#{ @debug_header }", *args
|
38
|
+
end
|
39
|
+
|
40
|
+
def open!
|
41
|
+
debug "opening..."
|
42
|
+
|
43
|
+
# make sure env var is not already set (basically just prevents you from
|
44
|
+
# accidentally opening two instances with the same name)
|
45
|
+
if ENV.key? @env_key
|
46
|
+
raise <<-END.squish
|
47
|
+
env already contains key #{ @env_key } with value #{ ENV[@env_key] }
|
48
|
+
END
|
49
|
+
end
|
50
|
+
|
51
|
+
@thread = Thread.new do
|
52
|
+
debug "thread started."
|
53
|
+
|
54
|
+
@server = UNIXServer.new @path.to_s
|
55
|
+
@socket = @server.accept
|
56
|
+
|
57
|
+
while (line = @socket.gets) do
|
58
|
+
@dest.puts line
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# set the env key so children can find the socket path
|
63
|
+
ENV[@env_key] = @path.to_s
|
64
|
+
debug "set env var #{ @env_key }=#{ ENV[@env_key] }"
|
65
|
+
|
66
|
+
debug "service open."
|
67
|
+
end # open
|
68
|
+
|
69
|
+
def close!
|
70
|
+
# clean up.
|
71
|
+
#
|
72
|
+
# TODO not sure how correct this is...
|
73
|
+
#
|
74
|
+
debug "closing..."
|
75
|
+
|
76
|
+
@thread.kill
|
77
|
+
@socket.close
|
78
|
+
@socket = nil
|
79
|
+
@server.close
|
80
|
+
@server = nil
|
81
|
+
FileUtils.rm @path
|
82
|
+
|
83
|
+
debug "closed."
|
84
|
+
end
|
85
|
+
end # Service
|
86
|
+
end # QB::Util::STDIO
|
data/lib/qb/version.rb
CHANGED
data/qb.gemspec
CHANGED
@@ -44,6 +44,7 @@ Gem::Specification.new do |spec|
|
|
44
44
|
spec.add_dependency "cmds",'~> 0.0', ">= 0.0.9"
|
45
45
|
spec.add_dependency "nrser-extras", '~> 0.0', ">= 0.0.3"
|
46
46
|
spec.add_dependency "state_mate", '~> 0.0', ">= 0.0.9"
|
47
|
+
spec.add_dependency 'parseconfig', '~> 1.0', '>= 1.0.8'
|
47
48
|
|
48
49
|
|
49
50
|
if QB::VERSION.end_with? '.dev'
|
@@ -0,0 +1,167 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#!/usr/bin/env ruby
|
4
|
+
# WANT_JSON
|
5
|
+
|
6
|
+
# init bundler in dev env
|
7
|
+
if ENV['QB_DEV_ENV']
|
8
|
+
ENV.each {|k, v|
|
9
|
+
if k.start_with? 'QB_DEV_ENV_'
|
10
|
+
ENV[k.sub('QB_DEV_ENV_', '')] = v
|
11
|
+
end
|
12
|
+
}
|
13
|
+
require 'bundler/setup'
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'qb'
|
17
|
+
require 'cmds'
|
18
|
+
require 'nrser'
|
19
|
+
|
20
|
+
class GitSubmoduleUpdate < QB::AnsibleModule
|
21
|
+
def main_dir
|
22
|
+
File.realpath @args['dir']
|
23
|
+
end
|
24
|
+
|
25
|
+
def resolve *path
|
26
|
+
QB::Util.resolve main_dir, *path
|
27
|
+
end
|
28
|
+
|
29
|
+
def submodules
|
30
|
+
out = Dir.chdir main_dir do
|
31
|
+
Cmds.out! "git submodule"
|
32
|
+
end
|
33
|
+
|
34
|
+
out.lines.map {|line|
|
35
|
+
match = line.match /([0-9a-f]{40})\s(\S+)\s/
|
36
|
+
commit = match[1]
|
37
|
+
rel_dir = match[2]
|
38
|
+
dir = resolve rel_dir
|
39
|
+
|
40
|
+
{
|
41
|
+
commit: commit,
|
42
|
+
dir: dir,
|
43
|
+
rel_dir: rel_dir,
|
44
|
+
detached: detached?(dir),
|
45
|
+
dirty: dirty?(dir),
|
46
|
+
}
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def dirty? repo_dir
|
51
|
+
Dir.chdir repo_dir do
|
52
|
+
!Cmds.out!("git status --porcelain").empty?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def detached? repo_dir
|
57
|
+
Dir.chdir repo_dir do
|
58
|
+
out = Cmds.out! "git branch"
|
59
|
+
|
60
|
+
!!(out.lines[0].match /\*\ \(HEAD\ detached\ at [0-9a-f]{7}\)/)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def branch_heads repo_dir
|
65
|
+
Dir.chdir repo_dir do
|
66
|
+
Cmds.out!("git show-ref").lines.map {|line|
|
67
|
+
m = line.match(/([0-9a-f]{40})\s+(\S+)\s/)
|
68
|
+
commit = m[1]
|
69
|
+
ref = m[2]
|
70
|
+
|
71
|
+
{
|
72
|
+
commit: commit,
|
73
|
+
ref: ref,
|
74
|
+
}
|
75
|
+
}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def branch_heads_for_commit submodule
|
80
|
+
branch_heads(submodule[:dir]).select {|branch_head|
|
81
|
+
branch_head[:commit] == submodule[:commit]
|
82
|
+
}.reject {|branch_head| branch_head[:ref].end_with? 'HEAD'}
|
83
|
+
end
|
84
|
+
|
85
|
+
def attach! submodule
|
86
|
+
branch_heads = branch_heads_for_commit submodule
|
87
|
+
branch_head = nil
|
88
|
+
|
89
|
+
case branch_heads.length
|
90
|
+
when 0
|
91
|
+
# commit does not point to any branch heads - which means it's
|
92
|
+
# probably a commit in a branch that's behind the head
|
93
|
+
#
|
94
|
+
# we could figure out which branch it's in but we don't want to
|
95
|
+
# automatically update it because that might break shit.
|
96
|
+
#
|
97
|
+
# do nothing
|
98
|
+
return false
|
99
|
+
when 1
|
100
|
+
# commit is head of only one branch
|
101
|
+
branch_head = branch_heads[0]
|
102
|
+
else
|
103
|
+
# commit is head of multiple branches
|
104
|
+
local = branch_heads.select {|bh| bh[:ref].start_with? 'refs/heads'}
|
105
|
+
|
106
|
+
case local.length
|
107
|
+
when 0
|
108
|
+
# commit is head of multiple remote branches
|
109
|
+
# see if one is master
|
110
|
+
branch_head = branch_heads.find {|bh| bh[:ref].end_with? 'master'}
|
111
|
+
|
112
|
+
# if none do we're hosed - not sure which one it should be on
|
113
|
+
if branch_head.nil?
|
114
|
+
raise NRSER.squish <<-END
|
115
|
+
submodule #{ submodule[:rel_dir] } points to commit
|
116
|
+
#{ submodule[:commit] } that heads multiple non-master remote
|
117
|
+
branches: #{ branch_heads.map {|bh| bh[:ref]} }
|
118
|
+
END
|
119
|
+
end
|
120
|
+
|
121
|
+
when 1
|
122
|
+
# the commit is head of one local branch, use it
|
123
|
+
branch_head = local[0]
|
124
|
+
|
125
|
+
else
|
126
|
+
# the commit heads multiple local branches
|
127
|
+
# again, see if one is master
|
128
|
+
branch_head = local.find {|b| b[:ref].end_with? 'master'}
|
129
|
+
|
130
|
+
# if none do we're hosed - not sure which one it should be on
|
131
|
+
if branch_head.nil?
|
132
|
+
raise NRSER.squish <<-END
|
133
|
+
submodule #{ submodule[:rel_dir] } points to commit
|
134
|
+
#{ submodule[:commit] } that heads multiple non-master local
|
135
|
+
branches: #{ local.map {|bh| bh[:ref]} }
|
136
|
+
END
|
137
|
+
end
|
138
|
+
end # case
|
139
|
+
end
|
140
|
+
|
141
|
+
branch = branch_head[:ref].split('/')[-1]
|
142
|
+
|
143
|
+
Dir.chdir submodule[:dir] do
|
144
|
+
# checkout the branch
|
145
|
+
Cmds! "git checkout <%= branch %>", branch: branch
|
146
|
+
|
147
|
+
# do a pull if the head was on the remote
|
148
|
+
if branch_head[:ref].start_with? 'refs/remotes'
|
149
|
+
Cmds! "git pull origin <%= branch %>", branch: branch
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
@changed = true
|
154
|
+
end
|
155
|
+
|
156
|
+
def main
|
157
|
+
submodules.select {|sub|
|
158
|
+
sub[:detached]
|
159
|
+
}.each {|sub|
|
160
|
+
attach! sub
|
161
|
+
}
|
162
|
+
|
163
|
+
nil
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
GitSubmoduleUpdate.new.run
|
@@ -0,0 +1,44 @@
|
|
1
|
+
---
|
2
|
+
# meta/qb.yml file for qb.git_submodule_update
|
3
|
+
#
|
4
|
+
# qb settings for this role. see README.md for more info.
|
5
|
+
#
|
6
|
+
|
7
|
+
# description of the role to show in it's help output.
|
8
|
+
description: null
|
9
|
+
|
10
|
+
# prefix for role variables
|
11
|
+
var_prefix: null
|
12
|
+
|
13
|
+
# how to get a default for `dir` if it's not provided as the only
|
14
|
+
# positional argument. if a positional argument is provided it will
|
15
|
+
# override the method defined here.
|
16
|
+
#
|
17
|
+
# options:
|
18
|
+
#
|
19
|
+
# - null
|
20
|
+
# - require the value on the command line.
|
21
|
+
# - git_root
|
22
|
+
# - use the git root fof the directory that the `qb` command is invoked
|
23
|
+
# from. useful for 'project-centric' commands so they can be invoked
|
24
|
+
# from anywhere in the repo.
|
25
|
+
# - cwd
|
26
|
+
# - use the directory the `qb` command is invoked form.
|
27
|
+
# - {exe: PATH}
|
28
|
+
# - invoke an execuable, passing a JSON serialization of the options
|
29
|
+
# mapping their CLI names to values. path can be relative to role
|
30
|
+
# directory.
|
31
|
+
default_dir: git_root
|
32
|
+
|
33
|
+
# default user to become for play
|
34
|
+
default_user: null
|
35
|
+
|
36
|
+
# set to false to not save options in .qb-options.yml files
|
37
|
+
save_options: false
|
38
|
+
|
39
|
+
options: []
|
40
|
+
# - name: example
|
41
|
+
# description: an example of a variable.
|
42
|
+
# required: false
|
43
|
+
# type: boolean # boolean (default) | string
|
44
|
+
# short: e
|
@@ -0,0 +1,11 @@
|
|
1
|
+
---
|
2
|
+
# tasks file for qb.git_submodule_update
|
3
|
+
|
4
|
+
- name: update submodules
|
5
|
+
command: git submodule update --init
|
6
|
+
args:
|
7
|
+
chdir: "{{ git_submodule_update_dir }}"
|
8
|
+
|
9
|
+
- name: checkout branch for any commits that are exactly one head
|
10
|
+
git_submodule_update:
|
11
|
+
dir: "{{ git_submodule_update_dir }}"
|
data/roles/qb.role/meta/qb.yml
CHANGED
@@ -115,3 +115,14 @@
|
|
115
115
|
dest: "{{ dir }}/vars/main.yml"
|
116
116
|
force: "{{ role_force }}"
|
117
117
|
when: role_vars
|
118
|
+
|
119
|
+
# readme
|
120
|
+
# ======
|
121
|
+
|
122
|
+
- name: create README.md
|
123
|
+
template:
|
124
|
+
src: README.md.j2
|
125
|
+
dest: "{{ dir }}/README.md"
|
126
|
+
force: "{{ role_force }}"
|
127
|
+
when: role_readme
|
128
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: qb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.37
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- nrser
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-12-
|
11
|
+
date: 2016-12-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -112,6 +112,26 @@ dependencies:
|
|
112
112
|
- - ">="
|
113
113
|
- !ruby/object:Gem::Version
|
114
114
|
version: 0.0.9
|
115
|
+
- !ruby/object:Gem::Dependency
|
116
|
+
name: parseconfig
|
117
|
+
requirement: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - "~>"
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '1.0'
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 1.0.8
|
125
|
+
type: :runtime
|
126
|
+
prerelease: false
|
127
|
+
version_requirements: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '1.0'
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: 1.0.8
|
115
135
|
description:
|
116
136
|
email:
|
117
137
|
- neil@ztkae.com
|
@@ -133,23 +153,37 @@ files:
|
|
133
153
|
- bin/console
|
134
154
|
- bin/print-error
|
135
155
|
- bin/qb
|
156
|
+
- bin/rake
|
136
157
|
- bin/setup
|
137
158
|
- bin/ungem
|
138
159
|
- dev/ansible.cfg
|
139
160
|
- dev/hosts
|
140
161
|
- dev/requirements.yml
|
162
|
+
- dev/scratch/ansible_module/defaults/main.yml
|
163
|
+
- dev/scratch/ansible_module/library/test
|
164
|
+
- dev/scratch/ansible_module/meta/main.yml
|
165
|
+
- dev/scratch/ansible_module/meta/qb.yml
|
166
|
+
- dev/scratch/ansible_module/tasks/main.yml
|
141
167
|
- dev/scratch/case.rb
|
142
168
|
- dev/scratch/empty/defaults/main.yml
|
143
169
|
- dev/scratch/empty/meta/main.yml
|
144
170
|
- dev/scratch/empty/meta/qb.yml
|
145
171
|
- dev/scratch/empty/tasks/main.yml
|
146
172
|
- dev/scratch/stateSpec.js
|
173
|
+
- dev/scratch/stdio/defaults/main.yml
|
174
|
+
- dev/scratch/stdio/library/test
|
175
|
+
- dev/scratch/stdio/meta/main.yml
|
176
|
+
- dev/scratch/stdio/meta/qb.yml
|
177
|
+
- dev/scratch/stdio/tasks/main.yml
|
147
178
|
- dev/setup.yml
|
148
179
|
- exe/qb
|
149
180
|
- lib/qb.rb
|
181
|
+
- lib/qb/ansible_module.rb
|
150
182
|
- lib/qb/options.rb
|
151
183
|
- lib/qb/options/option.rb
|
152
184
|
- lib/qb/role.rb
|
185
|
+
- lib/qb/util.rb
|
186
|
+
- lib/qb/util/stdio.rb
|
153
187
|
- lib/qb/version.rb
|
154
188
|
- library/git_mkdir.py
|
155
189
|
- library/qb_facts.py
|
@@ -283,6 +317,13 @@ files:
|
|
283
317
|
- roles/qb.git_repo/meta/main.yml
|
284
318
|
- roles/qb.git_repo/meta/qb.yml
|
285
319
|
- roles/qb.git_repo/tasks/main.yml
|
320
|
+
- roles/qb.git_submodule_update/.qb-options.yml
|
321
|
+
- roles/qb.git_submodule_update/README.md
|
322
|
+
- roles/qb.git_submodule_update/defaults/main.yml
|
323
|
+
- roles/qb.git_submodule_update/library/git_submodule_update
|
324
|
+
- roles/qb.git_submodule_update/meta/main.yml
|
325
|
+
- roles/qb.git_submodule_update/meta/qb.yml
|
326
|
+
- roles/qb.git_submodule_update/tasks/main.yml
|
286
327
|
- roles/qb.gitignore/defaults/main.yml
|
287
328
|
- roles/qb.gitignore/files/gitignore/.github/PULL_REQUEST_TEMPLATE.md
|
288
329
|
- roles/qb.gitignore/files/gitignore/Actionscript.gitignore
|
@@ -523,6 +564,7 @@ files:
|
|
523
564
|
- roles/qb.role/meta/qb.yml
|
524
565
|
- roles/qb.role/tasks/main.yml
|
525
566
|
- roles/qb.role/templates/.gitkeep
|
567
|
+
- roles/qb.role/templates/README.md.j2
|
526
568
|
- roles/qb.role/templates/defaults/main.yml.j2
|
527
569
|
- roles/qb.role/templates/handlers/main.yml.j2
|
528
570
|
- roles/qb.role/templates/meta/main.yml.j2
|