prima-twig 0.2.6 → 0.2.8
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.
- 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
|