prima-twig 1.0.23

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require_relative '../lib/prima_twig.rb'
5
+ require 'colorize'
6
+ require 'highline/import'
7
+ require 'launchy'
8
+
9
+
10
+ def help_content
11
+ <<-HELP
12
+
13
+ twig-circle
14
+ ===========
15
+
16
+ Opens circleci contextual to the current branch
17
+
18
+ Synopsis
19
+ --------
20
+
21
+ twig circle [-b|--branch <branch>]
22
+
23
+ Description
24
+ -----------
25
+
26
+ Opens circleci contextual to the current branch
27
+
28
+ Subcommand for Twig: <http://rondevera.github.io/twig/>
29
+ Author: Matteo Giachino <https://github.com/matteosister>
30
+
31
+ HELP
32
+ end
33
+
34
+ args = ARGV.dup
35
+
36
+ if args.include?('--help')
37
+ puts help_content
38
+ exit
39
+ end
40
+
41
+ class Circle
42
+ def initialize
43
+ @prima = Prima.new
44
+ end
45
+
46
+ def execute!
47
+ Launchy.open("https://circleci.com/gh/#{ @prima.repo_name }/tree/#{ @prima.current_branch_name }")
48
+ end
49
+ end
50
+
51
+ Circle.new.execute!
@@ -0,0 +1,248 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require_relative '../lib/prima_twig.rb'
5
+ require_relative '../lib/prima_aws_client.rb'
6
+ require 'colorize'
7
+ require 'highline/import'
8
+ require 'aws-sdk-batch'
9
+ require 'aws-sdk-cloudformation'
10
+ require 'aws-sdk-ecs'
11
+ require 'aws-sdk-s3'
12
+ require 'redcarpet'
13
+ require 'mail'
14
+ require 'erb'
15
+ require 'base64'
16
+ require 'rubyflare'
17
+ require 'pp'
18
+
19
+ def help_content
20
+ <<-HELP
21
+
22
+ twig-deploy
23
+ ===========
24
+
25
+ Deploys prima in production
26
+
27
+ Synopsis
28
+ --------
29
+
30
+ twig deploy
31
+
32
+ Description
33
+ -----------
34
+
35
+ It will ask you which artifact you want to deploy, run bin/deploy script and then send an email to the team when it's done.
36
+
37
+ Subcommand for Twig: <http://rondevera.github.io/twig/>
38
+ Author: Andrea Usuelli <https://github.com/andreausu>
39
+
40
+ HELP
41
+ end
42
+
43
+ args = ARGV.dup
44
+
45
+ if args.include?('--help')
46
+ puts help_content
47
+ exit
48
+ end
49
+
50
+ class Review
51
+ include Command
52
+ include PrimaAwsClient
53
+
54
+ def initialize
55
+ @prima = Prima.new
56
+ output "Controllo se ci sono aggiornamenti da fare..."
57
+ exec "gem update prima-twig && twig deploy #{ARGV.join ' '}" unless `gem outdated`.lines.grep(/^prima-twig \(.*\)/).empty?
58
+ @cf = Aws::CloudFormation::Client.new
59
+ @ecs = Aws::ECS::Client.new
60
+ @s3 = Aws::S3::Client.new
61
+ @batch = Aws::Batch::Client.new
62
+ @s3_bucket = "prima-artifacts-encrypted"
63
+ end
64
+
65
+ def execute! args
66
+ unless args.empty?
67
+ case args[0]
68
+ when "parameters"
69
+ reload_parameters!
70
+ else
71
+ stop_if true, [:wrong_args, ['parameters']]
72
+ end
73
+ else
74
+ deploy_revision!
75
+ end
76
+ end
77
+
78
+ def deploy_revision!
79
+ stop_if @prima.head_detached?, :detached_head
80
+ stop_if @prima.repo_has_modified_files?, "Non posso deployare con file non commitati"
81
+
82
+ output "Recupero degli artifacts in corso, attendi qualche secondo...".yellow
83
+ artifacts = get_artifacts[0..49]
84
+
85
+ artifact_rev = choose do |menu|
86
+ menu.prompt = 'Scegli la release da deployare: '.cyan
87
+ menu.shell = true
88
+
89
+ artifacts.each do |v|
90
+ title = @prima.reduce_size(v[:branch], 100)
91
+ commit_msg = @prima.reduce_size(v[:commit_msg], 250)
92
+ msg = "#{title} #{v[:created_at].strftime('%d/%m/%Y %H:%M:%S')} #{v[:rev]} #{commit_msg}".light_blue
93
+ menu.choice(msg) {v[:rev]}
94
+ end
95
+ end
96
+
97
+ user = `git config user.name`.delete "\n"
98
+ artifact = artifacts.select {|v| v[:rev] == artifact_rev}.first
99
+
100
+ do_deploy! artifact_rev
101
+
102
+ mail = Mail.new do
103
+ from 'deploy@prima.it'
104
+ to 'deploy@prima.it'
105
+ subject "#{user} ha effettuato il deploy della revision #{artifact[:rev]}"
106
+ end
107
+
108
+ commit_msg = clean_commit_message(artifact[:commit_msg])
109
+
110
+ body = "## Deploy in produzione effettuato con successo\n\n"
111
+ body << "Data: #{Time.now.strftime('%d/%m/%Y %H:%M:%S')}\n\n"
112
+ body << "Utente: #{user}\n\n"
113
+ body << "Revision: [#{artifact[:rev]}](https://github.com/primait/prima/commit/#{artifact[:rev]}) del #{artifact[:created_at].strftime('%d/%m/%Y %H:%M:%S')}\n\n"
114
+ body << "Branch: [#{artifact[:branch]}](https://github.com/primait/prima/tree/#{artifact[:branch]})\n\n"
115
+ body << "Commit: #{commit_msg.gsub(/_/, '\_')}\n\n"
116
+
117
+ htmlBody = Redcarpet::Markdown.new(Redcarpet::Render::HTML.new).render body
118
+
119
+ html_part = Mail::Part.new do
120
+ content_type 'text/html; charset=UTF-8'
121
+ body htmlBody
122
+ end
123
+ text_part = Mail::Part.new do
124
+ body htmlBody.gsub(/<br\s?\/?>/, "\r\n").gsub(/<\/?[^>]*>/, '')
125
+ end
126
+
127
+ mail.html_part = html_part
128
+ mail.text_part = text_part
129
+
130
+ opts = {address: 'email-smtp.eu-west-1.amazonaws.com', port: '587'}
131
+ opts[:user_name] = @prima.config['aws_username']
132
+ opts[:password] = @prima.config['aws_password']
133
+
134
+ exec_step "git checkout master"
135
+
136
+ mail.delivery_method(:smtp, opts)
137
+ mail.deliver
138
+
139
+ invalidate_prismic_cache
140
+
141
+ launch_crawler
142
+
143
+ exec_step "terminal-notifier -message 'Deploy terminato'" if which 'terminal-notifier'
144
+ end
145
+
146
+ def invalidate_prismic_cache
147
+ [
148
+ "guarantee",
149
+ "glossary",
150
+ "guide",
151
+ "faq"
152
+ ].each do |page|
153
+
154
+ exec_step "curl -X POST -H \"Content-Type: application/json\" https://www.prima.it/api/cms/update/#{page}?apikey=#{@prima.config['prima_apikey']}"
155
+ end
156
+ end
157
+
158
+ def reload_parameters!
159
+ artifact_rev = ''
160
+ resp = @cf.describe_stacks({
161
+ stack_name: "ecs-task-web-vpc-production"
162
+ })
163
+ resp.stacks[0].parameters.each do |param|
164
+ if param.parameter_key == 'ReleaseVersion'
165
+ artifact_rev = param.parameter_value
166
+ break
167
+ end
168
+ end
169
+
170
+ do_deploy!(artifact_rev, true)
171
+
172
+ output "\nFinito di aggiornare i parameters.yml\n".green
173
+ end
174
+
175
+ def do_deploy!(artifact_rev, reload_parameters=false)
176
+ deploy_command = "bin/deploy #{artifact_rev}"
177
+ deploy_command << " reloadparameters" if reload_parameters
178
+
179
+ output "Il comando per il deploy sara': #{deploy_command}".yellow
180
+ confirm_message = "Sei sicuro di voler effettuare "
181
+ reload_parameters ? (confirm_message << "il reload dei parameters ") : (confirm_message << " il deploy ")
182
+ confirm_message << "in produzione?"
183
+
184
+ exit unless @prima.yesno confirm_message.blue
185
+
186
+ exec_step "git fetch"
187
+ exec_step "git checkout #{artifact_rev}"
188
+ exec_step deploy_command
189
+
190
+ stack_name_web = 'ecs-task-web-vpc-production'
191
+ stack_name_consumer = 'ecs-task-consumer-vpc-production'
192
+ stack_name_cron = 'ecs-task-consumer-vpc-production'
193
+ stack_name_job = 'batch-job-php-production'
194
+ wait_for_stack_ready(stack_name_web) unless stack_ready?(stack_name_web)
195
+ wait_for_stack_ready(stack_name_consumer) unless stack_ready?(stack_name_consumer)
196
+ wait_for_stack_ready(stack_name_cron) unless stack_ready?(stack_name_cron)
197
+ wait_for_stack_ready(stack_name_job) unless stack_ready?(stack_name_job)
198
+ end
199
+
200
+ def get_artifacts
201
+ artifacts = []
202
+ resp = @s3.list_objects(bucket: @s3_bucket, prefix: 'prima')
203
+ resp.contents.each do |l|
204
+ # aggiungiamo solo gli artefatti prodotti a partire dal branch master, riconosciuti tramite i metadata
205
+ rev = l.key.match(/^prima\/(\w{15}).tar.gz$/).captures.first if l.key.match(/^prima\/(\w{15}).tar.gz$/)
206
+ if rev
207
+ object = @s3.head_object(bucket: @s3_bucket, key: l.key)
208
+ commit_msg = ''
209
+ commit_msg = Base64.decode64(object.metadata['commit_msg']).strip if object.metadata.has_key? 'commit_msg'
210
+ artifacts << {rev: rev, created_at: object.last_modified, branch: object.metadata['branch'], commit_msg: commit_msg } if (object.metadata.has_key? 'branch') && (object.metadata['branch'] == 'master')
211
+ end
212
+ end
213
+ artifacts.sort_by { |v| v[:created_at] }.reverse
214
+ end
215
+
216
+ def launch_crawler()
217
+ resp = describe_stack_resource('batch-job-crawler-production', 'JobDefinition')
218
+
219
+ @batch.submit_job({
220
+ job_name: "crawler", # required
221
+ job_queue: "tools-production", # required
222
+ job_definition: resp.stack_resource_detail.physical_resource_id # required
223
+ })
224
+
225
+ output "Crawler lanciato con successo!\n".green
226
+ end
227
+
228
+ end
229
+
230
+ def clean_commit_message(commit_msg)
231
+ commit_msg.gsub! /Merge pull request /i, ''
232
+ commit_msg.gsub! /from primait\/feature\//i, ''
233
+ commit_msg = commit_msg[/.+?\(#\d+\)/m] unless commit_msg[/.+?\(#\d+\)/m].nil? # rimuove tutto quello che sta dopo la fine del numero di issue
234
+ commit_msg[0..99]
235
+ end
236
+
237
+ def which(cmd)
238
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
239
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
240
+ exts.each { |ext|
241
+ exe = File.join(path, "#{cmd}#{ext}")
242
+ return exe if File.executable?(exe) && !File.directory?(exe)
243
+ }
244
+ end
245
+ return nil
246
+ end
247
+
248
+ Review.new.execute! args
@@ -0,0 +1,832 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require_relative '../lib/prima_twig.rb'
5
+ require_relative '../lib/prima_aws_client.rb'
6
+ require 'digest'
7
+ require 'json'
8
+ require 'launchy'
9
+ require 'pp'
10
+ require 'redis'
11
+
12
+ class Release
13
+ include Command
14
+ include PrimaAwsClient
15
+
16
+ def initialize(update_gem=true)
17
+ @prima = Prima.new
18
+ if update_gem
19
+ output 'Controllo se ci sono aggiornamenti da fare (potrebbe richiedere qualche minuto)'
20
+ unless `gem update prima-twig`=="Updating installed gems\nNothing to update\n"
21
+ output 'Gemma prima-twig aggiornata'
22
+ exec "twig feature #{ARGV.join ' '}"
23
+ end
24
+ end
25
+ @dns_record_identifier = nil
26
+ @ecs_cluster_name = nil
27
+ @deploy_update = false
28
+ @projects = {
29
+ 'prima' => {},
30
+ 'urania' => {},
31
+ 'ermes' => {},
32
+ 'bburago' => {},
33
+ 'hal9000' => {},
34
+ 'fidaty' => {},
35
+ 'peano' => {},
36
+ # 'rogoreport' => {},
37
+ 'assange' => {},
38
+ 'borat' => {},
39
+ 'crash' => {},
40
+ 'activia' => {},
41
+ 'skynet' => {},
42
+ 'roger' => {},
43
+ 'rachele' => {},
44
+ 'leftorium' => {},
45
+ 'pyxis-npm' => {},
46
+ 'starsky' => {},
47
+ 'hutch' => {},
48
+ 'maia' => {}
49
+ }
50
+ @base_stack_name_alb = 'ecs-alb-http-public-qa-'
51
+ @base_stack_name_alb_ws = 'ecs-alb-ws-public-qa-'
52
+ @git_branch = ''
53
+ @cloudflare = Rubyflare.connect_with(ENV['CLOUDFLARE_EMAIL'], ENV['CLOUDFLARE_APIKEY'])
54
+ end
55
+
56
+ def execute!(args)
57
+ case args[0]
58
+ when 'start'
59
+ start_feature!
60
+ when 'finish'
61
+ finish_feature!
62
+ when 'qainit'
63
+ abort('Non sei nella cartella di qainit') unless Dir.pwd.match 'qainit$' or Dir.pwd.match '/drone/src'
64
+ if ['terminate', 'stop', 'shutdown', 'halt', 'destroy'].include? args[1]
65
+ qainit_deploy_shutdown!
66
+ elsif 'update' == args[1]
67
+ qainit_deploy_update!
68
+ else
69
+ if args[1]
70
+ select_branches(args[1..-1])
71
+ else
72
+ select_branches
73
+ end
74
+ qainit_deploy!
75
+ end
76
+ when 'suite'
77
+ abort('Non sei nella cartella di qainit') unless Dir.pwd.match 'qainit$'
78
+ if 'deploy' == args[1]
79
+ suite_py_branches(args[2])
80
+ qainit_deploy!(true)
81
+ end
82
+ when 'deploy'
83
+ abort('Non sei nella cartella di artemide') unless Dir.pwd.match 'artemide$'
84
+ if 'lock' == args[1]
85
+ deploy_lock!
86
+ end
87
+ when 'aggregator'
88
+ if 'enable' == args[1]
89
+ aggregator_enable!
90
+ elsif 'disable' == args[1]
91
+ aggregator_disable!
92
+ else
93
+ stop_for_wrong_args
94
+ end
95
+ else
96
+ stop_for_wrong_args
97
+ end
98
+ end
99
+
100
+ def stop_for_wrong_args
101
+ puts help_content
102
+ stop_if true, [:wrong_args, ['start', 'finish', 'deploy', 'deploy project_name', 'deploy stop', 'deploy update', 'aggregator enable', 'aggregator disable']]
103
+ end
104
+
105
+ def start_feature!
106
+ branch_name = @prima.clean_branch_name(ask('Inserisci il nome del branch (puoi omettere feature/): '.cyan))
107
+ stop_unless !branch_name.empty?, 'Devi inserire il nome del branch'
108
+ branch_name.prepend 'feature/' unless branch_name.include? 'feature'
109
+
110
+ output "Il nome del branch sarà " + branch_name.yellow
111
+
112
+ exec_step "git checkout master"
113
+ exec_step "git pull origin master"
114
+ exec_step "git checkout -b " + branch_name
115
+ end
116
+
117
+ def aggregator_disable!
118
+ output 'Disable aggregator'
119
+
120
+ output "Recupero le informazioni relative al puntamento dei record DNS..."
121
+ output "Recupero le informazioni sui QA attivi..."
122
+ stack_list, envs = get_stacks()
123
+
124
+ env_hash = nil
125
+ unless envs.empty?
126
+ env_hash = envs.detect do |key, tags|
127
+ aggregator_enabled = tags.detect do |tag|
128
+ tag.key === "hostname_pattern_priority" and tag.value === "1"
129
+ end.is_a?(Aws::CloudFormation::Types::Tag)
130
+ aggregator_enabled
131
+ end[0]
132
+ dns_records = @cloudflare.get("zones/1fb634f19c43dfb0162cc4cb91915da2/dns_records", {per_page: 100, type: 'CNAME', content: get_alb_host(@base_stack_name_alb + env_hash[3..8])})
133
+ stop_if dns_records.body[:result].empty?, "I record DNS degli aggregatori non stanno puntando ad un QA".red
134
+ change_hostname_priority(env_hash, hostname_pattern_priority())
135
+ dns_to_staging(env_hash)
136
+ else
137
+ output 'Nessun QA trovato'.red
138
+ exit
139
+ end
140
+
141
+ output 'Finito!'.green
142
+ end
143
+
144
+ def aggregator_enable!
145
+ output 'Enable aggregator'
146
+
147
+ output 'Recupero le informazioni relative al puntamento dei record DNS...'
148
+ dns_records = @cloudflare.get('zones/1fb634f19c43dfb0162cc4cb91915da2/dns_records', { per_page: 100, type: 'CNAME', content: 'staging.prima.it' })
149
+ stop_if dns_records.body[:result].empty?, "I record DNS degli aggregatori stanno gia' puntando ad un QA".red
150
+
151
+ output "Recupero le informazioni sui QA attivi..."
152
+ stack_list, envs = get_stacks()
153
+
154
+ env_hash = nil
155
+ unless envs.empty?
156
+ env_hash = choose do |menu|
157
+ menu.prompt = "Scegli il QA al quale vuoi far puntare gli ambienti di staging dei comparatori: ".cyan
158
+ menu.shell = true
159
+ envs.each do |key, env|
160
+ title = ""
161
+ env.each do |e|
162
+ title << "\n#{e.key.upcase}: #{e.value}"
163
+ end
164
+ msg = "#{@prima.reduce_size(title, 1000)}".light_blue
165
+ menu.choice(msg) { key }
166
+ end
167
+ end
168
+ else
169
+ output "Nessun QA trovato".red
170
+ exit
171
+ end
172
+
173
+ change_hostname_priority(env_hash, "1")
174
+
175
+ dns_records.body[:result].each do |dns|
176
+ if dns[:name] =~ /^\w+\-\w+\-staging\.prima\.it$/
177
+ output "Changing #{dns[:name]} DNS record"
178
+ @cloudflare.put("zones/1fb634f19c43dfb0162cc4cb91915da2/dns_records/#{dns[:id]}", {type: 'CNAME', name: dns[:name], content: get_alb_host(@base_stack_name_alb + env_hash[3..8]), proxied: true, ttl: 1})
179
+ end
180
+ end
181
+
182
+ output "Finito!".green
183
+ end
184
+
185
+ def change_hostname_priority(env_hash, hostname_pattern_priority)
186
+ cluster_stack_name = "ecs-cluster-#{env_hash}"
187
+ tags = get_stack_tags(cluster_stack_name).map do |tag|
188
+ if tag.key === "hostname_pattern_priority"
189
+ {
190
+ key: "hostname_pattern_priority",
191
+ value: hostname_pattern_priority
192
+ }
193
+ else
194
+ tag
195
+ end
196
+ end
197
+
198
+ stack_list = stack_list()
199
+
200
+ stack_list.each do |stack|
201
+ if stack.stack_name.match(/#{env_hash}$/)
202
+ stack_name = stack.stack_name
203
+ update_stack(stack_name, get_stack_template(stack_name), get_stack_parameters(stack_name), tags)
204
+ end
205
+ end
206
+
207
+ stack_list.each do |stack|
208
+ if stack.stack_name.match(/#{env_hash}$/)
209
+ wait_for_stack_ready(stack.stack_name) unless stack_ready?(stack.stack_name)
210
+ end
211
+ end
212
+
213
+ stack_name_web = "ecs-task-web-#{env_hash}"
214
+ parameters = get_stack_parameters(stack_name_web).map do |param|
215
+ if param.parameter_key === "HostnamePatternPriority"
216
+ {
217
+ parameter_key: "HostnamePatternPriority",
218
+ parameter_value: hostname_pattern_priority
219
+ }
220
+ elsif param.parameter_key === "HostnamePatternAggregatorPriority"
221
+ {
222
+ parameter_key: "HostnamePatternAggregatorPriority",
223
+ parameter_value: (hostname_pattern_priority.to_i + 1).to_s
224
+ }
225
+ else
226
+ param
227
+ end
228
+ end
229
+
230
+ update_stack(stack_name_web, get_stack_template(stack_name_web), parameters)
231
+
232
+ wait_for_stack_ready(stack_name_web) unless stack_ready?(stack_name_web)
233
+ end
234
+
235
+ def dns_to_staging(env_hash)
236
+ output "Recupero le informazioni relative al puntamento dei record DNS..."
237
+ dns_records = @cloudflare.get("zones/1fb634f19c43dfb0162cc4cb91915da2/dns_records", {per_page: 100, type: 'CNAME', content: get_alb_host(@base_stack_name_alb + env_hash[3..8])})
238
+ dns_records.body[:result].each do |dns|
239
+ if dns[:name] =~ /^\w+\-\w+\-staging\.prima\.it$/
240
+ output "Changing #{dns[:name]} DNS record"
241
+ @cloudflare.put("zones/1fb634f19c43dfb0162cc4cb91915da2/dns_records/#{dns[:id]}", {type: 'CNAME', name: dns[:name], content: 'staging.prima.it', proxied: true, ttl: 1})
242
+ end
243
+ end
244
+ end
245
+
246
+ def finish_feature!
247
+ current_branch_name = @prima.twig.current_branch_name
248
+ stop_unless (current_branch_name =~ /^feature\//), "Non sei su un branch di feature, non posso mergiare nulla"
249
+
250
+ # Mergia la PR
251
+ pr = @prima.get_pr
252
+ stop_unless pr, 'Pull Request not found'
253
+ @prima.merge_pull_request pr
254
+
255
+ output "La Pull Request e' stata mergiata!".green
256
+
257
+ # Chiude la issue
258
+ issue_number = @prima.twig.get_branch_property(current_branch_name, 'issue')
259
+ @prima.close_issue(issue_number) if issue_number
260
+
261
+ # Rimuove il branch remoto
262
+ exec_step "git push origin :#{current_branch_name}" if @prima.yesno "Vuoi eliminare il branch remoto #{current_branch_name}?".blue
263
+ end
264
+
265
+ def deploy_shutdown!
266
+ output "Recupero le informazioni sui QA attivi..."
267
+ stack_list, envs = get_stacks
268
+
269
+ env_hash = nil
270
+ unless envs.empty?
271
+ env_hash = choose do |menu|
272
+ menu.prompt = "Scegli l'environment che vuoi spegnere: ".cyan
273
+ menu.shell = true
274
+
275
+ envs.each do |key, env|
276
+ title = ''
277
+ env.each do |e|
278
+ title << "\n#{e.key}: #{e.value} "
279
+ end
280
+ msg = @prima.reduce_size(title, 1000).to_s.light_blue
281
+ menu.choice(msg) { key }
282
+ end
283
+ end
284
+ else
285
+ output 'Nessun environment trovato'.red
286
+ exit
287
+ end
288
+
289
+ cluster_stack_name = nil
290
+ stacks_to_delete = []
291
+ stack_list.each do |stack|
292
+ if stack.stack_name.match(/#{env_hash}$/)
293
+ if stack.stack_name.match(/ecs-cluster/)
294
+ cluster_stack_name = stack.stack_name
295
+ else
296
+ break unless stack.stack_name.match(/#{env_hash}$/)
297
+ stacks_to_delete.push(stack.stack_name)
298
+ delete_stack(stack.stack_name)
299
+ end
300
+ end
301
+ end
302
+
303
+ cluster_stack_name = "ecs-cluster-#{env_hash}"
304
+ aggregator_enabled = get_stack_tags(cluster_stack_name).detect do |tag|
305
+ tag.key === "hostname_pattern_priority" and tag.value === "1"
306
+ end.is_a?(Aws::CloudFormation::Types::Tag)
307
+
308
+ if aggregator_enabled
309
+ dns_to_staging(env_hash)
310
+ end
311
+
312
+ # Se non ha finito di cancellare le altre non si puo' cancellare il cluster
313
+ output "Attendo 10 secondi per poter eliminare il cluster ECS"
314
+
315
+ while stacks_to_delete.length > 0
316
+ sleep 13
317
+ stacks_to_delete.each do |stack_name|
318
+ stacks_to_delete = stacks_to_delete - [stack_name] unless stack_exists?(stack_name)
319
+ end
320
+ output "Stack ancora attivi: #{stacks_to_delete.length.to_s}. Attendo altri 10 secondi per eliminare il cluster ECS"
321
+ end
322
+
323
+ delete_stack(cluster_stack_name)
324
+ delete_stack(@base_stack_name_alb + env_hash[3..8])
325
+ delete_stack(@base_stack_name_alb_ws + env_hash[3..8])
326
+ output "Finito!".green
327
+ end
328
+
329
+ def deploy_lock!
330
+ output "Deploy update menu"
331
+ `git pull && git submodule init && git submodule update`
332
+
333
+ @deploy_update = true
334
+
335
+ output "Recupero le informazioni sui QA attivi..."
336
+ stack_list, envs = get_clusters()
337
+
338
+ env_hash = nil
339
+ unless envs.empty?
340
+ env_hash = choose do |menu|
341
+ menu.prompt = "Scegli il QA che vuoi proteggere dallo spegnimento automatico: ".cyan
342
+ menu.shell = true
343
+ envs.each do |key, env|
344
+ title = ""
345
+ env.each do |e|
346
+ title << "#{e.value}" if e.key == 'qainit'
347
+ end
348
+ msg = "#{@prima.reduce_size(title, 1000)}".light_blue
349
+ menu.choice(msg) { key }
350
+ end
351
+ end
352
+ else
353
+ output "Nessun QA trovato".red
354
+ exit
355
+ end
356
+
357
+ cluster_stack_name = "ecs-cluster-#{env_hash}"
358
+ if stack_exists?(cluster_stack_name)
359
+ tags = get_stack_tags(cluster_stack_name)
360
+ tag_keep_data = Aws::CloudFormation::Types::Tag.new({key:'AUTOMATIC_DELETION_PROTECTION', value:'true'})
361
+ tags.push tag_keep_data
362
+ end
363
+
364
+ update_cluster_stack(cluster_stack_name, tags)
365
+
366
+ output "Finito!".green
367
+ end
368
+
369
+ def deploy_update!
370
+ output "Deploy update menu"
371
+ `git pull && git submodule init && git submodule update`
372
+
373
+ @deploy_update = true
374
+
375
+ output "Recupero le informazioni sui QA attivi..."
376
+ stack_list, envs = get_stacks()
377
+
378
+ env_hash = nil
379
+ unless envs.empty?
380
+ env_hash = choose do |menu|
381
+ menu.prompt = "Scegli il QA che vuoi aggiornare: ".cyan
382
+ menu.shell = true
383
+ envs.each do |key, env|
384
+ title = ""
385
+ env.each do |e|
386
+ title << "\n#{e.key.upcase}: #{e.value}"
387
+ end
388
+ msg = "#{@prima.reduce_size(title, 1000)}".light_blue
389
+ menu.choice(msg) { key }
390
+ end
391
+ end
392
+ else
393
+ output "Nessun QA trovato".red
394
+ exit
395
+ end
396
+
397
+ envs[env_hash].each do |env|
398
+ unless ['hostname_pattern_priority', 'AUTOMATIC_DELETION_PROTECTION'].include? env.key
399
+ @projects[env.key] = select_branch_to_deploy(env.key, env.value)
400
+ end
401
+ end
402
+ deploy_feature!
403
+
404
+ output "Finito!".green
405
+ end
406
+
407
+ def get_default_branch_name(projects)
408
+ projects.each_key do |project|
409
+ return projects[project]['name'] if not projects[project]['default_branch']
410
+ end
411
+ end
412
+
413
+ def suite_py_branches(args_json)
414
+ arg_projects = JSON.parse(args_json)
415
+
416
+ @projects.merge!(arg_projects)
417
+
418
+ @projects.each_key do |project|
419
+ if @projects[project].empty?
420
+ @projects[project] = choose_branch_to_deploy(project, true)
421
+ end
422
+ end
423
+ end
424
+
425
+ def get_git_user()
426
+ `git config user.name`.gsub(/[^A-Za-z]/, '').gsub("\n", '')
427
+ end
428
+
429
+ def get_git_mail()
430
+ `git config user.email`.gsub("\n", '')
431
+ end
432
+
433
+ def qainit_deploy!(quiet = false)
434
+ `git checkout master && git pull && git submodule update --init --recursive && git remote prune origin`
435
+
436
+ default_name = get_default_branch_name @projects
437
+ feature_number = ''
438
+ unless quiet
439
+ output "Inserisci la feature a cui si riferisce il QA: [#{default_name}]".cyan
440
+ feature_number = String(STDIN.gets.chomp)
441
+ end
442
+ feature_number = default_name if feature_number.empty?
443
+ branch_name = get_git_user + '_' + feature_number
444
+
445
+ if `git branch -l | grep #{branch_name}`.size > 0
446
+ `git checkout #{branch_name} && git pull`
447
+ else
448
+ `git checkout -b #{branch_name}`
449
+ end
450
+
451
+ @git_branch = branch_name
452
+
453
+ File.open('branch_names', 'w') { |file| file.write(JSON.generate(@projects)) }
454
+
455
+ update_drone_yml!
456
+
457
+ `git add projects && \
458
+ git add branch_names .drone.yml && \
459
+ git commit -m '#{branch_name}' && \
460
+ git push -f --set-upstream origin #{branch_name} && \
461
+ git checkout master`
462
+ end
463
+
464
+ def qainit_deploy_update!
465
+ `git checkout master && git pull`
466
+ # cancelliamo tutti i branch che non sono più sul repo remoto
467
+ `git fetch -p && for branch in \`git branch -vv | grep ': gone]' | awk '{print $1}'\`; do git branch -D $branch; done`
468
+ # leggiamo i nomi dei branch superstiti
469
+ former_branches = `git branch -a | grep remotes/ | grep -v HEAD | sed 's/ remotes\\/origin\\///g'`.split "\n"
470
+ git_user = get_git_user
471
+ # stampiamo la lista
472
+ chosen_branch = choose do |menu|
473
+ menu.prompt = "Scegli il QA che vuoi aggiornare: ".cyan
474
+ menu.shell = true
475
+ former_branches.delete('master')
476
+ former_branches.each_with_index do |branch, index|
477
+ msg = index.odd? ? branch.white : branch.light_yellow # uno bianco e uno giallo alternati
478
+ msg = branch.start_with?(git_user) ? msg.on_blue : msg.on_black # i branch creati da chi lancia l'update sono su sfondo più chiaro
479
+ menu.choice(msg) { branch }
480
+ end
481
+ end
482
+ # checkout master, checkout branch, pull branch
483
+ `git checkout master && git checkout #{chosen_branch} && git pull`
484
+
485
+ # aggiornare il commit (revision a cui fa riferimento)
486
+
487
+ # leggo il file branch_names / recupero i nomi dei branch / riscrivo tutto
488
+ projects = ''
489
+ File.open('branch_names', 'r') do |file|
490
+ file.each_line do |line|
491
+ projects = JSON.parse(line)
492
+ end
493
+ end
494
+
495
+ projects.each do |key, project|
496
+ @projects[key] = select_branch_to_deploy(key, project['name'])
497
+ @projects[key]['default_branch'] = project['default_branch']
498
+ end
499
+
500
+ File.open('branch_names', 'w') { |file| file.write(JSON.generate(@projects)) }
501
+
502
+ update_drone_yml!
503
+
504
+ `git add branch_names .drone.yml`
505
+ `git commit -m 'update'`
506
+ `git push && git checkout master`
507
+ end
508
+
509
+ def qainit_deploy_shutdown!(selection = nil)
510
+ `git checkout master && git pull && git remote prune origin`
511
+ # leggiamo i nomi dei branch
512
+ former_branches = `git branch -a | grep remotes/ | grep -v HEAD | sed 's/ remotes\\/origin\\///g'`.split "\n"
513
+ if selection.nil?
514
+ # stampiamo la lista
515
+ chosen_branch = choose do |menu|
516
+ menu.prompt = "Scegli il QA che vuoi spegnere: ".cyan
517
+ menu.shell = true
518
+ git_user = get_git_user
519
+ former_branches.delete('master')
520
+ former_branches.each_with_index do |branch, index|
521
+ msg = index.odd? ? branch.white : branch.light_yellow # uno bianco e uno giallo alternati
522
+ msg = branch.start_with?(git_user) ? msg.on_blue : msg.on_black # i branch creati da chi lancia l'update sono su sfondo blu
523
+ menu.choice(msg) { branch }
524
+ end
525
+ end
526
+ else
527
+ chosen_branch = selection
528
+ end
529
+ # checkout master, checkout branch, pull branch, push sul branch con commit vuoto
530
+ `git checkout master && git checkout #{chosen_branch} && git pull`
531
+ `git commit --allow-empty -m 'shutdown' && git push && git checkout master`
532
+ end
533
+
534
+ def qainit_drone_shutdown!
535
+ output "Recupero le informazioni sui QA attivi..."
536
+ stack_list, envs = get_stacks
537
+
538
+ env_hash = "qa-" + get_deploy_id
539
+
540
+ cluster_stack_name = nil
541
+ stacks_to_delete = []
542
+ stack_list.each do |stack|
543
+ if stack.stack_name.match(/#{env_hash}$/)
544
+ if stack.stack_name.match(/ecs-cluster/)
545
+ cluster_stack_name = stack.stack_name
546
+ else
547
+ break unless stack.stack_name.match(/#{env_hash}$/)
548
+ stacks_to_delete.push(stack.stack_name)
549
+ delete_stack(stack.stack_name)
550
+ end
551
+ end
552
+ end
553
+
554
+ cluster_stack_name = "ecs-cluster-#{env_hash}"
555
+ if stack_exists?(cluster_stack_name)
556
+ aggregator_enabled = get_stack_tags(cluster_stack_name).detect do |tag|
557
+ tag.key === "hostname_pattern_priority" and tag.value === "1"
558
+ end.is_a?(Aws::CloudFormation::Types::Tag)
559
+
560
+ if aggregator_enabled
561
+ dns_to_staging(env_hash)
562
+ end
563
+ end
564
+
565
+ # Se non ha finito di cancellare le altre non si puo' cancellare il cluster
566
+ output "Attendo 10 secondi per poter eliminare il cluster ECS"
567
+
568
+ while stacks_to_delete.length > 0
569
+ sleep 13
570
+ stacks_to_delete.each do |stack_name|
571
+ stacks_to_delete = stacks_to_delete - [stack_name] unless stack_exists?(stack_name)
572
+ end
573
+ output "Stack ancora attivi: #{stacks_to_delete.length.to_s}. Attendo altri 10 secondi per eliminare il cluster ECS"
574
+ end
575
+
576
+ delete_stack(cluster_stack_name) if stack_exists?(cluster_stack_name)
577
+ delete_stack(@base_stack_name_alb + env_hash[3..8]) if stack_exists?(@base_stack_name_alb + env_hash[3..8])
578
+ delete_stack(@base_stack_name_alb_ws + env_hash[3..8]) if stack_exists?(@base_stack_name_alb_ws + env_hash[3..8])
579
+ `git checkout master && git push origin --delete ${DRONE_BRANCH}`
580
+ output "Cancello il record DNS utilizzato da Lighthouse"
581
+ delete_lighthouse_dns()
582
+ output "Finito!".green
583
+ end
584
+
585
+ def qainit_write_output(file_message, output_message)
586
+ `mkdir -p /etc/qainit-output`
587
+ qa_file_name = "/etc/qainit-output/url_qa"
588
+ File.open(qa_file_name + '.txt', 'w') { |file| file.write(file_message) }
589
+ output "#{output_message} #{qa_file_name}".green
590
+ end
591
+
592
+ def update_drone_yml!()
593
+ drone_yml = File.read('.drone.yml')
594
+ @projects.each do |key, project|
595
+ drone_yml = drone_yml.gsub(/#{key}@.+\n/, "#{key}@#{project['revision']}\n")
596
+ end
597
+ File.open(".drone.yml", "w") do |f|
598
+ f.write(drone_yml)
599
+ end
600
+ end
601
+
602
+ def get_deploy_id
603
+ if @deploy_id
604
+ @deploy_id
605
+ else
606
+ @deploy_id = Digest::MD5.hexdigest(ENV['DRONE_BRANCH'])
607
+ @deploy_id
608
+ end
609
+ end
610
+
611
+ def get_alb_host(stack_name)
612
+ case
613
+ when stack_name.include?('web')
614
+ logical_resource_id = 'EcsApplicationLoadBalancerPublic'
615
+ when stack_name.include?('urania')
616
+ logical_resource_id = 'EcsApplicationLoadBalancerInternal'
617
+ when stack_name.include?('backoffice')
618
+ logical_resource_id = 'EcsApplicationLoadBalancerPublic'
619
+ when stack_name.include?('bburago')
620
+ logical_resource_id = 'EcsApplicationLoadBalancerInternal'
621
+ when stack_name.include?('hal9000')
622
+ logical_resource_id = 'EcsApplicationLoadBalancerInternal'
623
+ when stack_name.include?('fidaty')
624
+ logical_resource_id = 'EcsApplicationLoadBalancerInternal'
625
+ when stack_name.include?('activia')
626
+ logical_resource_id = 'EcsApplicationLoadBalancerInternal'
627
+ when stack_name.include?('skynet')
628
+ logical_resource_id = 'EcsApplicationLoadBalancerInternal'
629
+ when stack_name.include?('roger')
630
+ logical_resource_id = 'EcsApplicationLoadBalancerInternal'
631
+ when stack_name.include?('alb-http-public')
632
+ logical_resource_id = 'EcsApplicationLoadBalancerPublic'
633
+ when stack_name.include?('alb-ws-public')
634
+ logical_resource_id = 'EcsApplicationLoadBalancerPublic'
635
+ when stack_name.include?('peano')
636
+ logical_resource_id = 'EcsApplicationLoadBalancerInternal'
637
+ when stack_name.include?('leftorium')
638
+ logical_resource_id = 'EcsApplicationLoadBalancerInternal'
639
+ when stack_name.include?('assange')
640
+ logical_resource_id = 'EcsApplicationLoadBalancerPublic'
641
+ when stack_name.include?('borat')
642
+ logical_resource_id = 'EcsApplicationLoadBalancerPublic'
643
+ when stack_name.include?('crash')
644
+ logical_resource_id = 'EcsApplicationLoadBalancerPublic'
645
+ when stack_name.include?('rachele')
646
+ logical_resource_id = 'EcsApplicationLoadBalancerInternal'
647
+ when stack_name.include?('starsky')
648
+ logical_resource_id = 'EcsApplicationLoadBalancerPublic'
649
+ when stack_name.include?('hutch')
650
+ logical_resource_id = 'EcsApplicationLoadBalancerPublic'
651
+ when stack_name.include?('maia')
652
+ logical_resource_id = 'EcsApplicationLoadBalancerPublic'
653
+ end
654
+ resp = describe_stack_resource(stack_name, logical_resource_id)
655
+ resp = describe_load_balancers([resp.stack_resource_detail.physical_resource_id])
656
+ resp.load_balancers[0].dns_name
657
+ end
658
+
659
+ def deploy_pyxis?
660
+ if defined? @deploy_pyxis
661
+ @deploy_pyxis
662
+ else
663
+ pyxis_updated = `git log -p -1 --unified=0 | grep pyxis-npm:`.length > 0
664
+
665
+ update_pyxis = !@projects['pyxis-npm'].empty? && @projects['pyxis-npm']['name'] != 'master' && pyxis_updated
666
+
667
+ @deploy_pyxis = update_pyxis
668
+ return update_pyxis
669
+ end
670
+ end
671
+
672
+ def update_cluster_stack(stack_name, tags = [])
673
+ stack_body = IO.read('cloudformation/stacks/ecs-cluster.yml')
674
+ update_stack(stack_name, stack_body, [], tags)
675
+ end
676
+
677
+ def choose_branch_to_deploy(project_name, select_master = false)
678
+ Dir.chdir "projects/#{project_name}"
679
+ output "Recupero la lista dei branch del progetto #{project_name}..."
680
+ `git remote prune origin`
681
+ out = %x[ git fetch ]
682
+ branches = %x[ git for-each-ref --sort=-committerdate refs/remotes/ --format='%(refname) %(objectname) %(committeremail)' | sed 's/refs\\/remotes\\/origin\\///g' ]
683
+ .split("\n").delete_if { |b| b.include?('HEAD') }[0..49]
684
+
685
+ master_branch = nil
686
+
687
+ branches.each do |branch|
688
+ master_branch = branch if branch.match(/^master\s+/)
689
+ break unless master_branch.nil?
690
+ end
691
+
692
+ if select_master || branches.length == 1
693
+ branch_name = master_branch
694
+ else
695
+ branches.insert(0, branches.delete(master_branch))
696
+ branch_name = choose do |menu|
697
+ menu.prompt = "Scegli il branch di #{project_name} da deployare: ".cyan
698
+ menu.shell = true
699
+
700
+ git_mail = get_git_mail
701
+
702
+ branches.each_with_index do |branch, index|
703
+ title = @prima.reduce_size(branch, 100)
704
+ msg = index.odd? ? title.white : title.light_yellow # uno bianco e uno giallo alternati
705
+ msg = branch.include?(git_mail) ? msg.on_blue : msg.on_black # i branch aggiornati da chi lancia la creazione sono su sfondo blu
706
+ menu.choice(msg) { branch }
707
+ menu.default = branch if branch == master_branch
708
+ end
709
+ end
710
+ end
711
+
712
+ Dir.chdir "../../"
713
+
714
+ name = branch_name.split(' ')[0]
715
+ revision = branch_name.split(' ')[1]
716
+ committer_email = branch_name.split(' ')[2].tr('<>', '')
717
+ { 'name' => name, 'revision' => revision[0..14], 'committer' => committer_email, 'default_branch' => select_master }
718
+ end
719
+
720
+ def select_branch_to_deploy(project_name, branch_name)
721
+ Dir.chdir "projects/#{project_name}"
722
+ output "Recupero il branch #{project_name}:#{branch_name} ..."
723
+ `git remote prune origin`
724
+ out = %x[ git fetch ]
725
+ branch_name = %x[ git for-each-ref --sort=-committerdate refs/remotes/ --format='%(refname) %(objectname) %(committeremail)' | sed 's/refs\\/remotes\\/origin\\///g' ]
726
+ .split("\n").delete_if { |b| !b.match("^#{Regexp.escape(branch_name)}") }[0..49]
727
+ .first
728
+
729
+ Dir.chdir "../../"
730
+ name = branch_name.split(' ')[0]
731
+ revision = branch_name.split(' ')[1]
732
+ committer_email = branch_name.split(' ')[2].tr('<>', '')
733
+ { 'name' => name, 'revision' => revision[0..14], 'committer' => committer_email }
734
+ end
735
+
736
+ def get_stacks()
737
+ envs = {}
738
+ stack_list = stack_list()
739
+ stack_list.each do |stack|
740
+ unless stack.stack_name.match(/spotfleet-allinone-qa-(\w+)$/)
741
+ env_hash = stack.stack_name.match(/qa-(\w+)$/)[0]
742
+ envs[env_hash] = stack.tags unless envs.has_key?(env_hash) || stack.tags.empty?
743
+ end
744
+ end
745
+ return stack_list, envs
746
+ end
747
+
748
+ def get_clusters()
749
+ envs = {}
750
+ cluster_list = cluster_list()
751
+ cluster_list.each do |stack|
752
+ unless stack.stack_name.match(/spotfleet-allinone-qa-(\w+)$/)
753
+ env_hash = stack.stack_name.match(/qa-(\w+)$/)[0]
754
+ envs[env_hash] = stack.tags unless envs.has_key?(env_hash) || stack.tags.empty?
755
+ end
756
+ end
757
+ return cluster_list, envs
758
+ end
759
+
760
+ def hostname_pattern_priority()
761
+ (Time.now.to_i.to_s[-4..-1].to_i + Random.rand(40000)).to_s
762
+ end
763
+
764
+ def select_branches(project_names = nil)
765
+ output "Deploy feature menu"
766
+ if project_names.nil?
767
+ @projects.each{ |key, value| @projects[key] = choose_branch_to_deploy(key) }
768
+ else
769
+ project_names.each do |project|
770
+ @projects[project] = choose_branch_to_deploy(project)
771
+ end
772
+ @projects.each_key do |branch_project|
773
+ @projects[branch_project] = choose_branch_to_deploy(branch_project, true) unless project_names.include? branch_project
774
+ end
775
+ end
776
+ end
777
+ end
778
+
779
+ def help_content
780
+ <<-HELP
781
+
782
+ twig-feature
783
+ ===========
784
+
785
+ Manage feature branches
786
+
787
+ Synopsis
788
+ --------
789
+
790
+ twig release start
791
+ twig release finish
792
+ twig release deploy
793
+ twig release aggregator
794
+
795
+ Description
796
+ -----------
797
+
798
+ start creates a new feature branch
799
+ finish finishes the feature by merging to dev and master
800
+ qainit deploys a new environment with selected branches from every project
801
+ qainit $PROJECT_NAME deploys a new environment allowing to selected a branch from the input project (everything else is master)
802
+ qainit shutdown deletes a specific qa environment
803
+
804
+ Available only to devops (from artemide)
805
+ -----------
806
+ deploy deploys the feature branch to a temporary AWS Elastic Beanstalk env
807
+ deploy stop destroys the AWS Elastic Beanstalk env
808
+ deploy update updates a feature branch with current branches
809
+ deploy lock protects a qa environment from automatic deletion
810
+ aggregator enable/disable directs comparator's staging environments to a qa/staging
811
+
812
+ Subcommand for Twig: <http://rondevera.github.io/twig/>
813
+ Author: Andrea Usuelli <https://github.com/andreausu>
814
+
815
+ HELP
816
+ end
817
+
818
+ args = ARGV.dup
819
+
820
+ if args.include?('--help')
821
+ puts help_content
822
+ exit
823
+ end
824
+
825
+ gem_update = true
826
+ if args.include?('no-gem-update')
827
+ gem_update = false
828
+ end
829
+
830
+ args.delete('no-gem-update')
831
+
832
+ Release.new(gem_update).execute!(args)