shipit-engine 0.45.1 → 0.45.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 391e77ca5abe04617e4a8516804feb802778b777a37fe3797f37986d9344838d
4
- data.tar.gz: 7cbf010614b60f11a67ca39c25d3a5c3a15a404f59160566989b705690fa652f
3
+ metadata.gz: afae76c5544a76b67f5b809fcd76784ba9a24cdbfe2a38d3b6ac646e618e535a
4
+ data.tar.gz: f7756ed9a71bfb8c9b138206ea830a8d0d972e7e5cd3284b2b7a880afdb7112b
5
5
  SHA512:
6
- metadata.gz: be5fe9aba69281bef4a301180f0814c16e6094b1a3949f1903ae3e10bb2f8cd5375d9078994f210d8337eb30a84ae8afb585d838dd99d727ec9abb112df50677
7
- data.tar.gz: 9e64330a930d8cd30599196a750ee5eb8ed73c310dcbf76752307c10991c61198ae11ce063ab2831daa537318b9f332eede5f1d6455e07c295daba30e3c56cd7
6
+ metadata.gz: 45ad16450ce9f7f4cd94af2f109362b9da7fa74a02b54b017098f615c647711681c8b8f2ebc44b0bef6f8e5e03d0d415faf05191d377c6af8aab6e8cefe08b01
7
+ data.tar.gz: 881b0fb03c78cd01d7c25e8de9cf9421e74cb7abbd7855ecb8b7de5024364323b8ba531362c083d3019fa60dbd2075b34fd0fba2aa6e3141d10b7ce21ae6324d
@@ -69,7 +69,12 @@ module Shipit
69
69
  def refresh
70
70
  RefreshStatusesJob.perform_later(stack_id: stack.id)
71
71
  RefreshCheckRunsJob.perform_later(stack_id: stack.id)
72
- GithubSyncJob.perform_later(stack_id: stack.id)
72
+ # force_spec_cache: explicit refreshes always recompute the cached deploy
73
+ # spec, even when the head hasn't moved: refreshing is how a stale or
74
+ # broken cached spec is fixed. Threading it through the sync job (rather
75
+ # than enqueuing CacheDeploySpecJob directly) guarantees the spec is
76
+ # computed from the post-sync head.
77
+ GithubSyncJob.perform_later(stack_id: stack.id, force_spec_cache: true)
73
78
  render_resource(stack, status: :accepted)
74
79
  end
75
80
 
@@ -43,7 +43,9 @@ module Shipit
43
43
  end
44
44
 
45
45
  def update_params
46
- params.require(:api_client).permit(permissions: [])
46
+ permitted = params.require(:api_client).permit(permissions: [])
47
+ permitted[:permissions] = permitted[:permissions].reject(&:blank?)
48
+ permitted
47
49
  end
48
50
  end
49
51
  end
@@ -94,7 +94,12 @@ module Shipit
94
94
  def refresh
95
95
  RefreshStatusesJob.perform_later(stack_id: @stack.id)
96
96
  RefreshCheckRunsJob.perform_later(stack_id: @stack.id)
97
- GithubSyncJob.perform_later(stack_id: @stack.id)
97
+ # force_spec_cache: explicit refreshes always recompute the cached deploy
98
+ # spec, even when the head hasn't moved: refreshing is how a stale or
99
+ # broken cached spec is fixed. Threading it through the sync job (rather
100
+ # than enqueuing CacheDeploySpecJob directly) guarantees the spec is
101
+ # computed from the post-sync head.
102
+ GithubSyncJob.perform_later(stack_id: @stack.id, force_spec_cache: true)
98
103
  flash[:success] = 'Refresh scheduled'
99
104
  redirect_to(request.referer.presence || stack_path(@stack))
100
105
  end
@@ -7,13 +7,25 @@ module Shipit
7
7
 
8
8
  queue_as :deploys
9
9
 
10
+ # Caps job execution AND sets the dedupe lock expiration to match.
11
+ # Without it the lock falls back to Unique::DEFAULT_TIMEOUT (10s), which is
12
+ # far shorter than the job's runtime, letting duplicate jobs for the same
13
+ # stack run concurrently once the lock expires mid-run.
14
+ self.timeout = 15.minutes.to_i
15
+
10
16
  def perform(stack)
11
17
  return if stack.inaccessible?
12
18
 
19
+ commit = stack.commits.reachable.last
13
20
  commands = Commands.for(stack)
14
- commands.with_temporary_working_directory(commit: stack.commits.reachable.last) do |path|
21
+ commands.with_temporary_working_directory(commit:, recursive: false) do |path|
15
22
  stack.update!(cached_deploy_spec: DeploySpec::FileSystem.new(path, stack))
16
23
  end
24
+
25
+ # A duplicate enqueued while this job held the dedupe lock was dropped;
26
+ # if the head moved under us, that dropped job's work is still
27
+ # outstanding, so hand it off rather than leaving the spec stale.
28
+ CacheDeploySpecJob.perform_later(stack) if stack.commits.reachable.last&.id != commit&.id
17
29
  end
18
30
  end
19
31
  end
@@ -7,6 +7,17 @@ module Shipit
7
7
  queue_as :default
8
8
  on_duplicate :drop
9
9
 
10
+ # Transient Octokit::Unauthorized = GitHub installation-token propagation lag.
11
+ # attempts: 14 (~24h) outlasts the 50m token cache (GITHUB_TOKEN_RAILS_CACHE_LIFETIME).
12
+ # No token eviction here to avoid a remint storm across workers.
13
+ retry_on Octokit::Unauthorized, wait: :polynomially_longer, attempts: 14 do |job, exception|
14
+ record = job.arguments.first
15
+ Rails.logger.warn(
16
+ "[CreateOnGithubJob] Giving up on #{record.class.name} #{record.id} " \
17
+ "after GitHub authentication failures: #{exception.class} #{exception.message}"
18
+ )
19
+ end
20
+
10
21
  # We observe that some objects regularly take longer than the default 10 seconds to create, e.g. deployments
11
22
  self.timeout = 40
12
23
  self.lock_timeout = 20
@@ -19,6 +19,8 @@ module Shipit
19
19
  @stack = Stack.find(params[:stack_id])
20
20
  expected_head_sha = params[:expected_head_sha]
21
21
  retry_count = params[:retry_count] || 0
22
+ head_before_sync = spec_cache_target
23
+ appended_commits = []
22
24
 
23
25
  handle_github_errors do
24
26
  new_commits, shared_parent = fetch_missing_commits { stack.github_commits }
@@ -38,6 +40,11 @@ module Shipit
38
40
  stack.lock_reverted_commits! if appended_commits.any?(&:revert?)
39
41
  end
40
42
  end
43
+ sync_changed_nothing = appended_commits.empty? &&
44
+ spec_cache_target == head_before_sync &&
45
+ stack.cached_deploy_spec.present?
46
+ return if sync_changed_nothing && !params[:force_spec_cache]
47
+
41
48
  CacheDeploySpecJob.perform_later(stack)
42
49
  end
43
50
 
@@ -63,6 +70,13 @@ module Shipit
63
70
 
64
71
  protected
65
72
 
73
+ # The commit CacheDeploySpecJob would check out: the newest reachable one.
74
+ # If it didn't change during the sync (no appends, no detaches), the cached
75
+ # spec is still accurate and doesn't need to be recomputed.
76
+ def spec_cache_target
77
+ stack.commits.reachable.last&.sha
78
+ end
79
+
66
80
  def handle_github_errors
67
81
  yield
68
82
  rescue Octokit::NotFound
@@ -106,24 +106,30 @@ module Shipit
106
106
  build_config(config_file_path, config_obj)
107
107
  end
108
108
 
109
+ YAML_EXTENSIONS = ["yml", "yaml"].freeze
110
+
109
111
  def shipit_file_names_in_priority_order
110
- [
111
- "#{app_name}.#{@env}.yml",
112
- ".shipit/#{app_name}.#{@env}.yml",
112
+ YAML_EXTENSIONS.flat_map do |ext|
113
+ [
114
+ "#{app_name}.#{@env}.#{ext}",
115
+ ".shipit/#{app_name}.#{@env}.#{ext}",
113
116
 
114
- "#{app_name}.yml",
115
- ".shipit/#{app_name}.yml",
117
+ "#{app_name}.#{ext}",
118
+ ".shipit/#{app_name}.#{ext}",
116
119
 
117
- "shipit.#{@env}.yml",
118
- ".shipit/#{@env}.yml",
120
+ "shipit.#{@env}.#{ext}",
121
+ ".shipit/#{@env}.#{ext}",
119
122
 
120
- "shipit.yml",
121
- ".shipit/shipit.yml"
122
- ].uniq
123
+ "shipit.#{ext}",
124
+ ".shipit/shipit.#{ext}"
125
+ ]
126
+ end.uniq
123
127
  end
124
128
 
125
129
  def bare_shipit_filenames
126
- ["#{app_name}.yml", "shipit.yml", ".shipit/#{app_name}.yml", ".shipit/shipit.yml"].uniq
130
+ YAML_EXTENSIONS.flat_map do |ext|
131
+ ["#{app_name}.#{ext}", "shipit.#{ext}", ".shipit/#{app_name}.#{ext}", ".shipit/shipit.#{ext}"]
132
+ end.uniq
127
133
  end
128
134
 
129
135
  def config_file_path
@@ -117,11 +117,17 @@ module Shipit
117
117
  )
118
118
 
119
119
  def self.refresh_deployed_revisions
120
- find_each.select(&:supports_fetch_deployed_revision?).each(&:async_refresh_deployed_revision)
120
+ # where.not avoids deserializing every stack's cached_deploy_spec each
121
+ # minute: a stack without a cached spec cannot have fetch steps.
122
+ not_archived
123
+ .where.not(cached_deploy_spec: nil)
124
+ .find_each
125
+ .select(&:supports_fetch_deployed_revision?)
126
+ .each(&:async_refresh_deployed_revision)
121
127
  end
122
128
 
123
129
  def self.schedule_continuous_delivery
124
- where(continuous_deployment: true).find_each do |stack|
130
+ not_archived.where(continuous_deployment: true).find_each do |stack|
125
131
  ContinuousDeliveryJob.perform_later(stack)
126
132
  end
127
133
  end
@@ -17,6 +17,7 @@
17
17
  <section>
18
18
  <%= form_for @api_client, url: api_client_path(@api_client) do |f| %>
19
19
  <h3> Permissions </h3>
20
+ <%= hidden_field_tag 'api_client[permissions][]', '' %>
20
21
  <ul class="deploy-checklist">
21
22
  <% Shipit::ApiClient::PERMISSIONS.each do |permission| %>
22
23
  <li class="deploy-checklist__item">
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Shipit
4
- VERSION = '0.45.1'
4
+ VERSION = '0.45.3'
5
5
  end
@@ -260,8 +260,8 @@ module Shipit
260
260
  assert_json 'message', 'This operation requires the `write:stack` permission'
261
261
  end
262
262
 
263
- test "#refresh queues a GithubSyncJob" do
264
- assert_enqueued_with(job: GithubSyncJob, args: [stack_id: @stack.id]) do
263
+ test "#refresh queues a GithubSyncJob with force_spec_cache" do
264
+ assert_enqueued_with(job: GithubSyncJob, args: [stack_id: @stack.id, force_spec_cache: true]) do
265
265
  post :refresh, params: { id: @stack.to_param }
266
266
  end
267
267
  assert_response :accepted
@@ -204,7 +204,7 @@ module Shipit
204
204
 
205
205
  assert_enqueued_with(job: RefreshStatusesJob, args: [stack_id: @stack.id]) do
206
206
  assert_enqueued_with(job: RefreshCheckRunsJob, args: [stack_id: @stack.id]) do
207
- assert_enqueued_with(job: GithubSyncJob, args: [stack_id: @stack.id]) do
207
+ assert_enqueued_with(job: GithubSyncJob, args: [stack_id: @stack.id, force_spec_cache: true]) do
208
208
  post :refresh, params: { id: @stack.to_param }
209
209
  end
210
210
  end
@@ -14,11 +14,67 @@ module Shipit
14
14
  @stack.update!(cached_deploy_spec: DeploySpec.new('review' => { 'checklist' => %w[foo bar] }))
15
15
 
16
16
  dir = Pathname(Dir.tmpdir)
17
- StackCommands.any_instance.expects(:with_temporary_working_directory).with(commit: @last_commit).yields(dir)
17
+ StackCommands.any_instance.expects(:with_temporary_working_directory)
18
+ .with(commit: @last_commit, recursive: false).yields(dir)
18
19
 
19
20
  assert_equal %w[foo bar], @stack.checklist
20
21
  @job.perform(@stack)
21
22
  assert_equal [], @stack.reload.checklist
22
23
  end
24
+
25
+ test "the dedupe lock expiration covers the job runtime" do
26
+ assert_operator CacheDeploySpecJob.timeout, :>, BackgroundJob::Unique::DEFAULT_TIMEOUT
27
+ assert_equal 15.minutes.to_i, CacheDeploySpecJob.timeout
28
+ end
29
+
30
+ test "the redis lock is created with the job timeout as its expiration" do
31
+ mutex = mock
32
+ mutex.expects(:lock).yields
33
+ Redis::Lock.expects(:new)
34
+ .with(anything, anything, expiration: 15.minutes.to_i, timeout: 0)
35
+ .returns(mutex)
36
+
37
+ executed = false
38
+ CacheDeploySpecJob.new(@stack).acquire_lock { executed = true }
39
+ assert executed
40
+ end
41
+
42
+ test "#perform re-enqueues itself when the head moves during the run" do
43
+ moved_head = @stack.commits.reachable.first
44
+ reachable = mock
45
+ reachable.stubs(:last).returns(@last_commit, moved_head)
46
+ @stack.stubs(:commits).returns(stub(reachable:))
47
+ @stack.stubs(:update!) # side-effect callbacks are irrelevant to this test
48
+
49
+ StackCommands.any_instance.expects(:with_temporary_working_directory)
50
+ .with(commit: @last_commit, recursive: false).yields(Pathname(Dir.tmpdir))
51
+
52
+ assert_enqueued_with(job: CacheDeploySpecJob, args: [@stack]) do
53
+ @job.perform(@stack)
54
+ end
55
+ end
56
+
57
+ test "#perform does not re-enqueue itself when the head is unchanged" do
58
+ StackCommands.any_instance.expects(:with_temporary_working_directory)
59
+ .with(commit: @last_commit, recursive: false).yields(Pathname(Dir.tmpdir))
60
+
61
+ assert_no_enqueued_jobs(only: CacheDeploySpecJob) do
62
+ @job.perform(@stack)
63
+ end
64
+ end
65
+
66
+ test "a duplicate job for the same stack is dropped while the lock is held" do
67
+ job = CacheDeploySpecJob.new(@stack)
68
+ duplicate = CacheDeploySpecJob.new(@stack)
69
+ duplicate_ran = false
70
+
71
+ job.acquire_lock do
72
+ duplicate.acquire_lock do
73
+ duplicate_ran = true
74
+ end
75
+ end
76
+
77
+ refute duplicate_ran, "duplicate should have been dropped, not executed"
78
+ end
23
79
  end
24
80
  end
@@ -16,10 +16,50 @@ module Shipit
16
16
  @job.perform(stack_id: @stack.id)
17
17
  end
18
18
 
19
- test "#perform finally enqueue a CacheDeploySpecJob" do
19
+ test "#perform does not enqueue a CacheDeploySpecJob when the sync found nothing new" do
20
+ Stack.any_instance.stubs(:github_commits).returns(@github_commits)
21
+ @job.stubs(:fetch_missing_commits).yields.returns([[], nil])
22
+
23
+ assert_no_enqueued_jobs(only: CacheDeploySpecJob) do
24
+ @job.perform(stack_id: @stack.id)
25
+ end
26
+ end
27
+
28
+ test "#perform enqueues a CacheDeploySpecJob when the cached spec is missing" do
29
+ @stack.update!(cached_deploy_spec: nil)
30
+ Stack.any_instance.stubs(:github_commits).returns(@github_commits)
31
+ @job.stubs(:fetch_missing_commits).yields.returns([[], nil])
32
+
33
+ assert_enqueued_with(job: CacheDeploySpecJob, args: [@stack]) do
34
+ @job.perform(stack_id: @stack.id)
35
+ end
36
+ end
37
+
38
+ test "#perform enqueues a CacheDeploySpecJob when nothing changed but force_spec_cache is set" do
20
39
  Stack.any_instance.stubs(:github_commits).returns(@github_commits)
21
40
  @job.stubs(:fetch_missing_commits).yields.returns([[], nil])
22
41
 
42
+ assert_enqueued_with(job: CacheDeploySpecJob, args: [@stack]) do
43
+ @job.perform(stack_id: @stack.id, force_spec_cache: true)
44
+ end
45
+ end
46
+
47
+ test "#perform preserves force_spec_cache across eventual-consistency retries" do
48
+ expected_sha = "abcd1234"
49
+ Stack.any_instance.expects(:github_commits).returns(@github_commits)
50
+ @job.expects(:fetch_missing_commits).yields.returns([[], nil])
51
+ @job.expects(:commit_exists?).with(expected_sha).returns(false)
52
+
53
+ expected_args = { stack_id: @stack.id, expected_head_sha: expected_sha, force_spec_cache: true, retry_count: 1 }
54
+ assert_enqueued_with(job: GithubSyncJob, args: [expected_args]) do
55
+ @job.perform(stack_id: @stack.id, expected_head_sha: expected_sha, force_spec_cache: true)
56
+ end
57
+ end
58
+
59
+ test "#perform enqueues a CacheDeploySpecJob when commits are detached without new commits" do
60
+ Stack.any_instance.stubs(:github_commits).returns(@github_commits)
61
+ @job.stubs(:fetch_missing_commits).yields.returns([[], shipit_commits(:third)])
62
+
23
63
  assert_enqueued_with(job: CacheDeploySpecJob, args: [@stack]) do
24
64
  @job.perform(stack_id: @stack.id)
25
65
  end
@@ -155,7 +195,9 @@ module Shipit
155
195
  @job.expects(:fetch_missing_commits).yields.returns([[], nil])
156
196
  @job.expects(:commit_exists?).with(expected_sha).returns(true)
157
197
 
158
- assert_enqueued_with(job: CacheDeploySpecJob, args: [@stack]) do
198
+ # No retry is scheduled, and since the sync found nothing new,
199
+ # no spec re-cache is needed either.
200
+ assert_no_enqueued_jobs(only: [GithubSyncJob, CacheDeploySpecJob]) do
159
201
  @job.perform(stack_id: @stack.id, expected_head_sha: expected_sha)
160
202
  end
161
203
  end
@@ -107,7 +107,7 @@ module Shipit
107
107
  end
108
108
 
109
109
  test "mark deploy as error an unexpected exception is raised" do
110
- Command.any_instance.expects(:stream!).at_least_once.raises(Command::Denied)
110
+ Shipit::TaskExecutionStrategy::Default.any_instance.expects(:capture!).at_least_once.raises(Command::Denied)
111
111
 
112
112
  @job.perform(@deploy)
113
113
 
@@ -116,7 +116,7 @@ module Shipit
116
116
  end
117
117
 
118
118
  test "mark deploy as timedout if a command timeout" do
119
- Command.any_instance.expects(:stream!).at_least_once.raises(Command::TimedOut)
119
+ Shipit::TaskExecutionStrategy::Default.any_instance.expects(:capture!).at_least_once.raises(Command::TimedOut)
120
120
 
121
121
  @job.perform(@deploy)
122
122
 
@@ -129,7 +129,7 @@ module Shipit
129
129
  begin
130
130
  Shipit.timeout_exit_codes = [70].freeze
131
131
 
132
- Command.any_instance.expects(:stream!).at_least_once.raises(Command::Failed.new('Blah', 70))
132
+ Shipit::TaskExecutionStrategy::Default.any_instance.expects(:capture!).at_least_once.raises(Command::Failed.new('Blah', 70))
133
133
 
134
134
  @job.perform(@deploy)
135
135
 
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ module Shipit
6
+ class CreateOnGithubJobTest < ActiveSupport::TestCase
7
+ setup do
8
+ @deployment = shipit_commit_deployments(:shipit_pending_fourth)
9
+ end
10
+
11
+ test "#perform retries on GitHub authentication errors" do
12
+ CommitDeployment.any_instance.stubs(:create_on_github!).raises(Octokit::Unauthorized)
13
+
14
+ assert_enqueued_with(job: CreateOnGithubJob) do
15
+ CreateOnGithubJob.perform_now(@deployment)
16
+ end
17
+ end
18
+
19
+ test "#perform gives up without re-raising after exhausting authentication retries" do
20
+ CommitDeployment.any_instance.stubs(:create_on_github!).raises(Octokit::Unauthorized)
21
+ Rails.logger.stubs(:warn)
22
+
23
+ job = CreateOnGithubJob.new(@deployment)
24
+ job.exception_executions = { "[Octokit::Unauthorized]" => 13 }
25
+
26
+ assert_nothing_raised { job.perform_now }
27
+ end
28
+ end
29
+ end
@@ -11,6 +11,38 @@ module Shipit
11
11
  GithubHook.any_instance.stubs(:teardown!)
12
12
  end
13
13
 
14
+ test ".schedule_continuous_delivery skips archived stacks" do
15
+ archived = shipit_stacks(:archived_6hours_ago)
16
+ archived.update!(continuous_deployment: true)
17
+ @stack.update!(continuous_deployment: true)
18
+
19
+ Stack.schedule_continuous_delivery
20
+
21
+ enqueued_args = enqueued_jobs
22
+ .select { |job| job[:job] == ContinuousDeliveryJob }
23
+ .map { |job| job[:args].to_s }
24
+ assert enqueued_args.any? { |args| args.include?("Stack/#{@stack.id}") },
25
+ "expected a ContinuousDeliveryJob for the active stack"
26
+ refute enqueued_args.any? { |args| args.include?("Stack/#{archived.id}") },
27
+ "archived stacks must not trigger continuous delivery"
28
+ end
29
+
30
+ test ".refresh_deployed_revisions skips archived stacks" do
31
+ archived = shipit_stacks(:archived_6hours_ago)
32
+ archived.update!(cached_deploy_spec: DeploySpec.new('fetch' => ['echo 1']))
33
+ @stack.update!(cached_deploy_spec: DeploySpec.new('fetch' => ['echo 1']))
34
+
35
+ Stack.refresh_deployed_revisions
36
+
37
+ enqueued_args = enqueued_jobs
38
+ .select { |job| job[:job] == FetchDeployedRevisionJob }
39
+ .map { |job| job[:args].to_s }
40
+ assert enqueued_args.any? { |args| args.include?("Stack/#{@stack.id}") },
41
+ "expected a FetchDeployedRevisionJob for the active stack with fetch steps"
42
+ refute enqueued_args.any? { |args| args.include?("Stack/#{archived.id}") },
43
+ "archived stacks must not refresh deployed revisions"
44
+ end
45
+
14
46
  test "branch defaults to default branch name" do
15
47
  @stack.branch = ""
16
48
  Shipit.github.api.expects(:repo).with("shopify/shipit-engine").returns(
@@ -251,7 +251,12 @@
251
251
  var tag = document.createElement(this.options.tag),
252
252
  clusterize_prefix = 'clusterize-';
253
253
  tag.className = [clusterize_prefix + 'extra-row', clusterize_prefix + class_name].join(' ');
254
- height && (tag.style.height = height + 'px');
254
+ // CSP FIX: this element is serialized to markup via outerHTML and re-parsed by
255
+ // innerHTML below. Under a `style-src` policy without 'unsafe-inline', the browser
256
+ // refuses to apply a style ATTRIBUTE that came from parsed markup, so the spacer
257
+ // renders at height 0 and virtual scrolling collapses. Carry the height in a data
258
+ // attribute instead and apply it through CSSOM after insertion, which CSP allows.
259
+ height && tag.setAttribute('data-clusterize-height', height);
255
260
  return tag.outerHTML;
256
261
  },
257
262
  // if necessary verify data changed and insert to DOM
@@ -279,6 +284,14 @@
279
284
  }
280
285
  },
281
286
  // unfortunately ie <= 9 does not allow to use innerHTML for table elements, so make a workaround
287
+ // CSP FIX: apply spacer heights via CSSOM once the nodes are live in the document.
288
+ applyExtraRowHeights: function() {
289
+ var rows = this.content_elem.getElementsByClassName('clusterize-extra-row');
290
+ for(var i = 0; i < rows.length; i++) {
291
+ var h = rows[i].getAttribute('data-clusterize-height');
292
+ if(h) rows[i].style.height = h + 'px';
293
+ }
294
+ },
282
295
  html: function(data) {
283
296
  var content_elem = this.content_elem;
284
297
  if(ie && ie <= 9 && this.options.tag == 'tr') {
@@ -294,6 +307,7 @@
294
307
  } else {
295
308
  content_elem.innerHTML = data;
296
309
  }
310
+ this.applyExtraRowHeights();
297
311
  },
298
312
  getChildNodes: function(tag) {
299
313
  var child_nodes = tag.children, nodes = [];
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shipit-engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.45.1
4
+ version: 0.45.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jean Boussier
@@ -918,6 +918,7 @@ files:
918
918
  - test/jobs/refresh_status_job_test.rb
919
919
  - test/jobs/shipit/background_job_test.rb
920
920
  - test/jobs/shipit/continuous_delivery_job_test.rb
921
+ - test/jobs/shipit/create_on_github_job_test.rb
921
922
  - test/jobs/unique_job_test.rb
922
923
  - test/jobs/update_github_last_deployed_ref_job_test.rb
923
924
  - test/lib/shipit/deploy_commands_test.rb
@@ -1018,7 +1019,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1018
1019
  - !ruby/object:Gem::Version
1019
1020
  version: '0'
1020
1021
  requirements: []
1021
- rubygems_version: 4.0.8
1022
+ rubygems_version: 4.0.19
1022
1023
  specification_version: 4
1023
1024
  summary: Application deployment software
1024
1025
  test_files:
@@ -1150,6 +1151,7 @@ test_files:
1150
1151
  - test/jobs/refresh_status_job_test.rb
1151
1152
  - test/jobs/shipit/background_job_test.rb
1152
1153
  - test/jobs/shipit/continuous_delivery_job_test.rb
1154
+ - test/jobs/shipit/create_on_github_job_test.rb
1153
1155
  - test/jobs/unique_job_test.rb
1154
1156
  - test/jobs/update_github_last_deployed_ref_job_test.rb
1155
1157
  - test/lib/shipit/deploy_commands_test.rb