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,105 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require_relative '../lib/prima_twig.rb'
5
+
6
+ class Hotfix
7
+ include Command
8
+
9
+ def initialize
10
+ @prima = Prima.new
11
+ end
12
+
13
+ def execute! args
14
+ possible_args = ["start", "finish"]
15
+ stop_if args.empty?, [:wrong_args, possible_args]
16
+ stop_if args.length > 1, [:wrong_args, possible_args]
17
+
18
+ stop_if @prima.head_detached?, :detached_head
19
+ stop_unless @prima.is_clean?, :clean
20
+
21
+ case args[0]
22
+ when "start"
23
+ start_hotfix!
24
+ when "finish"
25
+ merge_hotfix!
26
+ else
27
+ stop_if true, [:wrong_args, possible_args]
28
+ end
29
+ end
30
+
31
+ def start_hotfix!
32
+
33
+ branch_name = @prima.clean_branch_name(ask('Inserisci il nome del branch (puoi omettere hotfix/): '.cyan))
34
+ stop_unless branch_name.length > 0, 'Devi inserire il nome del branch'
35
+ branch_name.prepend 'hotfix/' unless branch_name.include? 'hotfix'
36
+
37
+ output "Il nome del branch sarà " + branch_name.yellow
38
+
39
+ exec_step "git checkout master"
40
+ exec_step "git pull origin master"
41
+ exec_step "git checkout -b " + branch_name
42
+ end
43
+
44
+ def merge_hotfix!
45
+ current_branch_name = @prima.twig.current_branch_name
46
+ stop_unless (current_branch_name =~ /^hotfix\//), "Non sei su un branch di hotfix, non posso mergiare nulla"
47
+
48
+ # Mergia la PR
49
+ pr = @prima.get_pr
50
+ stop_unless pr, 'Pull Request not found'
51
+ @prima.merge_pull_request pr
52
+
53
+ output "La Pull Request e' stata mergiata!".green
54
+
55
+ # Mergia il branch su dev e pusha
56
+ exec_step 'git fetch'
57
+ exec_step 'git checkout dev'
58
+ exec_step 'git merge origin/dev'
59
+ exec_step "git merge --no-ff #{current_branch_name}"
60
+
61
+ exec_step 'git push origin dev' if @prima.yesno 'Vuoi effettuare il push di dev?'.blue
62
+
63
+ # Chiude la issue
64
+ issue_number = @prima.twig.get_branch_property(current_branch_name, 'issue')
65
+ @prima.close_issue(issue_number) if issue_number
66
+
67
+ # Rimuove il branch remoto
68
+ exec_step "git push origin :#{current_branch_name}" if @prima.yesno "Vuoi eliminare il branch remoto #{current_branch_name}?".blue
69
+ end
70
+ end
71
+
72
+ def help_content
73
+ <<-HELP
74
+
75
+ twig-hotfix
76
+ ===========
77
+
78
+ Manage hotfix creation and merging
79
+
80
+ Synopsis
81
+ --------
82
+
83
+ twig hotfix start
84
+ twig hotfix close
85
+
86
+ Description
87
+ -----------
88
+
89
+ start creates an hotfix from master branch
90
+ close closes the actual hotfix by merging to dev and master
91
+
92
+ Subcommand for Twig: <http://rondevera.github.io/twig/>
93
+ Author: Matteo Giachino <https://github.com/matteosister>
94
+
95
+ HELP
96
+ end
97
+
98
+ args = ARGV.dup
99
+
100
+ if args.include?('--help')
101
+ puts help_content
102
+ exit
103
+ end
104
+
105
+ Hotfix.new.execute!(args)
@@ -0,0 +1,115 @@
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-open-pr
14
+ ============
15
+
16
+ Opens a PR on github for this branch, if not already opened
17
+
18
+ Synopsis
19
+ --------
20
+
21
+ twig open-pr
22
+
23
+ Description
24
+ -----------
25
+
26
+ Checks if a pr is already opened for this branch, if not opens it, otherwise do nothing
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 OpenPR
42
+ def initialize
43
+ @prima = Prima.new
44
+ end
45
+
46
+ def execute!
47
+ unless @prima.gh.user_authenticated?
48
+ puts 'Non autenticato'
49
+ exit 1
50
+ end
51
+
52
+ unless @prima.is_a_valid_branch?
53
+ puts "Non puoi creare una pull request dal branch #{ @prima.current_branch_name }".red
54
+ exit 1
55
+ end
56
+
57
+ unless is_on_github?
58
+ puts "Il branch #{ @prima.current_branch_name } non è su github".red
59
+ push = @prima.yesno 'Vuoi effettuare il push su github'.blue
60
+ if push
61
+ cmd = %{ git push -u origin "#{ @prima.current_branch_name }"}
62
+ system(cmd)
63
+ else
64
+ exit 1
65
+ end
66
+ end
67
+
68
+ unless @prima.get_pr_url.nil?
69
+ puts "Esiste già una pull request per il branch #{ @prima.current_branch_name }. url: #{ @prima.get_pr_url }".yellow
70
+ pr_property = @prima.twig.get_branch_property(@prima.current_branch_name, 'pr')
71
+ if pr_property.nil?
72
+ cmd = %{ twig pr "#{ @prima.get_pr.number }"}
73
+ exec(cmd)
74
+ end
75
+ open = @prima.yesno 'Vuoi aprirla nel browser'.blue
76
+ if open
77
+ Launchy.open(@prima.get_pr.html_url)
78
+ end
79
+ exit 0
80
+ end
81
+
82
+ pr = create_pull_request('master')
83
+ puts "Pull Request ##{ pr.number } creata".green
84
+ cmd = %{ twig pr "#{ pr.number }"}
85
+ exec(cmd)
86
+ @prima.update_issue_with_label(@prima.get_property('pr'), Prima::LABEL_WIP)
87
+ puts "Aggiunta label \"#{ Prima::LABEL_WIP }\" sulla pull request ##{ pr.number }".green
88
+ puts pr.html_url
89
+ exit 0
90
+ end
91
+
92
+ def is_on_github?
93
+ begin
94
+ @prima.gh.branch @prima.repo_name, @prima.current_branch_name
95
+ true
96
+ rescue Octokit::NotFound
97
+ return false
98
+ end
99
+ end
100
+
101
+ def create_pull_request(base_branch)
102
+ current_branch_name = @prima.current_branch_name
103
+ puts "Creazione pull request sul branch #{ base_branch }"
104
+ title = ask('Titolo: ')
105
+ head = @prima.current_branch_name
106
+ body = ask('Body: ')
107
+ unless @prima.twig.get_branch_property(current_branch_name, 'issue').nil?
108
+ body << " Issue di riferimento: primait/board##{@prima.twig.get_branch_property(current_branch_name, 'issue')}"
109
+ end
110
+ pr = @prima.create_pull_request base_branch, head, title, body
111
+ @prima.assign_pull_request_to_me(pr.number)
112
+ end
113
+ end
114
+
115
+ OpenPR.new.execute!
@@ -0,0 +1,108 @@
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 'pp'
8
+
9
+ def help_content
10
+ <<-HELP
11
+
12
+ twig-pick-issue
13
+ ===============
14
+
15
+ Picks an issue from github and open a branch for it
16
+
17
+ Synopsis
18
+ --------
19
+
20
+ twig pick-issue
21
+
22
+
23
+ Subcommand for Twig: <http://rondevera.github.io/twig/>
24
+ Author: Matteo Giachino <https://github.com/matteosister>
25
+
26
+ HELP
27
+ end
28
+
29
+ args = ARGV.dup
30
+
31
+ if args.include?('--help')
32
+ puts help_content
33
+ exit
34
+ end
35
+
36
+
37
+ class PickIssue
38
+ include Command
39
+
40
+ def initialize
41
+ @prima = Prima.new
42
+ end
43
+
44
+ def execute!
45
+ unless @prima.is_clean?
46
+ puts 'hai dei file non committati...non posso continuare'.red
47
+ exit(1)
48
+ end
49
+
50
+ issues = @prima.list_issues
51
+
52
+ issue_number = nil
53
+ args = ARGV.dup
54
+ if args[0] && args[0].to_i > 0
55
+ issues.keep_if do |issue|
56
+ issue.number == args[0].to_i
57
+ end
58
+ stop_if issues.empty?, "Issue #{args[0].to_i} non trovata!"
59
+ issue_number = args[0].to_i
60
+ else
61
+ issues.delete_if do |issue|
62
+ issue.pull_request?
63
+ end
64
+ issue_number = choose_issue issues
65
+ end
66
+
67
+ issue_type = 'feature'
68
+ base_branch_name_default = 'master'
69
+ branch_name = ask('Inserisci il nome del branch (puoi omettere feature/): '.cyan)
70
+ if branch_name.length == 0
71
+ puts 'Devi inserire il nome del branch'.red
72
+ exit 1
73
+ end
74
+ branch_name.prepend issue_type + '/' unless branch_name.include? issue_type
75
+ branch_name << "-#{issue_number}"
76
+
77
+ base_branch_name = ask('from which branch?'.cyan) { |q| q.default = base_branch_name_default }
78
+
79
+ puts "creo branch con nome #{branch_name} partendo da #{base_branch_name}"
80
+ @prima.rugged.branches.create(branch_name, base_branch_name)
81
+
82
+ @prima.twig.set_branch_property(branch_name, 'issue', issue_number)
83
+ @prima.twig.set_branch_property(branch_name, 'diff-branch', base_branch_name)
84
+ @prima.rugged.checkout(branch_name)
85
+ @prima.update_issue_with_label(issue_number, Prima::LABEL_WIP)
86
+ puts "Aggiunta label \"#{ Prima::LABEL_WIP }\" sulla issue primait/board##{ issue_number }".green
87
+ @prima.assign_issue_to_me(issue_number)
88
+ puts "Issue primait/board##{ issue_number } assegnata a #{ @prima.user_login }".green
89
+ end
90
+
91
+ def choose_issue(issues)
92
+ choose do |menu|
93
+ menu.prompt = 'Pick an issue: '.cyan
94
+ menu.shell = true
95
+
96
+ issues.each do |issue|
97
+ milestone = issue.milestone
98
+ issue_number = "##{issue.number}".green
99
+ title = @prima.reduce_size(issue.title, 100).light_blue
100
+ msg = "#{issue_number} - #{title}"
101
+ msg << " [#{milestone.title}]".blue unless milestone.nil?
102
+ menu.choice(msg) {issue.number}
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ PickIssue.new.execute!
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require_relative '../lib/prima_twig.rb'
5
+ require 'colorize'
6
+ require 'highline/import'
7
+
8
+
9
+ def help_content
10
+ <<-HELP
11
+
12
+ twig-review
13
+ ===========
14
+
15
+ Puts the pull request for the current branch in review
16
+
17
+ Synopsis
18
+ --------
19
+
20
+ twig review
21
+
22
+ Description
23
+ -----------
24
+
25
+ Puts the current branch pr in review
26
+
27
+ Subcommand for Twig: <http://rondevera.github.io/twig/>
28
+ Author: Matteo Giachino <https://github.com/matteosister>
29
+
30
+ HELP
31
+ end
32
+
33
+ args = ARGV.dup
34
+
35
+ if args.include?('--help')
36
+ puts help_content
37
+ exit
38
+ end
39
+
40
+ class Review
41
+ def initialize
42
+ @prima = Prima.new
43
+ end
44
+
45
+ def execute!
46
+ unless @prima.is_a_valid_branch?
47
+ puts "Il branch #{ @prima.current_branch_name } non può essere in review".red
48
+ exit 1
49
+ end
50
+
51
+ unless @prima.has_property?('pr')
52
+ puts 'Il branch attuale non ha la proprietà PR'.red
53
+ exit 1
54
+ end
55
+
56
+ unless @prima.is_already_a_pr?
57
+ puts 'Il branch attuale non è una pull request'.red
58
+ exit 1
59
+ end
60
+
61
+ puts "Pull request ##{@prima.get_property('pr')}".cyan
62
+ puts "Issue ##{@prima.get_property('issue')}".cyan if @prima.has_property?('issue')
63
+
64
+ @prima.put_issue_in_review(@prima.get_property('pr'))
65
+ puts "La pull request #{ @prima.get_property('pr') } è ora in review".green
66
+
67
+ if @prima.has_property?('issue')
68
+ @prima.put_issue_in_review(@prima.get_property('issue'))
69
+ puts "La issue #{ @prima.get_property('issue') } è ora in review".green
70
+ end
71
+
72
+ exit 0
73
+ end
74
+ end
75
+
76
+ Review.new.execute!
@@ -0,0 +1,197 @@
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 'launchy'
7
+ require 'json'
8
+ require 'aws-sdk-s3'
9
+
10
+ class TwigUpdateAmi
11
+ include Command
12
+ include PrimaAwsClient
13
+ def initialize
14
+ output 'Controllo se ci sono aggiornamenti da fare...'
15
+ exec "gem update prima-twig && twig update-ami #{ARGV.join ' '}" unless `gem outdated`.lines.grep(/^prima-twig \(.*\)/).empty?
16
+ @s3 = Aws::S3::Client.new
17
+ @s3_bucket = 'prima-deploy'
18
+ @templates_base_url = "https://s3-eu-west-1.amazonaws.com"
19
+ end
20
+
21
+ def execute!(args)
22
+ update_amis(args[0], args[1], args[2], args[3], args[4])
23
+ end
24
+
25
+ private
26
+
27
+ def update_amis(ami_template, ami_id, ami_name, ami_description, env)
28
+ output "updating instance definition #{ami_template}".light_green
29
+ Dir.chdir 'ami'
30
+ update_instance_name(ami_id, ami_name, ami_description, ami_template)
31
+ output 'running packer update (this could take some time)'.light_green
32
+ new_ami_id = update_packer(ami_template, env)
33
+ # new_ami_id = 'ami-026890988d91ee8c6'
34
+ Dir.chdir '..'
35
+ stop_if(new_ami_id.to_s.empty?, 'Failed to generate AMI!'.red)
36
+ output "new ami id: #{new_ami_id}"
37
+
38
+ output 'searching for ami to update...'
39
+ ami_mappings = JSON.parse(@s3.get_object(bucket: @s3_bucket, key: "ami/ami-mappings.json")["body"].read())
40
+ old_amis = update_ami_mappings(ami_mappings, ami_template, env, new_ami_id)
41
+ stop_if(old_amis.empty?, "No ami to update! No #{ami_template} in env #{env}, exiting".yellow)
42
+
43
+ output "retrieving stacks that uses old ami ids: #{old_amis}"
44
+ exports = list_exports()
45
+ stacks = get_stacks_from_exports(exports, old_amis)
46
+ stop_if(stacks.empty?, "No stack to update found! This means that ami-mapping file is not in sync, please check manually")
47
+
48
+ stacks.each do |stack|
49
+ output "processing stack #{stack}"
50
+ if stack.include?('qa')
51
+ output "skipping stack #{stack} because is a qa"
52
+ next
53
+ else
54
+ stack_tags = tags_to_hashes(get_stack_tags(stack))
55
+ stack_tags['TemplateVersion'] = stack_tags['TemplateVersion'].to_i + 1
56
+
57
+ if stack.include?('batch')
58
+ stack_parameters = update_stack_parameters(get_stack_parameters(stack),
59
+ [
60
+ { parameter_key: 'AMIID', parameter_value: new_ami_id },
61
+ { parameter_key: 'TemplateVersion', parameter_value: stack_tags['TemplateVersion'].to_s }
62
+ ]
63
+ )
64
+ if stack.include?('offsite-backups')
65
+ stack_template = File.read("./cloudformation/stacks/batch/compute-environment-offsite-backups.yml")
66
+ else
67
+ stack_template = File.read("./cloudformation/stacks/batch/compute-environment.yml")
68
+ end
69
+ else
70
+ stack_parameters = update_stack_parameters(get_stack_parameters(stack),
71
+ [
72
+ { parameter_key: 'AMIID', parameter_value: new_ami_id },
73
+ { parameter_key: 'DesiredCapacity', parameter_value: get_desired_capacity(stack).to_s },
74
+ { parameter_key: 'TemplateVersion', parameter_value: stack_tags['TemplateVersion'].to_s }
75
+ ]
76
+ )
77
+ stack_template = File.read("./cloudformation/stacks/asg/#{stack.to_s.split("/")[1]}.yml")
78
+ end
79
+ update_stack(stack, stack_template, stack_parameters, hashes_to_tags(stack_tags))
80
+ end
81
+ end
82
+
83
+ stacks.each do |stack|
84
+ if stack.include?('qa')
85
+ next
86
+ end
87
+ wait_for_stack_ready(stack, ['CREATE_FAILED', 'ROLLBACK_IN_PROGRESS', 'ROLLBACK_FAILED', 'DELETE_IN_PROGRESS', 'DELETE_FAILED', 'DELETE_COMPLETE', 'UPDATE_ROLLBACK_FAILED', 'UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS', 'UPDATE_ROLLBACK_COMPLETE', 'ROLLBACK_COMPLETE'])
88
+ end
89
+
90
+ output 'writing new ami mapping'
91
+ File.open("ami/ami-mappings.json", 'w+') do |f|
92
+ mapping_file = JSON.pretty_generate(ami_mappings)
93
+ f.write(mapping_file)
94
+ @s3.put_object(bucket: @s3_bucket, key: "ami/ami-mappings.json", body: mapping_file)
95
+ end
96
+
97
+ output 'Update finished! ( ͡° ͜ʖ ͡°)'
98
+ end
99
+
100
+ def get_stacks_from_exports(exports, old_amis)
101
+ stacks = []
102
+ old_amis.each do |old_ami|
103
+ exports.each do |export|
104
+ if export.value.eql?(old_ami)
105
+ stacks.insert(0,export.exporting_stack_id)
106
+ end
107
+ end
108
+ end
109
+ stacks
110
+ end
111
+
112
+ def update_ami_mappings(mappings, ami_template, env, new_ami_id)
113
+ old_values = []
114
+ mappings.each do |item|
115
+ if item['ami_template'].eql?(ami_template) and item['env'].eql?(env)
116
+ old_values.insert(0,item['ami_id'])
117
+ item['ami_id'] = new_ami_id
118
+ end
119
+ end
120
+ old_values.uniq
121
+ end
122
+
123
+ def update_stack_parameters(stack_parameters, new_parameters)
124
+ new_parameters.each do |new_param|
125
+ stack_parameters.reject{ |k| k["parameter_key"] == new_param["parameter_key"] }
126
+ stack_parameters.push(new_param)
127
+ end
128
+ stack_parameters
129
+ end
130
+
131
+ def update_instance_name(ami_id, ami_name, ami_description, ecs_json_path)
132
+ ecs_json = JSON.parse File.read(ecs_json_path)
133
+
134
+ ecs_json['builders'][0]['source_ami'] = ami_id
135
+ ecs_json['builders'][0]['ami_name'] = ami_name
136
+ ecs_json['builders'][0]['ami_description'] = ami_description
137
+
138
+ File.open ecs_json_path, 'w' do |f|
139
+ f.write(JSON.pretty_generate(ecs_json))
140
+ end
141
+ end
142
+
143
+ def get_desired_capacity(stack_name)
144
+ stack_outputs = get_stack_outputs(stack_name)
145
+ stack_outputs.each do |out|
146
+ if out.export_name.include?('EC2Fleet') or out.export_name.include?('AutoScalingGroup')
147
+ return get_autoscaling_capacity(out.output_value)
148
+ end
149
+ end
150
+ end
151
+
152
+ def update_packer(json_filename, env)
153
+ execute_command "AWS_MAX_ATTEMPTS=90 AWS_POLL_DELAY_SECONDS=60 packer build -var datadog_apikey=`biscuit get -f ../configs/secrets/common.yml common_production_apikey_datadog` -var github_token=`biscuit get -f ../configs/secrets/common.yml common_private_repo_github_token` -var drone_key=\"`biscuit get -f ../configs/secrets/common.yml drone_license_key`\" -var env=#{env} -machine-readable ./#{json_filename} | tee build.log"
154
+ `grep 'artifact,0,id' build.log | cut -d, -f6 | cut -d: -f2`.sub(/\n/, '')
155
+ end
156
+
157
+ def help_content
158
+ <<-HELP
159
+
160
+ twig-update-ami
161
+ ===========
162
+
163
+ Updates ami version and applies it to stacks on cloudformation
164
+
165
+ Synopsis
166
+ --------
167
+
168
+ twig-update-ami
169
+
170
+ Description
171
+ -----------
172
+
173
+ from artemide main folder run
174
+ `twig-update-ami ${AMI_TEMPLATE} ${AMI_ID} ${AMI_NAME} ${AMI_DESCRIPTION} ${ENV}`
175
+
176
+ Subcommand for Twig: <http://rondevera.github.io/twig/>
177
+ Author: Eugenio Laghi <https://github.com/eugeniolaghi>
178
+
179
+ HELP
180
+ end
181
+
182
+ class ::Hash
183
+ def deep_merge(second)
184
+ merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2 }
185
+ self.merge(second.to_h, &merger)
186
+ end
187
+ end
188
+
189
+ args = ARGV.dup
190
+
191
+ if args.include?('--help')
192
+ puts help_content
193
+ exit
194
+ end
195
+
196
+ TwigUpdateAmi.new.execute!(args)
197
+ end