prima-twig 1.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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)