workEnv 0.1.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 +7 -0
- data/LICENSE +620 -0
- data/bin/wenv +10 -0
- data/lib/WorkEnvs/Action.rb +604 -0
- data/lib/WorkEnvs/Common/Arch.rb +185 -0
- data/lib/WorkEnvs/Common/Config.rb +364 -0
- data/lib/WorkEnvs/Common/Confirm.rb +80 -0
- data/lib/WorkEnvs/Common/DBInterface.rb +608 -0
- data/lib/WorkEnvs/Common/Defines.rb +20 -0
- data/lib/WorkEnvs/Common/Dependencies.rb +206 -0
- data/lib/WorkEnvs/Common/EnvOpts.rb +129 -0
- data/lib/WorkEnvs/Common/Package.rb +116 -0
- data/lib/WorkEnvs/Common/PackageDownloader.rb +760 -0
- data/lib/WorkEnvs/Common/VersionSelector.rb +278 -0
- data/lib/WorkEnvs/Common.rb +10 -0
- data/lib/WorkEnvs/Core.rb +1004 -0
- data/lib/WorkEnvs/Envs/Any.rb +49 -0
- data/lib/WorkEnvs/Envs/Basic.rb +204 -0
- data/lib/WorkEnvs/accessors.rb +147 -0
- data/lib/WorkEnvs/global.rb +749 -0
- data/lib/WorkEnvs.rb +75 -0
- data/workrc-completion.sh +284 -0
- metadata +65 -0
|
@@ -0,0 +1,760 @@
|
|
|
1
|
+
#!/usr/bin/ruby
|
|
2
|
+
|
|
3
|
+
require 'uri'
|
|
4
|
+
require 'tempfile'
|
|
5
|
+
require 'rbconfig'
|
|
6
|
+
require 'thread'
|
|
7
|
+
|
|
8
|
+
# Creates unique temporary directory in /tmp
|
|
9
|
+
def create_tmp_dir(tmp_dir_prefix=nil,tmp_root_dir=nil)
|
|
10
|
+
tmp_dir = nil
|
|
11
|
+
|
|
12
|
+
if(Dir.respond_to?('mktmpdir')) then
|
|
13
|
+
tmp_dir = Dir.mktmpdir(tmp_dir_prefix,tmp_root_dir)
|
|
14
|
+
else
|
|
15
|
+
tmp_root_dir = (tmp_root_dir.nil? ? Dir.tmpdir : tmp_root_dir)
|
|
16
|
+
tmp_dir_prefix = (tmp_dir_prefix.nil? ? "" : tmp_dir_prefix)
|
|
17
|
+
tmp_dir_name = File.join(tmp_root_dir,tmp_dir_prefix)
|
|
18
|
+
tmp_dir = `mktemp -d #{tmp_dir_name}XXXXXX`.chomp()
|
|
19
|
+
end
|
|
20
|
+
return tmp_dir
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Run a shell command and return its stdout
|
|
24
|
+
#
|
|
25
|
+
# Print command if verbose = false
|
|
26
|
+
#
|
|
27
|
+
# Returns the cimmand return status
|
|
28
|
+
def runCmd(cmd, silent = false, nRetry = 1)
|
|
29
|
+
ret = 0
|
|
30
|
+
1.upto(nRetry){|i|
|
|
31
|
+
retrying = (i == 1 ? "" : "Retrying: ")
|
|
32
|
+
puts retrying + cmd if silent == false
|
|
33
|
+
if(RbConfig::CONFIG['host_os'] =~ /mingw/) then
|
|
34
|
+
script = Tempfile.new("script")
|
|
35
|
+
script.puts "set -x"
|
|
36
|
+
script.puts "set -e"
|
|
37
|
+
script.puts cmd
|
|
38
|
+
script.flush()
|
|
39
|
+
system("cat #{script.path}")
|
|
40
|
+
ret = `bash #{script.path}`.chomp()
|
|
41
|
+
script.close()
|
|
42
|
+
else
|
|
43
|
+
ret = `#{cmd}`.chomp()
|
|
44
|
+
end
|
|
45
|
+
return ret if $? == 0
|
|
46
|
+
}
|
|
47
|
+
raise("Command failed:\npwd=#{Dir.pwd()}\nCommand=#{cmd}\nReturn=#{ret}") if $? != 0
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Execute cmd and returns true in case of success, false otherwise
|
|
51
|
+
def runTest(cmd, silent = false, nRetry = 1)
|
|
52
|
+
ret = 1
|
|
53
|
+
1.upto(nRetry){|i|
|
|
54
|
+
retrying = (i == 1 ? "" : "Retrying: ")
|
|
55
|
+
puts retrying + cmd if silent == false
|
|
56
|
+
if(RbConfig::CONFIG['host_os'] =~ /mingw/) then
|
|
57
|
+
script = Tempfile.new("script")
|
|
58
|
+
script.puts "set -x"
|
|
59
|
+
script.puts "set -e"
|
|
60
|
+
script.puts cmd
|
|
61
|
+
script.flush()
|
|
62
|
+
system("cat #{script.path}")
|
|
63
|
+
ret = `bash #{script.path}`.chomp()
|
|
64
|
+
script.close()
|
|
65
|
+
else
|
|
66
|
+
ret = `#{cmd}`.chomp()
|
|
67
|
+
end
|
|
68
|
+
return true if $? == 0
|
|
69
|
+
}
|
|
70
|
+
return false
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
module WorkEnvs
|
|
74
|
+
# Command to download a remote file through HTTP or HTTPS
|
|
75
|
+
WORK_WGET_CMD="wget -q --no-check-certificate"
|
|
76
|
+
# Command to check if a remote file exists (HTTP or HTTPS)
|
|
77
|
+
WORK_WGET_SPIDER_CMD="wget #{WorkEnvs::VERBOSE == true ? "" : "-q"} --no-check-certificate --spider"
|
|
78
|
+
# Command to copy a file
|
|
79
|
+
WORK_CP_CMD="cp"
|
|
80
|
+
|
|
81
|
+
# Exception thrown when a file was not found
|
|
82
|
+
class NoSuchFileException < StandardError
|
|
83
|
+
# Constructor
|
|
84
|
+
#
|
|
85
|
+
# - path = package path
|
|
86
|
+
# - server = locations searched
|
|
87
|
+
def initialize(path, server="")
|
|
88
|
+
super("\nERROR: File #{path} could not be found #{server}\n")
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Class used to manipulate packages
|
|
93
|
+
#
|
|
94
|
+
# This allows to query the DB for packages, query for dependencies (DB or files),
|
|
95
|
+
# download, extract packages and much more
|
|
96
|
+
class PackageDownloader
|
|
97
|
+
# Selected architectrue.
|
|
98
|
+
#
|
|
99
|
+
# This is a hash returned by WorkEnvs::getArch()
|
|
100
|
+
attr_reader :arch
|
|
101
|
+
|
|
102
|
+
# Table to use for DB queries (default = nil)
|
|
103
|
+
#
|
|
104
|
+
# Passed to the DBInterface #db_interface
|
|
105
|
+
attr_reader :table
|
|
106
|
+
|
|
107
|
+
# Global settings
|
|
108
|
+
attr_reader :settings
|
|
109
|
+
|
|
110
|
+
# DBInterface used for DB queries
|
|
111
|
+
attr_reader :db_interface
|
|
112
|
+
|
|
113
|
+
# Constructor
|
|
114
|
+
#
|
|
115
|
+
# - machine: label of the machine to run on. (default = nil)
|
|
116
|
+
# This is passed to WorkEnvs::getArch to fill the #arch attribute
|
|
117
|
+
# - table: Table to use for queries (default = nil)
|
|
118
|
+
# This is usually not set because the --table option modifies the settings
|
|
119
|
+
# and end up to be the default
|
|
120
|
+
# - silent: If false, activate verbosity (default = true)
|
|
121
|
+
def initialize(machine = nil, table=nil, silent = true)
|
|
122
|
+
@settings = WorkEnvs::settings[:db]
|
|
123
|
+
@arch = WorkEnvs::getArch(machine)
|
|
124
|
+
@table = table
|
|
125
|
+
|
|
126
|
+
@db_interface = DBInterface.new(machine, table, silent)
|
|
127
|
+
@be_silent = silent
|
|
128
|
+
@semaphore = Mutex.new
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Change the silent attribute for the PackageDownloader and its DBInterface
|
|
132
|
+
def be_silent=(val)
|
|
133
|
+
@be_silent = val
|
|
134
|
+
@db_interface.be_silent = val
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Modify a DBQuery to ignore specific version
|
|
138
|
+
#
|
|
139
|
+
# The goal of this is to handle package that moved from one project to
|
|
140
|
+
# another. This make sure we ignore the packages whose sha1
|
|
141
|
+
# matches SHA1 of the blacklisted project
|
|
142
|
+
def addBlacklist(db_query, blacklist)
|
|
143
|
+
if blacklist.length > 0 then
|
|
144
|
+
db_query.cond <<
|
|
145
|
+
{ :field => "sha1", :not => true,
|
|
146
|
+
:sub_query => DBQuery.new({
|
|
147
|
+
:qtype => [ "sha1" ],
|
|
148
|
+
:cond => [ {:field => "arch",:value =>@arch[:label]},
|
|
149
|
+
{:field => "project",:value => blacklist},
|
|
150
|
+
],
|
|
151
|
+
:limit => false,
|
|
152
|
+
:order => false
|
|
153
|
+
}).to_sql(@db_interface.table, true)[0]}
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Find the full package name of a package from its SHA1 and the #arch
|
|
158
|
+
#
|
|
159
|
+
# Returns a Package with its pack field filled or nil
|
|
160
|
+
#
|
|
161
|
+
# Throws EmptyQueryException if the package was not found and required = true
|
|
162
|
+
def queryPackage(package, sha1, required = false)
|
|
163
|
+
query = DBQuery.new({
|
|
164
|
+
:qtype => [ "name" ],
|
|
165
|
+
:cond => [ {:field => "sha1",:value =>sha1},
|
|
166
|
+
{:field => "arch",:value =>@arch[:label]},
|
|
167
|
+
{:field => "project",:value =>package.to_s}
|
|
168
|
+
]
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
packageName = @db_interface.doQuery(query, required)
|
|
172
|
+
return nil if packageName == WORK_EMPTY_QUERY
|
|
173
|
+
|
|
174
|
+
if package.instance_of?(WorkEnvs::Package) then
|
|
175
|
+
package.pack = packageName[0][0]
|
|
176
|
+
else
|
|
177
|
+
package = Package.new(package, {}, packageName[0][0])
|
|
178
|
+
end
|
|
179
|
+
return package
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Find the version of a package from its SHA1 and the #arch
|
|
183
|
+
#
|
|
184
|
+
# Returns the version String or nil
|
|
185
|
+
#
|
|
186
|
+
# Throws EmptyQueryException if the package was not found and required = true
|
|
187
|
+
def queryPackageVersion(project, sha1, required = false)
|
|
188
|
+
query = DBQuery.new({
|
|
189
|
+
:qtype => [ "version" ],
|
|
190
|
+
:cond => [ {:field => "sha1",:value =>sha1},
|
|
191
|
+
{:field => "arch",:value =>@arch[:label]},
|
|
192
|
+
{:field => "project",:value =>project}
|
|
193
|
+
]
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
packageVersion = @db_interface.doQuery(query, required)
|
|
197
|
+
if packageVersion then
|
|
198
|
+
return packageVersion[0][0]
|
|
199
|
+
else
|
|
200
|
+
return nil
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Find the version of a package and its SHA1 from a package
|
|
205
|
+
# full name and the #arch
|
|
206
|
+
#
|
|
207
|
+
# Returns dep_info (See #Dependencies) or nil
|
|
208
|
+
#
|
|
209
|
+
# Throws EmptyQueryException if the package was not found and required = true
|
|
210
|
+
def queryDepInfosFromName(env, name, required)
|
|
211
|
+
project = WorkEnvs::getMainPackage(env)
|
|
212
|
+
query = DBQuery.new({
|
|
213
|
+
:qtype => [ "version", "sha1" ],
|
|
214
|
+
:cond => [ {:field => "name",:value =>name},
|
|
215
|
+
{:field => "arch",:value =>@arch[:label]},
|
|
216
|
+
{:field => "project",:value =>project}
|
|
217
|
+
]
|
|
218
|
+
})
|
|
219
|
+
addBlacklist(query, WorkEnvs::getBlackList(env))
|
|
220
|
+
|
|
221
|
+
packageVersion = @db_interface.doQuery(query, required)
|
|
222
|
+
return nil if packageVersion == WORK_EMPTY_QUERY
|
|
223
|
+
return {
|
|
224
|
+
:version => packageVersion[0][0],
|
|
225
|
+
:sha1 => packageVersion[0][1]
|
|
226
|
+
}
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Find the version of a package from its SHA1 and the #arch
|
|
230
|
+
#
|
|
231
|
+
# Returns dep_info (See #Dependencies) or nil
|
|
232
|
+
#
|
|
233
|
+
# Throws EmptyQueryException if the package was not found and required = true
|
|
234
|
+
def queryDepInfosFromSHA1(env, sha1, required)
|
|
235
|
+
project = WorkEnvs::getMainPackage(env)
|
|
236
|
+
|
|
237
|
+
short_sha1 = nil
|
|
238
|
+
if sha1 !~ /[0-9a-f]{40}/ then
|
|
239
|
+
short_sha1 = sha1
|
|
240
|
+
sha1 += "%"
|
|
241
|
+
end
|
|
242
|
+
query = DBQuery.new({
|
|
243
|
+
:qtype => [ "version", "sha1" ],
|
|
244
|
+
:cond => [ {:field => "sha1",:value =>sha1},
|
|
245
|
+
{:field => "arch",:value =>@arch[:label]},
|
|
246
|
+
{:field => "project",:value =>project}
|
|
247
|
+
],
|
|
248
|
+
:limit => 1000
|
|
249
|
+
})
|
|
250
|
+
addBlacklist(query, WorkEnvs::getBlackList(env))
|
|
251
|
+
|
|
252
|
+
packageVersion = @db_interface.doQuery(query, required)
|
|
253
|
+
return nil if packageVersion == WORK_EMPTY_QUERY
|
|
254
|
+
if short_sha1 != nil then
|
|
255
|
+
if packageVersion.length > 1 then
|
|
256
|
+
raise("Multiple SHA1 matching the given prefix:\n" +
|
|
257
|
+
packageVersion.map(){|m| "\t#{m[1]}\t#{m[0]}\n"}.join(""))
|
|
258
|
+
else
|
|
259
|
+
puts "INFO: short sha1 #{short_sha1} expanded to (#{packageVersion[0][1]}, #{packageVersion[0][0]})"
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
return {
|
|
263
|
+
:version => packageVersion[0][0],
|
|
264
|
+
:sha1 => packageVersion[0][1]
|
|
265
|
+
}
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Find the SHA1 package from its version string and the #arch
|
|
269
|
+
#
|
|
270
|
+
# Returns dep_info (See #Dependencies) or nil
|
|
271
|
+
#
|
|
272
|
+
# Throws EmptyQueryException if the package was not found and required = true
|
|
273
|
+
def queryDepInfosFromVersion(env, version, required)
|
|
274
|
+
project = WorkEnvs::getMainPackage(env)
|
|
275
|
+
query = DBQuery.new({
|
|
276
|
+
:qtype => [ "sha1" ],
|
|
277
|
+
:cond => [ {:field => "version",:value =>version},
|
|
278
|
+
{:field => "arch",:value =>@arch[:label]},
|
|
279
|
+
{:field => "project",:value =>project}
|
|
280
|
+
]
|
|
281
|
+
})
|
|
282
|
+
addBlacklist(query, WorkEnvs::getBlackList(env))
|
|
283
|
+
|
|
284
|
+
packageVersion = @db_interface.doQuery(query, required)
|
|
285
|
+
return nil if packageVersion == WORK_EMPTY_QUERY
|
|
286
|
+
return {
|
|
287
|
+
:version => version,
|
|
288
|
+
:sha1 => packageVersion[0][0]
|
|
289
|
+
}
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# Return an array all package names using this SHA1 and this #arch
|
|
293
|
+
#
|
|
294
|
+
# Throws EmptyQueryException if no packages were not found and required = true
|
|
295
|
+
def queryMatchingSHA1(sha1, required = false)
|
|
296
|
+
query = DBQuery.new({
|
|
297
|
+
:qtype => [ "project" ],
|
|
298
|
+
:cond => [ {:field => "sha1",:value =>sha1},
|
|
299
|
+
{:field => "arch",:value =>@arch[:label]},
|
|
300
|
+
],
|
|
301
|
+
:limit => 10000
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
packageList = @db_interface.doQuery(query, required)
|
|
305
|
+
return packageList.map(){|col| col[0]}
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
# Return an array all the branches that have packages for env
|
|
309
|
+
#
|
|
310
|
+
# Throws EmptyQueryException if no branches were not found
|
|
311
|
+
def queryBranches(env)
|
|
312
|
+
project = WorkEnvs::getMainPackage(env)
|
|
313
|
+
query = DBQuery.new({
|
|
314
|
+
:qtype => [ "branch" ],
|
|
315
|
+
:cond => [ {:field => "arch",:value =>@arch[:label]},
|
|
316
|
+
{:field => "project",:value =>project}
|
|
317
|
+
],
|
|
318
|
+
:group_by => "branch",
|
|
319
|
+
:limit => 1000000
|
|
320
|
+
})
|
|
321
|
+
addBlacklist(query, WorkEnvs::getBlackList(env))
|
|
322
|
+
branches = @db_interface.doQuery(query, true, false)
|
|
323
|
+
return branches
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# Query the limit last packages named name on teh select branch
|
|
327
|
+
#
|
|
328
|
+
# if branch = nil, all branches are looked at
|
|
329
|
+
#
|
|
330
|
+
# Returns an array of EnvPackage
|
|
331
|
+
def queryPackages(project, branch=nil, limit=100000, blacklist=[])
|
|
332
|
+
query = DBQuery.new({
|
|
333
|
+
:qtype => [ "name", "sha1", "branch", "info" ],
|
|
334
|
+
:cond => [ {:field => "arch",:value =>@arch[:label]},
|
|
335
|
+
{:field => "project",:value => project},
|
|
336
|
+
{:field => "branch",:value => branch}
|
|
337
|
+
],
|
|
338
|
+
:limit => limit,
|
|
339
|
+
:order => "id",
|
|
340
|
+
:orderType => "desc"
|
|
341
|
+
})
|
|
342
|
+
addBlacklist(query, blacklist)
|
|
343
|
+
|
|
344
|
+
result = @db_interface.doQuery(query, true, false)
|
|
345
|
+
packages=[]
|
|
346
|
+
idx = 0
|
|
347
|
+
result.each() {|cols|
|
|
348
|
+
next if cols[1] == nil || cols[0] == nil
|
|
349
|
+
packages[idx] = EnvPackage.new(cols[0], cols[1], cols[2], cols[3])
|
|
350
|
+
idx += 1
|
|
351
|
+
}
|
|
352
|
+
return packages
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# Get a package full path
|
|
356
|
+
#
|
|
357
|
+
# Looks at all the possible package repositories to find a package
|
|
358
|
+
# that matches package full name and arch
|
|
359
|
+
#
|
|
360
|
+
# Call by getPackagePath when path is not yet resolved
|
|
361
|
+
#
|
|
362
|
+
# Returns the package path
|
|
363
|
+
#
|
|
364
|
+
# Throws NoSuchFileException if the package could not be found and ignore_err = false
|
|
365
|
+
def resolvePackagePath(package, ignore_err = false)
|
|
366
|
+
packageName = package.pack.gsub(/^ */, "")
|
|
367
|
+
@settings[:package_repos].each(){|url|
|
|
368
|
+
type = url.split("://")[0]
|
|
369
|
+
case(type)
|
|
370
|
+
when "http", "https"
|
|
371
|
+
tables = @db_interface.table + @settings[:package_db_tables]
|
|
372
|
+
tables.uniq!
|
|
373
|
+
tables.each(){
|
|
374
|
+
|table|
|
|
375
|
+
path = "#{url}/#{table}/#{@arch[:distrib]}/#{@arch[:version]}/#{@arch[:arch]}/#{packageName}"
|
|
376
|
+
begin
|
|
377
|
+
runCmd("#{WORK_WGET_SPIDER_CMD} #{path}", @be_silent)
|
|
378
|
+
return path
|
|
379
|
+
rescue
|
|
380
|
+
return path if ignore_err == true
|
|
381
|
+
end
|
|
382
|
+
}
|
|
383
|
+
when "file"
|
|
384
|
+
base = url.split("://")[1]
|
|
385
|
+
path = "#{type}://#{base}/#{packageName}"
|
|
386
|
+
puts "Checking file: #{path}" if !@be_silent
|
|
387
|
+
if File.exist?("#{base}/#{packageName}")
|
|
388
|
+
return path
|
|
389
|
+
end
|
|
390
|
+
path2 = "#{type}://#{base}/#{@arch[:distrib]}/#{@arch[:version]}/#{@arch[:arch]}/#{packageName}"
|
|
391
|
+
puts "Checking file: #{path2}" if !@be_silent
|
|
392
|
+
if File.exist?("#{base}/#{@arch[:distrib]}/#{@arch[:version]}/#{@arch[:arch]}/#{packageName}")
|
|
393
|
+
return path2
|
|
394
|
+
end
|
|
395
|
+
return path if ignore_err == true
|
|
396
|
+
else
|
|
397
|
+
raise("Unsupported WORK_PACKAGE_REPO_PROTO: #{type}")
|
|
398
|
+
end
|
|
399
|
+
}
|
|
400
|
+
raise NoSuchFileException.new(package, "in any repositories") if ignore_err != true
|
|
401
|
+
end
|
|
402
|
+
private :resolvePackagePath
|
|
403
|
+
|
|
404
|
+
# Returns a Package path
|
|
405
|
+
#
|
|
406
|
+
# If the path is not set, call #resolvePackagePath to fill it
|
|
407
|
+
#
|
|
408
|
+
# Throws NoSuchFileException if the package could not be found and ignore_err = false
|
|
409
|
+
def getPackagePath(package, ignore_err = false)
|
|
410
|
+
return package.path if package.path != nil
|
|
411
|
+
|
|
412
|
+
package.path = resolvePackagePath(package, ignore_err)
|
|
413
|
+
return package.path
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
# Make sure that at least one package repository is accesible
|
|
417
|
+
#
|
|
418
|
+
# Throws an exception on failure
|
|
419
|
+
def checkRepo()
|
|
420
|
+
errors=[]
|
|
421
|
+
raise("No package source provided") if @settings[:package_repos] == nil || @settings[:package_repos].length == 0
|
|
422
|
+
@settings[:package_repos].each(){|url|
|
|
423
|
+
urlType = url.split('://')[0]
|
|
424
|
+
case(urlType)
|
|
425
|
+
when "http", "https"
|
|
426
|
+
path = "#{url}/#{@db_interface.table[0]}/"+
|
|
427
|
+
"#{@arch[:distrib]}/#{@arch[:version]}/#{@arch[:arch]}/"
|
|
428
|
+
begin
|
|
429
|
+
runCmd("#{WORK_WGET_SPIDER_CMD} #{path}", @be_silent)
|
|
430
|
+
return
|
|
431
|
+
rescue
|
|
432
|
+
errors << "Could not connect to server #{url}"
|
|
433
|
+
end
|
|
434
|
+
when "file"
|
|
435
|
+
base = url.split("://")[1]
|
|
436
|
+
if !File.exist?(base) then
|
|
437
|
+
errors << "Local package repository '#{base}' does not exists"
|
|
438
|
+
else
|
|
439
|
+
return
|
|
440
|
+
end
|
|
441
|
+
else
|
|
442
|
+
errors << "Unsupported WORK_PACKAGE_REPO_PROTO: #{url}"
|
|
443
|
+
end
|
|
444
|
+
}
|
|
445
|
+
raise("Could not find any accessible package repositories\n" + errors.join("\n"))
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
# Returns true if Packahe is a gzip package
|
|
449
|
+
def isGzipPackage?(package)
|
|
450
|
+
return runTest("file #{package} | grep \"gzip\" >/dev/null 2>&1 ", @be_silent)
|
|
451
|
+
end
|
|
452
|
+
private :isGzipPackage?
|
|
453
|
+
|
|
454
|
+
# Extracts all the dependencies from a RPM file and store them in deps
|
|
455
|
+
#
|
|
456
|
+
# Format
|
|
457
|
+
# dep[ depName ] :
|
|
458
|
+
# - [ :operator ] : <, <=, =, >= or >
|
|
459
|
+
# - [ :version ] : Version required
|
|
460
|
+
# - { :provided ] : true if it is provided for
|
|
461
|
+
#
|
|
462
|
+
# This is used to extract all dependencies (not internal to envs)
|
|
463
|
+
def getLocalPackageDependencies(packageName, deps = {})
|
|
464
|
+
raise("Feature only supported on RHEL/Centos/Fedora packages") if @arch[:base] != "RHEL"
|
|
465
|
+
|
|
466
|
+
return if(isGzipPackage?(packageName))
|
|
467
|
+
|
|
468
|
+
runCmd("rpm -qp --requires #{packageName}",
|
|
469
|
+
@be_silent).chomp().split("\n").each(){|line|
|
|
470
|
+
|
|
471
|
+
next if line !~ /([^ ]+)( +([<>=]+) +(.*))?$/
|
|
472
|
+
|
|
473
|
+
if deps[$1] != nil and $2 != nil then
|
|
474
|
+
package=$1
|
|
475
|
+
operator = $3
|
|
476
|
+
version = $4
|
|
477
|
+
if (deps[package][:version] !~ /#{version}(.el5)?/ &&
|
|
478
|
+
version !~ /#{deps[package][:version]}(.el5)?/) && deps[package][:version] == '=' &&
|
|
479
|
+
operator == '='
|
|
480
|
+
raise("Incompatible dependencies: Package '#{package}' is required in "+
|
|
481
|
+
"version #{deps[package][:version]} and #{version}")
|
|
482
|
+
end
|
|
483
|
+
elsif deps[$1] == nil
|
|
484
|
+
deps[$1] = {}
|
|
485
|
+
deps[$1][:operator] = $3
|
|
486
|
+
deps[$1][:version] = $4
|
|
487
|
+
end
|
|
488
|
+
}
|
|
489
|
+
runCmd("rpm -qp --provides #{packageName}",
|
|
490
|
+
@be_silent).chomp().split("\n").each(){|line|
|
|
491
|
+
|
|
492
|
+
next if line !~ /([^ ]+)( +([<>=]+) +(.*))?$/
|
|
493
|
+
|
|
494
|
+
if deps[$1] != nil and $2 != nil then
|
|
495
|
+
package=$1
|
|
496
|
+
version = $4
|
|
497
|
+
if (deps[package][:version] !~ /#{version}(.el5)?/ &&
|
|
498
|
+
version !~ /#{deps[package][:version]}(.el5)?/) && deps[package][:version] == '=' then
|
|
499
|
+
raise("Incompatible dependencies: Package '#{package}' is required in "+
|
|
500
|
+
"version #{deps[package][:version]} and #{version}")
|
|
501
|
+
end
|
|
502
|
+
deps[package][:provided] = true
|
|
503
|
+
elsif deps[$1] != nil
|
|
504
|
+
deps[$1][:provided] = true
|
|
505
|
+
else
|
|
506
|
+
deps[$1] = {}
|
|
507
|
+
deps[$1][:provided] = true
|
|
508
|
+
deps[$1][:operator] = $3
|
|
509
|
+
deps[$1][:version] = $4
|
|
510
|
+
end
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return deps
|
|
514
|
+
end
|
|
515
|
+
private :getLocalPackageDependencies
|
|
516
|
+
|
|
517
|
+
# Download a Package
|
|
518
|
+
#
|
|
519
|
+
# Rename if to output if set
|
|
520
|
+
#
|
|
521
|
+
# The package is downloaded in the current directory
|
|
522
|
+
def download(package, output = nil)
|
|
523
|
+
path = getPackagePath(package)
|
|
524
|
+
output = File.basename(path) if output == nil
|
|
525
|
+
type = path.split("://")[0]
|
|
526
|
+
case type
|
|
527
|
+
when "http", "https"
|
|
528
|
+
runCmd("#{WORK_WGET_CMD} -O #{output} #{path}", @be_silent)
|
|
529
|
+
when "file"
|
|
530
|
+
base = path.split("://")[1]
|
|
531
|
+
runCmd("#{WORK_CP_CMD} #{base} #{output}", @be_silent)
|
|
532
|
+
else
|
|
533
|
+
raise("Unsupported protocol #{type}")
|
|
534
|
+
end
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
# Download a Package to the current directory
|
|
538
|
+
#
|
|
539
|
+
# Extract if if extract is true
|
|
540
|
+
#
|
|
541
|
+
# Erase afterwars if erase is set
|
|
542
|
+
#
|
|
543
|
+
def downloadAndExtract(package, extract, erase)
|
|
544
|
+
packageName = package.pack.gsub(/^ */, "")
|
|
545
|
+
runCmd("rm -f #{packageName}", @be_silent)
|
|
546
|
+
|
|
547
|
+
if @be_silent
|
|
548
|
+
puts "\t#{packageName}\n"
|
|
549
|
+
end
|
|
550
|
+
download(package)
|
|
551
|
+
|
|
552
|
+
return if(extract != true)
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
if(isGzipPackage?(packageName)) then
|
|
556
|
+
runCmd("tar --atime-preserve=system -zxf #{packageName} 2>&1 || "+
|
|
557
|
+
"tar -zxf #{packageName} 2>&1", @be_silent)
|
|
558
|
+
else
|
|
559
|
+
case(@arch[:base])
|
|
560
|
+
when "debian"
|
|
561
|
+
runCmd("dpkg --extract #{packageName} . 2>&1", @be_silent)
|
|
562
|
+
when "RHEL"
|
|
563
|
+
runCmd("rpm2cpio #{packageName} | cpio -id 2>&1", @be_silent, 5)
|
|
564
|
+
else
|
|
565
|
+
raise("Invalid package type")
|
|
566
|
+
end
|
|
567
|
+
end
|
|
568
|
+
if deps != nil
|
|
569
|
+
getLocalPackageDependencies(packageName, deps)
|
|
570
|
+
end
|
|
571
|
+
runCmd("rm -f #{packageName}", @be_silent) if erase == true
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
# Remove a the installed versions of Packages from the system (RPM/RHEL only)
|
|
575
|
+
#
|
|
576
|
+
# Throws an exception in uninstall failed
|
|
577
|
+
#
|
|
578
|
+
# Called by installPackages to remvoe former packages prior to enw install
|
|
579
|
+
def removePackage(package)
|
|
580
|
+
package = package.pack.gsub(/^ */, "")
|
|
581
|
+
raise ("Package '#{package}' is missing") if !File.exist?(package)
|
|
582
|
+
|
|
583
|
+
driver=runCmd("rpm -qp --qf='%{NAME}' #{package}", @be_silent)
|
|
584
|
+
installed = system("rpm -q #{driver}")
|
|
585
|
+
if(installed) then
|
|
586
|
+
begin
|
|
587
|
+
@semaphore.synchronize {
|
|
588
|
+
runCmd("sudo yum -y -q remove #{driver} || sudo yum -y -q remove #{driver}", @be_silent)
|
|
589
|
+
}
|
|
590
|
+
rescue => e
|
|
591
|
+
# Because we do it in //, we may have a race condition to uninstall this one
|
|
592
|
+
# So if it failed, just check if it's still there. If not, don't bother
|
|
593
|
+
installed = system("rpm -q #{driver}")
|
|
594
|
+
raise e if installed
|
|
595
|
+
end
|
|
596
|
+
end
|
|
597
|
+
end
|
|
598
|
+
private :removePackage
|
|
599
|
+
|
|
600
|
+
# Install packages on the system (yum, dpkg)
|
|
601
|
+
#
|
|
602
|
+
# Remove them prior to install using #removePackage
|
|
603
|
+
#
|
|
604
|
+
# Throws exception on unsupported arch, uninstall or install failure
|
|
605
|
+
def installPackages(packages)
|
|
606
|
+
pack_list=[]
|
|
607
|
+
packages.each(){|pack|
|
|
608
|
+
|
|
609
|
+
removePackage(pack)
|
|
610
|
+
package = pack.pack.gsub(/^ */, "")
|
|
611
|
+
raise ("Package '#{package}' is missing") if !File.exist?(package)
|
|
612
|
+
|
|
613
|
+
pack_list << package
|
|
614
|
+
}
|
|
615
|
+
case(@arch[:base])
|
|
616
|
+
when "debian"
|
|
617
|
+
raise("Do not know how to install DEB packages automatically")
|
|
618
|
+
when "RHEL"
|
|
619
|
+
puts "Installing"
|
|
620
|
+
runCmd("sudo rpm -i #{pack_list.join(" ")}", @be_silent)
|
|
621
|
+
else
|
|
622
|
+
raise("Invalid package type")
|
|
623
|
+
end
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
# Fetch the full names of two lists of Package matching sha1 and #arch
|
|
627
|
+
#
|
|
628
|
+
# This is used to generate a list of Package needed when updating an env
|
|
629
|
+
#
|
|
630
|
+
# Throws EmptyQueryException if a package in required could not be found
|
|
631
|
+
#
|
|
632
|
+
# Returns an array of Packages
|
|
633
|
+
def getPackagesName(required, extras, sha1)
|
|
634
|
+
packages = []
|
|
635
|
+
required.each() {|p|
|
|
636
|
+
package = queryPackage(p, sha1, true)
|
|
637
|
+
|
|
638
|
+
next if package == nil
|
|
639
|
+
|
|
640
|
+
list = package.pack.split("\n")
|
|
641
|
+
if list.length > 1 then
|
|
642
|
+
STDERR.puts "###################################################"
|
|
643
|
+
STDERR.puts "# ERROR: Query returned more than one package #"
|
|
644
|
+
STDERR.puts "# in a single DB entry..... #"
|
|
645
|
+
list.each(){|pack|
|
|
646
|
+
STDERR.puts "# => #{pack}".ljust(50) + "#"
|
|
647
|
+
}
|
|
648
|
+
STDERR.puts "###################################################"
|
|
649
|
+
raise()
|
|
650
|
+
end
|
|
651
|
+
packages << package
|
|
652
|
+
}
|
|
653
|
+
extras.each() {|p|
|
|
654
|
+
package = queryPackage(p, sha1, false)
|
|
655
|
+
next if package == nil
|
|
656
|
+
|
|
657
|
+
list = package.pack.split("\n")
|
|
658
|
+
if list.length > 1 then
|
|
659
|
+
STDERR.puts "###################################################"
|
|
660
|
+
STDERR.puts "# ERROR: Query returned more than one package #"
|
|
661
|
+
STDERR.puts "# in a single DB entry..... #"
|
|
662
|
+
list.each(){|pack|
|
|
663
|
+
STDERR.puts "# => #{pack}".ljust(50) + "#"
|
|
664
|
+
}
|
|
665
|
+
STDERR.puts "###################################################"
|
|
666
|
+
raise()
|
|
667
|
+
end
|
|
668
|
+
packages << package
|
|
669
|
+
}
|
|
670
|
+
return packages
|
|
671
|
+
end
|
|
672
|
+
|
|
673
|
+
# Extract dependencies (RPM, Deb) from a Package
|
|
674
|
+
#
|
|
675
|
+
# - Try to fetch the dependencies from the DB
|
|
676
|
+
# - On failure
|
|
677
|
+
# - Get the dependencies from the package
|
|
678
|
+
# - Publish them to the dependencies DB
|
|
679
|
+
#
|
|
680
|
+
# Returns a list of dependencies string
|
|
681
|
+
def listDependencies(pack)
|
|
682
|
+
remotePack = getPackagePath(pack)
|
|
683
|
+
extname = File.extname(remotePack)
|
|
684
|
+
return [] if(extname != ".deb" and extname != ".rpm")
|
|
685
|
+
res = nil
|
|
686
|
+
|
|
687
|
+
file = runCmd("mktemp", true)
|
|
688
|
+
|
|
689
|
+
tables = @db_interface.table + @settings[:package_db_tables]
|
|
690
|
+
tables.uniq!
|
|
691
|
+
|
|
692
|
+
# Try to do it on the SQL server first
|
|
693
|
+
query = DBDepsQuery.new({
|
|
694
|
+
:qtype => [ "dependencies" ],
|
|
695
|
+
:cond => [ {:field => "name",:value => pack.pack },
|
|
696
|
+
{:field => "arch",:value =>@arch[:label] }
|
|
697
|
+
],
|
|
698
|
+
:limit => 1,
|
|
699
|
+
})
|
|
700
|
+
begin
|
|
701
|
+
res = @db_interface.doDepQuery(query)[0][0].to_s.split("\n")
|
|
702
|
+
return res
|
|
703
|
+
rescue => e
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
runCmd("rm -f #{file}", true)
|
|
707
|
+
case(@arch[:base])
|
|
708
|
+
when "RHEL"
|
|
709
|
+
res = runCmd("rpm -qp --requires #{remotePack} 2>/dev/null ", @be_silent).split("\n")
|
|
710
|
+
when "debian"
|
|
711
|
+
file = runCmd("mktemp", true)
|
|
712
|
+
download(pack, file)
|
|
713
|
+
res = runCmd("dpkg --info #{file} | /bin/grep -E '^ Depends:'",
|
|
714
|
+
@be_silent).gsub(/^ Depends: /, '').split(", ")
|
|
715
|
+
runCmd("rm -f #{file}", @be_silent)
|
|
716
|
+
else
|
|
717
|
+
raise("Invalid package type")
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
begin
|
|
721
|
+
query = DBDepsQuery.new({
|
|
722
|
+
:cond => [ {:field => "name",:value => pack.pack },
|
|
723
|
+
{:field => "arch",:value => @arch[:label] },
|
|
724
|
+
{ :field => "dependencies", :value => res.join("\n") },
|
|
725
|
+
],
|
|
726
|
+
:insert => true
|
|
727
|
+
})
|
|
728
|
+
@db_interface.doInsertDep(query)
|
|
729
|
+
rescue
|
|
730
|
+
end
|
|
731
|
+
return res
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
# Look for depenency to the package name in a list generated by #listDependencies
|
|
735
|
+
#
|
|
736
|
+
# Return the version of name required
|
|
737
|
+
def extractDependency(dependencies, name)
|
|
738
|
+
res = nil
|
|
739
|
+
case(@arch[:base])
|
|
740
|
+
when "RHEL"
|
|
741
|
+
dependencies.each(){|line|
|
|
742
|
+
next if line !~ /#{name}[[:space:]]*=/
|
|
743
|
+
res = line.gsub(/\.(fc|el)[0-9]*$/, '').gsub(/.*=[[:space:]]*(.*)$/, '\1')
|
|
744
|
+
puts "Dependency to: #{name} = #{res} " if @be_silent == false
|
|
745
|
+
return res
|
|
746
|
+
}
|
|
747
|
+
when "debian"
|
|
748
|
+
dependencies.each(){|line|
|
|
749
|
+
next if line !~ /^#{name}[[:space:]]*\(/
|
|
750
|
+
res = line.gsub(/.*=[[:space:]]*/, '').gsub(/\)/, '')
|
|
751
|
+
puts "Dependency to: #{name} = #{res} " if @be_silent == false
|
|
752
|
+
return res
|
|
753
|
+
}
|
|
754
|
+
else
|
|
755
|
+
raise("Invalid package type")
|
|
756
|
+
end
|
|
757
|
+
return res
|
|
758
|
+
end
|
|
759
|
+
end
|
|
760
|
+
end
|