firespring_dev_commands 2.1.8 → 2.1.10.pre.alpha.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 56b724b0a6f7c0ab20b780894cc5c312fe545efb7a0460801bdde3fd25ea5d0c
4
- data.tar.gz: 940ff9bff43521dbc47be595adb02cc369ec7e0e1f70c0ea1361ecf0f32a796f
3
+ metadata.gz: c75f83066598edad92a8d34dd12d898d9f5229cf80bd8eeba166cd7e5451296e
4
+ data.tar.gz: 832006c6ec28b9b8a288e3a0d510f628f3c468b3c0cb8614d1eae5927f25184a
5
5
  SHA512:
6
- metadata.gz: 3faa331db0c214a38e4d17c352f86c4978e2813985111b59b1e43fc7a775907e7bf1fe178ccfbd2df275933345a16fe4882c99ac6a9779e8dee877a66143b0e5
7
- data.tar.gz: 0e5da5e49aa2664c7fb678cd4cd11c37d85a4787c3aa91210022f1d20f1084109815cb3e875729c81932eb074a1f81b3372eb9e5f21c41a3ddeb2caae921669e
6
+ metadata.gz: f3ea07c7f9be119c8ec47914c213560069ee1c17b6a0e8dcbe43b41cb246febd258079b12843d2819cdd4b1cc7aa180e3515fd8edeaca8fea9a5866758614a33
7
+ data.tar.gz: b395757808d58b999546d5df762ef46caf59ebf670599ec77d0b1c91098381c9f6da96e102141050f1a638ea83972c2757d795016e90cff1bb0474d0865b5db0
@@ -203,6 +203,7 @@ module Dev
203
203
 
204
204
  # Checks out the given branch in the given repo
205
205
  # Defaults to the current directory
206
+ # optionally raise errors
206
207
  def checkout(branch, dir: default_project_dir, raise_errors: false)
207
208
  raise 'branch is required' if branch.to_s.strip.empty?
208
209
  return unless File.exist?(dir)
@@ -231,6 +232,8 @@ module Dev
231
232
  end
232
233
 
233
234
  # Create the given branch in the given repo
235
+ # Defaults to the current directory
236
+ # optionally raise errors
234
237
  def create_branch(branch, dir: default_project_dir, raise_errors: false)
235
238
  raise 'branch is required' if branch.to_s.strip.empty?
236
239
  raise "refusing to create protected branch '#{branch}'" if %w(master develop).any?(branch.to_s.strip)
@@ -259,6 +262,9 @@ module Dev
259
262
  false
260
263
  end
261
264
 
265
+ # Add the given paths to git
266
+ # Defaults to the current directory
267
+ # optionally raise errors
262
268
  def add(*paths, dir: default_project_dir, raise_errors: false)
263
269
  g = ::Git.open(dir)
264
270
  indent g.add(paths)
@@ -292,6 +298,8 @@ module Dev
292
298
  end
293
299
 
294
300
  # Merge the given branch into the given repo
301
+ # Defaults to the current directory
302
+ # optionally raise errors
295
303
  def merge(branch, dir: default_project_dir, raise_errors: false)
296
304
  raise 'branch is required' if branch.to_s.strip.empty?
297
305
  return unless File.exist?(dir)
@@ -337,6 +345,8 @@ module Dev
337
345
  end
338
346
 
339
347
  # Pull the given repo
348
+ # Defaults to the current directory
349
+ # optionally raise errors
340
350
  def pull(dir: default_project_dir, raise_errors: false)
341
351
  return unless File.exist?(dir)
342
352
 
@@ -374,6 +384,8 @@ module Dev
374
384
  end
375
385
 
376
386
  # Push the given repo
387
+ # Defaults to the current directory
388
+ # optionally raise errors
377
389
  def push(dir: default_project_dir, raise_errors: false)
378
390
  return unless File.exist?(dir)
379
391
 
@@ -1,6 +1,8 @@
1
1
  module Dev
2
2
  class Jira
3
+ # Class which provides a helper method for converting the changelog data to history objects
3
4
  class Histories
5
+ # If changelog is present in the given data, return an array of history objects for each changelog entry
4
6
  def self.populate(data)
5
7
  return nil unless data.attrs.key?('changelog')
6
8
 
@@ -2,6 +2,7 @@ require 'date'
2
2
 
3
3
  module Dev
4
4
  class Jira
5
+ # Class contains history data for jira objects
5
6
  class History
6
7
  attr_accessor :date, :id, :author, :created, :items
7
8
 
@@ -21,21 +21,25 @@ module Dev
21
21
  @last_closed_history = nil
22
22
  end
23
23
 
24
+ # Returns the cycle time of the issue (time between in progress and closed states)
24
25
  def cycle_time
25
26
  # Calculate the difference and convert to days
26
27
  ((last_closed_history.created - last_in_progress_history.created) / 60 / 60 / 24).round(2)
27
28
  end
28
29
 
30
+ # Returns the time the issue was in progress (time between in progress and in review states)
29
31
  def in_progress_cycle_time
30
32
  # Calculate the difference and convert to days
31
33
  ((first_in_review_history.created - last_in_progress_history.created) / 60 / 60 / 24).round(2)
32
34
  end
33
35
 
36
+ # Returns the time the issue was in review (time between in review and closed states)
34
37
  def in_review_cycle_time
35
38
  # Calculate the difference and convert to days
36
39
  ((last_closed_history.created - first_in_review_history.created) / 60 / 60 / 24).round(2)
37
40
  end
38
41
 
42
+ # Loop through the issue history and find the most recent state change from Open to In Progress
39
43
  private def last_in_progress_history
40
44
  raise 'you must expand the changelog field to calculate cycle time' if histories.nil?
41
45
 
@@ -50,6 +54,7 @@ module Dev
50
54
  @last_in_progress_history
51
55
  end
52
56
 
57
+ # Loop through the issue history and find the oldest state change to In Review
53
58
  private def first_in_review_history
54
59
  raise 'you must expand the changelog field to calculate cycle time' if histories.nil?
55
60
 
@@ -64,6 +69,7 @@ module Dev
64
69
  @first_in_review_history
65
70
  end
66
71
 
72
+ # Loop through the issue history and find the most recent state change to closed
67
73
  private def last_closed_history
68
74
  raise 'you must expand the changelog field to calculate cycle time' if histories.nil?
69
75
 
@@ -1,8 +1,11 @@
1
1
  module Dev
2
2
  class Common
3
+ # Class which returns information about the current platform
3
4
  class Platform
5
+ # Constant containing all supported architectures
4
6
  ALLOWED_ARCHITECTURES = %w(arm64 amd64).freeze
5
7
 
8
+ # Normalize the ruby platform to return a docker platform architecture format
6
9
  def determine_compute_architecture
7
10
  case RUBY_PLATFORM
8
11
  when /x86_64|amd64/
@@ -14,6 +17,9 @@ module Dev
14
17
  end
15
18
  end
16
19
 
20
+ # Determine the platform architecture
21
+ # If one was specified in the DOCKER_ARCHITECTURE variable, use it
22
+ # Otherwise, use the RUBY_PLATFORM built-in to auto-detect and architecture
17
23
  def architecture
18
24
  docker_architecture = ENV['DOCKER_ARCHITECTURE'].to_s.strip.downcase
19
25
  if docker_architecture.empty?
@@ -22,13 +28,11 @@ module Dev
22
28
  raise "Missing 'linux/' prefix in DOCKER_ARCHITECTURE: #{docker_architecture}" unless docker_architecture.start_with?('linux/')
23
29
 
24
30
  architecture_name = docker_architecture.split('/')[1]
25
-
26
31
  unless ALLOWED_ARCHITECTURES.include?(architecture_name)
27
32
  raise "Invalid DOCKER_ARCHITECTURE: #{architecture_name}. Allowed architectures are #{ALLOWED_ARCHITECTURES.join(', ')}"
28
33
  end
29
34
 
30
35
  docker_architecture
31
-
32
36
  end
33
37
  end
34
38
  end
@@ -0,0 +1,14 @@
1
+ module Dev
2
+ class TargetProcess
3
+ # Class containing project information
4
+ class Project
5
+ attr_accessor :id, :type, :name
6
+
7
+ def initialize(data)
8
+ @id = data['Id']
9
+ @type = data['ResourceType']
10
+ @name = data['Name']
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,82 @@
1
+ module Dev
2
+ class TargetProcess
3
+ # Class for writing target process query statements
4
+ class Query
5
+ attr_accessor :where, :incl, :take
6
+
7
+ def initialize
8
+ @where = []
9
+ @incl = []
10
+ @take = 250
11
+ end
12
+
13
+ # Add a new query clause
14
+ def <<(item)
15
+ where << item
16
+ end
17
+
18
+ # Add the item to the where clause
19
+ def where=(item)
20
+ if item.is_a?(Array)
21
+ where.concat(item)
22
+ else
23
+ where << item
24
+ end
25
+ end
26
+
27
+ # Add the item to the include clause
28
+ def include=(item)
29
+ if item.is_a?(Array)
30
+ incl.concat(item)
31
+ else
32
+ incl << item
33
+ end
34
+ end
35
+
36
+ # Generate the string representation for this query
37
+ def generate
38
+ {}.tap do |clause|
39
+ clause[:where] = where.join(' and ') unless where.nil? || where.empty?
40
+ clause[:include] = "[#{incl.join(',')}]" unless incl.nil? || incl.empty?
41
+ clause[:take] = take if take.to_i.positive?
42
+ end
43
+ end
44
+
45
+ # Generate the string representation for this query
46
+ def to_s
47
+ generate
48
+ end
49
+
50
+ # Add a filter that looks for stories whose id is contained in the list of ids given
51
+ def filter_by_user_story_ids(user_story_ids)
52
+ self << "(Id in ('#{user_story_ids.join("', '")}'))"
53
+ end
54
+
55
+ # Add a filter that looks for stories whose project id is contained in the list of ids given
56
+ def filter_by_project(projects)
57
+ self << "(Project.Name in ('#{projects.join("', '")}'))"
58
+ end
59
+
60
+ # Add a filter that looks for stories whose state is contained in the list of states given
61
+ def filter_by_states(states)
62
+ self << "(EntityState.Name in ('#{states.join("', '")}'))" unless states.nil? || states.empty?
63
+ end
64
+
65
+ # Add a filter that looks for stories whose state is set to final
66
+ def filter_by_final
67
+ self << "(EntityState.IsFinal eq 'true')"
68
+ end
69
+
70
+ # Add a filter that looks for stories whose end date is between the given dates
71
+ def filter_by_end_dates(start_date, end_date)
72
+ self << "(EndDate gt '#{start_date}')" if start_date
73
+ self << "(EndDate lt '#{end_date}')" if end_date
74
+ end
75
+
76
+ # Add a filter that looks for stories which do not have a linked test plan
77
+ def filter_by_missing_tests
78
+ self << '(LinkedTestPlan is nil)'
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,14 @@
1
+ module Dev
2
+ class TargetProcess
3
+ # Class containing release information
4
+ class Release
5
+ attr_accessor :id, :type, :name
6
+
7
+ def initialize(data)
8
+ @id = data['Id']
9
+ @type = data['ResourceType']
10
+ @name = data['Name']
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module Dev
2
+ class TargetProcess
3
+ # Class containing team information
4
+ class Team
5
+ attr_accessor :id, :type, :name
6
+
7
+ def initialize(data)
8
+ @id = data['Id']
9
+ @type = data['ResourceType']
10
+ @name = data['Name']
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ module Dev
2
+ class TargetProcess
3
+ # Class containing user information
4
+ class User
5
+ attr_accessor :id, :type, :name, :login
6
+
7
+ def initialize(data)
8
+ @id = data['Id']
9
+ @type = data['ResourceType']
10
+ @name = data['FullName']
11
+ @login = data['Login']
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,48 @@
1
+ module Dev
2
+ class TargetProcess
3
+ # Class containing user story information
4
+ class UserStory
5
+ # The api path for user story requests
6
+ PATH = '/UserStories'.freeze
7
+
8
+ attr_accessor :type, :id, :name, :description, :start_date, :end_date, :create_date, :modify_date, :tags, :effort, :time_spent, :last_state_change_date, :project,
9
+ :owner, :creator, :release, :team, :priority, :state, :original_data
10
+
11
+ def initialize(data)
12
+ @id = data['Id']
13
+ @type = data['ResourceType']
14
+ @name = data['Name']
15
+ @description = data['Description']
16
+ @state = data['EntityState']['Name']
17
+ @project = Project.new(data['Project']) if data['Project']
18
+ @owner = User.new(data['Owner']) if data['Owner']
19
+ @creator = User.new(data['Creator']) if data['Creator']
20
+ @release = Release.new(data['Release']) if data['Release']
21
+ @team = Team.new(data['Team']) if data['Team']
22
+ @start_date = parse_time(data['StartDate'])
23
+ @end_date = parse_time(data['EndDate'])
24
+ @create_date = parse_time(data['CreateDate'])
25
+ @modify_date = parse_time(data['ModifyDate'])
26
+ @tags = data['Tags']
27
+ @effort = data['Effort']
28
+ @time_spent = data['TimeSpent']
29
+ @last_state_change_date = parse_time(data['LastStateChangeDate'])
30
+ @original_data = original_data
31
+ end
32
+
33
+ # Parse the dot net time representation into something that ruby can use
34
+ def parse_time(string)
35
+ return nil unless string && !string.empty?
36
+
37
+ Time.at(string.slice(6, 10).to_i)
38
+ end
39
+
40
+ # Calculate the cycle time as the amount of time the story was open
41
+ def cycle_time
42
+ return 1.0 unless start_date && end_date
43
+
44
+ (end_date - start_date).to_f
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,105 @@
1
+ require 'net/http'
2
+
3
+ module Dev
4
+ # Class for querying target process data from their api
5
+ class TargetProcess
6
+ # The config file to try to load credentials from
7
+ CONFIG_FILE = "#{Dir.home}/.env.tp".freeze
8
+
9
+ # The text of the username variable key
10
+ TP_USERNAME = 'TP_USERNAME'.freeze
11
+
12
+ # The text of the password variable key
13
+ TP_PASSWORD = 'TP_PASSWORD'.freeze
14
+
15
+ # The text of the url variable key
16
+ TP_URL = 'TP_URL'.freeze
17
+
18
+ # Config object for setting top level jira config options
19
+ Config = Struct.new(:username, :password, :url, :http_debug) do
20
+ def initialize
21
+ Dotenv.load(CONFIG_FILE) if File.exist?(CONFIG_FILE)
22
+
23
+ self.username = ENV.fetch(TP_USERNAME, nil)
24
+ self.password = ENV.fetch(TP_PASSWORD, nil)
25
+ self.url = ENV.fetch(TP_URL, nil)
26
+ self.http_debug = false
27
+ end
28
+ end
29
+
30
+ class << self
31
+ # Instantiates a new top level config object if one hasn't already been created
32
+ # Yields that config object to any given block
33
+ # Returns the resulting config object
34
+ def config
35
+ @config ||= Config.new
36
+ yield(@config) if block_given?
37
+ @config
38
+ end
39
+
40
+ # Alias the config method to configure for a slightly clearer access syntax
41
+ alias_method :configure, :config
42
+ end
43
+
44
+ attr_accessor :username, :password, :url, :auth, :client, :headers
45
+
46
+ # Initialize a new target process client using the given inputs
47
+ def initialize(username: self.class.config.username, password: self.class.config.password, url: self.class.config.url)
48
+ @username = username
49
+ @password = password
50
+ @auth = Base64.strict_encode64("#{@username}:#{@password}")
51
+ @url = url
52
+ uri = URI.parse(@url)
53
+ @client = Net::HTTP.new(uri.host, uri.port)
54
+ @client.use_ssl = true
55
+ @client.verify_mode = OpenSSL::SSL::VERIFY_PEER
56
+ @client.set_debug_output(LOG) if self.class.config.http_debug
57
+ @headers = {
58
+ 'authorization' => "Basic #{auth}",
59
+ 'content-type' => 'application/json',
60
+ 'accept' => 'application/json'
61
+ }
62
+ end
63
+
64
+ # Perform a query to the user story api path
65
+ # Call the given block (if present) with each user story
66
+ # Return all user stories
67
+ def user_stories(query, &)
68
+ [].tap do |ary|
69
+ get(UserStory::PATH, query) do |result|
70
+ ary << UserStory.new(result)
71
+ end
72
+ ary.each(&)
73
+ end
74
+ end
75
+
76
+ # Perform a get request to the given path using the given query
77
+ # Call the given block (if present) with each piece of data
78
+ # Return all pieces of data
79
+ def get(path, query, &)
80
+ query_string = query.generate
81
+ url = "/api/v1/#{path}"
82
+ url << "?#{URI.encode_www_form(query_string)}" unless query_string.empty?
83
+
84
+ response = client.request_get(url, headers)
85
+ raise "Error querying #{url} [#{query_string}]: #{response.inspect}" unless response.response.is_a?(Net::HTTPSuccess)
86
+
87
+ parsed_response = JSON.parse(response.body)
88
+ return parsed_response unless parsed_response.key?('Items')
89
+
90
+ parsed_response['Items'].each(&)
91
+
92
+ while parsed_response['Next']
93
+ response = client.request_get(parsed_response['Next'], headers)
94
+ raise "Error querying #{parsed_response['Next']} [#{query_string}]: #{response.inspect}" unless response.response.is_a?(Net::HTTPSuccess)
95
+
96
+ parsed_response = JSON.parse(response.body)
97
+ return parsed_response unless parsed_response.key?('Items')
98
+
99
+ parsed_response['Items'].each(&)
100
+ end
101
+
102
+ nil
103
+ end
104
+ end
105
+ end
@@ -6,6 +6,6 @@ module Dev
6
6
  # Use 'v.v.v.pre.alpha.v' for pre-release vesions
7
7
  # Use 'v.v.v.beta.v for beta versions
8
8
  # Use semantic versioning for any releases (https://semver.org/)
9
- VERSION = '2.1.8'.freeze
9
+ VERSION = '2.1.10.pre.alpha.2'.freeze
10
10
  end
11
11
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: firespring_dev_commands
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.8
4
+ version: 2.1.10.pre.alpha.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Firespring
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-07 00:00:00.000000000 Z
11
+ date: 2023-10-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 7.0.6
19
+ version: 7.1.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 7.0.6
26
+ version: 7.1.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: aws-sdk-cloudformation
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -305,6 +305,13 @@ files:
305
305
  - lib/firespring_dev_commands/tar.rb
306
306
  - lib/firespring_dev_commands/tar/pax_header.rb
307
307
  - lib/firespring_dev_commands/tar/type_flag.rb
308
+ - lib/firespring_dev_commands/target_process.rb
309
+ - lib/firespring_dev_commands/target_process/project.rb
310
+ - lib/firespring_dev_commands/target_process/query.rb
311
+ - lib/firespring_dev_commands/target_process/release.rb
312
+ - lib/firespring_dev_commands/target_process/team.rb
313
+ - lib/firespring_dev_commands/target_process/user.rb
314
+ - lib/firespring_dev_commands/target_process/user_story.rb
308
315
  - lib/firespring_dev_commands/templates/aws.rb
309
316
  - lib/firespring_dev_commands/templates/base_interface.rb
310
317
  - lib/firespring_dev_commands/templates/ci.rb
@@ -333,9 +340,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
333
340
  version: '3.1'
334
341
  required_rubygems_version: !ruby/object:Gem::Requirement
335
342
  requirements:
336
- - - ">="
343
+ - - ">"
337
344
  - !ruby/object:Gem::Version
338
- version: '0'
345
+ version: 1.3.1
339
346
  requirements: []
340
347
  rubygems_version: 3.4.10
341
348
  signing_key: