firespring_dev_commands 2.5.0.pre.alpha.2 → 3.0.0.pre.alpha.1

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.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/lib/firespring_dev_commands/audit/report.rb +9 -2
  3. data/lib/firespring_dev_commands/aws/account.rb +3 -7
  4. data/lib/firespring_dev_commands/aws/login.rb +0 -20
  5. data/lib/firespring_dev_commands/bloom_growth/rock.rb +34 -0
  6. data/lib/firespring_dev_commands/bloom_growth/seat.rb +16 -0
  7. data/lib/firespring_dev_commands/bloom_growth/user.rb +43 -0
  8. data/lib/firespring_dev_commands/bloom_growth.rb +132 -0
  9. data/lib/firespring_dev_commands/common.rb +11 -22
  10. data/lib/firespring_dev_commands/coverage/base.rb +21 -0
  11. data/lib/firespring_dev_commands/coverage/cobertura.rb +86 -0
  12. data/lib/firespring_dev_commands/coverage/none.rb +25 -0
  13. data/lib/firespring_dev_commands/docker.rb +23 -25
  14. data/lib/firespring_dev_commands/node.rb +13 -12
  15. data/lib/firespring_dev_commands/php.rb +14 -10
  16. data/lib/firespring_dev_commands/platform.rb +1 -1
  17. data/lib/firespring_dev_commands/ruby.rb +19 -7
  18. data/lib/firespring_dev_commands/target_process/query.rb +30 -4
  19. data/lib/firespring_dev_commands/target_process.rb +3 -1
  20. data/lib/firespring_dev_commands/templates/aws.rb +0 -2
  21. data/lib/firespring_dev_commands/templates/base_interface.rb +2 -2
  22. data/lib/firespring_dev_commands/templates/docker/application.rb +2 -2
  23. data/lib/firespring_dev_commands/templates/docker/node/application.rb +47 -10
  24. data/lib/firespring_dev_commands/templates/docker/php/application.rb +45 -19
  25. data/lib/firespring_dev_commands/templates/docker/ruby/application.rb +44 -8
  26. data/lib/firespring_dev_commands/version.rb +1 -1
  27. data/lib/firespring_dev_commands.rb +1 -1
  28. metadata +42 -35
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 83bb511b8d20db07b3d3f001d82464c9c3edf81a7a719206f4840878d1301ecd
4
- data.tar.gz: 07545c8d8388f32658b09b1b4137f57d766456b1bc1e66696493b1e943330ae5
3
+ metadata.gz: 72bba6fb9e4d227bad3c551b1ac78eb42819178141353aefbf9a6186bd986175
4
+ data.tar.gz: d7f127460c9daedbe0bfe7e5902cc40196cd8e383a29c971ae0241ae76978f28
5
5
  SHA512:
6
- metadata.gz: 67e6bf9497c126a6c00fb008d0341a7f1d8a1a792974fa7196d1c0a2eccf2e8beeb19485c9b4441f3c4e7eccd2a073fc82131ccccb900e77b3b1c8cba0faff0d
7
- data.tar.gz: 45769e10d0434c05e2c87a0fcb545bb6d225839a7fbd9e951be26b6ffa6c6769e052118b30b6cef93d170ef2a74002f8ae80ed1300a944756030846e25d705f8
6
+ metadata.gz: 10690a7a9e253d00f7ca60d36358893248cd111a07b28918564b1dd79e2f19dcf77ea5d980c905005d05c1f2ed8aba395c595c3af3426c76c431062405f3eb86
7
+ data.tar.gz: 369af054ddaaeaae52e347ec02a49b26edff5f2b9c1b88c39c4b7061c68ec64ad091d052b193828034bd2f3d6165550fc920f752524ccbe70c8dc828d71beec1
@@ -3,11 +3,12 @@ module Dev
3
3
  class Audit
4
4
  # The class containing standardized information about an audit report
5
5
  class Report
6
- attr_accessor :items, :min_severity, :ignorelist, :filtered_items
6
+ attr_accessor :items, :min_severity, :error_on_unknown, :ignorelist, :filtered_items
7
7
 
8
8
  def initialize(
9
9
  items,
10
10
  min_severity: ENV.fetch('MIN_SEVERITY', nil),
11
+ error_on_unknown: ENV.fetch('ERROR_ON_UNKNOWN', nil),
11
12
  ignorelist: ENV['IGNORELIST'].to_s.split(/\s*,\s*/)
12
13
  )
13
14
  # Items should be an array of Item objects
@@ -15,12 +16,18 @@ module Dev
15
16
  raise 'items must all be report items' unless @items.all?(Dev::Audit::Report::Item)
16
17
 
17
18
  @min_severity = min_severity || Level::HIGH
19
+ @error_on_unknown = error_on_unknown
18
20
  @ignorelist = Array(ignorelist).compact
19
21
  end
20
22
 
21
23
  # Get all severities greater than or equal to the minimum severity
22
24
  def desired_severities
23
- LEVELS.slice(LEVELS.find_index(min_severity)..-1)
25
+ max_severity = if error_on_unknown.to_s.strip == 'true'
26
+ -1
27
+ else
28
+ -2
29
+ end
30
+ LEVELS.slice(LEVELS.find_index(min_severity)..max_severity)
24
31
  end
25
32
 
26
33
  # Run the filters against the report items and filter out any which should be excluded
@@ -3,8 +3,7 @@ module Dev
3
3
  # Class containing useful methods for interacting with the Aws account
4
4
  class Account
5
5
  # Config object for setting top level Aws account config options
6
- # TODO: registry is deprecated and should be removed on the next major release
7
- Config = Struct.new(:root, :children, :default, :registry, :ecr_registry_ids, :login_to_account_ecr_registry, :default_login_role_name)
6
+ Config = Struct.new(:root, :children, :default, :ecr_registry_ids, :login_to_account_ecr_registry, :default_login_role_name)
8
7
 
9
8
  # Instantiates a new top level config object if one hasn't already been created
10
9
  # Yields that config object to any given block
@@ -28,8 +27,7 @@ module Dev
28
27
  IniFile.new(filename: CONFIG_FILE, default: 'default')
29
28
  end
30
29
 
31
- # TODO: registry is deprecated and should be removed on the next major release
32
- attr_accessor :root, :children, :default, :registry, :ecr_registry_ids
30
+ attr_accessor :root, :children, :default, :ecr_registry_ids
33
31
 
34
32
  # Instantiate an account object
35
33
  # Requires that root account and at least one child account have been configured
@@ -44,11 +42,9 @@ module Dev
44
42
  @default = self.class.config.default
45
43
 
46
44
  # Create the ecr registry list based off several possible configuration values
47
- @ecr_registry_ids = [self.class.config.registry]
45
+ @ecr_registry_ids = Array(self.class.config.ecr_registry_ids)
48
46
  @ecr_registry_ids << Dev::Aws::Profile.new.current if self.class.config.login_to_account_ecr_registry
49
- @ecr_registry_ids.concat(Array(self.class.config.ecr_registry_ids))
50
47
  @ecr_registry_ids = @ecr_registry_ids.flatten.compact.reject(&:empty?).uniq
51
- @registry = @ecr_registry_ids.first
52
48
  end
53
49
 
54
50
  # Returns all configured account information objects
@@ -116,17 +116,6 @@ module Dev
116
116
  ENV['ECR_REGISTRY'] ||= registry
117
117
  end
118
118
 
119
- # Authroizes the docker cli to pull/push images from the Aws container registry
120
- # (e.g. if docker compose needs to pull an image)
121
- # @deprecated Please use {Dev::Aws::Login#registry_login!} instead
122
- def docker_login!(registry_id: nil, region: nil)
123
- registry_id ||= Dev::Aws::Account.new.ecr_registry_ids.first
124
- region ||= Dev::Aws::Credentials.new.logged_in_region || Dev::Aws::DEFAULT_REGION
125
- warn '[DEPRECATION] `Dev::Aws::Login#docker_login!` is deprecated. Please use `Dev::Aws::Login#registry_login!` instead.'
126
- docker_cli_login!(registry: "#{registry_id}.dkr.ecr.#{region}.amazonaws.com", region:)
127
- puts
128
- end
129
-
130
119
  # Authroizes the docker cli to pull/push images from the Aws container registry
131
120
  # (e.g. if docker compose needs to pull an image)
132
121
  private def docker_cli_login!(registry:, region:)
@@ -137,15 +126,6 @@ module Dev
137
126
  Dev::Common.new.run_command([login_cmd])
138
127
  end
139
128
 
140
- # Authroizes the docker ruby library to pull/push images from the Aws container registry
141
- # @deprecated Please use {Dev::Aws::Login#registry_login!} instead
142
- def ecr_login!(registry_id: nil, region: nil)
143
- registry_id ||= Dev::Aws::Account.new.ecr_registry_ids.first
144
- region ||= Dev::Aws::Credentials.new.logged_in_region || Dev::Aws::DEFAULT_REGION
145
- warn '[DEPRECATION] `Dev::Aws::Login#ecr_login!` is deprecated. Please use `Dev::Aws::Login#registry_login!` instead.'
146
- docker_lib_login!(registry_id:, region:)
147
- end
148
-
149
129
  # Authroizes the docker ruby library to pull/push images from the Aws container registry
150
130
  private def docker_lib_login!(registry_id:, region:)
151
131
  # Grab your authentication token from AWS ECR
@@ -0,0 +1,34 @@
1
+ module Dev
2
+ class BloomGrowth
3
+ # Class containing rock information
4
+ class Rock
5
+ attr_accessor :data, :id, :type, :name, :owner, :complete, :completion_id, :created, :due
6
+ attr_reader :state
7
+
8
+ def initialize(data)
9
+ @data = data
10
+ @id = data['Id']
11
+ @type = data['Type']
12
+ @name = data['Name'].to_s.strip
13
+ @owner = User.new(data['Owner']) if data['Owner']
14
+ @complete = data['Complete']
15
+ @completion_id = data['Completion']
16
+ @created = Time.parse(data['CreateTime']) if data['CreateTime']
17
+ @due = Time.parse(data['DueDate']) if data['DueDate']
18
+ @archived = data['Archived']
19
+ end
20
+
21
+ # Convert the completion_id bloom growth gives us into a text version
22
+ def state
23
+ case completion_id
24
+ when 0
25
+ 'Off Track'
26
+ when 1
27
+ 'On Track'
28
+ when 2
29
+ 'Complete'
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,16 @@
1
+ module Dev
2
+ class BloomGrowth
3
+ # Class containing seat information
4
+ class Seat
5
+ attr_accessor :data, :id, :type, :name
6
+
7
+ def initialize(data)
8
+ @data = data
9
+ position = data.dig('Group', 'Position')
10
+ @id = position&.fetch('Id')
11
+ @type = position&.fetch('Type')
12
+ @name = position&.fetch('Name').to_s.strip
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,43 @@
1
+ module Dev
2
+ class BloomGrowth
3
+ # Class containing user information
4
+ class User
5
+ attr_accessor :data, :id, :type, :name, :rocks, :direct_reports, :seats
6
+
7
+ def initialize(data)
8
+ @data = data
9
+ @id = data['Id']
10
+ @type = data['Type']
11
+ @name = data['Name'].to_s.strip
12
+ @rocks = nil
13
+ @direct_reports = nil
14
+ @seats = nil
15
+ end
16
+
17
+ def rocks
18
+ @rocks ||= [].tap do |ary|
19
+ Dev::BloomGrowth.new.get("/api/v1/rocks/user/#{id}") do |data|
20
+ ary << Rock.new(data)
21
+ end
22
+ end
23
+ end
24
+
25
+ def direct_reports
26
+ @direct_reports ||= [].tap do |ary|
27
+ Dev::BloomGrowth.new.get("/api/v1/users/#{id}/directreports") do |data|
28
+ ary << User.new(data)
29
+ end
30
+ end
31
+ end
32
+
33
+ def seats
34
+ @seats ||= [].tap do |ary|
35
+ Dev::BloomGrowth.new.get("/api/v1/users/#{id}/seats") do |data|
36
+ ary << Seat.new(data)
37
+ puts ary.last.inspect
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,132 @@
1
+ require 'net/http'
2
+
3
+ module Dev
4
+ # Class for interacting with the Bloom Growth api
5
+ class BloomGrowth
6
+ # The config file to try to load credentials from
7
+ CONFIG_FILE = "#{Dir.home}/.env.bloom".freeze
8
+
9
+ # The text of the username variable key
10
+ BLOOM_USERNAME = 'BLOOM_USERNAME'.freeze
11
+
12
+ # The text of the password variable key
13
+ BLOOM_PASSWORD = 'BLOOM_PASSWORD'.freeze
14
+
15
+ # The text of the token variable key
16
+ BLOOM_TOKEN = 'BLOOM_TOKEN'.freeze
17
+
18
+ # The text of the url variable key
19
+ BLOOM_URL = 'BLOOM_URL'.freeze
20
+
21
+ # Config object for setting top level bloom growth config options
22
+ Config = Struct.new(:username, :password, :url, :http_debug) do
23
+ def initialize
24
+ Dotenv.load(CONFIG_FILE) if File.exist?(CONFIG_FILE)
25
+
26
+ self.username = ENV.fetch(BLOOM_USERNAME, nil)
27
+ self.password = ENV.fetch(BLOOM_PASSWORD, nil)
28
+ self.url = ENV.fetch(BLOOM_URL, 'https://app.bloomgrowth.com')
29
+ self.http_debug = false
30
+ end
31
+ end
32
+
33
+ class << self
34
+ # Instantiates a new top level config object if one hasn't already been created
35
+ # Yields that config object to any given block
36
+ # Returns the resulting config object
37
+ def config
38
+ @config ||= Config.new
39
+ yield(@config) if block_given?
40
+ @config
41
+ end
42
+
43
+ # Alias the config method to configure for a slightly clearer access syntax
44
+ alias_method :configure, :config
45
+ end
46
+
47
+ attr_accessor :username, :password, :url, :token, :client, :default_headers
48
+
49
+ # Initialize a new target process client using the given inputs
50
+ def initialize(username: self.class.config.username, password: self.class.config.password, url: self.class.config.url)
51
+ raise 'username is required' if username.to_s.strip.empty?
52
+ raise 'password is required' if password.to_s.strip.empty?
53
+ raise 'url is required' if url.to_s.strip.empty?
54
+
55
+ @username = username
56
+ @password = password
57
+ @url = url
58
+ uri = URI.parse(@url)
59
+ @client = Net::HTTP.new(uri.host, uri.port)
60
+ @client.use_ssl = true
61
+ @client.verify_mode = OpenSSL::SSL::VERIFY_PEER
62
+ @client.set_debug_output(LOG) if self.class.config.http_debug
63
+ @default_headers = {
64
+ 'authorization' => "Bearer #{token}",
65
+ 'content-type' => 'application/json',
66
+ 'accept' => 'application/json'
67
+ }
68
+ end
69
+
70
+ # Method for getting a bearer token for the bloom growth api. There are a couple of possible logic paths
71
+ # - If a token has already been defined, use it
72
+ # - If a token is found in the ENV, use it
73
+ # - Otherwise, use the username and passowrd that has been configured to request a new token from bloom
74
+ def token
75
+ @token ||= ENV.fetch(BLOOM_TOKEN, nil)
76
+
77
+ unless @token
78
+ response = post(
79
+ '/Token',
80
+ {
81
+ grant_type: 'password',
82
+ userName: username,
83
+ password:
84
+ },
85
+ headers: {
86
+ 'content-type' => 'application/json',
87
+ 'accept' => 'application/json'
88
+ }
89
+ )
90
+ # TODO: Should we look at https://github.com/DannyBen/lightly for caching the token?
91
+ @token = ENV[BLOOM_TOKEN] = response['access_token']
92
+ LOG.info("Retrieved BloomGrowth token. Expires on #{Time.now + response['expires_in']}")
93
+ end
94
+
95
+ @token
96
+ end
97
+
98
+ # Return all user objects visible to the logged in user
99
+ def visible_users(&)
100
+ [].tap do |ary|
101
+ get('/api/v1/users/mineviewable') do |user_data|
102
+ ary << User.new(user_data)
103
+ end
104
+ ary.each(&)
105
+ end
106
+ end
107
+
108
+ # Perform a get request to the given path using the given query
109
+ # Call the given block (if present) with each piece of data
110
+ # Return all pieces of data
111
+ def get(path, query_string: nil, headers: default_headers, &)
112
+ url = path
113
+ url << "?#{URI.encode_www_form(query_string)}" unless query_string.to_s.strip.empty?
114
+
115
+ response = client.request_get(url, headers)
116
+ raise "Error querying #{url} [#{query_string}]: #{response.inspect}" unless response.response.is_a?(Net::HTTPSuccess)
117
+
118
+ JSON.parse(response.body).each(&)
119
+ nil
120
+ end
121
+
122
+ # Perform a post request to the given path using the gien data
123
+ # Return the parsed json body
124
+ def post(path, data, headers: default_headers)
125
+ data = data.to_json unless data.is_a?(String)
126
+ response = client.request_post(path, data, headers)
127
+ raise "Error querying #{url}/#{path}: #{response.inspect}" unless response.response.is_a?(Net::HTTPSuccess)
128
+
129
+ JSON.parse(response.body)
130
+ end
131
+ end
132
+ end
@@ -37,28 +37,6 @@ module Dev
37
37
  output
38
38
  end
39
39
 
40
- # Wraps a block of code in a y/n question.
41
- # If the user answers 'y' then the block is executed.
42
- # If the user answers 'n' then the block is skipped.
43
- # @deprecated Please use {Common#when_confirmed} instead
44
- def with_confirmation(message, default = 'y', color_message: true)
45
- message = "\n #{message}" << '? '.light_green
46
- message = message.light_green if color_message
47
- print message
48
- print '('.light_green << 'y'.light_yellow << '/'.light_green << 'n'.light_yellow << ') '.light_green
49
-
50
- answer = default
51
- answer = $stdin.gets unless ENV['NON_INTERACTIVE'] == 'true'
52
-
53
- unless answer.strip.casecmp('y').zero?
54
- puts "\n Cancelled.\n".light_yellow
55
- exit 1
56
- end
57
- puts
58
-
59
- yield
60
- end
61
-
62
40
  # Exits unless the user confirms they want to continue
63
41
  # If the user answers 'y' then the code will continue
64
42
  # All other inputs cause the code to exit
@@ -187,5 +165,16 @@ module Dev
187
165
  return false
188
166
  end
189
167
  end
168
+
169
+ # Print the given filesize using the most appropriate units
170
+ def filesize(size)
171
+ return '0.0 B' if size.to_i.zero?
172
+
173
+ units = %w(B KB MB GB TB Pb EB)
174
+ exp = (Math.log(size) / Math.log(1024)).to_i
175
+ exp = 6 if exp > 6
176
+
177
+ format('%.1f %s', size.to_f / (1024**exp), units[exp])
178
+ end
190
179
  end
191
180
  end
@@ -0,0 +1,21 @@
1
+ module Dev
2
+ module Coverage
3
+ class Base
4
+ def php_options
5
+ raise 'not implemented'
6
+ end
7
+
8
+ def node_options
9
+ raise 'not implemented'
10
+ end
11
+
12
+ def ruby_options
13
+ raise 'not implemented'
14
+ end
15
+
16
+ def check(*)
17
+ raise 'not implemented'
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,86 @@
1
+ module Dev
2
+ # Module containing different classes for interfacing with coverage files
3
+ module Coverage
4
+ # Class for checking code coverage using cobertura
5
+ class Cobertura < Base
6
+ attr_reader :local_filename, :container_filename, :filename, :threshold, :exclude
7
+
8
+ def initialize(filename: File.join('coverage', 'cobertura.xml'), threshold: nil, container_path: nil, local_path: nil, exclude: nil)
9
+ super()
10
+
11
+ @filename = filename
12
+ @local_filename = File.join(local_path || '.', @filename)
13
+ @container_filename = File.join(container_path || '.', @filename)
14
+ @threshold = threshold
15
+ @exclude = (exclude || []).map do |it|
16
+ next it if it.is_a?(Regex)
17
+
18
+ Regex.new(it)
19
+ end
20
+ end
21
+
22
+ # Remove any previous versions of the local file that will be output
23
+ # return the phpunit options needed to regenerate the cobertura xml file
24
+ def php_options
25
+ # Remove any previous coverage info
26
+ FileUtils.rm_f(local_filename, verbose: true)
27
+
28
+ # Return the needed php commands to generate the cobertura report
29
+ %W(--coverage-cobertura #{container_filename})
30
+ end
31
+
32
+ # Parse the cobertura file and check the lines missed against the desired threshold
33
+ def check(application: nil)
34
+ # If an application has been specified and the file does not exist locally, attempt to copy it back from the docker container
35
+ if application && !File.exist?(local_filename)
36
+ container = Dev::Docker::Compose.new.container_by_name(application)
37
+ Dev::Docker.new.copy_from_container(container, container_filename, local_filename, required: true)
38
+ end
39
+
40
+ report = Ox.load(File.read(local_filename))
41
+ total_missed = report.coverage.locate('packages/package').sum { |package| parse_package_missed(package) }
42
+ puts "Lines missing coverage was #{total_missed}"
43
+ puts "Configured threshold was #{threshold}" if threshold
44
+ raise 'Code coverage not met' if threshold && total_missed > threshold
45
+ end
46
+
47
+ # Go through the package and add up all of the lines that were missed
48
+ # Ignore if the file was in the exlude list
49
+ private def parse_package_missed(package)
50
+ filename = package.attributes[:name]
51
+ return if exclude.any? { |it| it.match(filename) }
52
+
53
+ missed = 0
54
+ lines_processed = Set.new
55
+ package.locate('classes/class/lines/line').each do |line|
56
+ # Don't count lines multiple times
57
+ line_number = line.attributes[:number]
58
+ next if lines_processed.include?(line_number)
59
+
60
+ lines_processed << line_number
61
+ missed += 1 unless line.attributes[:hits].to_i.positive?
62
+ end
63
+ total = lines_processed.length
64
+
65
+ sanity_check_coverage_against_cobertura_values(package, missed, total)
66
+ missed
67
+ end
68
+
69
+ # Calculate the coverage percent based off the numbers we got and compare to the
70
+ # value cobertura reported. This is meant as a sanity check that we are reading the data correctly
71
+ # TODO: This should be removed after the above logic has been vetted
72
+ private def sanity_check_coverage_against_cobertura_values(package, missed, total)
73
+ line_rate = package.attributes[:'line-rate']
74
+ cobertura_reported_coverage = line_rate.to_f
75
+ cobertura_reported_precision = line_rate.split('.').last.length
76
+
77
+ file_coverage = 0.0
78
+ file_coverage = ((total - missed).to_f / total).round(cobertura_reported_precision) if total.positive?
79
+ return if file_coverage == cobertura_reported_coverage
80
+
81
+ filename = package.attributes[:name]
82
+ puts "WARNINNG: #{filename} coverage (#{file_coverage}) differed from what cobertura reported (#{cobertura_reported_coverage})"
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,25 @@
1
+ module Dev
2
+ module Coverage
3
+ class None < Base
4
+ def initialize(*)
5
+ super()
6
+ end
7
+
8
+ def php_options
9
+ []
10
+ end
11
+
12
+ def node_options
13
+ []
14
+ end
15
+
16
+ def ruby_options
17
+ []
18
+ end
19
+
20
+ def check(*)
21
+ # Nothing to do here
22
+ end
23
+ end
24
+ end
25
+ end
@@ -77,6 +77,22 @@ module Dev
77
77
  _prune('volumes', opts:)
78
78
  end
79
79
 
80
+ def prune_project_volumes(project_name:)
81
+ project_name = project_name.to_s.strip
82
+ raise 'No project name defined' if project_name.empty?
83
+
84
+ ::Docker::Volume.all.each do |volume|
85
+ next unless volume.info['Name'].start_with?(project_name)
86
+
87
+ begin
88
+ volume.remove
89
+ LOG.info "Removed volume #{volume.id[0, 12]}"
90
+ rescue => e
91
+ LOG.error "Error removing volume #{volume.id[0, 12]}: #{e}"
92
+ end
93
+ end
94
+ end
95
+
80
96
  # Prunes/removes all unused images
81
97
  def prune_images
82
98
  _prune('images')
@@ -103,18 +119,7 @@ module Dev
103
119
  LOG.info "\nDeleted #{type.capitalize}"
104
120
  deleted_items = info["#{type}Deleted"] || []
105
121
  deleted_items.each { |it| LOG.info " #{it}" }
106
- LOG.info "Total reclaimed space: #{filesize(info['SpaceReclaimed'])}"
107
- end
108
-
109
- # Print the given filesize using the most appropriate units
110
- private def filesize(size)
111
- return '0.0 B' if size.to_i.zero?
112
-
113
- units = %w(B KB MB GB TB Pb EB)
114
- exp = (Math.log(size) / Math.log(1024)).to_i
115
- exp = 6 if exp > 6
116
-
117
- format('%.1f %s', size.to_f / (1024**exp), units[exp])
122
+ LOG.info "Total reclaimed space: #{Dev::Common.new.filesize(info['SpaceReclaimed'])}"
118
123
  end
119
124
 
120
125
  # Remove docker images with the "force" option set to true
@@ -124,22 +129,14 @@ module Dev
124
129
  ::Docker::Image.remove(images[0].id, force: true) unless images.empty?
125
130
  end
126
131
 
127
- # Calls the docker compose method with the given inputs
128
- # @deprecated Please use {Docker::Compose#container_by_name} instead
129
- def container_by_name(service_name, prefix = nil, status: [Docker::Status::RUNNING])
130
- warn '[DEPRECATION] `Docker#container_by_name` is deprecated. Please use `Docker::Compose#container_by_name` instead.'
131
- Docker::Compose.new.container_by_name(service_name, prefix, status)
132
- end
133
-
134
- # Calls the docker compose method with the given inputs
135
- # @deprecated Please use {Docker::Compose#mapped_public_port} instead
136
- def mapped_public_port(name, private_port)
137
- warn '[DEPRECATION] `Docker#mapped_public_port` is deprecated. Please use `Docker::Compose#mapped_public_port` instead.'
138
- Docker::Compose.new.mapped_public_port(name, private_port)
132
+ # Gets the default working dir of the container
133
+ def working_dir(container)
134
+ container.json['Config']['WorkingDir']
139
135
  end
140
136
 
141
137
  # Copies the source path on your local machine to the destination path on the container
142
138
  def copy_to_container(container, source_path, dest_path)
139
+ dest_path = File.join(working_dir(container), dest_path) unless dest_path.start_with?(File::SEPARATOR)
143
140
  LOG.info "Copying #{source_path} to #{dest_path}... "
144
141
 
145
142
  container.archive_in(source_path, dest_path, overwrite: true)
@@ -154,6 +151,7 @@ module Dev
154
151
  # Copies the source path on the container to the destination path on your local machine
155
152
  # If required is set to true, the command will fail if the source path does not exist on the container
156
153
  def copy_from_container(container, source_path, dest_path, required: true)
154
+ source_path = File.join(working_dir(container), source_path) unless source_path.start_with?(File::SEPARATOR)
157
155
  LOG.info "Copying #{source_path} to #{dest_path}... "
158
156
 
159
157
  tar = StringIO.new
@@ -205,7 +203,7 @@ module Dev
205
203
  arch = "#{arch}/#{variant}" if variant
206
204
  id = image.info&.dig('id')&.split(':')&.last&.slice(0..11)
207
205
  created = timesince(Time.at(image.info&.dig('Created')))
208
- size = filesize(image.info&.dig('Size'))
206
+ size = Dev::Common.new.filesize(image.info&.dig('Size'))
209
207
 
210
208
  repo_urls = image.info&.dig('RepoTags')
211
209
  repo_urls ||= ["#{image.info&.dig('RepoDigests')&.first&.split(':')&.first&.split('@')&.first}:<none>"]