etsy-deployinator 1.0.1 → 1.1.1
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/.gitignore +3 -0
- data/.travis.yml +8 -0
- data/Gemfile +0 -1
- data/README.md +47 -10
- data/Rakefile +0 -1
- data/bin/deployinator-tailer.rb +2 -2
- data/deployinator.gemspec +17 -10
- data/lib/deployinator/app.rb +53 -12
- data/lib/deployinator/controller.rb +8 -3
- data/lib/deployinator/helpers/concurrency.rb +70 -0
- data/lib/deployinator/helpers/dsh.rb +15 -5
- data/lib/deployinator/helpers/git.rb +16 -9
- data/lib/deployinator/helpers/version.rb +52 -52
- data/lib/deployinator/helpers.rb +109 -13
- data/lib/deployinator/static/css/maintenance.css +9 -0
- data/lib/deployinator/static/css/style.css +25 -5
- data/lib/deployinator/static/images/maintenance.gif +0 -0
- data/lib/deployinator/static/js/stats_load.js +10 -0
- data/lib/deployinator/templates/exception.mustache +11 -0
- data/lib/deployinator/templates/generic_single_push.mustache +2 -1
- data/lib/deployinator/templates/layout.mustache +5 -4
- data/lib/deployinator/templates/log_table.mustache +18 -5
- data/lib/deployinator/templates/maintenance.mustache +5 -0
- data/lib/deployinator/templates/stats.mustache +180 -0
- data/lib/deployinator/version.rb +1 -1
- data/lib/deployinator/views/log_table.rb +36 -0
- data/lib/deployinator/views/maintenance.rb +15 -0
- data/lib/deployinator/views/stats.rb +96 -0
- data/lib/deployinator.rb +20 -1
- data/test/unit/concurrency_test.rb +72 -0
- data/test/unit/git_test.rb +70 -0
- data/test/unit/helpers_test.rb +61 -2
- data/test/unit/version_test.rb +12 -12
- metadata +129 -27
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
<!-- content -->
|
|
2
|
+
<section id="main" class="info stats">
|
|
3
|
+
<!-- heading -->
|
|
4
|
+
<div class="heading clearfix">
|
|
5
|
+
<h2>Deployments Per Day (US/Eastern)</h2>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="stats-main">
|
|
8
|
+
<button id="scatterOrLine">Line</button>
|
|
9
|
+
<label><input type="checkbox" id="combined"> Combined</label>
|
|
10
|
+
<div id="placeholder" style="width:940px;height:300px;"></div>
|
|
11
|
+
<div id="overview" style="width:940px;height:120px;"></div>
|
|
12
|
+
<script>var data = {}</script>
|
|
13
|
+
|
|
14
|
+
<p id="choices"></p>
|
|
15
|
+
|
|
16
|
+
<div class="holder">
|
|
17
|
+
|
|
18
|
+
{{# per_day}}
|
|
19
|
+
<div class="stats">
|
|
20
|
+
<table class="stats">
|
|
21
|
+
<tr>
|
|
22
|
+
<th>Day</th>
|
|
23
|
+
<th>Count</th>
|
|
24
|
+
</tr>
|
|
25
|
+
{{# data}}
|
|
26
|
+
<tr>
|
|
27
|
+
<td>{{date}}</td>
|
|
28
|
+
<td class="count">{{count}}</td>
|
|
29
|
+
</tr>
|
|
30
|
+
{{/ data}}
|
|
31
|
+
</table>
|
|
32
|
+
<h3>{{stack}}</h3>
|
|
33
|
+
<script type="text/javascript" charset="utf-8">
|
|
34
|
+
data["{{stack}}"] = {data: {{json}}, label: "{{stack}}"}
|
|
35
|
+
</script>
|
|
36
|
+
</div>
|
|
37
|
+
{{/ per_day}}
|
|
38
|
+
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
</section>
|
|
43
|
+
|
|
44
|
+
<script type="text/javascript" charset="utf-8">
|
|
45
|
+
var x1, x2;
|
|
46
|
+
|
|
47
|
+
function getData(ignoreX) {
|
|
48
|
+
var d = [];
|
|
49
|
+
var c = { data: [], label: "combined" };
|
|
50
|
+
|
|
51
|
+
$("#choices").find("input:checked").each(function() {
|
|
52
|
+
if ($(this).attr("id") == "combined") { return; }
|
|
53
|
+
|
|
54
|
+
var key = $(this).attr("name");
|
|
55
|
+
|
|
56
|
+
if (key && data[key]) {
|
|
57
|
+
// combine all the data points if requested
|
|
58
|
+
if ($("#combined")[0].checked) {
|
|
59
|
+
for (var i=0; i<data[key].data.length; i++) {
|
|
60
|
+
var found = false;
|
|
61
|
+
var m = data[key].data[i];
|
|
62
|
+
for (var j=0; j<c.data.length; j++) {
|
|
63
|
+
var n = c.data[j];
|
|
64
|
+
if (n[0] == m[0]) {
|
|
65
|
+
n[1] += m[1];
|
|
66
|
+
found = true;
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (! found) { c.data[i] = [m[0], m[1]]; }
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
d.push(data[key]);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if ($("#combined")[0].checked) { d = [c]; }
|
|
80
|
+
|
|
81
|
+
var newD = [];
|
|
82
|
+
for (var i=0; i<d.length; i++) {
|
|
83
|
+
var dAr = [];
|
|
84
|
+
for (var j=0; j<d[i].data.length; j++) {
|
|
85
|
+
n = d[i].data[j];
|
|
86
|
+
dAr.push(n);
|
|
87
|
+
}
|
|
88
|
+
newD[i] = { label: d[i].label, data: dAr };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if ($("#combined")[0].checked) { outputTable(c); }
|
|
92
|
+
return newD;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function outputTable(c) {
|
|
96
|
+
$("#holderCombined").remove();
|
|
97
|
+
|
|
98
|
+
var table = $("<table id='holderCombined'>")
|
|
99
|
+
.html("<tr><th>Date</th><th>Combined</th></tr>")
|
|
100
|
+
.appendTo(".stats-main");
|
|
101
|
+
|
|
102
|
+
for (var i=0; i<c.data.length; i++) {
|
|
103
|
+
var m = c.data[i];
|
|
104
|
+
var dO = new Date(m[0]);
|
|
105
|
+
var dateS = (dO.getYear() + 1900) + "-" + dO.getMonth() + "-" + dO.getDate();
|
|
106
|
+
$("<tr><td>" + dateS + "</td><td>" + m[1] + "</td></tr>")
|
|
107
|
+
.appendTo(table);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function options() {
|
|
112
|
+
return {
|
|
113
|
+
xaxis: { mode: "time", timeformat: "%y/%m/%d", minTickSize: [1, "day"] },
|
|
114
|
+
legend: { position: "nw" },
|
|
115
|
+
points: { show: ($("#scatterOrLine").html() == "Scatter") },
|
|
116
|
+
lines: { show: ($("#scatterOrLine").html() == "Line") },
|
|
117
|
+
selection: { mode: "x" }
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
function sliceD(d, msg) {
|
|
122
|
+
return d.slice(d.slice(d.indexOf(x1), d.indexOf(x2)));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function drawGraphs() {
|
|
126
|
+
var d = getData();
|
|
127
|
+
|
|
128
|
+
var plot = $.plot($("#placeholder"), sliceD(d, "main"), options());
|
|
129
|
+
|
|
130
|
+
var overview = $.plot($("#overview"), d, {
|
|
131
|
+
legend: { show: false },
|
|
132
|
+
selection: { mode: "x" },
|
|
133
|
+
lines: { show: true },
|
|
134
|
+
xaxis: { mode: "time", timeformat: "%y/%m/%d" }
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
$("#placeholder").unbind("plotselected");
|
|
138
|
+
$("#placeholder").bind("plotselected", function (event, cRanges) {
|
|
139
|
+
ranges = cRanges;
|
|
140
|
+
x1 = Math.floor(ranges.xaxis.from);
|
|
141
|
+
x2 = Math.floor(ranges.xaxis.to);
|
|
142
|
+
|
|
143
|
+
// do the zooming
|
|
144
|
+
plot = $.plot($("#placeholder"), sliceD(d, "zoom"),
|
|
145
|
+
$.extend(true, {}, options(), {
|
|
146
|
+
xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to },
|
|
147
|
+
yaxis: { min: ranges.yaxis.from, max: ranges.yaxis.to }
|
|
148
|
+
}));
|
|
149
|
+
|
|
150
|
+
// don't fire event on the overview to prevent eternal loop
|
|
151
|
+
overview.setSelection(ranges, true);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
$("#overview").unbind("plotselected");
|
|
155
|
+
$("#overview").bind("plotselected", function (event, ranges) {
|
|
156
|
+
plot.setSelection(ranges);
|
|
157
|
+
|
|
158
|
+
x1 = Math.floor(ranges.xaxis.from);
|
|
159
|
+
x2 = Math.floor(ranges.xaxis.to);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
if (x1 && x2) {
|
|
163
|
+
ranges = {xaxis: {from: x1, to: x2}};
|
|
164
|
+
|
|
165
|
+
overview.setSelection(ranges);
|
|
166
|
+
plot.setSelection(ranges);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
$("#scatterOrLine").live("click", function () {
|
|
171
|
+
$(this).html(($(this).html() == "Line") ? "Scatter" : "Line");
|
|
172
|
+
|
|
173
|
+
drawGraphs();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
$("#combined").live("click", function () {
|
|
177
|
+
drawGraphs();
|
|
178
|
+
});
|
|
179
|
+
</script>
|
|
180
|
+
<script src="/js/stats_load.js"></script>
|
data/lib/deployinator/version.rb
CHANGED
|
@@ -17,6 +17,42 @@ module Deployinator::Views
|
|
|
17
17
|
@params[:show_counts] == "true"
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
def next_page_params
|
|
21
|
+
params = [
|
|
22
|
+
{
|
|
23
|
+
:name => "page",
|
|
24
|
+
:value => next_page
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
unless @params[:stack].nil?
|
|
29
|
+
params << {
|
|
30
|
+
:name => "stack",
|
|
31
|
+
:value => @params[:stack]
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
return params
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def prev_page_params
|
|
39
|
+
params = [
|
|
40
|
+
{
|
|
41
|
+
:name => "page",
|
|
42
|
+
:value => prev_page
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
unless @params[:stack].nil?
|
|
47
|
+
params << {
|
|
48
|
+
:name => "stack",
|
|
49
|
+
:value => @params[:stack]
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
return params
|
|
54
|
+
end
|
|
55
|
+
|
|
20
56
|
def prev_page
|
|
21
57
|
return unless @params && @params[:page]
|
|
22
58
|
page = @params[:page].to_i
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Deployinator::Views
|
|
2
|
+
class Maintenance < Layout
|
|
3
|
+
self.template_file = "#{File.dirname(__FILE__)}/../templates/maintenance.mustache"
|
|
4
|
+
def additional_header_html
|
|
5
|
+
<<-EOS
|
|
6
|
+
#{super}
|
|
7
|
+
<link rel="stylesheet" href="/css/maintenance.css" type="text/css" media="screen">
|
|
8
|
+
EOS
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def maintenance_contact
|
|
12
|
+
Deployinator.maintenance_contact
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require 'time'
|
|
3
|
+
|
|
4
|
+
module Deployinator::Views
|
|
5
|
+
class Stats < Layout
|
|
6
|
+
|
|
7
|
+
self.template_file = "#{File.dirname(__FILE__)}/../templates/stats.mustache"
|
|
8
|
+
|
|
9
|
+
@@ignored_stacks = Deployinator.stats_ignored_stacks || []
|
|
10
|
+
|
|
11
|
+
def deploys
|
|
12
|
+
@deploys ||= begin
|
|
13
|
+
log_to_hash({
|
|
14
|
+
:no_global => false,
|
|
15
|
+
:stack => Deployinator.stats_included_stacks,
|
|
16
|
+
:env => "production|search|prod",
|
|
17
|
+
:extragrep => Deployinator.stats_extra_grep,
|
|
18
|
+
:no_limit => true,
|
|
19
|
+
:limit => 10000
|
|
20
|
+
})
|
|
21
|
+
end
|
|
22
|
+
# note that the stack param will help but will send bring back extra lines that matched
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def timings
|
|
26
|
+
deploys
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def inject_renamed_stacks(renamed_stacks)
|
|
30
|
+
return if nil == renamed_stacks
|
|
31
|
+
|
|
32
|
+
renamed_stacks.each do |ops|
|
|
33
|
+
previous_stack = ops[:previous_stack]
|
|
34
|
+
new_stack_name = ops[:new_name]
|
|
35
|
+
|
|
36
|
+
renamed_stack_data = log_to_hash(previous_stack)
|
|
37
|
+
|
|
38
|
+
renamed_stack_data.each do |data|
|
|
39
|
+
data[:stack] = new_stack_name
|
|
40
|
+
deploys.push(data)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def per_day
|
|
46
|
+
inject_renamed_stacks(Deployinator.stats_renamed_stacks)
|
|
47
|
+
|
|
48
|
+
original_zone = ENV["TZ"]
|
|
49
|
+
ENV["TZ"] = "US/Eastern"
|
|
50
|
+
|
|
51
|
+
early_day = Time.now.strftime("%Y-%m-%d")
|
|
52
|
+
stack_days = deploys.inject({}) do |h, deploy|
|
|
53
|
+
if deploy[:time] && deploy[:stack]
|
|
54
|
+
if @@ignored_stacks.include?(deploy[:stack])
|
|
55
|
+
# puts "SKIPPING " + deploy[:stack]
|
|
56
|
+
# something breaks if you just next here so don't
|
|
57
|
+
else
|
|
58
|
+
day = Date.parse(deploy[:time].localtime.strftime("%Y-%m-%d"))
|
|
59
|
+
early_day = day if day.to_s < early_day.to_s
|
|
60
|
+
h[deploy[:stack]] ||= {}
|
|
61
|
+
h[deploy[:stack]][day] ||= 0
|
|
62
|
+
h[deploy[:stack]][day] += 1
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
h
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# fill in zero days
|
|
69
|
+
day_seconds = 24 * 60 * 60
|
|
70
|
+
(0..((Time.now - Time.parse(early_day.to_s)) / day_seconds).to_i).each do |days_ago|
|
|
71
|
+
stack_days.keys.each do |stack|
|
|
72
|
+
|
|
73
|
+
next if @@ignored_stacks.include?(stack)
|
|
74
|
+
day = Date.parse((Time.now - (day_seconds * days_ago)).strftime("%Y-%m-%d"))
|
|
75
|
+
stack_days[stack][day] ||= 0
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
n = stack_days.keys.map do |stack|
|
|
80
|
+
next if @@ignored_stacks.include?(stack)
|
|
81
|
+
|
|
82
|
+
data = []
|
|
83
|
+
json = []
|
|
84
|
+
stack_days[stack].sort.reverse.each do |d,c|
|
|
85
|
+
data << {:date => d, :count => c}
|
|
86
|
+
json << [d.strftime("%s").to_i * 1000, c]
|
|
87
|
+
end
|
|
88
|
+
{:stack => stack, :data => data, :json => json.to_json}
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
ENV["TZ"] = original_zone
|
|
92
|
+
|
|
93
|
+
n
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
data/lib/deployinator.rb
CHANGED
|
@@ -54,10 +54,27 @@ module Deployinator
|
|
|
54
54
|
attr_accessor :admin_groups
|
|
55
55
|
@admin_groups = []
|
|
56
56
|
|
|
57
|
+
attr_accessor :maintenance_mode
|
|
58
|
+
|
|
59
|
+
attr_accessor :maintenance_contact
|
|
60
|
+
|
|
57
61
|
# the controller class. defaults to Deployinator::Controller
|
|
58
62
|
# if you override this it should be a subclass of Deployinator::Controller
|
|
59
63
|
attr_accessor :deploy_controller
|
|
60
64
|
|
|
65
|
+
# include stacks in /stats
|
|
66
|
+
attr_accessor :stats_included_stacks
|
|
67
|
+
|
|
68
|
+
# exclude stacks in /stats, even if present in stats_included_stacks
|
|
69
|
+
attr_accessor :stats_ignored_stacks
|
|
70
|
+
|
|
71
|
+
# filter log entries in /stats
|
|
72
|
+
attr_accessor :stats_extra_grep
|
|
73
|
+
|
|
74
|
+
# list of configurations for grouping historical / renamed stacks in /stats
|
|
75
|
+
attr_accessor :stats_renamed_stacks
|
|
76
|
+
@@stats_renamed_stacks = []
|
|
77
|
+
|
|
61
78
|
def initialize
|
|
62
79
|
@stack_plugins = {}
|
|
63
80
|
@global_plugins = []
|
|
@@ -101,12 +118,14 @@ end
|
|
|
101
118
|
Deployinator.root_dir = Dir.pwd
|
|
102
119
|
Deployinator.app_context = {}
|
|
103
120
|
Deployinator.admin_groups = []
|
|
121
|
+
Deployinator.maintenance_mode = false
|
|
122
|
+
Deployinator.maintenance_contact = "admin@deployinator.example.com"
|
|
104
123
|
Deployinator.global_plugins = []
|
|
105
124
|
Deployinator.log_file = Deployinator.root(["log", "development.log"])
|
|
106
125
|
Deployinator.log_path = Deployinator.root(["log", "deployinator.log"])
|
|
107
126
|
Deployinator.timing_log_path = Deployinator.root(["log", "deployinator-timing.log"])
|
|
108
127
|
Deployinator.git_sha_length = "10"
|
|
109
|
-
Deployinator.default_user =
|
|
128
|
+
Deployinator.default_user = `/usr/bin/whoami`
|
|
110
129
|
Deployinator.stack_tailer_port = 7778
|
|
111
130
|
Deployinator.github_host = 'github.com'
|
|
112
131
|
Deployinator.app_context['context'] = 'dev'
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require 'deployinator'
|
|
2
|
+
require 'deployinator/helpers'
|
|
3
|
+
require 'deployinator/helpers/concurrency'
|
|
4
|
+
require 'test/unit'
|
|
5
|
+
require 'mocha/setup'
|
|
6
|
+
|
|
7
|
+
Celluloid.logger = nil
|
|
8
|
+
class ConcurrencyTest < Test::Unit::TestCase
|
|
9
|
+
# Celluloid recommends doing this before each test
|
|
10
|
+
# https://github.com/celluloid/celluloid/wiki/Gotchas#testing
|
|
11
|
+
include Deployinator::Helpers::ConcurrencyHelpers
|
|
12
|
+
def setup
|
|
13
|
+
Celluloid.boot
|
|
14
|
+
@@futures = {}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def teardown
|
|
18
|
+
Celluloid.shutdown
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_spawn_off_concurrent_thread
|
|
22
|
+
run_parallel(:test) do
|
|
23
|
+
"Running inside thread"
|
|
24
|
+
end
|
|
25
|
+
assert_equal(get_value(:test), "Running inside thread")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_future_name_type
|
|
29
|
+
assert_raise NoMethodError do
|
|
30
|
+
run_parallel({:test => 'going crazy'}) do
|
|
31
|
+
"Running inside thread"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def test_fibers_with_same_reference
|
|
37
|
+
run_parallel(:test) do
|
|
38
|
+
"Running inside thread"
|
|
39
|
+
end
|
|
40
|
+
assert_raise Deployinator::Helpers::ConcurrencyHelpers::DuplicateReferenceError do
|
|
41
|
+
run_parallel(:test) do
|
|
42
|
+
"Running inside thread"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def test_get_non_symbol_value
|
|
48
|
+
run_parallel(:test) do
|
|
49
|
+
"Running inside thread"
|
|
50
|
+
end
|
|
51
|
+
assert_raise NoMethodError do
|
|
52
|
+
get_value({:test => 'going crazy - jpaul'})
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def test_reference_taken
|
|
57
|
+
assert_equal(reference_taken?(:test), false)
|
|
58
|
+
@@futures[:test] = 'a value'
|
|
59
|
+
assert_equal(reference_taken?(:test), true)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_multiple_futures_return
|
|
63
|
+
run_parallel(:test1) do
|
|
64
|
+
"future1"
|
|
65
|
+
end
|
|
66
|
+
run_parallel(:test2) do
|
|
67
|
+
"future2"
|
|
68
|
+
end
|
|
69
|
+
assert_equal(get_values(:test1, :test2), {:test1 => "future1", :test2 => "future2"})
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require 'deployinator'
|
|
2
|
+
require 'deployinator/helpers'
|
|
3
|
+
require 'deployinator/helpers/git'
|
|
4
|
+
|
|
5
|
+
include Deployinator::Helpers::GitHelpers
|
|
6
|
+
|
|
7
|
+
class HelpersTest < Test::Unit::TestCase
|
|
8
|
+
|
|
9
|
+
def test_git_url_https
|
|
10
|
+
GitHelpers.expects(:which_github_host).returns("www.testmagic.com")
|
|
11
|
+
GitHelpers.expects(:git_info_for_stack).returns({:stack => {:repository => 'drills', :user => 'construction'}})
|
|
12
|
+
GitHelpers.expects(:git_info_for_stack).returns({:stack => {:repository => 'drills', :user => 'construction'}})
|
|
13
|
+
assert_equal('https://www.testmagic.com/construction/drills.git', GitHelpers.git_url(:stack, 'https', false))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_git_url_default
|
|
17
|
+
GitHelpers.expects(:which_github_host).returns("www.testmagic.com")
|
|
18
|
+
GitHelpers.expects(:git_info_for_stack).returns({:stack => {:repository => 'drills', :user => 'construction'}})
|
|
19
|
+
GitHelpers.expects(:git_info_for_stack).returns({:stack => {:repository => 'drills', :user => 'construction'}})
|
|
20
|
+
assert_equal('git://www.testmagic.com/construction/drills', GitHelpers.git_url(:stack))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_git_url_read_write
|
|
24
|
+
GitHelpers.expects(:which_github_host).returns("www.testmagic.com")
|
|
25
|
+
GitHelpers.expects(:git_info_for_stack).returns({:stack => {:repository => 'drills', :user => 'construction'}})
|
|
26
|
+
GitHelpers.expects(:git_info_for_stack).returns({:stack => {:repository => 'drills', :user => 'construction'}})
|
|
27
|
+
assert_equal('git@www.testmagic.com:construction/drills', GitHelpers.git_url(:stack, "git", true))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_git_freshen_or_clone_https_passthrough
|
|
31
|
+
GitHelpers.expects(:git_checkout_path).returns("/dev/null")
|
|
32
|
+
GitHelpers.expects(:is_git_repo).with("/dev/null", "extra-ssh").returns(:missing)
|
|
33
|
+
GitHelpers.expects(:log_and_stream).returns(nil)
|
|
34
|
+
GitHelpers.expects(:git_url).with(:stack, "https", false).returns("https://www.testmagic.com/construction/drills.git")
|
|
35
|
+
GitHelpers.expects(:git_clone).with(:stack, "https://www.testmagic.com/construction/drills.git", "extra-ssh", "/dev/null", "merge99")
|
|
36
|
+
GitHelpers.git_freshen_or_clone(:stack, "extra-ssh", "/dev/null", "merge99", false, "https")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def test_git_freshen_or_clone_git_update
|
|
40
|
+
GitHelpers.expects(:git_checkout_path).returns("/dev/null")
|
|
41
|
+
GitHelpers.expects(:is_git_repo).with("/dev/null", "extra-ssh").returns(:true)
|
|
42
|
+
GitHelpers.expects(:log_and_stream).returns(nil)
|
|
43
|
+
GitHelpers.expects(:git_freshen_clone).with(:stack, "extra-ssh", "/dev/null", "merge99", false)
|
|
44
|
+
GitHelpers.git_freshen_or_clone(:stack, "extra-ssh", "/dev/null", "merge99", false, "https")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def test_git_freshen_or_clone_git_update_force
|
|
48
|
+
GitHelpers.expects(:git_checkout_path).returns("/dev/null")
|
|
49
|
+
GitHelpers.expects(:is_git_repo).with("/dev/null", "extra-ssh").returns(:true)
|
|
50
|
+
GitHelpers.expects(:log_and_stream).returns(nil)
|
|
51
|
+
GitHelpers.expects(:git_freshen_clone).with(:stack, "extra-ssh", "/dev/null", "merge99", true)
|
|
52
|
+
GitHelpers.git_freshen_or_clone(:stack, "extra-ssh", "/dev/null", "merge99", false, "https", true)
|
|
53
|
+
end
|
|
54
|
+
def test_git_freshen_or_clone_git_bad_repo
|
|
55
|
+
GitHelpers.expects(:git_checkout_path).returns("/dev/null")
|
|
56
|
+
GitHelpers.expects(:is_git_repo).with("/dev/null", "extra-ssh").returns(:false)
|
|
57
|
+
GitHelpers.expects(:log_and_stream).returns(nil)
|
|
58
|
+
GitHelpers.git_freshen_or_clone(:stack, "extra-ssh", "/dev/null", "merge99", false, "https")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def test_git_head_rev_should_cache_results
|
|
62
|
+
FileUtils.rm_f('/tmp/rev_head_cache_some_stack')
|
|
63
|
+
|
|
64
|
+
head_rev_sha = 'ba83f60523008e48950f77bd0d3a773f9cb2805c'
|
|
65
|
+
GitHelpers.expects(:get_git_head_rev).with('some_stack', 'master').returns(head_rev_sha).once
|
|
66
|
+
assert_equal(head_rev_sha, GitHelpers.git_head_rev('some_stack'))
|
|
67
|
+
# Calling it a second time should just use the cached result on disk
|
|
68
|
+
assert_equal(head_rev_sha, GitHelpers.git_head_rev('some_stack'))
|
|
69
|
+
end
|
|
70
|
+
end
|
data/test/unit/helpers_test.rb
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
1
3
|
require 'deployinator'
|
|
2
4
|
require 'deployinator/helpers'
|
|
3
5
|
require 'deployinator/helpers/dsh'
|
|
6
|
+
require 'tempfile'
|
|
4
7
|
|
|
5
8
|
class HelpersTest < Test::Unit::TestCase
|
|
6
9
|
include Deployinator::Helpers,
|
|
@@ -12,6 +15,7 @@ class HelpersTest < Test::Unit::TestCase
|
|
|
12
15
|
@issue2 = "DEF-456"
|
|
13
16
|
@issue1_linked = "#{@jurl % ([@issue1] * 2)}"
|
|
14
17
|
@issue2_linked = "#{@jurl % ([@issue2] * 2)}"
|
|
18
|
+
@utf8_canary = 'Iñtërnâtiônàlizætiøn'
|
|
15
19
|
Deployinator.default_user = "testuser"
|
|
16
20
|
Deployinator.deploy_host = "deploytest.vm.ny4dev.etsy.com"
|
|
17
21
|
Deployinator.issue_tracker = proc do |issue|
|
|
@@ -61,9 +65,9 @@ class HelpersTest < Test::Unit::TestCase
|
|
|
61
65
|
|
|
62
66
|
def test_with_timeout_success
|
|
63
67
|
res = with_timeout 3 do
|
|
64
|
-
`sleep 2 && echo
|
|
68
|
+
`sleep 2 && echo "foo"`
|
|
65
69
|
end
|
|
66
|
-
assert_equal "foo", res
|
|
70
|
+
assert_equal "foo\n", res
|
|
67
71
|
end
|
|
68
72
|
|
|
69
73
|
def test_strip_to_nil
|
|
@@ -74,4 +78,59 @@ class HelpersTest < Test::Unit::TestCase
|
|
|
74
78
|
assert_equal('host01,host02', strip_ws_to_nil(' host01 , host02 '))
|
|
75
79
|
assert_equal('host01', strip_ws_to_nil('host01'))
|
|
76
80
|
end
|
|
81
|
+
|
|
82
|
+
def test_get_from_cache
|
|
83
|
+
Tempfile.open('cache_file', encoding: 'UTF-8') do |tf|
|
|
84
|
+
tf.write(@utf8_canary)
|
|
85
|
+
tf.flush
|
|
86
|
+
|
|
87
|
+
cached_content = get_from_cache(tf.path)
|
|
88
|
+
assert_equal(@utf8_canary, cached_content)
|
|
89
|
+
assert_equal(Encoding.find('UTF-8'), cached_content.encoding)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def test_get_from_cache_when_file_does_not_exist
|
|
94
|
+
assert_equal(false, get_from_cache('/does/not/exist'))
|
|
95
|
+
assert_equal(false, get_from_cache('/does/not/exist', -1))
|
|
96
|
+
assert_equal(false, get_from_cache('/does/not/exist', 10))
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def test_get_from_cache_when_file_is_old
|
|
100
|
+
Tempfile.open('cache_file') do |tf|
|
|
101
|
+
File.stubs(:mtime).with(tf.path).returns(Time.now - 10)
|
|
102
|
+
|
|
103
|
+
assert_equal(false, get_from_cache(tf.path))
|
|
104
|
+
assert_not_equal(false, get_from_cache(tf.path, 30))
|
|
105
|
+
assert_not_equal(false, get_from_cache(tf.path, -1))
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def test_write_to_cache
|
|
110
|
+
Tempfile.open('cache_file') do |tf|
|
|
111
|
+
write_to_cache(tf.path, @utf8_canary)
|
|
112
|
+
|
|
113
|
+
cached_content = get_from_cache(tf.path)
|
|
114
|
+
assert_equal(@utf8_canary, cached_content)
|
|
115
|
+
assert_equal(Encoding.find('UTF-8'), cached_content.encoding)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def test_is_admin_groups_nil
|
|
120
|
+
assert_false(is_admin?)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def test_is_admin_groups_true
|
|
124
|
+
assert_true(is_admin?(["foo", "bar"], ["bar"]))
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def test_is_admin_groups_false
|
|
128
|
+
assert_false(is_admin?(["foo", "bar"], ["bla"]))
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def test_is_admin_groups_true_with_fallback
|
|
132
|
+
@groups = ["foo", "bar"]
|
|
133
|
+
Deployinator.admin_groups = ["bar"]
|
|
134
|
+
assert_true(is_admin?(["foo", "bar"], ["bar"]))
|
|
135
|
+
end
|
|
77
136
|
end
|