firespring_dev_commands 1.3.0

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 (59) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +83 -0
  4. data/lib/firespring_dev_commands/audit/report/item.rb +33 -0
  5. data/lib/firespring_dev_commands/audit/report/levels.rb +36 -0
  6. data/lib/firespring_dev_commands/audit/report.rb +49 -0
  7. data/lib/firespring_dev_commands/aws/account/info.rb +15 -0
  8. data/lib/firespring_dev_commands/aws/account.rb +164 -0
  9. data/lib/firespring_dev_commands/aws/cloudformation/parameters.rb +26 -0
  10. data/lib/firespring_dev_commands/aws/cloudformation.rb +188 -0
  11. data/lib/firespring_dev_commands/aws/codepipeline.rb +96 -0
  12. data/lib/firespring_dev_commands/aws/credentials.rb +136 -0
  13. data/lib/firespring_dev_commands/aws/login.rb +131 -0
  14. data/lib/firespring_dev_commands/aws/parameter.rb +32 -0
  15. data/lib/firespring_dev_commands/aws/profile.rb +55 -0
  16. data/lib/firespring_dev_commands/aws/s3.rb +42 -0
  17. data/lib/firespring_dev_commands/aws.rb +10 -0
  18. data/lib/firespring_dev_commands/boolean.rb +7 -0
  19. data/lib/firespring_dev_commands/common.rb +112 -0
  20. data/lib/firespring_dev_commands/daterange.rb +171 -0
  21. data/lib/firespring_dev_commands/docker/compose.rb +271 -0
  22. data/lib/firespring_dev_commands/docker/status.rb +38 -0
  23. data/lib/firespring_dev_commands/docker.rb +276 -0
  24. data/lib/firespring_dev_commands/dotenv.rb +6 -0
  25. data/lib/firespring_dev_commands/env.rb +38 -0
  26. data/lib/firespring_dev_commands/eol/product_version.rb +86 -0
  27. data/lib/firespring_dev_commands/eol.rb +58 -0
  28. data/lib/firespring_dev_commands/git/info.rb +13 -0
  29. data/lib/firespring_dev_commands/git.rb +420 -0
  30. data/lib/firespring_dev_commands/jira/issue.rb +33 -0
  31. data/lib/firespring_dev_commands/jira/project.rb +13 -0
  32. data/lib/firespring_dev_commands/jira/user/type.rb +20 -0
  33. data/lib/firespring_dev_commands/jira/user.rb +31 -0
  34. data/lib/firespring_dev_commands/jira.rb +78 -0
  35. data/lib/firespring_dev_commands/logger.rb +8 -0
  36. data/lib/firespring_dev_commands/node/audit.rb +39 -0
  37. data/lib/firespring_dev_commands/node.rb +107 -0
  38. data/lib/firespring_dev_commands/php/audit.rb +71 -0
  39. data/lib/firespring_dev_commands/php.rb +109 -0
  40. data/lib/firespring_dev_commands/rake.rb +24 -0
  41. data/lib/firespring_dev_commands/ruby/audit.rb +30 -0
  42. data/lib/firespring_dev_commands/ruby.rb +113 -0
  43. data/lib/firespring_dev_commands/second.rb +22 -0
  44. data/lib/firespring_dev_commands/tar/pax_header.rb +49 -0
  45. data/lib/firespring_dev_commands/tar/type_flag.rb +49 -0
  46. data/lib/firespring_dev_commands/tar.rb +149 -0
  47. data/lib/firespring_dev_commands/templates/aws.rb +84 -0
  48. data/lib/firespring_dev_commands/templates/base_interface.rb +54 -0
  49. data/lib/firespring_dev_commands/templates/ci.rb +138 -0
  50. data/lib/firespring_dev_commands/templates/docker/application.rb +177 -0
  51. data/lib/firespring_dev_commands/templates/docker/default.rb +200 -0
  52. data/lib/firespring_dev_commands/templates/docker/node/application.rb +145 -0
  53. data/lib/firespring_dev_commands/templates/docker/php/application.rb +190 -0
  54. data/lib/firespring_dev_commands/templates/docker/ruby/application.rb +146 -0
  55. data/lib/firespring_dev_commands/templates/eol.rb +23 -0
  56. data/lib/firespring_dev_commands/templates/git.rb +147 -0
  57. data/lib/firespring_dev_commands/version.rb +11 -0
  58. data/lib/firespring_dev_commands.rb +21 -0
  59. metadata +436 -0
@@ -0,0 +1,96 @@
1
+ require 'aws-sdk-codepipeline'
2
+
3
+ module Dev
4
+ class Aws
5
+ # Class for performing codepipeline functions
6
+ class CodePipeline
7
+ attr_reader :client
8
+
9
+ def initialize
10
+ @client = nil
11
+ end
12
+
13
+ # Create/set a new client if none is present
14
+ # Return the client
15
+ def client
16
+ @client ||= ::Aws::CodePipeline::Client.new
17
+ end
18
+
19
+ # Get a list of all pipelines in the aws account
20
+ # Optionally filter by the regex_match
21
+ def pipelines(regex_match = nil)
22
+ raise 'regex_match must be a regexp' if regex_match && !regex_match.is_a?(Regexp)
23
+
24
+ params = {}
25
+ response = client.list_pipelines(params)
26
+ pipelines = response.pipelines
27
+
28
+ next_token = response.next_token
29
+ while next_token
30
+ params[:next_token] = next_token
31
+ response = client.list_pipelines(params)
32
+ pipelines += response.pipelines
33
+ next_token = response.next_token
34
+ end
35
+
36
+ pipelines.select! { |it| it.name.match(regex_match) } if regex_match
37
+ pipelines
38
+ end
39
+
40
+ # Find all pipelines matching the regex_match and find the status for each of them
41
+ def status(name)
42
+ pipeline = client.get_pipeline_state(name: name)
43
+ print_pipeline_information(pipeline)
44
+ rescue ::Aws::CodePipeline::Errors::PipelineNotFoundException
45
+ LOG.error "No pipeline found with name #{name}".light_yellow
46
+ end
47
+
48
+ # Iterate over each pipeline stage and call the method to print stage information for each
49
+ private def print_pipeline_information(pipeline)
50
+ puts pipeline.pipeline_name.light_white
51
+ pipeline.stage_states.each do |stage_state|
52
+ print_stage_state_information(stage_state)
53
+ end
54
+ end
55
+
56
+ # Print Stage information
57
+ # Iterate over each stage action state and call the method to print information for each
58
+ # Ignore the source steps because they are usually boring
59
+ private def print_stage_state_information(stage_state)
60
+ # Source step is not exciting - don't print it
61
+ return if stage_state.stage_name.to_s.strip == 'Source'
62
+
63
+ puts " Stage: #{stage_state.stage_name.light_blue}"
64
+ stage_state.action_states.each do |action_state|
65
+ print_action_state_information(action_state)
66
+ end
67
+ end
68
+
69
+ # Print Action State information
70
+ # Call the method to print execution information
71
+ private def print_action_state_information(action_state)
72
+ puts " Action: #{action_state.action_name.light_blue}"
73
+ print_latest_execution_information(action_state.latest_execution)
74
+ end
75
+
76
+ # Print the latest execution information
77
+ private def print_latest_execution_information(latest_execution)
78
+ status = latest_execution&.status
79
+ last_status_change = latest_execution&.last_status_change
80
+ error_details = latest_execution&.error_details
81
+
82
+ puts " Date: #{last_status_change&.to_s&.light_white}"
83
+ puts " State: #{colorize_status(status)}"
84
+ puts " Details: #{error_details&.message&.light_yellow}" if error_details
85
+ end
86
+
87
+ # Colorize the status output based on the success or failure of the pipeline
88
+ private def colorize_status(status)
89
+ return status.light_green if status == 'Succeeded'
90
+ return status.light_yellow if %w(Abandoned InProgress).include?(status)
91
+
92
+ status&.light_red
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,136 @@
1
+ require 'aws-sdk-sts'
2
+ require 'aws-sdk-ssm'
3
+ require 'inifile'
4
+ require 'json'
5
+ require 'net/http'
6
+
7
+ module Dev
8
+ class Aws
9
+ # Class contains methods for interacting with your Aws credentials
10
+ class Credentials
11
+ # The local file where temporary credentials are stored
12
+ CONFIG_FILE = "#{Dev::Aws::CONFIG_DIR}/credentials".freeze
13
+
14
+ # The account the profile is currently logged in to
15
+ def logged_in_account
16
+ ::Aws::STS::Client.new.get_caller_identity.account
17
+ end
18
+
19
+ # The region associated with the current login
20
+ def logged_in_region
21
+ ::Aws::STS::Client.new.send(:config).region
22
+ end
23
+
24
+ # Whether or not the current credentials are still active
25
+ def active?(profile = Dev::Aws::Profile.new.current)
26
+ # If there is a metadata uri then we are in an AWS env - assume we are good
27
+ return true if ENV.fetch('ECS_CONTAINER_METADATA_URI', nil)
28
+
29
+ # Otherwise there should either be an aws config directory or access key configured
30
+ return false unless File.exist?(Dev::Aws::CONFIG_DIR) || ENV.fetch('AWS_ACCESS_KEY_ID', nil)
31
+
32
+ # TODO: I'd prefer to still validate creds if using a METADATA_URI
33
+ # However this appears to require additional permissions which might not be present. Is there a better check here?
34
+ # return false if !ENV.fetch('ECS_CONTAINER_METADATA_URI', nil) && !(File.exist?(Dev::Aws::CONFIG_DIR) || ENV.fetch('AWS_ACCESS_KEY_ID', nil))
35
+
36
+ # Check for expired credentials
37
+ begin
38
+ ::Aws::STS::Client.new(profile: profile).get_caller_identity
39
+ rescue
40
+ return false
41
+ end
42
+
43
+ # Check for invalid credentials
44
+ begin
45
+ # TODO: Is there a better check we can do here?
46
+ ::Aws::SSM::Client.new(profile: profile).describe_parameters(max_results: 1)
47
+ rescue
48
+ return false
49
+ end
50
+
51
+ # If the credentials are valid, make sure they are set in the ruby process environment for use later
52
+ export!
53
+ true
54
+ end
55
+
56
+ # Setup base Aws credential settings
57
+ def base_setup!
58
+ # Make the base config directory
59
+ FileUtils.mkdir_p(Dev::Aws::CONFIG_DIR)
60
+
61
+ puts
62
+ puts 'Configuring default credential values'
63
+
64
+ # Write access key / secret key in the credentials file
65
+ credini = IniFile.new(filename: "#{Dev::Aws::CONFIG_DIR}/credentials", default: 'default')
66
+ defaultini = credini['default']
67
+
68
+ access_key_default = defaultini['aws_access_key_id']
69
+ defaultini['aws_access_key_id'] = Dev::Common.new.ask('AWS Access Key ID', access_key_default)
70
+
71
+ secret_key_default = defaultini['aws_secret_access_key']
72
+ defaultini['aws_secret_access_key'] = Dev::Common.new.ask('AWS Secret Access Key', secret_key_default)
73
+
74
+ credini.write
75
+ end
76
+
77
+ # Write Aws account specific settings to the credentials file
78
+ def write!(account, creds)
79
+ # Write access key / secret key / session token in the credentials file
80
+ credini = IniFile.new(filename: CONFIG_FILE, default: 'default')
81
+ defaultini = credini[account]
82
+
83
+ defaultini['aws_access_key_id'] = creds.access_key_id
84
+ defaultini['aws_secret_access_key'] = creds.secret_access_key
85
+ defaultini['aws_session_token'] = creds.session_token
86
+
87
+ credini.write
88
+ end
89
+
90
+ # Export our current credentials into the ruby environment
91
+ def export!
92
+ export_profile_credentials!
93
+ export_container_credentials!
94
+ end
95
+
96
+ # Exports the credentials if there is an active credentials uri
97
+ def export_container_credentials!
98
+ # If we already have creds defined, don't do anything
99
+ return if ENV.fetch('AWS_ACCESS_KEY_ID', nil)
100
+
101
+ # If a container credentials url is not present, don't do anything
102
+ ecs_creds = ENV.fetch('AWS_CONTAINER_CREDENTIALS_RELATIVE_URI', nil)
103
+ return unless ecs_creds
104
+
105
+ # Otherwise query the local creds, parse the json response, and store in the environment
106
+ response = Net::HTTP.get_response(URI.parse("http://169.254.170.2#{ecs_creds}"))
107
+ raise 'Error getting container credentials' unless response.is_a?(Net::HTTPSuccess)
108
+
109
+ creds = JSON.parse(response.body)
110
+ ENV['AWS_ACCESS_KEY_ID'] = creds['AccessKeyId']
111
+ ENV['AWS_SECRET_ACCESS_KEY'] = creds['SecretAccessKey']
112
+ ENV['AWS_SESSION_TOKEN'] = creds['Token']
113
+ ENV['AWS_DEFAULT_REGION'] = logged_in_region
114
+ end
115
+
116
+ # Exports the credentials if there is a configured aws profile
117
+ def export_profile_credentials!
118
+ # If we already have creds defined, don't do anything
119
+ return if ENV.fetch('AWS_ACCESS_KEY_ID', nil)
120
+
121
+ # If a profile config file is not present, don't do anything
122
+ return unless File.exist?(CONFIG_FILE)
123
+
124
+ # Otherwise load access key / secret key / session token from the credentials file into the environment
125
+ credini = IniFile.new(filename: CONFIG_FILE, default: 'default')
126
+ profile_credentials = credini[Dev::Aws::Profile.new.current]
127
+ return unless profile_credentials
128
+
129
+ ENV['AWS_ACCESS_KEY_ID'] = profile_credentials['aws_access_key_id']
130
+ ENV['AWS_SECRET_ACCESS_KEY'] = profile_credentials['aws_secret_access_key']
131
+ ENV['AWS_SESSION_TOKEN'] = profile_credentials['aws_session_token']
132
+ ENV['AWS_DEFAULT_REGION'] = logged_in_region
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,131 @@
1
+ require 'aws-sdk-ecr'
2
+ require 'aws-sdk-sts'
3
+ require 'inifile'
4
+
5
+ module Dev
6
+ class Aws
7
+ # Class containing methods for helping a user log in to aws
8
+ class Login
9
+ # Main interface for logging in to an AWS account
10
+ # If an account is not specified the user is given an account selection menu
11
+ # If an account registry has been configured, the user is also logged in to the docker registry
12
+ def login!(account = nil)
13
+ # If more than one child account has been configured, have the user select the account they want to log in to
14
+ account ||= Dev::Aws::Account.new.select
15
+
16
+ # Authorize if our creds are not active
17
+ authorize!(account) unless Dev::Aws::Credentials.new.active?(account)
18
+
19
+ # Ensure the local env is pointed to the profile we selected
20
+ Dev::Aws::Profile.new.write!(account)
21
+
22
+ # Load credentials into the ENV for subprocesses
23
+ Dev::Aws::Credentials.new.export!
24
+
25
+ # Login in to the docker registry if the user has configured one
26
+ registry_login! if Dev::Aws::Account.new.registry
27
+ end
28
+
29
+ # Authorize your local credentials
30
+ # User is prompted for an MFA code
31
+ # Temporary credentials are written back to the credentials file
32
+ def authorize!(account)
33
+ # Make sure the account has been set up
34
+ cfgini = IniFile.new(filename: "#{Dev::Aws::CONFIG_DIR}/config", default: 'default')
35
+ unless cfgini.has_section?("profile #{account}")
36
+ Dev::Aws::Account.new.write!(account)
37
+ cfgini = IniFile.new(filename: "#{Dev::Aws::CONFIG_DIR}/config", default: 'default')
38
+ end
39
+
40
+ defaultini = cfgini['default']
41
+ profileini = cfgini["profile #{account}"]
42
+
43
+ serial = profileini['mfa_serial'] || defaultini['mfa_serial']
44
+ role = profileini['role_arn'] || defaultini['role_arn']
45
+ session_name = profileini['role_session_name'] || defaultini['role_session_name']
46
+ session_duration = profileini['session_duration'] || defaultini['session_duration']
47
+
48
+ puts
49
+ puts " Logging in to #{account} as #{role}".light_yellow
50
+ puts
51
+
52
+ code = ENV['AWS_TOKEN_CODE'] || Dev::Common.new.ask("Enter the MFA code for the #{ENV.fetch('USERNAME', '')} user serial #{serial}")
53
+ raise 'MFA is required' unless code.to_s.strip
54
+
55
+ sts = ::Aws::STS::Client.new(profile: 'default')
56
+ creds = sts.assume_role(
57
+ serial_number: serial,
58
+ role_arn: role,
59
+ role_session_name: session_name,
60
+ token_code: code,
61
+ duration_seconds: session_duration
62
+ ).credentials
63
+ puts
64
+
65
+ Dev::Aws::Credentials.new.write!(account, creds)
66
+ end
67
+
68
+ # Authroizes the docker cli to pull/push images from the Aws container registry (e.g. if docker compose needs to pull an image)
69
+ # Authroizes the docker ruby library to pull/push images from the Aws container registry
70
+ def registry_login!(registry_id: Dev::Aws::Account.new.registry, region: Dev::Aws::DEFAULT_REGION)
71
+ raise 'registry_id is required' if registry_id.to_s.strip.empty?
72
+ raise 'region is required' if region.to_s.strip.empty?
73
+
74
+ registry = "#{registry_id}.dkr.ecr.#{region}.amazonaws.com"
75
+ docker_cli_login!(registry: registry, region: region)
76
+ docker_lib_login!(registry_id: registry_id, region: region)
77
+
78
+ ENV['ECR_REGISTRY_ID'] ||= registry_id
79
+ ENV['ECR_REGISTRY'] ||= registry
80
+ end
81
+
82
+ # Authroizes the docker cli to pull/push images from the Aws container registry
83
+ # (e.g. if docker compose needs to pull an image)
84
+ # @deprecated Please use {Dev::Aws::Login#registry_login!} instead
85
+ def docker_login!(registry_id: Dev::Aws::Account.new.registry, region: Dev::Aws::DEFAULT_REGION)
86
+ warn '[DEPRECATION] `Dev::Aws::Login#docker_login!` is deprecated. Please use `Dev::Aws::Login#registry_login!` instead.'
87
+ docker_cli_login!(registry: "#{registry_id}.dkr.ecr.#{region}.amazonaws.com", region: region)
88
+ end
89
+
90
+ # Authroizes the docker cli to pull/push images from the Aws container registry
91
+ # (e.g. if docker compose needs to pull an image)
92
+ private def docker_cli_login!(registry:, region:)
93
+ print(' Logging in to ECR in docker... ')
94
+ login_cmd = "aws --profile=#{Dev::Aws::Profile.new.current} ecr --region=#{region} get-login-password"
95
+ login_cmd << ' | '
96
+ login_cmd << "docker login --password-stdin --username AWS #{registry}"
97
+ Dev::Common.new.run_command([login_cmd])
98
+ puts
99
+ end
100
+
101
+ # Authroizes the docker ruby library to pull/push images from the Aws container registry
102
+ # @deprecated Please use {Dev::Aws::Login#registry_login!} instead
103
+ def ecr_login!(registry_id: Dev::Aws::Account.new.registry, region: Dev::Aws::DEFAULT_REGION)
104
+ warn '[DEPRECATION] `Dev::Aws::Login#ecr_login!` is deprecated. Please use `Dev::Aws::Login#registry_login!` instead.'
105
+ docker_lib_login!(registry_id: registry_id, region: region)
106
+ end
107
+
108
+ # Authroizes the docker ruby library to pull/push images from the Aws container registry
109
+ private def docker_lib_login!(registry_id:, region:)
110
+ # Grab your authentication token from AWS ECR
111
+ ecr_client = ::Aws::ECR::Client.new(region: region)
112
+ tokens = ecr_client.get_authorization_token(registry_ids: Array(registry_id)).authorization_data
113
+ tokens.each do |token|
114
+ # Remove the https:// to authenticate
115
+ repo_url = token.proxy_endpoint.gsub('https://', '')
116
+
117
+ # Authorization token is given as username:password, split it out
118
+ user_pass_token = Base64.decode64(token.authorization_token).split(':')
119
+
120
+ # Call the authenticate method with the options
121
+ ::Docker.authenticate!(
122
+ username: user_pass_token.first,
123
+ password: user_pass_token.last,
124
+ email: 'none',
125
+ serveraddress: repo_url
126
+ )
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,32 @@
1
+ require 'aws-sdk-ssm'
2
+
3
+ module Dev
4
+ class Aws
5
+ # Class containing methods for get/put ssm parameters in Aws
6
+ class Parameter
7
+ attr_accessor :client
8
+
9
+ def initialize
10
+ @client = nil
11
+ end
12
+
13
+ # Create/set a new client if none is present
14
+ # Return the client
15
+ def client
16
+ @client ||= ::Aws::SSM::Client.new
17
+ end
18
+
19
+ # Get the value of the given parameter name
20
+ def get_value(name, with_decryption: true)
21
+ get(name, with_decryption: with_decryption)&.value
22
+ end
23
+
24
+ # Retrieve the ssm parameter object with the given name
25
+ def get(name, with_decryption: true)
26
+ client.get_parameter(name: name, with_decryption: with_decryption)&.parameter
27
+ rescue ::Aws::SSM::Errors::ParameterNotFound
28
+ raise "parameter #{name} does not exist in #{Dev::Aws::Profile.new.current}"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,55 @@
1
+ # Load any existing profile if we haven't set one in the environment
2
+ require 'dotenv'
3
+
4
+ module Dev
5
+ class Aws
6
+ # Class containing methods to help write/maintain aws profile information
7
+ class Profile
8
+ # The filename where we store the local profile information
9
+ CONFIG_FILE = "#{Dir.home}/.env.profile".freeze
10
+
11
+ # The name of the profile identifier we use
12
+ IDENTIFIER = 'AWS_PROFILE'.freeze
13
+
14
+ # Always load the env profile
15
+ Dotenv.load(CONFIG_FILE) if File.exist?(CONFIG_FILE)
16
+
17
+ # Retrieve the current profile value
18
+ # Returns nil if one has not been configured
19
+ def current
20
+ ENV.fetch(IDENTIFIER, nil)
21
+ end
22
+
23
+ # Write the new profile value to the env file
24
+ def write!(profile)
25
+ override = Dev::Env.new(CONFIG_FILE)
26
+ override.set(IDENTIFIER, profile)
27
+ override.write
28
+
29
+ # Update any existing ENV variables
30
+ Dotenv.overload(CONFIG_FILE)
31
+ end
32
+
33
+ # Print the profile info for the current account
34
+ def info
35
+ Dev::Aws::Credentials.new.export!
36
+ puts
37
+ puts " Currently logged in to the #{Dev::Aws::Account.new.name_by_account(current)} (#{current})".light_yellow
38
+ puts
39
+ puts ' To use this profile in your local aws cli, you must either pass the profile as a command line argument ' \
40
+ 'or export the corresponding aws variable:'.light_white
41
+ puts " aws --profile=#{current} s3 ls"
42
+ puts ' -OR-'.light_white
43
+ puts " export #{IDENTIFIER}=#{current}"
44
+ puts ' aws s3 ls'
45
+ puts
46
+ puts ' To use temporary credentials in your terminal, run the following:'.light_white
47
+ puts " export AWS_DEFAULT_REGION=#{ENV.fetch('AWS_DEFAULT_REGION', nil)}"
48
+ puts " export AWS_ACCESS_KEY_ID=#{ENV.fetch('AWS_ACCESS_KEY_ID', nil)}"
49
+ puts " export AWS_SECRET_ACCESS_KEY=#{ENV.fetch('AWS_SECRET_ACCESS_KEY', nil)}"
50
+ puts " export AWS_SESSION_TOKEN=#{ENV.fetch('AWS_SESSION_TOKEN', nil)}"
51
+ puts
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,42 @@
1
+ require 'aws-sdk-s3'
2
+
3
+ module Dev
4
+ class Aws
5
+ # Class for performing S3 functions
6
+ class S3
7
+ attr_reader :client
8
+
9
+ def initialize
10
+ @client = nil
11
+ end
12
+
13
+ # Create/set a new client if none is present
14
+ # Return the client
15
+ def client
16
+ @client ||= ::Aws::S3::Client.new
17
+ end
18
+
19
+ # Puts the local filename into the given bucket named as the given key
20
+ # Optionally specify an acl. defaults to 'private'
21
+ # Returns the URL of the uploaded file
22
+ def put(bucket:, key:, filename:, acl: 'private')
23
+ begin
24
+ File.open(filename, 'rb') do |file|
25
+ client.put_object(bucket: bucket, key: key, body: file, acl: acl)
26
+ end
27
+ rescue => e
28
+ raise "s3 file upload failed: #{e.message}"
29
+ end
30
+
31
+ url = "https://#{bucket}.s3.#{Dev::Aws::Credentials.new.logged_in_region}.amazonaws.com/#{key}"
32
+ LOG.info "Uploaded #{url}"
33
+ url
34
+ end
35
+
36
+ # Finds the specific name of the appropriate "cf-templates-" bucket to use to upload cloudformation templates to
37
+ def cf_bucket
38
+ client.list_buckets.buckets.find { |bucket| bucket.name.match(/^cf-templates-.*-#{Dev::Aws::Credentials.new.logged_in_region}/) }
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,10 @@
1
+ module Dev
2
+ # Class contains base aws constants
3
+ class Aws
4
+ # The config dir for the user's AWS settings
5
+ CONFIG_DIR = "#{Dir.home}/.aws".freeze
6
+
7
+ # The default region used if none have been configured in the AWS settings
8
+ DEFAULT_REGION = 'us-east-1'.freeze
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ # Core Object class for ruby
2
+ class Object
3
+ # Returns "true" if the object is a boolean
4
+ def boolean?
5
+ is_a?(TrueClass) || is_a?(FalseClass)
6
+ end
7
+ end
@@ -0,0 +1,112 @@
1
+ require 'colorize'
2
+
3
+ module Dev
4
+ # Class contains several common useful development methods
5
+ class Common
6
+ # Runs a command in a subshell.
7
+ # By default, the subshell is connected to the stdin/stdout/stderr of the current program
8
+ # By default, the current environment is passed to the subshell
9
+ # You can capture the output of the command by setting capture to true
10
+ def run_command(command, stdin: $stdin, stdout: $stdout, stderr: $stderr, env: ENV, capture: false)
11
+ command = Array(command)
12
+ output = nil
13
+
14
+ # If capture was specified, write stdout to a pipe so we can return it
15
+ stdoutread, stdout = ::IO.pipe if capture
16
+
17
+ # Spawn a subprocess to run the command
18
+ pid = ::Process.spawn(env, *command, in: stdin, out: stdout, err: stderr)
19
+
20
+ # Wait for the subprocess to finish and capture the result
21
+ _, result = ::Process.wait2(pid)
22
+
23
+ # If capture was specified, close the write pipe, read the output from the read pipe, close the read pipe, and return the output
24
+ if capture
25
+ stdout.close
26
+ output = stdoutread.readlines.join
27
+ stdoutread.close
28
+ end
29
+
30
+ # If the exitstatus was non-zero, exit with an error
31
+ unless result.exitstatus.zero?
32
+ puts output if capture
33
+ LOG.error "#{result.exitstatus} exit status while running [ #{command.join(' ')} ]\n".red
34
+ exit result.exitstatus
35
+ end
36
+
37
+ output
38
+ end
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
+ def with_confirmation(message, default = 'y')
44
+ print "\n #{message}? ".light_green
45
+ print '('.light_green << 'y'.light_yellow << '/'.light_green << 'n'.light_yellow << ') '.light_green
46
+
47
+ answer = default
48
+ answer = $stdin.gets unless ENV['NON_INTERACTIVE'] == 'true'
49
+
50
+ unless answer.strip.casecmp('y').zero?
51
+ puts "\n Cancelled.\n".light_yellow
52
+ exit 1
53
+ end
54
+ puts
55
+
56
+ yield
57
+ end
58
+
59
+ # Asks for user input using the given message and returns it
60
+ # If a default was specified and the user doesn't give any input, the default will be returned
61
+ def ask(message, default = nil)
62
+ msg = " #{message}"
63
+ msg << " [#{default}]" if default
64
+ msg << ': '
65
+ print msg
66
+ answer = $stdin.gets.to_s.strip
67
+ return default if default && answer == ''
68
+
69
+ answer
70
+ end
71
+
72
+ # This method breaks up a string by spaces, however if it finds quoted strings in it,
73
+ # it attempts to preserve those as a single element
74
+ # e.g. "foo 'bin baz' bar" => [foo, 'bin baz', bar]
75
+ def tokenize(str)
76
+ str.split(/\s(?=(?:[^'"]|'[^']*'|"[^"]*")*$)/)
77
+ .reject(&:empty?)
78
+ .map { |s| s.gsub(/(^ +)|( +$)|(^["']+)|(["']+$)/, '') }
79
+ end
80
+
81
+ # Checks if CODEBUILD_INITIATOR or INITIATOR env variable are set
82
+ # If they are not set, it assumes it is not running in codebuild and return false
83
+ # Otherwise it returns true
84
+ def running_codebuild?
85
+ return false if ENV['CODEBUILD_INITIATOR'].to_s.strip.empty? && ENV['INITIATOR'].to_s.strip.empty?
86
+
87
+ true
88
+ end
89
+
90
+ # Remove all leading non '{' characters
91
+ # Remove all trailing non '}' characters
92
+ def strip_non_json(str)
93
+ str.sub(/\A[^{]*{/m, '{').sub(/}[^}]*\z/m, '}')
94
+ end
95
+
96
+ # Takes two versions and attempts to compare them
97
+ # Returns true if the actual_version is greater than the required version (false otherwise)
98
+ def version_greater_than(required_version, actual_version)
99
+ required_version = required_version.to_s.split('.')
100
+ actual_version = actual_version.to_s.split('.')
101
+
102
+ required_version.each_with_index do |required, index|
103
+ required = required.to_i
104
+ actual = actual_version[index].to_i
105
+ return true if actual > required
106
+ next if actual == required
107
+
108
+ return false
109
+ end
110
+ end
111
+ end
112
+ end