firespring_dev_commands 2.2.8.pre.alpha.1 → 2.5.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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/firespring_dev_commands/audit/report.rb +2 -9
- data/lib/firespring_dev_commands/aws/account.rb +1 -1
- data/lib/firespring_dev_commands/aws/cloudformation.rb +3 -10
- data/lib/firespring_dev_commands/aws/login.rb +1 -1
- data/lib/firespring_dev_commands/common.rb +2 -22
- data/lib/firespring_dev_commands/docker/status.rb +0 -20
- data/lib/firespring_dev_commands/docker.rb +16 -86
- data/lib/firespring_dev_commands/eol/aws.rb +2 -10
- data/lib/firespring_dev_commands/eol.rb +2 -22
- data/lib/firespring_dev_commands/git.rb +24 -37
- data/lib/firespring_dev_commands/jira/issue.rb +1 -3
- data/lib/firespring_dev_commands/node.rb +1 -1
- data/lib/firespring_dev_commands/php/audit.rb +0 -4
- data/lib/firespring_dev_commands/php.rb +12 -28
- data/lib/firespring_dev_commands/platform.rb +31 -38
- data/lib/firespring_dev_commands/ruby.rb +3 -6
- data/lib/firespring_dev_commands/target_process/query.rb +4 -30
- data/lib/firespring_dev_commands/target_process/release.rb +1 -1
- data/lib/firespring_dev_commands/target_process/team_assignment.rb +1 -1
- data/lib/firespring_dev_commands/target_process/user.rb +1 -13
- data/lib/firespring_dev_commands/target_process/user_story.rb +1 -1
- data/lib/firespring_dev_commands/target_process/user_story_history.rb +1 -1
- data/lib/firespring_dev_commands/target_process.rb +7 -24
- data/lib/firespring_dev_commands/templates/aws.rb +6 -33
- data/lib/firespring_dev_commands/templates/base_interface.rb +2 -2
- data/lib/firespring_dev_commands/templates/ci.rb +11 -16
- data/lib/firespring_dev_commands/templates/docker/application.rb +2 -2
- data/lib/firespring_dev_commands/templates/docker/node/application.rb +5 -55
- data/lib/firespring_dev_commands/templates/docker/php/application.rb +16 -58
- data/lib/firespring_dev_commands/templates/docker/ruby/application.rb +5 -54
- data/lib/firespring_dev_commands/templates/eol.rb +2 -9
- data/lib/firespring_dev_commands/templates/git.rb +0 -165
- data/lib/firespring_dev_commands/version.rb +1 -1
- data/lib/firespring_dev_commands.rb +1 -1
- metadata +35 -125
- data/lib/firespring_dev_commands/aws/route53.rb +0 -177
- data/lib/firespring_dev_commands/bloom_growth/rock.rb +0 -34
- data/lib/firespring_dev_commands/bloom_growth/seat.rb +0 -16
- data/lib/firespring_dev_commands/bloom_growth/user.rb +0 -43
- data/lib/firespring_dev_commands/bloom_growth.rb +0 -132
- data/lib/firespring_dev_commands/certificate.rb +0 -59
- data/lib/firespring_dev_commands/coverage/base.rb +0 -16
- data/lib/firespring_dev_commands/coverage/cobertura.rb +0 -86
- data/lib/firespring_dev_commands/coverage/none.rb +0 -21
- data/lib/firespring_dev_commands/dns/resource.rb +0 -93
- data/lib/firespring_dev_commands/docker/desktop.rb +0 -61
- data/lib/firespring_dev_commands/eol/node.rb +0 -42
- data/lib/firespring_dev_commands/eol/php.rb +0 -50
- data/lib/firespring_dev_commands/eol/ruby.rb +0 -42
- data/lib/firespring_dev_commands/jira/parent.rb +0 -19
- data/lib/firespring_dev_commands/os.rb +0 -35
- data/lib/firespring_dev_commands/port.rb +0 -24
- data/lib/firespring_dev_commands/target_process/time.rb +0 -32
- data/lib/firespring_dev_commands/templates/aws/services/route53.rb +0 -130
- data/lib/firespring_dev_commands/templates/certificate.rb +0 -41
@@ -1,132 +0,0 @@
|
|
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
|
@@ -1,59 +0,0 @@
|
|
1
|
-
module Dev
|
2
|
-
# Class contains methods for requesting a certificate from route53.
|
3
|
-
# You must have a hosted zone defined for the desired domain
|
4
|
-
class Certificate
|
5
|
-
attr_accessor :domains, :email
|
6
|
-
|
7
|
-
def initialize(domains, email)
|
8
|
-
@domains = Array(domains)
|
9
|
-
@email = email
|
10
|
-
raise 'No certificate domains specified' if domains.empty?
|
11
|
-
end
|
12
|
-
|
13
|
-
# Request the certificate using the route53 docker image
|
14
|
-
# Certificate is stored in /etc/letsencrypt
|
15
|
-
def request
|
16
|
-
puts
|
17
|
-
puts 'Getting SSL Certs For:'
|
18
|
-
puts domains.join("\n")
|
19
|
-
puts
|
20
|
-
puts 'This process can take up to 10 minutes'
|
21
|
-
puts
|
22
|
-
puts Time.now
|
23
|
-
|
24
|
-
# TODO: Really should use the docker api for this
|
25
|
-
cmd = %w(docker run -it --rm --name certbot)
|
26
|
-
cmd << '-e' << 'AWS_ACCESS_KEY_ID'
|
27
|
-
cmd << '-e' << 'AWS_SECRET_ACCESS_KEY'
|
28
|
-
cmd << '-e' << 'AWS_SESSION_TOKEN'
|
29
|
-
cmd << '-v' << '/etc/letsencrypt:/etc/letsencrypt'
|
30
|
-
cmd << 'certbot/dns-route53:latest'
|
31
|
-
cmd << 'certonly'
|
32
|
-
cmd << '-n'
|
33
|
-
cmd << '--agree-tos'
|
34
|
-
cmd << '--dns-route53'
|
35
|
-
cmd << '-d' << domains.join(',')
|
36
|
-
cmd << '--email' << email
|
37
|
-
cmd << '--server' << 'https://acme-v02.api.letsencrypt.org/directory'
|
38
|
-
puts cmd.join(' ')
|
39
|
-
Dev::Common.new.run_command(cmd)
|
40
|
-
end
|
41
|
-
|
42
|
-
# Saves the latest version of the certificate into the given dest_dir
|
43
|
-
def save(dest_dir)
|
44
|
-
raise "directory #{dest_dir} must be an existing directory" unless File.directory?(dest_dir)
|
45
|
-
|
46
|
-
domain = domains.first.sub(/^\*\./, '') # Need to strip off the '*.' if this is a wildcard cert
|
47
|
-
directories = Dir.glob("/etc/letsencrypt/live/#{domain}*/")
|
48
|
-
no_suffix = directories.delete("/etc/letsencrypt/live/#{domain}/")
|
49
|
-
biggest_suffix = directories.max
|
50
|
-
source_dir = biggest_suffix || no_suffix
|
51
|
-
raise "unable to determine certificate directory for #{domain}" unless source_dir
|
52
|
-
|
53
|
-
FileUtils.cp("#{source_dir}privkey.pem", dest_dir, verbose: true)
|
54
|
-
FileUtils.cp("#{source_dir}cert.pem", dest_dir, verbose: true)
|
55
|
-
FileUtils.cp("#{source_dir}chain.pem", dest_dir, verbose: true)
|
56
|
-
FileUtils.cp("#{source_dir}fullchain.pem", dest_dir, verbose: true)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
module Dev
|
2
|
-
module Coverage
|
3
|
-
# Class which defines the methods which must be implemented to function as a coverage class
|
4
|
-
class Base
|
5
|
-
# Raises not implemented
|
6
|
-
def php_options
|
7
|
-
raise 'not implemented'
|
8
|
-
end
|
9
|
-
|
10
|
-
# Raises not implemented
|
11
|
-
def check(*)
|
12
|
-
raise 'not implemented'
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
@@ -1,86 +0,0 @@
|
|
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
|
@@ -1,21 +0,0 @@
|
|
1
|
-
module Dev
|
2
|
-
# Module with a variety of coverage methods for different languages
|
3
|
-
module Coverage
|
4
|
-
# Class which provides methods to effectvely skip coverage
|
5
|
-
class None < Base
|
6
|
-
def initialize(*)
|
7
|
-
super()
|
8
|
-
end
|
9
|
-
|
10
|
-
# Returns the php options for generating code coverage file
|
11
|
-
def php_options
|
12
|
-
[]
|
13
|
-
end
|
14
|
-
|
15
|
-
# Checks the code coverage against the defined threshold
|
16
|
-
def check(*)
|
17
|
-
puts 'Coverage not configured'
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,93 +0,0 @@
|
|
1
|
-
require 'whois'
|
2
|
-
|
3
|
-
module Dev
|
4
|
-
class Dns
|
5
|
-
class Resource
|
6
|
-
attr_reader :domain
|
7
|
-
|
8
|
-
def initialize(domain)
|
9
|
-
@domain = domain
|
10
|
-
end
|
11
|
-
|
12
|
-
# Returns whether or not the given value is a valid IPv4 or IPv6 address
|
13
|
-
def self.ip?(value)
|
14
|
-
ipv4?(value) || ipv6?(value)
|
15
|
-
end
|
16
|
-
|
17
|
-
# Returns whether or not the given value is a valid IPv4 address
|
18
|
-
def self.ipv4?(value)
|
19
|
-
value.match?(Resolv::IPv4::Regex)
|
20
|
-
end
|
21
|
-
|
22
|
-
# Returns whether or not the given value is a valid IPv6 address
|
23
|
-
def self.ipv6?(value)
|
24
|
-
value.match?(Resolv::IPv6::Regex)
|
25
|
-
end
|
26
|
-
|
27
|
-
# Determines the registrar(s) of the given name. Not perfect and can be rate limited.
|
28
|
-
def registrar_lookup(name = domain)
|
29
|
-
Whois.whois(name.chomp('.')).parts.map(&:host)
|
30
|
-
rescue Whois::Error
|
31
|
-
sleep(0.75)
|
32
|
-
retry
|
33
|
-
end
|
34
|
-
|
35
|
-
# Recursively determine the correct nameservers for the given domain.
|
36
|
-
# If nameservers are not found, strip subdomains off until we've reached the TLD
|
37
|
-
def recursive_nameserver_lookup(name = domain)
|
38
|
-
records = lookup(name, type: Resolv::DNS::Resource::IN::NS)
|
39
|
-
|
40
|
-
# Strip the subdomain and try again if we didn't find any nameservers (this can happen with wildcards)
|
41
|
-
return recursive_nameserver_lookup(name.split('.', 2).last) if records.empty?
|
42
|
-
|
43
|
-
# Look up the IPs for the nameservers
|
44
|
-
records
|
45
|
-
end
|
46
|
-
|
47
|
-
# Recursively attempt to find an A record for the given domain.
|
48
|
-
# If one isn't found, also check for CNAMEs continually until we have either found an IP or run out of things to check
|
49
|
-
def recursive_a_lookup(name = domain)
|
50
|
-
# Try looking up an A record first. If we find one, we are done.
|
51
|
-
records = lookup(name, type: Resolv::DNS::Resource::IN::A)
|
52
|
-
return records unless records.empty?
|
53
|
-
|
54
|
-
# Try looking up a CNAME record
|
55
|
-
records = lookup(name, type: Resolv::DNS::Resource::IN::CNAME)
|
56
|
-
|
57
|
-
# If we didn't find an A record _or_ a CNAME, just return empty
|
58
|
-
return records if records.empty?
|
59
|
-
|
60
|
-
# If we found more than one CNAME that is a DNS error
|
61
|
-
raise "Found more than one CNAME entry for #{name}. This is not allowed by DNS" if records.length > 1
|
62
|
-
|
63
|
-
recursive_a_lookup(records.first)
|
64
|
-
end
|
65
|
-
|
66
|
-
# Lookup the given name using the record type provided.
|
67
|
-
def lookup(name = domain, type: Resolv::DNS::Resource::IN::A)
|
68
|
-
# Validate the type
|
69
|
-
raise 'lookup type must be a Resolv::DNS::Resource' unless type.ancestors.include?(Resolv::DNS::Resource)
|
70
|
-
|
71
|
-
# If we were given a tld, return empty
|
72
|
-
return [] unless name.include?('.')
|
73
|
-
|
74
|
-
# Look up NS records for the given host
|
75
|
-
records = Resolv::DNS.new.getresources(name, type)
|
76
|
-
|
77
|
-
# Return the record names
|
78
|
-
records.map do |record|
|
79
|
-
if record.respond_to?(:address)
|
80
|
-
record.address.to_s
|
81
|
-
elsif record.respond_to?(:name)
|
82
|
-
record.name.to_s
|
83
|
-
else
|
84
|
-
''
|
85
|
-
end
|
86
|
-
end
|
87
|
-
rescue
|
88
|
-
sleep(1)
|
89
|
-
retry
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
module Dev
|
2
|
-
class Docker
|
3
|
-
# Class for configuring docker desktop
|
4
|
-
# This is mostly around configuring the docker URL correctly
|
5
|
-
class Desktop
|
6
|
-
# A snippet of a docker compose file which forwards a socket to a local port so that we can read it in the docker library
|
7
|
-
WIN_TCP_COMPOSE_CONTENT = "
|
8
|
-
---
|
9
|
-
version: '3.8'
|
10
|
-
services:
|
11
|
-
windows_tcp:
|
12
|
-
image: alpine/socat
|
13
|
-
network_mode: bridge
|
14
|
-
ports:
|
15
|
-
- 127.0.0.1:23750:2375
|
16
|
-
volumes:
|
17
|
-
- /var/run/docker.sock:/var/run/docker.sock
|
18
|
-
command: tcp-listen:2375,reuseaddr,fork unix-connect:/var/run/docker.sock
|
19
|
-
restart: always".freeze
|
20
|
-
|
21
|
-
# Set up the local ports/sockets correctly based off of the os type
|
22
|
-
def configure
|
23
|
-
if Dev::Os.new.windows?
|
24
|
-
# Start up a small proxy container if running Docker Desktop on windows
|
25
|
-
# This is needed because the docker api library cannot connect to the windows socket
|
26
|
-
unless Dev::Port.new('127.0.0.1', 23_750).open?
|
27
|
-
LOG.info('Starting local proxy port for docker')
|
28
|
-
|
29
|
-
# Write the compose data to a tmp file
|
30
|
-
tmp_compose_file = Tempfile.new('windows_tcp')
|
31
|
-
tmp_compose_file.write(WIN_TCP_COMPOSE_CONTENT)
|
32
|
-
tmp_compose_file.close
|
33
|
-
|
34
|
-
# Start up the container
|
35
|
-
Dev::Docker::Compose.new(
|
36
|
-
compose_files: tmp_compose_file.path,
|
37
|
-
options: ['--detach'],
|
38
|
-
project_name: 'proxy'
|
39
|
-
).up
|
40
|
-
|
41
|
-
# Wait 1 second before we continue
|
42
|
-
sleep 1
|
43
|
-
end
|
44
|
-
|
45
|
-
# Configure the docker url to use 23750 on windows
|
46
|
-
::Docker.url = 'tcp://127.0.0.1:23750'
|
47
|
-
|
48
|
-
else
|
49
|
-
context = Dev::Common.new.run_command(
|
50
|
-
"docker context inspect --format '{{.Endpoints.docker.Host}}'",
|
51
|
-
capture: true
|
52
|
-
).to_s.strip
|
53
|
-
raise 'context is empty' unless context
|
54
|
-
|
55
|
-
# If a user based socket has been defined, default to that
|
56
|
-
::Docker.url = context
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
module Dev
|
2
|
-
class EndOfLife
|
3
|
-
# Class which checks for eol packges referenced by the node package manager
|
4
|
-
class Node
|
5
|
-
attr_reader :node, :lockfile
|
6
|
-
|
7
|
-
def initialize(node = Dev::Node.new)
|
8
|
-
@node = node
|
9
|
-
@lockfile = File.join(node.local_path, "#{node.package_file.reverse.split('.')[-1].reverse}-lock.json")
|
10
|
-
end
|
11
|
-
|
12
|
-
# Default to NPM products
|
13
|
-
def default_products
|
14
|
-
npm_products
|
15
|
-
end
|
16
|
-
|
17
|
-
# 1.) Parse the npm lock file
|
18
|
-
# 2.) Do some package name and version manipulation
|
19
|
-
# 3.) Return the product if it looks like something that the EOL library tracks
|
20
|
-
def npm_products
|
21
|
-
eol = Dev::EndOfLife.new
|
22
|
-
major_version_only_products = %w(ckeditor jquery)
|
23
|
-
|
24
|
-
[].tap do |ary|
|
25
|
-
packages = JSON.parse(File.read(lockfile))&.fetch('packages', [])
|
26
|
-
packages.each do |key, info|
|
27
|
-
name = key.split('node_modules/').last
|
28
|
-
product = name
|
29
|
-
|
30
|
-
# Make sure what we found is supported by the EOL library
|
31
|
-
next unless eol.product?(product)
|
32
|
-
|
33
|
-
version = info['version'].reverse.split('.')[-2..].join('.').reverse.tr('v', '')
|
34
|
-
version = version.split('.').first if major_version_only_products.include?(product)
|
35
|
-
version.chop! if version.end_with?('.00')
|
36
|
-
ary << Dev::EndOfLife::ProductVersion.new(product, version, name)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
module Dev
|
2
|
-
class EndOfLife
|
3
|
-
# Class which checks for eol packges referenced by the php package manager
|
4
|
-
class Php
|
5
|
-
attr_reader :php, :lockfile
|
6
|
-
|
7
|
-
def initialize(php = Dev::Php.new)
|
8
|
-
@php = php
|
9
|
-
@lockfile = File.join(php.local_path, "#{php.package_file.reverse.split('.')[-1].reverse}.lock")
|
10
|
-
end
|
11
|
-
|
12
|
-
# Default to Composer products
|
13
|
-
def default_products
|
14
|
-
composer_products
|
15
|
-
end
|
16
|
-
|
17
|
-
# 1.) Parse the composer lock file
|
18
|
-
# 2.) Do some package name and version manipulation
|
19
|
-
# 3.) Return the product if it looks like something that the EOL library tracks
|
20
|
-
def composer_products
|
21
|
-
eol = Dev::EndOfLife.new
|
22
|
-
major_version_only_products = ['laravel']
|
23
|
-
laravel_products = ['laravel/framework']
|
24
|
-
symfony_products = ['symfony/http-client', 'symfony/mailer', 'symfony/mailchimp-mailer']
|
25
|
-
|
26
|
-
[].tap do |ary|
|
27
|
-
packages = JSON.parse(File.read(lockfile))&.fetch('packages', [])
|
28
|
-
packages&.each do |package|
|
29
|
-
name = package['name']
|
30
|
-
product = if laravel_products.include?(name)
|
31
|
-
'laravel'
|
32
|
-
elsif symfony_products.include?(name)
|
33
|
-
'symfony'
|
34
|
-
else
|
35
|
-
name
|
36
|
-
end
|
37
|
-
|
38
|
-
# Make sure what we found is supported by the EOL library
|
39
|
-
next unless eol.product?(product)
|
40
|
-
|
41
|
-
version = package['version'].reverse.split('.')[-2..].join('.').reverse.tr('v', '')
|
42
|
-
version = version.split('.').first if major_version_only_products.include?(product)
|
43
|
-
version.chop! if version.end_with?('.00')
|
44
|
-
ary << Dev::EndOfLife::ProductVersion.new(product, version, name)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
module Dev
|
2
|
-
class EndOfLife
|
3
|
-
# Class which checks for eol packges referenced by the ruby package manager
|
4
|
-
class Ruby
|
5
|
-
attr_reader :ruby, :lockfile
|
6
|
-
|
7
|
-
def initialize(ruby = Dev::Ruby.new)
|
8
|
-
@ruby = ruby
|
9
|
-
@lockfile = File.join(ruby.local_path, "#{ruby.package_file.reverse.split('.')[-1].reverse}.lock")
|
10
|
-
end
|
11
|
-
|
12
|
-
# Default to Rubygems products
|
13
|
-
def default_products
|
14
|
-
rubygems_products
|
15
|
-
end
|
16
|
-
|
17
|
-
# 1.) Parse the rubygems lock file
|
18
|
-
# 2.) Do some package name and version manipulation
|
19
|
-
# 3.) Return the product if it looks like something that the EOL library tracks
|
20
|
-
def rubygems_products
|
21
|
-
eol = Dev::EndOfLife.new
|
22
|
-
major_version_only_products = []
|
23
|
-
|
24
|
-
[].tap do |ary|
|
25
|
-
packages = Bundler::LockfileParser.new(Bundler.read_file(lockfile)).specs
|
26
|
-
packages.each do |package|
|
27
|
-
name = package.name
|
28
|
-
product = name
|
29
|
-
|
30
|
-
# Make sure what we found is supported by the EOL library
|
31
|
-
next unless eol.product?(product)
|
32
|
-
|
33
|
-
version = package.version.to_s.reverse.split('.')[-2..].join('.').reverse.tr('v', '')
|
34
|
-
version = version.split('.').first if major_version_only_products.include?(product)
|
35
|
-
version.chop! if version.end_with?('.00')
|
36
|
-
ary << Dev::EndOfLife::ProductVersion.new(product, version, name)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module Dev
|
2
|
-
class Jira
|
3
|
-
# Contains information on the Jira parent issue
|
4
|
-
class Parent
|
5
|
-
attr_accessor :data, :id, :title
|
6
|
-
|
7
|
-
def initialize(data)
|
8
|
-
@data = data.parent
|
9
|
-
@id = data.parent['key']
|
10
|
-
@title = data.parent['fields']['summary']
|
11
|
-
end
|
12
|
-
|
13
|
-
# Converts the jira parent object to a string representation
|
14
|
-
def to_s
|
15
|
-
"[#{id}] #{title}"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
module Dev
|
2
|
-
# Class containing methods for determining operating system information
|
3
|
-
class Os
|
4
|
-
attr_accessor :os
|
5
|
-
|
6
|
-
def initialize
|
7
|
-
@os = ::RbConfig::CONFIG['host_os']
|
8
|
-
end
|
9
|
-
|
10
|
-
# Returns true if the host_os contains windowsy text
|
11
|
-
def windows?
|
12
|
-
os.match?(/(mingw|mswin|windows)/i)
|
13
|
-
end
|
14
|
-
|
15
|
-
# Returns true if the host_os contains darwinsy text
|
16
|
-
def darwin?
|
17
|
-
os.match?(/(darwin|mac os)/i)
|
18
|
-
end
|
19
|
-
|
20
|
-
# Returns true if the host_os contains macsy text
|
21
|
-
def mac?
|
22
|
-
darwin?
|
23
|
-
end
|
24
|
-
|
25
|
-
# Returns true if the host_os contains nixy text
|
26
|
-
def nix?
|
27
|
-
os.match?(/(linux|bsd|aix|solaris)/i)
|
28
|
-
end
|
29
|
-
|
30
|
-
# Returns true if the host_os contains cygwiny text
|
31
|
-
def cygwin?
|
32
|
-
os.match?(/(cygwin)/i)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|