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,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