prima-twig 0.2.6 → 0.2.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/twig-deploy +125 -0
- data/bin/twig-feature +403 -0
- data/bin/twig-hotfix +4 -3
- data/bin/twig-release +224 -0
- data/lib/prima_twig.rb +69 -7
- metadata +51 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7988c66d94da4a89f9b0f4e355f839377e399932
|
4
|
+
data.tar.gz: 1eac804cf0d0a260823c0e5188b14c69c311e30d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f30302ca0f5c462314f1feec9a5da6993034dfa389fcb05da3ab0df515a11a14fd4aee098e8dc3a6b612b263f95e225e567d4be587abef4055fbf1eca02f3a7a
|
7
|
+
data.tar.gz: ed5e356f70c54777163ca6f92295b33ea82597a2f3b710d8e23a3886612d441bde842232808d2ea87c60b75ff5f7960d9a2c29b46cc682953da00d414a6de219
|
data/bin/twig-deploy
ADDED
@@ -0,0 +1,125 @@
|
|
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 'aws-sdk'
|
8
|
+
require 'redcarpet'
|
9
|
+
require 'mail'
|
10
|
+
require 'erb'
|
11
|
+
|
12
|
+
def help_content
|
13
|
+
<<-HELP
|
14
|
+
|
15
|
+
twig-deploy
|
16
|
+
===========
|
17
|
+
|
18
|
+
Updates master and push to origin
|
19
|
+
|
20
|
+
Synopsis
|
21
|
+
--------
|
22
|
+
|
23
|
+
twig deploy
|
24
|
+
|
25
|
+
Description
|
26
|
+
-----------
|
27
|
+
|
28
|
+
Merges dev in master and push dev and master to origin, than returns to the actual branch
|
29
|
+
|
30
|
+
Subcommand for Twig: <http://rondevera.github.io/twig/>
|
31
|
+
Author: Andrea Usuelli <https://github.com/andreausu>
|
32
|
+
|
33
|
+
HELP
|
34
|
+
end
|
35
|
+
|
36
|
+
args = ARGV.dup
|
37
|
+
|
38
|
+
if args.include?('--help')
|
39
|
+
puts help_content
|
40
|
+
exit
|
41
|
+
end
|
42
|
+
|
43
|
+
class Review
|
44
|
+
include Command
|
45
|
+
|
46
|
+
def initialize
|
47
|
+
@prima = Prima.new
|
48
|
+
end
|
49
|
+
|
50
|
+
def execute!
|
51
|
+
output "Recupero degli artifacts in corso, attendi qualche secondo...".yellow
|
52
|
+
artifacts = get_artifacts
|
53
|
+
|
54
|
+
artifact = choose do |menu|
|
55
|
+
menu.prompt = 'Scegli la release da deployare: '.cyan
|
56
|
+
menu.shell = true
|
57
|
+
|
58
|
+
artifacts.each do |k, v|
|
59
|
+
branch = k
|
60
|
+
title = @prima.reduce_size(branch, 100).light_blue
|
61
|
+
msg = "#{title}"
|
62
|
+
menu.choice(msg) {k}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
deploy_command = "bin/deploy #{artifacts[artifact]}"
|
67
|
+
|
68
|
+
output "Il comando per il deploy sara': #{deploy_command}".yellow
|
69
|
+
|
70
|
+
exec_step deploy_command if @prima.yesno "Sei sicuro di voler effettuare il deploy in produzione?".blue
|
71
|
+
|
72
|
+
user = `git config user.name`
|
73
|
+
|
74
|
+
mail = Mail.new do
|
75
|
+
from 'developers@prima.it'
|
76
|
+
to 'developers@prima.it'
|
77
|
+
subject "#{user} ha effettuato il deploy della release #{artifact}"
|
78
|
+
end
|
79
|
+
|
80
|
+
body = "# Deploy in produzione avvenuto con successo!\n\n"
|
81
|
+
body << "### Data: #{Time.now.strftime('%d/%m/%Y %H:%M:%S')}\n"
|
82
|
+
body << "### Utente: #{user}\n"
|
83
|
+
body << "### Release: [#{artifact}](https://github.com/primait/prima/releases/tag/#{ERB::Util.url_encode(artifact)})\n"
|
84
|
+
|
85
|
+
htmlBody = Redcarpet::Markdown.new(Redcarpet::Render::HTML.new).render body
|
86
|
+
|
87
|
+
html_part = Mail::Part.new do
|
88
|
+
content_type 'text/html; charset=UTF-8'
|
89
|
+
body htmlBody
|
90
|
+
end
|
91
|
+
text_part = Mail::Part.new do
|
92
|
+
body htmlBody.gsub(/<br\s?\/?>/, "\r\n").gsub(/<\/?[^>]*>/, '')
|
93
|
+
end
|
94
|
+
|
95
|
+
mail.html_part = html_part
|
96
|
+
mail.text_part = text_part
|
97
|
+
|
98
|
+
opts = {address: 'smtp.mandrillapp.com', port: '587'}
|
99
|
+
opts[:user_name] = 'developers@prima.it'
|
100
|
+
opts[:password] = @prima.config['mandrill']
|
101
|
+
|
102
|
+
mail.delivery_method(:smtp, opts)
|
103
|
+
mail.deliver
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
def get_artifacts
|
108
|
+
rev_already_downloaded = []
|
109
|
+
artifacts = {}
|
110
|
+
s3 = Aws::S3::Client.new
|
111
|
+
resp = s3.list_objects(bucket: 'prima-deploy', prefix: 'artifacts')
|
112
|
+
resp.contents.each do |l|
|
113
|
+
rev = l.key.match(/_(\w{15}).zip$/).captures.first if l.key.match(/_(\w{15}).zip$/)
|
114
|
+
if rev && l.key.include?('prod_') && !rev_already_downloaded.include?(rev)
|
115
|
+
rev_already_downloaded << rev
|
116
|
+
object = s3.head_object(bucket: 'prima-deploy', key: l.key)
|
117
|
+
artifacts[object.metadata['branch']] = rev if object.metadata.has_key? 'branch'
|
118
|
+
end
|
119
|
+
end
|
120
|
+
artifacts
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
Review.new.execute!
|
data/bin/twig-feature
ADDED
@@ -0,0 +1,403 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require_relative '../lib/prima_twig.rb'
|
5
|
+
require 'launchy'
|
6
|
+
|
7
|
+
class Release
|
8
|
+
include Command
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@prima = Prima.new
|
12
|
+
@eb = Aws::ElasticBeanstalk::Client.new
|
13
|
+
@ec2 = Aws::EC2::Client.new
|
14
|
+
@s3_bucket = 'elasticbeanstalk-eu-west-1-001575623345'
|
15
|
+
@current_branch_name = @prima.twig.current_branch_name
|
16
|
+
@s3_key = @current_branch_name.gsub('/', '-') + '.zip'
|
17
|
+
@application_name = 'prima-qa'
|
18
|
+
@environment_name = sanitize_environment_name(@s3_key.gsub('.zip', ''))
|
19
|
+
@git_rev = `git rev-parse HEAD`
|
20
|
+
@version_label = @environment_name + '-' + @git_rev[0,5]
|
21
|
+
@user = `git config user.name`.downcase
|
22
|
+
@user = @user[0,1] + @user.split(' ').last
|
23
|
+
end
|
24
|
+
|
25
|
+
def execute! args
|
26
|
+
possible_args = ["start", "finish", "deploy"]
|
27
|
+
stop_if args.empty?, [:wrong_args, possible_args]
|
28
|
+
stop_if args.length > 2, [:wrong_args, possible_args]
|
29
|
+
|
30
|
+
stop_if @prima.head_detached?, :detached_head
|
31
|
+
|
32
|
+
case args[0]
|
33
|
+
when "start"
|
34
|
+
start_feature!
|
35
|
+
when "finish"
|
36
|
+
finish_feature!
|
37
|
+
when "deploy"
|
38
|
+
if ['terminate', 'stop', 'shutdown', 'halt', 'destroy'].include? args[1]
|
39
|
+
deploy_shutdown!
|
40
|
+
else
|
41
|
+
deploy_feature!
|
42
|
+
end
|
43
|
+
else
|
44
|
+
stop_if true, [:wrong_args, possible_args]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def start_feature!
|
49
|
+
end
|
50
|
+
|
51
|
+
def finish_feature!
|
52
|
+
end
|
53
|
+
|
54
|
+
def deploy_feature!
|
55
|
+
app_name = 'prima-qa'
|
56
|
+
|
57
|
+
ssh_key_path = "~/.ssh/eb_#{@user}"
|
58
|
+
ec2_key_name = "eb_#{@user}"
|
59
|
+
artifact_path = '/tmp/prima-artifact.zip'
|
60
|
+
|
61
|
+
stop_if application_version_exists?(@application_name, @version_label), "La versione #{@version_label} esiste gia', ti sei ricordato di committare?".red
|
62
|
+
|
63
|
+
manage_ec2_keypair(ec2_key_name, ssh_key_path)
|
64
|
+
|
65
|
+
build_release!
|
66
|
+
|
67
|
+
exec_step 'rsync -avd --exclude-from app/deploy/aws_docker/qa/.rsync-exclude-qa . /tmp/prima'
|
68
|
+
exec_step 'cd /tmp/prima && cp app/deploy/aws_docker/qa/Dockerrun.aws.json .'
|
69
|
+
exec_step "rm -f #{artifact_path}; cd /tmp/prima && zip -ryq #{artifact_path} ."
|
70
|
+
output "Upload dell'artifact in corso (#{(File.size(artifact_path).to_f / 2**20).round(2)} MiB)\n".yellow
|
71
|
+
s3 = Aws::S3::Resource.new
|
72
|
+
obj = s3.bucket(@s3_bucket).object(@s3_key)
|
73
|
+
obj.upload_file(artifact_path)
|
74
|
+
|
75
|
+
output "#{@s3_bucket}/#{@s3_key} uploadato con successo!\n".green
|
76
|
+
|
77
|
+
resp = @eb.create_application_version({
|
78
|
+
application_name: @application_name,
|
79
|
+
version_label: @version_label,
|
80
|
+
description: @current_branch_name,
|
81
|
+
source_bundle: {
|
82
|
+
s3_bucket: @s3_bucket,
|
83
|
+
s3_key: @s3_key,
|
84
|
+
},
|
85
|
+
auto_create_application: false,
|
86
|
+
})
|
87
|
+
|
88
|
+
output "Application version #{@version_label} creata con successo!\n".green
|
89
|
+
|
90
|
+
if environment_exists?(@application_name, @environment_name)
|
91
|
+
is_eb_ready = is_eb_ready?(@application_name, @environment_name)
|
92
|
+
wait_for_eb_ready(@application_name, @environment_name) unless is_eb_ready
|
93
|
+
@eb.update_application_version({
|
94
|
+
application_name: @application_name,
|
95
|
+
version_label: @version_label
|
96
|
+
})
|
97
|
+
run_commands(@environment_name, ssh_key_path)
|
98
|
+
else
|
99
|
+
output "Environment #{@environment_name} non presente, inizio la creazione...\n".yellow
|
100
|
+
create_eb_environment(@application_name, @environment_name, @version_label, @git_rev, ec2_key_name)
|
101
|
+
output "Environment #{@environment_name} creato!\n".green
|
102
|
+
is_eb_ready = is_eb_ready?(@application_name, @environment_name)
|
103
|
+
wait_for_eb_ready(@application_name, @environment_name) unless is_eb_ready
|
104
|
+
run_commands(@environment_name, ssh_key_path)
|
105
|
+
end
|
106
|
+
|
107
|
+
endpoint_url = get_eb_endpoint_url(@application_name, @environment_name)
|
108
|
+
output "Endpoint url: #{endpoint_url}\n".cyan
|
109
|
+
Launchy.open(endpoint_url) if @prima.yesno 'Vuoi aprirlo nel browser?'.blue
|
110
|
+
output "Deploy effettuato, everything is awesome!\n".green
|
111
|
+
end
|
112
|
+
|
113
|
+
def deploy_shutdown!
|
114
|
+
stop_unless environment_exists?(@application_name, @environment_name), "L'environment #{@environment_name} non esiste!"
|
115
|
+
shutdown_environment(@environment_name) if @prima.yesno("Sei sicuro di voler spegnere l'env?".blue)
|
116
|
+
output "L'environment #{@environment_name} e' stato disrutto con successo!\n".green
|
117
|
+
output "Potrebbe comunque passare qualche minuto prima che l'operazione finisca\n".yellow
|
118
|
+
end
|
119
|
+
|
120
|
+
def build_release!
|
121
|
+
output "Preparo l'artifact .zip\n".yellow
|
122
|
+
|
123
|
+
base_cmd = 'docker exec -it prima_'
|
124
|
+
[
|
125
|
+
"docker run -v $PWD:/usr/app/src -w /usr/app/src blinkmobile/bower install --allow-root",
|
126
|
+
"#{base_cmd}web_1 composer install",
|
127
|
+
"#{base_cmd}php_1 ./app/console -e=prod assets:install web",
|
128
|
+
"#{base_cmd}php_1 ./app/console -e=prod fos:js-routing:dump",
|
129
|
+
"#{base_cmd}php_1 ./app/console -e=prod cypress:ng-routing:dump",
|
130
|
+
"#{base_cmd}php_1 ./app/console -e=prod bazinga:js-translation:dump web",
|
131
|
+
"#{base_cmd}php_1 ./node_modules/.bin/brunch build --production",
|
132
|
+
"#{base_cmd}web_1 composer dump-autoload --optimize --no-dev",
|
133
|
+
"#{base_cmd}compass_1 compass compile --force -c compass/config_prod.rb compass/"
|
134
|
+
].each do |cmd|
|
135
|
+
output "Eseguo #{cmd}".yellow
|
136
|
+
res = %x[ #{cmd} ]
|
137
|
+
if $?.exitstatus != 0
|
138
|
+
color = 'red'
|
139
|
+
else
|
140
|
+
color = 'green'
|
141
|
+
end
|
142
|
+
output res.send color
|
143
|
+
stop_if (color == 'red'), "Errore durante la build dell'artifact".red
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def environment_exists?(application_name, environment_name)
|
148
|
+
resp = @eb.describe_environments({
|
149
|
+
application_name: application_name,
|
150
|
+
environment_names: [environment_name],
|
151
|
+
include_deleted: false
|
152
|
+
})
|
153
|
+
!resp.environments[0].nil?
|
154
|
+
end
|
155
|
+
|
156
|
+
def shutdown_environment(environment_name)
|
157
|
+
@eb.terminate_environment({
|
158
|
+
environment_name: environment_name,
|
159
|
+
terminate_resources: true
|
160
|
+
})
|
161
|
+
end
|
162
|
+
|
163
|
+
def manage_ec2_keypair(ec2_key_name, path)
|
164
|
+
path = File.expand_path(path)
|
165
|
+
return if File.exist?(path)
|
166
|
+
stop_unless @prima.yesno("Sei sicuro di voler creare la chiave SSH in #{path}?".blue), "Non puoi continuare senza chiave SSH".red
|
167
|
+
%x[ ssh-keygen -b 2048 -t rsa -f #{path} -q -N '' ]
|
168
|
+
upload_ssh_public_key(ec2_key_name, path + '.pub')
|
169
|
+
output "Chiave SSH creata e uploadata con successo!\n".green
|
170
|
+
end
|
171
|
+
|
172
|
+
def upload_ssh_public_key(ec2_key_name, path)
|
173
|
+
@ec2.import_key_pair({
|
174
|
+
dry_run: false,
|
175
|
+
key_name: ec2_key_name,
|
176
|
+
public_key_material: IO.read(path)
|
177
|
+
})
|
178
|
+
end
|
179
|
+
|
180
|
+
def create_eb_environment(application_name, environment_name, version_label, git_rev, ec2_key_name)
|
181
|
+
resp = @eb.create_environment({
|
182
|
+
application_name: application_name,
|
183
|
+
environment_name: environment_name,
|
184
|
+
version_label: version_label,
|
185
|
+
solution_stack_name: "64bit Amazon Linux 2015.09 v2.0.4 running Multi-container Docker 1.7.1 (Generic)",
|
186
|
+
option_settings: [
|
187
|
+
{
|
188
|
+
namespace: "aws:autoscaling:launchconfiguration",
|
189
|
+
option_name: "IamInstanceProfile",
|
190
|
+
value: "prima-qa",
|
191
|
+
},
|
192
|
+
{
|
193
|
+
namespace: "aws:autoscaling:launchconfiguration",
|
194
|
+
option_name: "EC2KeyName",
|
195
|
+
value: ec2_key_name,
|
196
|
+
},
|
197
|
+
{
|
198
|
+
namespace: "aws:autoscaling:launchconfiguration",
|
199
|
+
option_name: "InstanceType",
|
200
|
+
value: "t2.medium",
|
201
|
+
},
|
202
|
+
{
|
203
|
+
namespace: "aws:autoscaling:asg",
|
204
|
+
option_name: "MaxSize",
|
205
|
+
value: "1",
|
206
|
+
},
|
207
|
+
{
|
208
|
+
namespace: "aws:autoscaling:asg",
|
209
|
+
option_name: "MinSize",
|
210
|
+
value: "1",
|
211
|
+
},
|
212
|
+
{
|
213
|
+
namespace: "aws:cloudformation:template:parameter",
|
214
|
+
option_name: "EnvironmentVariables",
|
215
|
+
value: "SYMFONY__PRIMA__GIT__REVISION=#{git_rev},PRIMA_ENV_NAME=web-qa,PARAMETERS_FILE=prima-deploy/parameters_qa.yml",
|
216
|
+
},
|
217
|
+
{
|
218
|
+
namespace: "aws:elasticbeanstalk:application:environment",
|
219
|
+
option_name: "PARAMETERS_FILE",
|
220
|
+
value: "prima-deploy/parameters_qa.yml",
|
221
|
+
},
|
222
|
+
{
|
223
|
+
namespace: "aws:elasticbeanstalk:application:environment",
|
224
|
+
option_name: "PRIMA_ENV_NAME",
|
225
|
+
value: "web-qa",
|
226
|
+
},
|
227
|
+
{
|
228
|
+
namespace: "aws:elasticbeanstalk:application:environment",
|
229
|
+
option_name: "SYMFONY__PRIMA__GIT__REVISION",
|
230
|
+
value: git_rev,
|
231
|
+
}
|
232
|
+
],
|
233
|
+
tags: [
|
234
|
+
{
|
235
|
+
key: "user",
|
236
|
+
value: @user
|
237
|
+
}
|
238
|
+
]
|
239
|
+
})
|
240
|
+
end
|
241
|
+
|
242
|
+
def is_eb_ready?(application_name, environment_name)
|
243
|
+
resp = @eb.describe_environments({
|
244
|
+
application_name: application_name,
|
245
|
+
environment_names: [environment_name],
|
246
|
+
include_deleted: false
|
247
|
+
})
|
248
|
+
resp.environments[0].status != 'Launching'
|
249
|
+
end
|
250
|
+
|
251
|
+
def get_eb_endpoint_url(application_name, environment_name)
|
252
|
+
resp = @eb.describe_environments({
|
253
|
+
application_name: application_name,
|
254
|
+
environment_names: [environment_name],
|
255
|
+
include_deleted: false
|
256
|
+
})
|
257
|
+
"http://#{resp.environments[0].endpoint_url}"
|
258
|
+
end
|
259
|
+
|
260
|
+
def get_eb_events(application_name, environment_name, start_time=nil, end_time=nil)
|
261
|
+
start_time ||= Time.utc(1980,"jan",1,0,0,0)
|
262
|
+
end_time ||= Time.now + 1
|
263
|
+
resp = @eb.describe_events({
|
264
|
+
application_name: application_name,
|
265
|
+
environment_name: environment_name,
|
266
|
+
start_time: start_time,
|
267
|
+
end_time: end_time
|
268
|
+
})
|
269
|
+
resp.events.reverse
|
270
|
+
end
|
271
|
+
|
272
|
+
def wait_for_eb_ready(application_name, environment_name)
|
273
|
+
ready = false
|
274
|
+
start_time = Time.now - 60
|
275
|
+
output "Attendo che l'environment finisca di essere inizializzato...\n".yellow
|
276
|
+
while !ready
|
277
|
+
print '...'.yellow; STDOUT.flush
|
278
|
+
|
279
|
+
events = get_eb_events(application_name, environment_name, start_time)
|
280
|
+
start_time = Time.now
|
281
|
+
|
282
|
+
print "\n" if events.length > 0
|
283
|
+
STDOUT.flush
|
284
|
+
|
285
|
+
events.each do |event|
|
286
|
+
if ["WARN", "ERROR", "FATAL"].include? event.severity
|
287
|
+
color = 'red'
|
288
|
+
else
|
289
|
+
color = 'cyan'
|
290
|
+
end
|
291
|
+
output "#{event.event_date} - #{event.severity} #{event.message}".send color
|
292
|
+
end
|
293
|
+
|
294
|
+
ready = true if is_eb_ready?(application_name, environment_name)
|
295
|
+
sleep 10 unless ready
|
296
|
+
end
|
297
|
+
|
298
|
+
output "Environment inizializzato!\n".green
|
299
|
+
end
|
300
|
+
|
301
|
+
def run_commands(environment_name, ssh_key_path)
|
302
|
+
instance_id = @eb.describe_environment_resources({
|
303
|
+
environment_name: environment_name,
|
304
|
+
}).environment_resources.instances[0].id
|
305
|
+
|
306
|
+
resp = @ec2.describe_instances({instance_ids: [instance_id]})
|
307
|
+
|
308
|
+
public_ip_address = resp.reservations[0].instances[0].public_ip_address
|
309
|
+
|
310
|
+
output "L'IP pubblico della macchina e': #{public_ip_address}\n".cyan
|
311
|
+
|
312
|
+
base_cmd = "ssh -q -tt -o StrictHostKeyChecking=no -i #{ssh_key_path} ec2-user@#{public_ip_address}"
|
313
|
+
cmd = "#{base_cmd} sudo -i docker ps -a --filter='name=php' | awk '{print $1 }' | grep -v CONTAINER"
|
314
|
+
container_id = %x[ #{cmd} | grep -v 'Warning:' ].chomp
|
315
|
+
|
316
|
+
base_docker_cmd = "sudo -i docker exec #{container_id}"
|
317
|
+
|
318
|
+
[
|
319
|
+
"#{base_cmd} #{base_docker_cmd} ./app/console doctrine:database:drop --no-interaction --force",
|
320
|
+
"#{base_cmd} #{base_docker_cmd} ./app/console doctrine:database:create --no-interaction",
|
321
|
+
"#{base_cmd} #{base_docker_cmd} ./app/console doctrine:database:drop --no-interaction --connection=msa --force; true",
|
322
|
+
"#{base_cmd} #{base_docker_cmd} ./app/console doctrine:database:create --no-interaction --connection=msa",
|
323
|
+
"#{base_cmd} #{base_docker_cmd} ./app/console doctrine:schema:create --no-interaction",
|
324
|
+
"#{base_cmd} #{base_docker_cmd} ./app/console doctrine:fixtures:load --no-interaction --fixtures=src/Prima/DataFixtures/"
|
325
|
+
].each do |cmd|
|
326
|
+
output "Eseguo #{cmd}".yellow
|
327
|
+
res = %x[ #{cmd} ]
|
328
|
+
if $?.exitstatus != 0
|
329
|
+
color = 'red'
|
330
|
+
else
|
331
|
+
color = 'green'
|
332
|
+
end
|
333
|
+
output res.send color
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
def application_version_exists?(application_name, version_label)
|
338
|
+
resp = @eb.describe_application_versions({
|
339
|
+
application_name: application_name,
|
340
|
+
version_labels: [ version_label ]
|
341
|
+
})
|
342
|
+
!resp.application_versions[0].nil?
|
343
|
+
end
|
344
|
+
|
345
|
+
def sanitize_environment_name(environment_name)
|
346
|
+
environment_name.gsub!(/([^\w]|_)/, '-')
|
347
|
+
if environment_name.length > 23
|
348
|
+
if environment_name.start_with?('feature')
|
349
|
+
environment_name = environment_name[8,23]
|
350
|
+
elsif environment_name.start_with?('hotfix')
|
351
|
+
environment_name = environment_name[7,23]
|
352
|
+
else
|
353
|
+
environment_name = environment_name[0,23]
|
354
|
+
end
|
355
|
+
end
|
356
|
+
if environment_name[0] == '-'
|
357
|
+
environment_name = environment_name[1..-1]
|
358
|
+
end
|
359
|
+
if environment_name[-1] == '-'
|
360
|
+
environment_name = environment_name[0..-2]
|
361
|
+
end
|
362
|
+
environment_name
|
363
|
+
end
|
364
|
+
|
365
|
+
end
|
366
|
+
|
367
|
+
def help_content
|
368
|
+
<<-HELP
|
369
|
+
|
370
|
+
twig-feature
|
371
|
+
===========
|
372
|
+
|
373
|
+
Manage feature branches
|
374
|
+
|
375
|
+
Synopsis
|
376
|
+
--------
|
377
|
+
|
378
|
+
twig release start
|
379
|
+
twig release finish
|
380
|
+
twig release deploy
|
381
|
+
|
382
|
+
Description
|
383
|
+
-----------
|
384
|
+
|
385
|
+
start creates a new feature branch
|
386
|
+
finish finishes the feature by merging to dev and master
|
387
|
+
deploy deploys the feature branch to a temporary AWS Elastic Beanstalk env
|
388
|
+
deploy stop destroys the AWS Elastic Beanstalk env
|
389
|
+
|
390
|
+
Subcommand for Twig: <http://rondevera.github.io/twig/>
|
391
|
+
Author: Andrea Usuelli <https://github.com/andreausu>
|
392
|
+
|
393
|
+
HELP
|
394
|
+
end
|
395
|
+
|
396
|
+
args = ARGV.dup
|
397
|
+
|
398
|
+
if args.include?('--help')
|
399
|
+
puts help_content
|
400
|
+
exit
|
401
|
+
end
|
402
|
+
|
403
|
+
Release.new.execute!(args)
|
data/bin/twig-hotfix
CHANGED
@@ -31,8 +31,8 @@ class Hotfix
|
|
31
31
|
def start_hotfix!
|
32
32
|
|
33
33
|
branch_name = @prima.clean_branch_name(ask('Inserisci il nome del branch (puoi omettere hotfix/): '.cyan))
|
34
|
-
|
35
|
-
branch_name.prepend
|
34
|
+
stop_unless branch_name.length > 0, 'Devi inserire il nome del branch'
|
35
|
+
branch_name.prepend 'hotfix/' unless branch_name.include? 'hotfix'
|
36
36
|
|
37
37
|
output "Il nome del branch sarà " + branch_name.yellow
|
38
38
|
|
@@ -61,7 +61,8 @@ class Hotfix
|
|
61
61
|
exec_step 'git push origin dev' if @prima.yesno 'Vuoi effettuare il push di dev?'.blue
|
62
62
|
|
63
63
|
# Chiude la issue
|
64
|
-
@prima.
|
64
|
+
issue_number = @prima.twig.get_branch_property(current_branch_name, 'issue')
|
65
|
+
@prima.close_issue(issue_number) if issue_number
|
65
66
|
|
66
67
|
# Rimuove il branch remoto
|
67
68
|
exec_step "git push origin :#{current_branch_name}" if @prima.yesno "Vuoi eliminare il branch remoto #{current_branch_name}?".blue
|
data/bin/twig-release
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'redcarpet'
|
5
|
+
require 'mail'
|
6
|
+
require_relative '../lib/prima_twig.rb'
|
7
|
+
|
8
|
+
class Release
|
9
|
+
include Command
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@prima = Prima.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute! args
|
16
|
+
possible_args = ["start", "finish"]
|
17
|
+
stop_if args.empty?, [:wrong_args, possible_args]
|
18
|
+
stop_if args.length > 1, [:wrong_args, possible_args]
|
19
|
+
|
20
|
+
stop_if @prima.head_detached?, :detached_head
|
21
|
+
stop_unless @prima.is_clean?, :clean
|
22
|
+
|
23
|
+
case args[0]
|
24
|
+
when "start"
|
25
|
+
start_release!
|
26
|
+
when "finish"
|
27
|
+
finish_release!
|
28
|
+
else
|
29
|
+
stop_if true, [:wrong_args, possible_args]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def start_release!
|
34
|
+
|
35
|
+
release_name = @prima.clean_branch_name(ask('Inserisci il nome della release (senza prefisso release/): '.cyan))
|
36
|
+
|
37
|
+
branch_name = "release/#{release_name}/#{Time.now.strftime('%Y%m%d')}"
|
38
|
+
|
39
|
+
output "Il nome del branch sarà #{branch_name.yellow}"
|
40
|
+
|
41
|
+
exec_step "git checkout dev"
|
42
|
+
exec_step "git pull origin dev"
|
43
|
+
exec_step "git checkout -b #{branch_name}"
|
44
|
+
exec_step "git push -u origin #{branch_name}"
|
45
|
+
|
46
|
+
pr = @prima.create_pull_request('master', branch_name, branch_name, '')
|
47
|
+
@prima.twig.set_branch_property(branch_name, 'pr', pr.number)
|
48
|
+
@prima.twig.set_branch_property(branch_name, 'diff-branch', 'dev')
|
49
|
+
|
50
|
+
commits = get_commits(pr.number)
|
51
|
+
issues = get_issues(pr.number)
|
52
|
+
|
53
|
+
issues.each do |issue|
|
54
|
+
@prima.delete_label_from_issue(issue.number, Prima::LABEL_NEXT_RELEASE)
|
55
|
+
@prima.update_issue_with_label(issue.number, Prima::LABEL_IN_STAGING)
|
56
|
+
end
|
57
|
+
|
58
|
+
pr_body = build_pr_body(issues, commits)
|
59
|
+
@prima.update_pr pr.number, {:body => pr_body}
|
60
|
+
|
61
|
+
mail = Mail.new do
|
62
|
+
from 'developers@prima.it'
|
63
|
+
to 'developers@prima.it'
|
64
|
+
subject "#{`git config user.name`} ha iniziato la creazione della release #{branch_name}"
|
65
|
+
end
|
66
|
+
|
67
|
+
htmlBody = build_pr_body(issues, commits, 'html')
|
68
|
+
|
69
|
+
html_part = Mail::Part.new do
|
70
|
+
content_type 'text/html; charset=UTF-8'
|
71
|
+
body htmlBody
|
72
|
+
end
|
73
|
+
text_part = Mail::Part.new do
|
74
|
+
body htmlBody.gsub(/<br\s?\/?>/, "\r\n").gsub(/<\/?[^>]*>/, '')
|
75
|
+
end
|
76
|
+
|
77
|
+
mail.html_part = html_part
|
78
|
+
mail.text_part = text_part
|
79
|
+
|
80
|
+
opts = {address: 'smtp.mandrillapp.com', port: '587'}
|
81
|
+
opts[:user_name] = 'developers@prima.it'
|
82
|
+
opts[:password] = @prima.config['mandrill']
|
83
|
+
|
84
|
+
mail.delivery_method(:smtp, opts)
|
85
|
+
mail.deliver
|
86
|
+
end
|
87
|
+
|
88
|
+
def finish_release!
|
89
|
+
current_branch_name = @prima.twig.current_branch_name
|
90
|
+
stop_unless (current_branch_name =~ /^release\//), "Non sei su un branch di release, non posso mergiare nulla"
|
91
|
+
|
92
|
+
# Mergia la PR
|
93
|
+
pr = @prima.get_pr
|
94
|
+
stop_unless pr, 'Pull Request not found'
|
95
|
+
@prima.merge_pull_request pr
|
96
|
+
|
97
|
+
output "La Pull Request e' stata mergiata!".green
|
98
|
+
|
99
|
+
# Mergia il branch su dev e pusha
|
100
|
+
exec_step 'git fetch'
|
101
|
+
exec_step 'git checkout dev'
|
102
|
+
exec_step 'git merge origin/dev'
|
103
|
+
exec_step "git merge --no-ff #{current_branch_name}"
|
104
|
+
|
105
|
+
exec_step 'git push origin dev' if @prima.yesno 'Vuoi effettuare il push di dev?'.blue
|
106
|
+
|
107
|
+
# Aggiorno le issue
|
108
|
+
issues = get_issues(pr.number)
|
109
|
+
issues.each do |issue|
|
110
|
+
@prima.delete_label_from_issue(issue.number, Prima::LABEL_IN_STAGING)
|
111
|
+
@prima.close_issue issue.number
|
112
|
+
end
|
113
|
+
|
114
|
+
# Rimuove il branch remoto
|
115
|
+
exec_step "git push origin :#{current_branch_name}" if @prima.yesno "Vuoi eliminare il branch remoto #{current_branch_name}?".blue
|
116
|
+
|
117
|
+
# Crea il tag e la release con la description figa
|
118
|
+
exec_step 'git checkout master'
|
119
|
+
exec_step 'git pull'
|
120
|
+
res = @prima.rugged.tags.create(current_branch_name, @prima.rugged.head.target_id)
|
121
|
+
|
122
|
+
exec_step 'git push --tags'
|
123
|
+
|
124
|
+
commits = get_commits(pr.number)
|
125
|
+
pr_body = build_pr_body(issues, commits)
|
126
|
+
|
127
|
+
@prima.create_release(current_branch_name, {:name => current_branch_name, :body => pr_body})
|
128
|
+
|
129
|
+
output 'Release creata con successo!'.green
|
130
|
+
end
|
131
|
+
|
132
|
+
def get_commits(pr_number)
|
133
|
+
commits = []
|
134
|
+
|
135
|
+
@prima.get_pr_commits(pr_number).each do |item|
|
136
|
+
commits << item
|
137
|
+
end
|
138
|
+
|
139
|
+
commits
|
140
|
+
end
|
141
|
+
|
142
|
+
def get_issues(pr_number)
|
143
|
+
issues = []
|
144
|
+
issues_ids = []
|
145
|
+
|
146
|
+
self.get_commits(pr_number).each do |item|
|
147
|
+
issues_ids << item.commit.message.match(/^#(\d+)\s-/).captures.first if item.commit.message =~ /^#(\d+)\s-/
|
148
|
+
end
|
149
|
+
|
150
|
+
issues_ids.each do |id|
|
151
|
+
issues << @prima.get_issue(id)
|
152
|
+
end
|
153
|
+
|
154
|
+
issues
|
155
|
+
end
|
156
|
+
|
157
|
+
def build_pr_body(issues, commits, output_format = 'markdown')
|
158
|
+
body = ''
|
159
|
+
|
160
|
+
body << "# Issues\n\n"
|
161
|
+
|
162
|
+
issues.each do |issue|
|
163
|
+
body << "### [##{issue.number} - #{issue.title}](#{get_issue_url(issue.number)})\n\n"
|
164
|
+
body << "#{issue.body}\n\n"
|
165
|
+
end
|
166
|
+
|
167
|
+
body << "# Commits\n\n"
|
168
|
+
|
169
|
+
commits.each do |commit|
|
170
|
+
body << "* [#{commit.commit.message}](#{get_commit_url(commit.sha)})\n"
|
171
|
+
end
|
172
|
+
|
173
|
+
if output_format == 'html'
|
174
|
+
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML.new)
|
175
|
+
body = markdown.render body
|
176
|
+
end
|
177
|
+
|
178
|
+
body
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
def get_commit_url(sha)
|
183
|
+
"https://github.com/#{@prima.repo_name}/commit/#{sha}"
|
184
|
+
end
|
185
|
+
|
186
|
+
def get_issue_url(number)
|
187
|
+
"https://github.com/#{@prima.repo_name}/issues/#{number}"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def help_content
|
192
|
+
<<-HELP
|
193
|
+
|
194
|
+
twig-release
|
195
|
+
===========
|
196
|
+
|
197
|
+
Manage release creation
|
198
|
+
|
199
|
+
Synopsis
|
200
|
+
--------
|
201
|
+
|
202
|
+
twig release start
|
203
|
+
twig release finish
|
204
|
+
|
205
|
+
Description
|
206
|
+
-----------
|
207
|
+
|
208
|
+
start creates an release from dev branch
|
209
|
+
finish finishes the actual release by merging to dev and master
|
210
|
+
|
211
|
+
Subcommand for Twig: <http://rondevera.github.io/twig/>
|
212
|
+
Author: Andrea Usuelli <https://github.com/andreausu>
|
213
|
+
|
214
|
+
HELP
|
215
|
+
end
|
216
|
+
|
217
|
+
args = ARGV.dup
|
218
|
+
|
219
|
+
if args.include?('--help')
|
220
|
+
puts help_content
|
221
|
+
exit
|
222
|
+
end
|
223
|
+
|
224
|
+
Release.new.execute!(args)
|
data/lib/prima_twig.rb
CHANGED
@@ -4,19 +4,25 @@ require 'octokit'
|
|
4
4
|
require 'yaml'
|
5
5
|
require 'rugged'
|
6
6
|
require 'highline/import'
|
7
|
+
require 'aws-sdk'
|
7
8
|
|
8
9
|
class Prima
|
9
10
|
LABEL_REVIEW='review'
|
10
11
|
LABEL_WIP='wip'
|
11
12
|
LABEL_HOTFIX='hotfix'
|
12
|
-
|
13
|
-
|
13
|
+
LABEL_NEXT_RELEASE='next release'
|
14
|
+
LABEL_IN_STAGING='in staging'
|
15
|
+
CONFIG_KEYS=['github', 'mandrill', 'aws_key_id', 'aws_key_secret']
|
16
|
+
attr_reader :gh, :twig, :config, :rugged, :aws
|
14
17
|
|
15
18
|
def initialize
|
16
19
|
@twig = Twig.new(:read_options => true)
|
17
20
|
unless has_config?
|
18
21
|
generate_config
|
19
22
|
end
|
23
|
+
unless has_latest_config?
|
24
|
+
update_config
|
25
|
+
end
|
20
26
|
@config = YAML.load_file 'twig.yml'
|
21
27
|
@rugged = Rugged::Repository.new('.')
|
22
28
|
create_clients
|
@@ -26,21 +32,51 @@ class Prima
|
|
26
32
|
File.exist? 'twig.yml'
|
27
33
|
end
|
28
34
|
|
35
|
+
def has_latest_config?
|
36
|
+
existing_config = YAML.load_file 'twig.yml'
|
37
|
+
CONFIG_KEYS.each do |k|
|
38
|
+
unless existing_config.has_key? k
|
39
|
+
return false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
29
44
|
def generate_config
|
30
45
|
puts 'Progetto non inizializzato'.red
|
31
46
|
puts 'Creazione del file di configurazione'.yellow
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
47
|
+
conf = {}
|
48
|
+
CONFIG_KEYS.each do |k|
|
49
|
+
token = ask "#{k} token: "
|
50
|
+
conf[k] = token
|
51
|
+
end
|
52
|
+
write_config conf
|
53
|
+
end
|
54
|
+
|
55
|
+
def update_config
|
56
|
+
puts 'Mancano alcuni parametri nella tua configurazione'.yellow
|
57
|
+
existing_config = YAML.load_file 'twig.yml'
|
58
|
+
CONFIG_KEYS.each do |k|
|
59
|
+
unless existing_config.has_key? k
|
60
|
+
token = ask "#{k} token: "
|
61
|
+
existing_config[k] = token
|
62
|
+
end
|
63
|
+
end
|
64
|
+
write_config existing_config
|
65
|
+
end
|
66
|
+
|
67
|
+
def write_config(conf = {})
|
36
68
|
File.open('twig.yml', 'w') { |file|
|
37
69
|
file.write conf.to_yaml
|
38
|
-
puts 'File di configurazione
|
70
|
+
puts 'File di configurazione aggiornato'.green
|
39
71
|
}
|
40
72
|
end
|
41
73
|
|
42
74
|
def create_clients
|
43
75
|
@gh = Octokit::Client.new(:access_token => @config['github'])
|
76
|
+
Aws.config.update({
|
77
|
+
region: 'eu-west-1',
|
78
|
+
credentials: Aws::Credentials.new(@config['aws_key_id'], @config['aws_key_secret']),
|
79
|
+
})
|
44
80
|
end
|
45
81
|
|
46
82
|
def disabled_branches
|
@@ -145,6 +181,12 @@ class Prima
|
|
145
181
|
end
|
146
182
|
end
|
147
183
|
|
184
|
+
def delete_label_from_issue(issue_number, label)
|
185
|
+
if issue_has_label?(issue_number, label)
|
186
|
+
@gh.remove_label repo_name, issue_number, label
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
148
190
|
def user_login
|
149
191
|
user = @gh.user
|
150
192
|
user.login
|
@@ -165,6 +207,22 @@ class Prima
|
|
165
207
|
results
|
166
208
|
end
|
167
209
|
|
210
|
+
def update_pr(pr_number, options = {})
|
211
|
+
@gh.update_pull_request repo_name, pr_number, options
|
212
|
+
end
|
213
|
+
|
214
|
+
def get_issue(issue_number)
|
215
|
+
@gh.issue repo_name, issue_number
|
216
|
+
end
|
217
|
+
|
218
|
+
def get_pr_commits(pr_number)
|
219
|
+
@gh.pull_request_commits repo_name, pr_number
|
220
|
+
end
|
221
|
+
|
222
|
+
def create_pull_request(base_branch, head, title, body = '')
|
223
|
+
@gh.create_pull_request repo_name, base_branch, head, title, body
|
224
|
+
end
|
225
|
+
|
168
226
|
def merge_pull_request(pr, commit_message='')
|
169
227
|
raise "Invalid Pull Request" unless pr
|
170
228
|
pr_number = pr[:number]
|
@@ -179,6 +237,10 @@ class Prima
|
|
179
237
|
`git config remote.origin.url`
|
180
238
|
end
|
181
239
|
|
240
|
+
def create_release(tag_name, options = {})
|
241
|
+
@gh.create_release repo_name, tag_name, options
|
242
|
+
end
|
243
|
+
|
182
244
|
def yesno(prompt = 'Continue?', default = true)
|
183
245
|
a = ''
|
184
246
|
s = default ? '[Y/n]' : '[y/N]'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prima-twig
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matteo Giachino
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2015-11-
|
14
|
+
date: 2015-11-30 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: twig
|
@@ -97,21 +97,69 @@ dependencies:
|
|
97
97
|
- - "~>"
|
98
98
|
- !ruby/object:Gem::Version
|
99
99
|
version: '0.22'
|
100
|
+
- !ruby/object:Gem::Dependency
|
101
|
+
name: redcarpet
|
102
|
+
requirement: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - "~>"
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '3.3'
|
107
|
+
type: :runtime
|
108
|
+
prerelease: false
|
109
|
+
version_requirements: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - "~>"
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '3.3'
|
114
|
+
- !ruby/object:Gem::Dependency
|
115
|
+
name: mail
|
116
|
+
requirement: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - "~>"
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '2.6'
|
121
|
+
type: :runtime
|
122
|
+
prerelease: false
|
123
|
+
version_requirements: !ruby/object:Gem::Requirement
|
124
|
+
requirements:
|
125
|
+
- - "~>"
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '2.6'
|
128
|
+
- !ruby/object:Gem::Dependency
|
129
|
+
name: aws-sdk
|
130
|
+
requirement: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - "~>"
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '2'
|
135
|
+
type: :runtime
|
136
|
+
prerelease: false
|
137
|
+
version_requirements: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - "~>"
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '2'
|
100
142
|
description: Our tools to manage git and github
|
101
143
|
email: matteo.giachino@prima.it
|
102
144
|
executables:
|
103
145
|
- twig-circle
|
146
|
+
- twig-deploy
|
147
|
+
- twig-feature
|
104
148
|
- twig-hotfix
|
105
149
|
- twig-open-pr
|
106
150
|
- twig-pick-issue
|
151
|
+
- twig-release
|
107
152
|
- twig-review
|
108
153
|
extensions: []
|
109
154
|
extra_rdoc_files: []
|
110
155
|
files:
|
111
156
|
- bin/twig-circle
|
157
|
+
- bin/twig-deploy
|
158
|
+
- bin/twig-feature
|
112
159
|
- bin/twig-hotfix
|
113
160
|
- bin/twig-open-pr
|
114
161
|
- bin/twig-pick-issue
|
162
|
+
- bin/twig-release
|
115
163
|
- bin/twig-review
|
116
164
|
- lib/command.rb
|
117
165
|
- lib/prima_twig.rb
|
@@ -135,7 +183,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
135
183
|
version: '0'
|
136
184
|
requirements: []
|
137
185
|
rubyforge_project:
|
138
|
-
rubygems_version: 2.4.
|
186
|
+
rubygems_version: 2.4.7
|
139
187
|
signing_key:
|
140
188
|
specification_version: 4
|
141
189
|
summary: The Prima twig toolbelt
|