firespring_dev_commands 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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