qb 0.3.7 → 0.3.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/qb.rb +10 -4
- data/lib/qb/package.rb +102 -0
- data/lib/qb/package/gem.rb +153 -0
- data/lib/qb/package/version.rb +445 -422
- data/lib/qb/path.rb +219 -19
- data/lib/qb/repo/git.rb +90 -19
- data/lib/qb/role.rb +22 -4
- data/lib/qb/util/resource.rb +36 -0
- data/lib/qb/version.rb +1 -1
- data/qb.gemspec +1 -1
- data/roles/qb.qb_role/templates/qb.yml.j2 +3 -3
- data/roles/qb/gem/bin_stubs/defaults/main.yml +7 -0
- data/roles/qb/gem/bin_stubs/meta/main.yml +8 -0
- data/roles/qb/gem/bin_stubs/meta/qb.yml +76 -0
- data/roles/qb/gem/bin_stubs/tasks/main.yml +28 -0
- data/roles/qb/gem/bin_stubs/templates/console +25 -0
- data/roles/qb/gem/bin_stubs/templates/rake +3 -0
- data/roles/qb/gem/bin_stubs/templates/rspec +3 -0
- metadata +14 -4
data/lib/qb/path.rb
CHANGED
@@ -16,6 +16,8 @@ require 'nrser'
|
|
16
16
|
# Package
|
17
17
|
# -----------------------------------------------------------------------
|
18
18
|
|
19
|
+
require_relative './repo/git'
|
20
|
+
|
19
21
|
|
20
22
|
# Refinements
|
21
23
|
# =======================================================================
|
@@ -36,8 +38,9 @@ module QB; end
|
|
36
38
|
# Definitions
|
37
39
|
# =======================================================================
|
38
40
|
|
39
|
-
|
40
|
-
#
|
41
|
+
# An extension of {Pathname} that implements the facts we want about paths
|
42
|
+
# as {NRSER::Meta::Props}.
|
43
|
+
#
|
41
44
|
class QB::Path < Pathname
|
42
45
|
|
43
46
|
# Mixins
|
@@ -46,29 +49,137 @@ class QB::Path < Pathname
|
|
46
49
|
include NRSER::Meta::Props
|
47
50
|
|
48
51
|
|
49
|
-
#
|
52
|
+
# Props
|
50
53
|
# ======================================================================
|
51
54
|
|
55
|
+
# Principle path properties
|
56
|
+
# ---------------------------------------------------------------------
|
57
|
+
#
|
58
|
+
# Values stored as variables on the instance.
|
59
|
+
#
|
52
60
|
|
53
|
-
#
|
54
|
-
#
|
61
|
+
# The current working directory *relevant* to the path - basically, when
|
62
|
+
# and where the instance was created, which may be on a totally different
|
63
|
+
# system if the instance was loaded from data.
|
64
|
+
#
|
65
|
+
# TODO Not totally flushed out yet, could imagine a lot of problems and
|
66
|
+
# weirdness, but it seems like we need something like this to make
|
67
|
+
# instances transportable. Issues with relative paths come to mind...
|
68
|
+
#
|
69
|
+
prop :cwd,
|
70
|
+
type: t.pathname,
|
71
|
+
to_data: :to_s
|
55
72
|
|
73
|
+
# The raw path argument used to instantiate the path object.
|
74
|
+
#
|
75
|
+
# NOTE Because we reduce {Pathname} instances to {String} when converting
|
76
|
+
# to data, there is some lossy-ness. I guess it's similar to how
|
77
|
+
# symbols and strings all become strings when run through {JSON}
|
78
|
+
# dump and load.
|
79
|
+
#
|
80
|
+
prop :raw,
|
81
|
+
type: t.path,
|
82
|
+
to_data: :to_s
|
56
83
|
|
57
|
-
# Props
|
58
|
-
# ======================================================================
|
59
84
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
85
|
+
# Derived path properties
|
86
|
+
# ---------------------------------------------------------------------
|
87
|
+
#
|
88
|
+
# On-demand values computed via method calls.
|
89
|
+
#
|
90
|
+
|
91
|
+
prop :expanded,
|
92
|
+
type: t.path,
|
93
|
+
source: ->() { expand_path.to_s }
|
94
|
+
|
95
|
+
prop :exists,
|
96
|
+
type: t.bool,
|
97
|
+
source: :exist?
|
98
|
+
|
99
|
+
prop :is_expanded,
|
100
|
+
type: t.bool,
|
101
|
+
source: :expanded?
|
102
|
+
|
103
|
+
prop :is_absolute,
|
104
|
+
type: t.bool,
|
105
|
+
source: :absolute?
|
106
|
+
|
107
|
+
prop :is_relative,
|
108
|
+
type: t.bool,
|
109
|
+
source: :relative?
|
110
|
+
|
111
|
+
prop :is_dir,
|
112
|
+
type: t.bool,
|
113
|
+
source: :directory?
|
114
|
+
|
115
|
+
prop :is_file,
|
116
|
+
type: t.bool,
|
117
|
+
source: :file?
|
118
|
+
|
119
|
+
prop :is_cwd,
|
120
|
+
type: t.bool,
|
121
|
+
source: :cwd?
|
122
|
+
|
123
|
+
prop :relative,
|
124
|
+
type: t.maybe( t.path ),
|
125
|
+
source: :relative,
|
126
|
+
to_data: :to_s
|
127
|
+
|
128
|
+
prop :realpath,
|
129
|
+
type: t.maybe( t.path ),
|
130
|
+
source: :try_realpath,
|
131
|
+
to_data: :to_s
|
132
|
+
|
133
|
+
prop :is_realpath,
|
134
|
+
type: t.bool,
|
135
|
+
source: :realpath?
|
136
|
+
|
137
|
+
|
138
|
+
# Composed properties
|
139
|
+
# ---------------------------------------------------------------------
|
140
|
+
#
|
141
|
+
# On-demand values that point to other {NRSER::Meta::Props} instances.
|
142
|
+
#
|
143
|
+
|
144
|
+
prop :git,
|
145
|
+
type: QB::Repo::Git,
|
146
|
+
source: :git
|
147
|
+
|
64
148
|
|
65
149
|
# Constructor
|
66
150
|
# ======================================================================
|
67
151
|
|
68
|
-
#
|
69
|
-
|
70
|
-
|
71
|
-
|
152
|
+
# @overload initialize path
|
153
|
+
# Initialize in the same way as you would a {Pathname}. {#cwd} is set to
|
154
|
+
# the current directory (via {Pathname#getwd}) and the `path` argument is
|
155
|
+
# assigned to {#raw}.
|
156
|
+
#
|
157
|
+
# @param [String | Pathname] path
|
158
|
+
# Target path.
|
159
|
+
#
|
160
|
+
# @overload initialize **values
|
161
|
+
# Initialize by invoking {NRSER::Meta::Props#initialize_props}.
|
162
|
+
#
|
163
|
+
# The {#raw} value is passed up to {Pathname#initialize}.
|
164
|
+
#
|
165
|
+
# {#cwd} is accepted in `values`, allowing a re-instantiated object to
|
166
|
+
# "make sense" when the process' current directory may no longer be the
|
167
|
+
# one that data was constructed against.
|
168
|
+
#
|
169
|
+
# {#cwd} defaults to the current directory (via {Pathname.getwd}) if not
|
170
|
+
# provided.
|
171
|
+
#
|
172
|
+
# @param **values see {NRSER::Meta::Props#initialize_props}
|
173
|
+
#
|
174
|
+
def initialize arg
|
175
|
+
case arg
|
176
|
+
when Hash
|
177
|
+
super arg[:raw]
|
178
|
+
initialize_props cwd: Pathname.getwd, **arg
|
179
|
+
else
|
180
|
+
super arg
|
181
|
+
initialize_props raw: arg, cwd: Pathname.getwd
|
182
|
+
end
|
72
183
|
end # #initialize
|
73
184
|
|
74
185
|
|
@@ -76,13 +187,102 @@ class QB::Path < Pathname
|
|
76
187
|
# ======================================================================
|
77
188
|
|
78
189
|
# @return [Boolean]
|
79
|
-
# `true` if
|
190
|
+
# `true` if `self` is equal to {#expand_path}.
|
191
|
+
#
|
80
192
|
def expanded?
|
193
|
+
self == expand_path
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
# @return [Boolean]
|
198
|
+
# `true` if `self` is equal to {#cwd}.
|
199
|
+
#
|
200
|
+
def cwd?
|
201
|
+
self == cwd
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
# Relative path from {#cwd} to `self`, if one exists.
|
206
|
+
#
|
207
|
+
# @return [QB::Path]
|
208
|
+
# If a relative path from {#cwd} to `self` exists.
|
209
|
+
#
|
210
|
+
# @return [nil]
|
211
|
+
# If no relative path from {#cwd} to `self` exists. Can't recall exactly
|
212
|
+
# how this happens, but I guess it can...
|
213
|
+
#
|
214
|
+
def relative
|
215
|
+
begin
|
216
|
+
relative_path_from cwd
|
217
|
+
rescue ArgumentError => error
|
218
|
+
nil
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
|
223
|
+
# Like {Pathname#realpath} but returns {nil} instead of raising if there
|
224
|
+
# isn't one.
|
225
|
+
#
|
226
|
+
# @return [nil]
|
227
|
+
# If there is no real path.
|
228
|
+
#
|
229
|
+
# @return [Pathname]
|
230
|
+
# If there is a real path.
|
231
|
+
#
|
232
|
+
def try_realpath
|
233
|
+
begin
|
234
|
+
realpath
|
235
|
+
rescue SystemCallError => error
|
236
|
+
nil
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
|
241
|
+
# Is `self` already it's real path?
|
242
|
+
#
|
243
|
+
# @return [Boolean]
|
244
|
+
# `true` if `self` and {#try_realpath} are equal.
|
245
|
+
#
|
246
|
+
def realpath?
|
247
|
+
self == try_realpath
|
248
|
+
end # #realpath?
|
249
|
+
|
250
|
+
|
251
|
+
# @return [Pathname]
|
252
|
+
# A regular (non-{QB::Path}) {Pathname} version of `self`.
|
253
|
+
def path
|
254
|
+
Pathname.new self
|
255
|
+
end
|
256
|
+
|
257
|
+
|
258
|
+
# Composed Sub-Instances
|
259
|
+
# ---------------------------------------------------------------------
|
260
|
+
|
261
|
+
# {QB::Repo::Git} resource for the Git repo {#path} is in one, or {nil} if
|
262
|
+
# it isn't.
|
263
|
+
#
|
264
|
+
# @return [QB::Repo::Git]
|
265
|
+
# If {#path} is in a Git repo.
|
266
|
+
#
|
267
|
+
# @return [nil]
|
268
|
+
# If {#path} is not in a Git repo.
|
269
|
+
#
|
270
|
+
def git
|
271
|
+
unless instance_variable_defined? :@git
|
272
|
+
@git = QB::Repo::Git.from_path path
|
273
|
+
end
|
81
274
|
|
275
|
+
@git
|
276
|
+
end
|
277
|
+
|
278
|
+
|
279
|
+
def gem
|
280
|
+
unless instance_variable_defined? :@gem
|
281
|
+
@gem = QB::Package::Gem.from_root_path path
|
282
|
+
end
|
283
|
+
|
284
|
+
@gem
|
82
285
|
end
|
83
286
|
|
84
287
|
end # class QB::Path
|
85
288
|
|
86
|
-
|
87
|
-
# Post-Processing
|
88
|
-
# =======================================================================
|
data/lib/qb/repo/git.rb
CHANGED
@@ -1,17 +1,43 @@
|
|
1
|
+
# Requirements
|
2
|
+
# =======================================================================
|
3
|
+
|
4
|
+
# Stdlib
|
5
|
+
# -----------------------------------------------------------------------
|
6
|
+
|
7
|
+
# Deps
|
8
|
+
# -----------------------------------------------------------------------
|
1
9
|
require 'cmds'
|
2
|
-
require 'nrser/refinements/types'
|
3
10
|
|
11
|
+
# Project / Package
|
12
|
+
# -----------------------------------------------------------------------
|
13
|
+
|
14
|
+
|
15
|
+
# Refinements
|
16
|
+
# =======================================================================
|
17
|
+
|
18
|
+
require 'nrser/refinements'
|
19
|
+
using NRSER
|
20
|
+
|
21
|
+
require 'nrser/refinements/types'
|
4
22
|
using NRSER::Types
|
5
23
|
|
6
|
-
|
7
|
-
|
24
|
+
|
25
|
+
# Declarations
|
26
|
+
# =======================================================================
|
27
|
+
|
28
|
+
module QB; end
|
29
|
+
module QB::Repo; end
|
30
|
+
|
31
|
+
|
32
|
+
# Definitions
|
33
|
+
# =======================================================================
|
8
34
|
|
9
35
|
# Encapsulate information about a Git repository and expose useful operations as
|
10
36
|
# instance methods.
|
11
37
|
#
|
12
38
|
# The main entry point is {QB::Repo::Git.from_path}, which creates a
|
13
39
|
#
|
14
|
-
class Git < NRSER::Meta::Props::Base
|
40
|
+
class QB::Repo::Git < NRSER::Meta::Props::Base
|
15
41
|
GITHUB_SSH_URL_RE = /^git@github\.com\:(?<owner>.*)\/(?<name>.*)\.git$/
|
16
42
|
GITHUB_HTTPS_URL_RE = /^https:\/\/github\.com\/(?<owner>.*)\/(?<name>.*)\.git$/
|
17
43
|
|
@@ -93,19 +119,30 @@ class Git < NRSER::Meta::Props::Base
|
|
93
119
|
# Class Methods
|
94
120
|
# =====================================================================
|
95
121
|
|
96
|
-
#
|
122
|
+
# Instantiate a {QB::Package::Git} resource for whatever Git repo `path`
|
123
|
+
# is in, or return `nil` if `path` is not in a Git repo.
|
97
124
|
#
|
98
|
-
# @param [String, Pathname]
|
99
|
-
# A path that
|
125
|
+
# @param [String, Pathname] path
|
126
|
+
# A path that may be in the Git repo.
|
127
|
+
#
|
128
|
+
# @param [Boolean] use_github_api:
|
129
|
+
# When `true` will will contact the GitHub API for information to populate
|
130
|
+
# the {QB::Repo::Git#github} property for repos that have a GitHub origin
|
131
|
+
# URL.
|
132
|
+
#
|
133
|
+
# Otherwise we will just assume GitHub repos are private since it's the
|
134
|
+
# safe guess, resulting in a {QB::Repo::Git#github} value of
|
135
|
+
# `{private: true}`.
|
100
136
|
#
|
101
137
|
# @return [QB::Repo::Git]
|
138
|
+
# If `path` is in a Git repo.
|
139
|
+
#
|
140
|
+
# @return [nil]
|
141
|
+
# If `path` is not in a Git repo.
|
102
142
|
#
|
103
143
|
# @raise [QB::FSStateError]
|
104
144
|
# - If we can't find any existing directory to look in based on
|
105
145
|
# `input_path`.
|
106
|
-
#
|
107
|
-
# - If the directory we do find to look in does not seems to be part of
|
108
|
-
# a Git repo.
|
109
146
|
#
|
110
147
|
def self.from_path path, use_github_api: false
|
111
148
|
raw_input_path = path
|
@@ -129,9 +166,7 @@ class Git < NRSER::Meta::Props::Base
|
|
129
166
|
root_result = Cmds.capture "git rev-parse --show-toplevel"
|
130
167
|
|
131
168
|
unless root_result.ok?
|
132
|
-
|
133
|
-
"Path #{ raw_input_path.inspect } does not appear to be in a " +
|
134
|
-
"Git repo (looked in #{ closest_dir.inspect })."
|
169
|
+
return nil
|
135
170
|
end
|
136
171
|
|
137
172
|
root = Pathname.new root_result.out.chomp
|
@@ -217,11 +252,39 @@ class Git < NRSER::Meta::Props::Base
|
|
217
252
|
end # .from_path
|
218
253
|
|
219
254
|
|
255
|
+
# Instantiate a {QB::Package::Git} resource for whatever Git repo `path`
|
256
|
+
# is in, raising an error if it's not in one.
|
257
|
+
#
|
258
|
+
# @param [String, Pathname] path
|
259
|
+
# A path that is in the Git repo.
|
260
|
+
#
|
261
|
+
# @param use_github_api: see #from_path
|
262
|
+
#
|
263
|
+
# @return [QB::Repo::Git]
|
264
|
+
#
|
265
|
+
# @raise [QB::FSStateError]
|
266
|
+
# - If we can't find any existing directory to look in based on
|
267
|
+
# `input_path`.
|
268
|
+
#
|
269
|
+
# - If the directory we do find to look in does not seems to be part of
|
270
|
+
# a Git repo.
|
271
|
+
#
|
272
|
+
def from_path! path, use_github_api: false
|
273
|
+
from_path( path, use_github_api: use_github_api ).tap { |git|
|
274
|
+
if git.nil?
|
275
|
+
raise QB::FSStateError,
|
276
|
+
"Path #{ path.inspect } does not appear to be in a " +
|
277
|
+
"Git repo."
|
278
|
+
end
|
279
|
+
}
|
280
|
+
end # #from_path!
|
281
|
+
|
282
|
+
|
220
283
|
# Props
|
221
284
|
# =====================================================================
|
222
285
|
|
223
|
-
prop :raw_input_path, type: t.
|
224
|
-
prop :root, type:
|
286
|
+
prop :raw_input_path, type: t.path, default: nil, to_data: :to_s
|
287
|
+
prop :root, type: t.pathname, to_data: :to_s
|
225
288
|
prop :user, type: User
|
226
289
|
prop :is_clean, type: t.bool
|
227
290
|
prop :head, type: t.maybe(t.str), default: nil
|
@@ -231,6 +294,10 @@ class Git < NRSER::Meta::Props::Base
|
|
231
294
|
prop :name, type: t.maybe(t.str), default: nil
|
232
295
|
prop :github, type: t.maybe(t.hash_), default: nil
|
233
296
|
|
297
|
+
|
298
|
+
# Derived Properties
|
299
|
+
# ---------------------------------------------------------------------
|
300
|
+
|
234
301
|
prop :head_short, type: t.maybe(t.str), source: :head_short
|
235
302
|
prop :full_name, type: t.maybe(t.str), source: :full_name
|
236
303
|
prop :is_github, type: t.bool, source: :github?
|
@@ -251,11 +318,15 @@ class Git < NRSER::Meta::Props::Base
|
|
251
318
|
!github.nil?
|
252
319
|
end
|
253
320
|
|
321
|
+
|
322
|
+
# Reading Repo State
|
323
|
+
# ---------------------------------------------------------------------
|
324
|
+
|
325
|
+
# @return [Boolean]
|
326
|
+
# `false` if the repo has any uncommitted changes or untracked files.
|
327
|
+
#
|
254
328
|
def clean?
|
255
329
|
is_clean
|
256
330
|
end
|
257
331
|
|
258
|
-
end # class Git
|
259
|
-
|
260
|
-
end # module Repo
|
261
|
-
end # module QB
|
332
|
+
end # class QB::Repo::Git
|
data/lib/qb/role.rb
CHANGED
@@ -411,19 +411,37 @@ class QB::Role
|
|
411
411
|
path = QB::Util.resolve dest
|
412
412
|
|
413
413
|
search_dirs = search_path.find_all { |pathname|
|
414
|
-
path.fnmatch? pathname
|
414
|
+
path.fnmatch?( pathname / '**' )
|
415
415
|
}
|
416
416
|
|
417
417
|
case search_dirs.length
|
418
418
|
when 0
|
419
419
|
# It's not in any of the search directories
|
420
420
|
#
|
421
|
-
# If it has 'roles' as a segment than use what's after
|
421
|
+
# If it has 'roles' as a segment than use what's after the last occurrence
|
422
|
+
# of that (unless there isn't anything).
|
422
423
|
#
|
423
|
-
|
424
|
+
segments = path.to_s.split File::SEPARATOR
|
425
|
+
|
426
|
+
if index = segments.rindex( 'roles' )
|
427
|
+
name_segs = segments[index..-1]
|
428
|
+
|
429
|
+
unless name_segs.empty?
|
430
|
+
return File.join name_segs
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
# Ok, that didn't work... just return the basename I guess...
|
435
|
+
File.basename path
|
436
|
+
|
437
|
+
when 1
|
438
|
+
|
439
|
+
else
|
440
|
+
# Multiple matches?!?!?
|
441
|
+
|
424
442
|
|
425
443
|
end
|
426
|
-
end
|
444
|
+
end # #guess_role_name
|
427
445
|
|
428
446
|
|
429
447
|
# Instance Attributes
|