Module: HelixVersioningEngine::Util
- Defined in:
- lib/helix_versioning_engine/util.rb
Overview
TODO most of the methods in this module should probably be removed
Namespace for p4ruby conventions used in the P4 Web API.
Constant Summary
- PROPERTIES =
[ :password, :port, :user, :api_level, :charset, :client, :host, :handler, :maxlocktime, :maxresults, :maxscanrows, :prog, :ticketfile ]
- BRANCHES_MAP =
Data 'normalization'
A very annoying aspect of our tagged output is that it often is slightly different between 'plural' and spec forms. This logic is made available as an option in this API
{ 'branch' => 'Branch' }
- BRANCHES_DATES =
%w(Update Access)
- CHANGES_MAP =
{ 'path' => 'Path', 'change' => 'Change', 'time' => 'Date', 'client' => 'Client', 'user' => 'User', 'status' => 'Status', 'type' => 'Type', 'changeType' => 'Type', 'desc' => 'Description' }
- CHANGES_DATES =
['Date']
- CLIENTS_MAP =
{ 'client' => 'Client' }
- CLIENTS_DATES =
%w(Update Access)
- DEPOTS_MAP =
{ 'name' => 'Depot', 'time' => 'Date', 'type' => 'Type', 'map' => 'Map', 'desc' => 'Description' }
- DEPOTS_DATES =
['Date']
- DESCRIBE_MAP =
Note that individual files are handled differently, since the keys are prefixed
CHANGES_MAP
- DESCRIBE_DATES =
CHANGES_DATES
- DIRS_MAP =
{ 'dir' => 'Dir' }
- FILES_MAP =
{ 'depotFile' => 'DepotFile', 'rev' => 'Revision', 'change' => 'Change', 'action' => 'Action', 'type' => 'Type', 'time' => 'Date' }
- FILES_DATES =
%w(Date)
- GROUPS_MAP =
{ 'group' => 'Group', 'maxResults' => 'MaxResults', 'maxScanRows' => 'MaxScanRows', 'maxLockTime' => 'MaxLockTime', 'timeout' => 'Timeout', 'desc' => 'Description', 'user' => 'User', 'isSubGroup' => 'IsSubGroup', 'isOwner' => 'IsOwner', 'isUser' => 'IsUser', 'passTimeout' => 'PasswordTimeout' }
- JOBS_MAP =
{}
- JOBS_DATES =
%w(Date)
- LABELS_MAP =
{ 'label' => 'Label' }
- LABELS_DATES =
%w(Update Access)
- STREAMS_MAP =
This isn't supported but kept here for reference. We might support this. OPENED_MAP = { 'change' => 'Change', 'client' => 'Client', 'user' => 'User' }
{ 'desc' => 'Description' }
- STREAMS_DATES =
%w(Update Access)
- USERS_MAP =
{ 'passwordChange' => 'PasswordChange' }
- USERS_DATES =
%w(Update Access PasswordChange)
- GROUP_PROPERTIES =
These are 'single values' of properties of Group objects we use while collating them.
%w( Group MaxResults MaxScanRows MaxLockTime Timeout PasswordTimeout
- FILE_KEYS =
%w(depotFile action type rev digest fileSize)
Class Method Summary (collapse)
-
+ (Object) assert_no_special_paths(paths)
Assert that no relative directory or Perforce wildcard is in use for each string in the
paths
array. -
+ (Object) collate_group_results(results)
Will apply the normalizer to set up a consistent field naming and date format, but then 'collates' the output into a single list of 'groups'.
-
+ (Boolean) date_str?(str)
Returns true if we can parse the string as a date.
-
+ (Boolean) error?(p4)
Check for P4 errors.
- + (Boolean) existing_path?(existing_results, depot_path)
-
+ (Boolean) i?(str)
Returns true if .to_i will actually convert this string to an integer.
- + (Object) init_changelist(p4, description)
- + (Object) local_path(depot_path, root)
- + (Object) make_normalizer(field_map, offset, date_fields = nil)
- + (Object) map_describe_file_key(key)
- + (Object) mark_change(type, p4, change_id, root, depot_path)
-
+ (Object) normalize_describe_files(results)
Each file entry in the base describe tagged output contains several fields, suffixed with an index value.
-
+ (Object) normalizer(spec_type, offset)
For each 'spec type' returns a function that will ensure that: - case is consistent between different return calls, prefer 'spec' types - dates are returned.
-
+ (Object) open(options = {})
Creates your p4 connection using some common forms.
- + (Object) p4_date_offset(str)
-
+ (Object) p4_date_to_i(offset, p4date)
In general we get dates without any offset information, which has to be retrieved via the serverDate field from 'p4 info'.
-
+ (Boolean) p4_ticket?(str)
Returns true if the string looks like a Perforce authentication ticket.
-
+ (Object) raise_error(p4)
Raise an exception if necessary on a P4 Error.
-
+ (Boolean) relative_dir?(str)
Returns true if str is '.' or '..'.
- + (Object) replace_unset_with_0(results)
- + (Object) resolve_charset(env, settings)
- + (Object) resolve_host(env, settings)
- + (Object) resolve_password(env, settings)
- + (Object) resolve_port(env, settings)
- + (Object) save_content(root, depot_path, content)
- + (Object) singular(plural)
-
+ (Boolean) uuid?(str)
We only count uuids that were returned via SecureRandom.uuid used to generate internal security tokens.
-
+ (Boolean) wildcard?(str)
Returns true when
str
contains a Perforce wildcard.
Class Method Details
+ (Object) assert_no_special_paths(paths)
Assert that no relative directory or Perforce wildcard is in use for each
string in the paths
array.
67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/helix_versioning_engine/util.rb', line 67 def self.assert_no_special_paths(paths) paths.each do |path| if Util.wildcard?(path) fail P4Error.default_error("The path '#{path}' contains a Perforce "\ 'wildcard, which is not allowed') end if Util.relative_dir?(path) fail P4Error.default_error("The path '#{path}' is a relative " \ 'directory, which is not allowed') end end end |
+ (Object) collate_group_results(results)
Will apply the normalizer to set up a consistent field naming and date format, but then 'collates' the output into a single list of 'groups'.
The groups output is kind of funny, because it basically lists users, with group access fields. It's very non-obvious, and in the end, most clients will need to run logic like this anyway.
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
# File 'lib/helix_versioning_engine/util.rb', line 334 def self.collate_group_results(results) collated = results.group_by { |x| x['Group'] } updated = collated.map do |_key, items| # The first item sets most of the values, we then figure out array # subvalues group = {} GROUP_PROPERTIES.each do |p| group[p] = items.first[p] if items.first.key?(p) end group['Users'] = items.find_all { |x| x['IsUser'] == '1' }.map { |x| x['User'] } group['Subgroups'] = items.find_all { |x| x['IsSubGroup'] == '1' }.map { |x| x['User'] } group['Owners'] = items.find_all { |x| x['IsOwner'] == '1' }.map { |x| x['User'] } group end results.replace(updated) end |
+ (Boolean) date_str?(str)
Returns true if we can parse the string as a date
418 419 420 |
# File 'lib/helix_versioning_engine/util.rb', line 418 def self.date_str?(str) Date.parse(str) rescue false end |
+ (Boolean) error?(p4)
Check for P4 errors
55 56 57 |
# File 'lib/helix_versioning_engine/util.rb', line 55 def self.error?(p4) !p4.errors.empty? end |
+ (Boolean) existing_path?(existing_results, depot_path)
117 118 119 120 121 |
# File 'lib/helix_versioning_engine/util.rb', line 117 def self.existing_path?(existing_results, depot_path) existing_results.any? do |result| result['depotFile'] == depot_path end end |
+ (Boolean) i?(str)
Returns true if .to_i will actually convert this string to an integer
413 414 415 |
# File 'lib/helix_versioning_engine/util.rb', line 413 def self.i?(str) (str =~ /\A[-+]?\d+\z/) end |
+ (Object) init_changelist(p4, description)
102 103 104 105 106 107 |
# File 'lib/helix_versioning_engine/util.rb', line 102 def self.init_changelist(p4, description) change_spec = p4.fetch_change change_spec._description = description results = p4.save_change(change_spec) results[0].gsub(/^Change (\d+) created./, '\1') end |
+ (Object) local_path(depot_path, root)
128 129 130 131 |
# File 'lib/helix_versioning_engine/util.rb', line 128 def self.local_path(depot_path, root) stripped = depot_path.gsub(/^\/+/, '') File.join(root, stripped) end |
+ (Object) make_normalizer(field_map, offset, date_fields = nil)
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
# File 'lib/helix_versioning_engine/util.rb', line 298 def self.make_normalizer(field_map, offset, date_fields = nil) lambda do |results| return unless results # We need to ignore any instances of P4::Spec since that will 'validate' # fields on it's accessor methods results.map! do |result| if result.class <= P4::Spec spec = result result = Hash.new result.merge!(spec) else result end end results.each do |result| update_fields(field_map, result) update_dates(date_fields, result, offset) end results end end |
+ (Object) map_describe_file_key(key)
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 |
# File 'lib/helix_versioning_engine/util.rb', line 395 def self.map_describe_file_key(key) case key when /^depotFile$/ return 'DepotFile' when /^action$/ return 'Action' when /^type$/ return 'Type' when /^rev$/ return 'Revision' when /^fileSize$/ return 'FileSize' when /^digest$/ return 'Digest' end end |
+ (Object) mark_change(type, p4, change_id, root, depot_path)
123 124 125 126 |
# File 'lib/helix_versioning_engine/util.rb', line 123 def self.mark_change(type, p4, change_id, root, depot_path) local_file = local_path(depot_path, root) results = p4.run(type, '-c', change_id, local_file) end |
+ (Object) normalize_describe_files(results)
Each file entry in the base describe tagged output contains several fields, suffixed with an index value.
P4 Ruby returns the individual 'index fields' already collated by the key type.
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 |
# File 'lib/helix_versioning_engine/util.rb', line 373 def self.normalize_describe_files(results) results.each do |r| # We have to collect each index we discover as we iterate over the keys idx_2_file = {} FILE_KEYS.each do |key| if r.key?(key) r[key].each_index do |idx| mapped_key = Util.map_describe_file_key(key) idx_2_file[idx] = {} unless idx_2_file.key?(idx) idx_2_file[idx][mapped_key] = r[key][idx] end r.delete(key) end end r['Files'] = idx_2_file.values end results end |
+ (Object) normalizer(spec_type, offset)
For each 'spec type' returns a function that will ensure that: - case is consistent between different return calls, prefer 'spec' types - dates are returned
Parameters: - spec_type is the 'plural' form of spec class, e.g., 'users', 'clients' - offset is the current server offset, retrieve this via 'p4 info' command
rubocop:disable Metrics/CyclomaticComplexity
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 |
# File 'lib/helix_versioning_engine/util.rb', line 255 def self.normalizer(spec_type, offset) case spec_type when 'branches' make_normalizer(BRANCHES_MAP, offset, BRANCHES_DATES) when 'changes' make_normalizer(CHANGES_MAP, offset, CHANGES_DATES) when 'clients' make_normalizer(CLIENTS_MAP, offset, CLIENTS_DATES) when 'depots' make_normalizer(DEPOTS_MAP, offset, DEPOTS_DATES) when 'describe' # This only affects 'base' fields, not fields related to each file base_normalize = make_normalizer(DESCRIBE_MAP, offset, DESCRIBE_DATES) lambda do |results| results = base_normalize.call(results) Util.normalize_describe_files(results) results end when 'dirs' make_normalizer(DIRS_MAP, offset) when 'files' make_normalizer(FILES_MAP, offset, FILES_DATES) when 'groups' date_and_case = make_normalizer(GROUPS_MAP, offset) lambda do |results| results = date_and_case.call(results) Util.replace_unset_with_0(results) results end when 'jobs' make_normalizer(JOBS_MAP, offset, JOBS_DATES) when 'labels' make_normalizer(LABELS_MAP, offset, LABELS_DATES) when 'streams' make_normalizer(STREAMS_MAP, offset, STREAMS_DATES) when 'users' make_normalizer(USERS_MAP, offset, USERS_DATES) else # By default, do no translation return ->(x) { x } end end |
+ (Object) open(options = {})
Creates your p4 connection using some common forms.
If you call open with a block, this will call connect before your block executes, and disconnect afterwards.
If you do not call open with a block, it is up to the caller to connect and disconnect. (It's assumed you are calling w/o a block because you want to manage when the connection actually needs to happen.)
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/helix_versioning_engine/util.rb', line 34 def self.open( = {}) p4 = create_p4() # Again, if we're calling using the block, we'll connect and disconnect. # Otherwise, just return the created p4 object. if block_given? begin p4.connect yield p4 rescue P4Exception => ex puts "Util.open ex #{ex}" raise make_p4_error(p4) end else return p4 end ensure p4.disconnect if block_given? && p4 && p4.connected? end |
+ (Object) p4_date_offset(str)
428 429 430 |
# File 'lib/helix_versioning_engine/util.rb', line 428 def self.p4_date_offset(str) DateTime.parse(str).zone end |
+ (Object) p4_date_to_i(offset, p4date)
In general we get dates without any offset information, which has to be retrieved via the serverDate field from 'p4 info'
424 425 426 |
# File 'lib/helix_versioning_engine/util.rb', line 424 def self.p4_date_to_i(offset, p4date) DateTime.parse("#{p4date} #{offset}").to_time.to_i end |
+ (Boolean) p4_ticket?(str)
Returns true if the string looks like a Perforce authentication ticket.
91 92 93 |
# File 'lib/helix_versioning_engine/util.rb', line 91 def self.p4_ticket?(str) /^[a-zA-Z0-9]{32,}$/.match(str) != nil end |
+ (Object) raise_error(p4)
Raise an exception if necessary on a P4 Error
60 61 62 63 |
# File 'lib/helix_versioning_engine/util.rb', line 60 def self.raise_error(p4) err = p4..find { |m| m.severity > 2 } fail Perforce::P4Error.new(err.msgid, err.severity, err.to_s) end |
+ (Boolean) relative_dir?(str)
Returns true if str is '.' or '..'
86 87 88 |
# File 'lib/helix_versioning_engine/util.rb', line 86 def self.relative_dir?(str) str == '.' || str == '..' end |
+ (Object) replace_unset_with_0(results)
360 361 362 363 364 |
# File 'lib/helix_versioning_engine/util.rb', line 360 def self.replace_unset_with_0(results) results.each do |r| r.each_key { |k| r[k] = '0' if r[k] == 'unset' } end end |
+ (Object) resolve_charset(env, settings)
448 449 450 451 452 453 454 |
# File 'lib/helix_versioning_engine/util.rb', line 448 def self.resolve_charset(env, settings) if env.key?('P4_CHARSET') && settings.allow_env_p4_config env['P4_CHARSET'] else settings.p4['charset'] if settings.p4.key?('charset') end end |
+ (Object) resolve_host(env, settings)
432 433 434 435 436 437 438 |
# File 'lib/helix_versioning_engine/util.rb', line 432 def self.resolve_host(env, settings) if env.key?('P4_HOST') && settings.allow_env_p4_config env['P4_HOST'] else settings.p4['host'] end end |
+ (Object) resolve_password(env, settings)
456 457 458 459 460 461 462 463 |
# File 'lib/helix_versioning_engine/util.rb', line 456 def self.resolve_password(env, settings) password = env['AUTH_CREDENTIALS'].last if !Util.uuid?(password) password else Auth.read_token(password, settings)['ticket'] end end |
+ (Object) resolve_port(env, settings)
440 441 442 443 444 445 446 |
# File 'lib/helix_versioning_engine/util.rb', line 440 def self.resolve_port(env, settings) if env.key?('P4_PORT') && settings.allow_env_p4_config env['P4_PORT'] else settings.p4['port'] end end |
+ (Object) save_content(root, depot_path, content)
109 110 111 112 113 114 115 |
# File 'lib/helix_versioning_engine/util.rb', line 109 def self.save_content(root, depot_path, content) local_file = local_path(depot_path, root) dir = File.dirname(local_file) FileUtils.mkpath(dir) unless Dir.exist?(dir) IO.write(local_file, content) end |
+ (Object) singular(plural)
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/helix_versioning_engine/util.rb', line 133 def self.singular(plural) matches = { branches: 'branch', clients: 'client', depots: 'depot', groups: 'group', jobs: 'job', labels: 'label', protects: 'protect', servers: 'server', streams: 'stream', triggers: 'trigger', users: 'user' } matches[plural.to_sym] end |
+ (Boolean) uuid?(str)
We only count uuids that were returned via SecureRandom.uuid used to generate internal security tokens.
97 98 99 100 |
# File 'lib/helix_versioning_engine/util.rb', line 97 def self.uuid?(str) /^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$/ .match(str) end |
+ (Boolean) wildcard?(str)
Returns true when str
contains a Perforce wildcard
81 82 83 |
# File 'lib/helix_versioning_engine/util.rb', line 81 def self.wildcard?(str) (str =~ /\.\.\./ || str =~ /\*/) != nil end |