etsy-deployinator 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ <div id="maintenance">
2
+ <h1> Deployinator is in maintenance mode.</h1>
3
+ <img src="/images/maintenance.gif"></img>
4
+ <h1> We'll be back shortly. But please see {{ maintenance_contact }} for more details. </h1>
5
+ </div>
@@ -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>
@@ -1,3 +1,3 @@
1
1
  module Deployinator
2
- VERSION = "1.0.2"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -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
@@ -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