nub 0.0.75 → 0.0.76
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/lib/nub/core.rb +40 -2
- data/lib/nub/fileutils.rb +152 -18
- data/lib/nub/pacman.rb +94 -0
- data/lib/nub/sys.rb +0 -13
- data/lib/nub.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa43f1a038ebba265bc6b032dcb802cd5870e9b1cc703fe46fb5a96f6240d71a
|
4
|
+
data.tar.gz: 5f6201fb958875f52935cb9b0257831671d3dd3db21f2ccbcb49356fdd1cf8d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da19249c6af9fac56ac2d8c7ec4c6a525792bbca53fe1b63b8dc5e47c0439e525b5e918ddfdc82fb820579e708dc4ed12602722491638f4be3681701e9d75019
|
7
|
+
data.tar.gz: 902fbbaae0773ca9cd7a36b6d3575d9fba4b602ea85e3ae6789c9a599d4fc30cc5fb273bd18d7f7f71b9503027315a037199be38f0b60fc4700dd749f019c64f
|
data/lib/nub/core.rb
CHANGED
@@ -20,6 +20,7 @@
|
|
20
20
|
#SOFTWARE.
|
21
21
|
|
22
22
|
require 'erb'
|
23
|
+
require 'ostruct'
|
23
24
|
|
24
25
|
ColorPair = Struct.new(:str, :color_code, :color_name)
|
25
26
|
ColorMap = {
|
@@ -77,6 +78,27 @@ class ERBResolve
|
|
77
78
|
|
78
79
|
return data
|
79
80
|
end
|
81
|
+
|
82
|
+
# Resolve variables for the given data type, changes are done inplace
|
83
|
+
# @data [string/array/hash] data to replace vars
|
84
|
+
def resolve!(data)
|
85
|
+
|
86
|
+
# Recurse
|
87
|
+
if data.is_a?(Array)
|
88
|
+
for i in 0...data.size
|
89
|
+
resolve!(data[i])
|
90
|
+
end
|
91
|
+
elsif data.is_a?(Hash)
|
92
|
+
data.each{|k,v| data[k] = resolve!(v)}
|
93
|
+
end
|
94
|
+
|
95
|
+
# Base case
|
96
|
+
if data.is_a?(String)
|
97
|
+
data.gsub!(data, ERB.new(data).result(@context))
|
98
|
+
end
|
99
|
+
|
100
|
+
return data
|
101
|
+
end
|
80
102
|
end
|
81
103
|
|
82
104
|
# Hash extensions
|
@@ -94,6 +116,12 @@ class Hash
|
|
94
116
|
def erb(vars = {})
|
95
117
|
ERBResolve.new(vars).resolve(self)
|
96
118
|
end
|
119
|
+
|
120
|
+
# Easily inject ERB variables into hash values
|
121
|
+
# +vars+:: hash of variables to inject into the string
|
122
|
+
def erb!(vars = {})
|
123
|
+
ERBResolve.new(vars).resolve!(self)
|
124
|
+
end
|
97
125
|
end
|
98
126
|
|
99
127
|
# Array extensions
|
@@ -109,6 +137,12 @@ class Array
|
|
109
137
|
def erb(vars = {})
|
110
138
|
ERBResolve.new(vars).resolve(self)
|
111
139
|
end
|
140
|
+
|
141
|
+
# Easily inject ERB variables into Array values
|
142
|
+
# +vars+:: hash of variables to inject into the string
|
143
|
+
def erb!(vars = {})
|
144
|
+
ERBResolve.new(vars).resolve!(self)
|
145
|
+
end
|
112
146
|
end
|
113
147
|
|
114
148
|
# Monkey patch string with some useful methods
|
@@ -120,6 +154,12 @@ class String
|
|
120
154
|
ERBResolve.new(vars).resolve(self)
|
121
155
|
end
|
122
156
|
|
157
|
+
# Easily inject ERB variables into a string
|
158
|
+
# @param vars [Hash] of variables to inject into the string
|
159
|
+
def erb!(vars = {})
|
160
|
+
ERBResolve.new(vars).resolve!(self)
|
161
|
+
end
|
162
|
+
|
123
163
|
# Convert the string to ascii, stripping out or converting all non-ascii characters
|
124
164
|
def to_ascii
|
125
165
|
options = {
|
@@ -175,8 +215,6 @@ class String
|
|
175
215
|
|
176
216
|
return tokens
|
177
217
|
end
|
178
|
-
|
179
|
-
|
180
218
|
end
|
181
219
|
|
182
220
|
# vim: ft=ruby:ts=2:sw=2:sts=2
|
data/lib/nub/fileutils.rb
CHANGED
@@ -19,11 +19,45 @@
|
|
19
19
|
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
20
20
|
#SOFTWARE.
|
21
21
|
|
22
|
+
require 'digest'
|
22
23
|
require 'fileutils'
|
24
|
+
require 'yaml'
|
25
|
+
|
26
|
+
require_relative 'core'
|
23
27
|
|
24
28
|
# Monkey patch FileUtils with some useful methods
|
25
29
|
module FileUtils
|
26
30
|
|
31
|
+
# Check if any digests have changed based on the given files
|
32
|
+
# @param key [yaml] yalm section heading to give digests
|
33
|
+
# @param digestfile [string] digest file to check against
|
34
|
+
# @param files [array] files to get digests for
|
35
|
+
# @returns (newfiles, modifiedfiles, deletedfiles)
|
36
|
+
def self.digests_changed?(key, digestfile, files)
|
37
|
+
files = [files] if files.is_a?(String)
|
38
|
+
newfiles, modifiedfiles, deletedfiles = [], [], []
|
39
|
+
|
40
|
+
# Since layers are stacked and the digest file may be from a lower
|
41
|
+
# layer this should be ignored unless the layer matches
|
42
|
+
if (digests = File.exist?(digestfile) ? YAML.load_file(digestfile)[key] : nil)
|
43
|
+
|
44
|
+
# New files: in files but not yaml
|
45
|
+
newfiles = files.select{|x| not digests[x]}
|
46
|
+
|
47
|
+
# Delete files: in yaml but not files
|
48
|
+
deletedfiles = digests.map{|k,v| k}.select{|x| not files.include?(x)}
|
49
|
+
|
50
|
+
# Modified files: in both but digest is different
|
51
|
+
modifiedfiles = files.select{|x| digests[x] and
|
52
|
+
Digest::MD5.hexdigest(File.binread(x)) != digests[x]}
|
53
|
+
else
|
54
|
+
newfiles = files
|
55
|
+
end
|
56
|
+
|
57
|
+
return newfiles, modifiedfiles, deletedfiles
|
58
|
+
end
|
59
|
+
|
60
|
+
|
27
61
|
# Check PATH for executable
|
28
62
|
# @param name [String] name of executable to find
|
29
63
|
# @param path [Array] path to search
|
@@ -48,9 +82,10 @@ module FileUtils
|
|
48
82
|
# @return new version
|
49
83
|
def self.inc_version(path, regex, major:false, minor:false, rev:true, override:nil)
|
50
84
|
version = nil
|
51
|
-
self.update(path){|
|
85
|
+
self.update(path){|data|
|
52
86
|
|
53
87
|
# Increment version
|
88
|
+
line = data.split("\n").find{|x| x[regex, 1]}
|
54
89
|
if ver = line[regex, 1]
|
55
90
|
new_ver = nil
|
56
91
|
if not override
|
@@ -65,15 +100,92 @@ module FileUtils
|
|
65
100
|
|
66
101
|
# Modify the original line
|
67
102
|
version = new_ver
|
68
|
-
line.gsub
|
103
|
+
newline = line.gsub(ver, new_ver)
|
104
|
+
data.gsub!(line, newline)
|
69
105
|
end
|
70
106
|
}
|
71
107
|
|
72
108
|
return version
|
73
109
|
end
|
74
110
|
|
111
|
+
# Insert into a file
|
112
|
+
# Location of insert is determined by the given regex and offset.
|
113
|
+
# Append is used if no regex is given.
|
114
|
+
# @param file [string] path of file to modify
|
115
|
+
# @param values [array] string or list of string values to insert
|
116
|
+
# @param regex [string] regular expression for location, not used if offset is nil
|
117
|
+
# @param offset [int] offset insert location by this amount for regexs
|
118
|
+
# @returns true if a change was made to the file
|
119
|
+
def self.insert(file, values, regex:nil, offset:1)
|
120
|
+
return false if not values or values.empty?
|
121
|
+
|
122
|
+
changed = false
|
123
|
+
values = [values] if values.is_a?(String)
|
124
|
+
FileUtils.touch(file) if not File.exist?(file)
|
125
|
+
|
126
|
+
begin
|
127
|
+
data = nil
|
128
|
+
File.open(file, 'r+') do |f|
|
129
|
+
data = f.read
|
130
|
+
lines = data.split("\n")
|
131
|
+
|
132
|
+
# Match regex for insert location
|
133
|
+
regex = Regexp.new(regex) if regex.is_a?(String)
|
134
|
+
i = regex ? lines.index{|x| x =~ regex} : lines.size
|
135
|
+
return false if not i
|
136
|
+
i += offset if regex and offset
|
137
|
+
|
138
|
+
# Insert at offset
|
139
|
+
values.each{|x|
|
140
|
+
lines.insert(i, x) and i += 1
|
141
|
+
changed = true
|
142
|
+
}
|
143
|
+
|
144
|
+
# Truncate then write out new content
|
145
|
+
f.seek(0)
|
146
|
+
f.truncate(0)
|
147
|
+
f.puts(lines)
|
148
|
+
end
|
149
|
+
rescue
|
150
|
+
# Revert back to the original incase of failure
|
151
|
+
File.open(file, 'w'){|f| f << data} if data
|
152
|
+
raise
|
153
|
+
end
|
154
|
+
|
155
|
+
return changed
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
# Replace in file
|
160
|
+
# @param file [string] file to modify
|
161
|
+
# @param regex [string] regular expression match on
|
162
|
+
# @param value [string] regular expression string replacement
|
163
|
+
# @returns true on change
|
164
|
+
def self.replace(file, regex, value)
|
165
|
+
changed = self.update(file){|data|
|
166
|
+
lines = data.split("\n")
|
167
|
+
|
168
|
+
# Search replace
|
169
|
+
regex = Regexp.new(regex) if regex.is_a?(String)
|
170
|
+
lines.each{|x| x.gsub!(regex, value)}
|
171
|
+
|
172
|
+
# Change data inline
|
173
|
+
data.gsub!(data, lines * "\n")
|
174
|
+
}
|
175
|
+
|
176
|
+
return changed
|
177
|
+
end
|
178
|
+
|
179
|
+
# Resolve template
|
180
|
+
# @param file [string] file to resolve templates for
|
181
|
+
# @param vars [hash/ostruct] templating variables to use while resolving
|
182
|
+
# @returns true on change
|
183
|
+
def self.resolve(file, vars)
|
184
|
+
return self.update(file){|data| data.erb!(vars)}
|
185
|
+
end
|
186
|
+
|
75
187
|
# Update the file using a block, revert on failure.
|
76
|
-
# Use this for
|
188
|
+
# Use this for file edits. Block is passed in the file data
|
77
189
|
# @param filename [String] name of the file to change
|
78
190
|
# @return true on change
|
79
191
|
def self.update(filename)
|
@@ -81,23 +193,25 @@ module FileUtils
|
|
81
193
|
block = Proc.new # Most efficient way to get block
|
82
194
|
|
83
195
|
begin
|
84
|
-
|
196
|
+
data = nil
|
85
197
|
File.open(filename, 'r+'){|f|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
198
|
+
data = f.read
|
199
|
+
data_orig = data.dup
|
200
|
+
|
201
|
+
# Call block
|
202
|
+
block.call(data)
|
203
|
+
changed |= data_orig != data
|
92
204
|
|
93
205
|
# Save the changes back to the file
|
94
|
-
|
95
|
-
|
96
|
-
|
206
|
+
if changed
|
207
|
+
f.seek(0)
|
208
|
+
f.truncate(0)
|
209
|
+
f << data
|
210
|
+
end
|
97
211
|
}
|
98
|
-
rescue
|
212
|
+
rescue Exception => e
|
99
213
|
# Revert back to the original incase of failure
|
100
|
-
File.open(filename, 'w'){|f| f
|
214
|
+
File.open(filename, 'w'){|f| f << data} if data
|
101
215
|
raise
|
102
216
|
end
|
103
217
|
|
@@ -112,21 +226,41 @@ module FileUtils
|
|
112
226
|
def self.update_copyright(path, copyright, year:Time.now.year)
|
113
227
|
set_copyright = false
|
114
228
|
|
115
|
-
changed = self.update(path){|
|
229
|
+
changed = self.update(path){|data|
|
230
|
+
line = data.split("\n").find{|x| x[/#{Regexp.quote(copyright)}/]}
|
116
231
|
if !set_copyright && line =~ /#{Regexp.quote(copyright)}/
|
117
232
|
set_copyright = true
|
118
233
|
_year = line[/#{Regexp.quote(copyright)}\s+((\d{4}\s)|(\d{4}-\d{4})).*/, 1].strip
|
119
234
|
if _year.include?("-")
|
120
235
|
years = _year.split("-")
|
121
|
-
|
236
|
+
if years.last != year
|
237
|
+
newline = line.gsub(_year, "#{years.first}-#{year}")
|
238
|
+
data.gsub!(line, newline)
|
239
|
+
end
|
122
240
|
elsif prev_year = _year != year.to_s ? year.to_i - 1 : nil
|
123
|
-
line.gsub
|
241
|
+
newline = line.gsub(_year.to_s, "#{prev_year}-#{year}")
|
242
|
+
data.gsub!(line, newline)
|
124
243
|
end
|
125
244
|
end
|
126
245
|
}
|
127
246
|
return changed
|
128
247
|
end
|
129
248
|
|
249
|
+
# Generate digests for array of files given and save them
|
250
|
+
# @param key [yaml] yalm section heading to give digests
|
251
|
+
# @param digestfile [string] digest file to update
|
252
|
+
# @param files [array] files to get digests for
|
253
|
+
def self.update_digests(key, digestfile, files)
|
254
|
+
files = [files] if files.is_a?(String)
|
255
|
+
|
256
|
+
# Build up digests structure
|
257
|
+
digests = {}
|
258
|
+
files.each{|x| digests[x] = Digest::MD5.hexdigest(File.binread(x)) if File.exist?(x)}
|
259
|
+
|
260
|
+
# Write out digests structure as yaml with named header
|
261
|
+
File.open(digestfile, 'w'){|f| f.puts({key => digests}.to_yaml)}
|
262
|
+
end
|
263
|
+
|
130
264
|
# Get SemVer formatted versions
|
131
265
|
# @param path [String] file to increment version for
|
132
266
|
# @param regex [Regex] capture pattern for version match
|
data/lib/nub/pacman.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
#MIT License
|
2
|
+
#Copyright (c) 2018 phR0ze
|
3
|
+
#
|
4
|
+
#Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
#of this software and associated documentation files (the "Software"), to deal
|
6
|
+
#in the Software without restriction, including without limitation the rights
|
7
|
+
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
#copies of the Software, and to permit persons to whom the Software is
|
9
|
+
#furnished to do so, subject to the following conditions:
|
10
|
+
#
|
11
|
+
#The above copyright notice and this permission notice shall be included in all
|
12
|
+
#copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
20
|
+
#SOFTWARE.
|
21
|
+
|
22
|
+
require 'fileutils'
|
23
|
+
require_relative 'log'
|
24
|
+
require_relative 'sys'
|
25
|
+
require_relative 'module'
|
26
|
+
|
27
|
+
# Wrapper around system Arch Linux pacman
|
28
|
+
module Pacman
|
29
|
+
extend self
|
30
|
+
mattr_accessor(:path, :config, :sysroot)
|
31
|
+
|
32
|
+
# Configure pacman for the given root
|
33
|
+
# @param path [String] path where all pacman artifacts will be (i.e. logs, cache etc...)
|
34
|
+
# @param config [String] config file path to use, note gets copied in
|
35
|
+
# @param sysroot [String] path to the system root to use
|
36
|
+
def init(path, config, sysroot)
|
37
|
+
self.path = path
|
38
|
+
self.sysroot = sysroot
|
39
|
+
self.config = File.join(path, File.basename(config))
|
40
|
+
|
41
|
+
# Validate incoming params
|
42
|
+
Log.die("pacman path '#{path}' doesn't exist") unless Dir.exist?(path)
|
43
|
+
Log.die("pacman sysroot '#{sysroot}' doesn't exist") unless Dir.exist?(sysroot)
|
44
|
+
Log.die("pacman config file '#{config}' doesn't exist") unless File.exist?(config)
|
45
|
+
|
46
|
+
# Update the given pacman config file to use the given path
|
47
|
+
FileUtils.rm_rf(File.join(path, '.'))
|
48
|
+
FileUtils.cp(config, path, preserve: true)
|
49
|
+
|
50
|
+
Sys.exec("cp -a #{@pacman_src_mirrors} #{@pacman_path}")
|
51
|
+
Fedit.replace(@pacman_conf, /(Architecture = ).*/, "\\1#{@vars.arch}")
|
52
|
+
# Leave DBPath set as /var/lib/pacman and copy out sync
|
53
|
+
Fedit.replace(@pacman_conf, /#(CacheDir\s+= ).*/, "\\1#{File.join(@pacman_path, 'cache')}")
|
54
|
+
Fedit.replace(@pacman_conf, /#(LogFile\s+= ).*/, "\\1#{File.join(@pacman_path, 'pacman.log')}")
|
55
|
+
Fedit.replace(@pacman_conf, /#(GPGDir\s+= ).*/, "\\1#{File.join(@pacman_path, 'gnupg')}")
|
56
|
+
Fedit.replace(@pacman_conf, /#(HookDir\s+= ).*/, "\\1#{File.join(@pacman_path, 'hooks')}")
|
57
|
+
Fedit.replace(@pacman_conf, /.*(\/.*mirrorlist).*/, "Include = #{@pacman_path}\\1")
|
58
|
+
|
59
|
+
repos = Dir[File.join(@pacman_path, "*.mirrorlist")].map{|x| File.basename(x, '.mirrorlist')}
|
60
|
+
Sys.exec("pacman-key --config #{@pacman_conf} --init")
|
61
|
+
Sys.exec("pacman-key --config #{@pacman_conf} --populate #{repos * ' '}")
|
62
|
+
|
63
|
+
# Initialize pacman keyring
|
64
|
+
#Sys.exec("pacman-key --config #{self.config} --init")
|
65
|
+
#Sys.exec("pacman-key --config #{self.config} --populate #{repos * ' '}")
|
66
|
+
end
|
67
|
+
|
68
|
+
# Update the pacman database
|
69
|
+
def update
|
70
|
+
success = false
|
71
|
+
while not success
|
72
|
+
begin
|
73
|
+
cmd = "pacman -Sy"
|
74
|
+
cmd += " --sysroot #{self.sysroot}" if self.sysroot
|
75
|
+
Sys.exec(cmd)
|
76
|
+
success = true
|
77
|
+
rescue Exception => e
|
78
|
+
puts(e.message)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Install the given packages
|
84
|
+
# @param pkgs [Array] List of packages to install
|
85
|
+
def install()
|
86
|
+
puts("Pacstrap package installs for '#{deployment}'...".colorize(:cyan))
|
87
|
+
cmd = ['pacstrap', '-GMc', deployment_work, '--config', @pacman_conf, '--needed', *apps[:reg]]
|
88
|
+
cmd += ['--ignore', apps[:ignore] * ','] if apps[:ignore].any?
|
89
|
+
!puts("Error: Failed to install packages correctly".colorize(:red)) and
|
90
|
+
exit unless Sys.exec(cmd, env:@proxyenv)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# vim: ft=ruby:ts=2:sw=2:sts=2
|
data/lib/nub/sys.rb
CHANGED
@@ -107,19 +107,6 @@ module Sys
|
|
107
107
|
return pass
|
108
108
|
end
|
109
109
|
|
110
|
-
# Update pacman database
|
111
|
-
def pacman_update
|
112
|
-
success = false
|
113
|
-
while not success
|
114
|
-
begin
|
115
|
-
Sys.exec("pacman -Sy")
|
116
|
-
success = true
|
117
|
-
rescue Exception => e
|
118
|
-
puts(e.message)
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
110
|
# Remove given dir or file
|
124
111
|
# Params:
|
125
112
|
# +path+:: path to delete
|
data/lib/nub.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nub
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.76
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Patrick Crummett
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-06-
|
11
|
+
date: 2018-06-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -97,6 +97,7 @@ files:
|
|
97
97
|
- lib/nub/log.rb
|
98
98
|
- lib/nub/module.rb
|
99
99
|
- lib/nub/net.rb
|
100
|
+
- lib/nub/pacman.rb
|
100
101
|
- lib/nub/sys.rb
|
101
102
|
- lib/nub/thread_comm.rb
|
102
103
|
- lib/nub/user.rb
|