prima-twig 1.1.0

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