firespring_dev_commands 2.2.8.pre.alpha.1 → 2.5.0.pre.alpha.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|