prima-twig 1.1.0

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