shipit-engine 0.33.0 → 0.34.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.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -2
  3. data/app/assets/stylesheets/_pages/_deploy.scss +0 -2
  4. data/app/controllers/shipit/api/ccmenu_controller.rb +1 -1
  5. data/app/controllers/shipit/api/deploys_controller.rb +2 -0
  6. data/app/controllers/shipit/api/rollbacks_controller.rb +2 -1
  7. data/app/controllers/shipit/api/stacks_controller.rb +1 -0
  8. data/app/controllers/shipit/deploys_controller.rb +1 -1
  9. data/app/controllers/shipit/stacks_controller.rb +2 -2
  10. data/app/controllers/shipit/tasks_controller.rb +2 -2
  11. data/app/controllers/shipit/webhooks_controller.rb +23 -4
  12. data/app/helpers/shipit/shipit_helper.rb +0 -1
  13. data/app/jobs/shipit/deliver_hook_job.rb +1 -1
  14. data/app/jobs/shipit/github_sync_job.rb +13 -9
  15. data/app/jobs/shipit/update_github_last_deployed_ref_job.rb +1 -1
  16. data/app/models/shipit/anonymous_user.rb +6 -2
  17. data/app/models/shipit/check_run.rb +36 -0
  18. data/app/models/shipit/commit.rb +20 -9
  19. data/app/models/shipit/commit_checks.rb +13 -13
  20. data/app/models/shipit/commit_deployment.rb +3 -3
  21. data/app/models/shipit/commit_deployment_status.rb +3 -3
  22. data/app/models/shipit/deploy.rb +16 -11
  23. data/app/models/shipit/deploy_spec/lerna_discovery.rb +12 -4
  24. data/app/models/shipit/duration.rb +2 -0
  25. data/app/models/shipit/hook.rb +26 -2
  26. data/app/models/shipit/merge_request.rb +9 -7
  27. data/app/models/shipit/pull_request.rb +1 -1
  28. data/app/models/shipit/release_status.rb +1 -1
  29. data/app/models/shipit/repository.rb +9 -3
  30. data/app/models/shipit/review_stack.rb +16 -2
  31. data/app/models/shipit/stack.rb +59 -25
  32. data/app/models/shipit/status/group.rb +1 -1
  33. data/app/models/shipit/task.rb +6 -2
  34. data/app/models/shipit/task_execution_strategy/default.rb +4 -5
  35. data/app/models/shipit/team.rb +4 -2
  36. data/app/models/shipit/user.rb +4 -0
  37. data/app/models/shipit/webhooks/handlers/pull_request/review_stack_adapter.rb +1 -1
  38. data/app/models/shipit/webhooks/handlers/push_handler.rb +4 -1
  39. data/app/serializers/shipit/merge_request_serializer.rb +1 -1
  40. data/app/validators/subset_validator.rb +1 -1
  41. data/app/views/layouts/merge_status.html.erb +1 -1
  42. data/app/views/shipit/stacks/_banners.html.erb +2 -1
  43. data/app/views/shipit/stacks/new.html.erb +1 -1
  44. data/config/secrets.development.example.yml +24 -0
  45. data/config/secrets.development.shopify.yml +20 -9
  46. data/db/migrate/20210325194053_remove_stacks_branch_default.rb +5 -0
  47. data/db/migrate/20210504200438_add_github_updated_at_to_check_runs.rb +5 -0
  48. data/lib/shipit.rb +39 -15
  49. data/lib/shipit/command.rb +7 -6
  50. data/lib/shipit/commands.rb +9 -2
  51. data/lib/shipit/engine.rb +2 -0
  52. data/lib/shipit/flock.rb +8 -1
  53. data/lib/shipit/github_app.rb +7 -5
  54. data/lib/shipit/octokit_iterator.rb +3 -3
  55. data/lib/shipit/simple_message_verifier.rb +2 -2
  56. data/lib/shipit/stack_commands.rb +28 -4
  57. data/lib/shipit/task_commands.rb +6 -0
  58. data/lib/shipit/version.rb +1 -1
  59. data/lib/snippets/publish-lerna-independent-packages +35 -34
  60. data/lib/snippets/publish-lerna-independent-packages-legacy +39 -0
  61. data/test/controllers/api/ccmenu_controller_test.rb +1 -1
  62. data/test/controllers/api/deploys_controller_test.rb +17 -0
  63. data/test/controllers/api/stacks_controller_test.rb +21 -7
  64. data/test/controllers/webhooks_controller_test.rb +26 -11
  65. data/test/dummy/app/assets/config/manifest.js +3 -0
  66. data/test/dummy/config/application.rb +1 -1
  67. data/test/dummy/config/database.yml +9 -0
  68. data/test/dummy/config/environments/development.rb +1 -1
  69. data/test/dummy/config/secrets_double_github_app.yml +79 -0
  70. data/test/dummy/db/schema.rb +5 -4
  71. data/test/dummy/db/seeds.rb +1 -0
  72. data/test/fixtures/payloads/check_suite_master.json +2 -30
  73. data/test/fixtures/payloads/push_master.json +1 -1
  74. data/test/fixtures/payloads/push_not_master.json +1 -1
  75. data/test/fixtures/shipit/commits.yml +2 -2
  76. data/test/fixtures/shipit/hooks.yml +1 -0
  77. data/test/fixtures/shipit/tasks.yml +1 -1
  78. data/test/helpers/json_helper.rb +5 -1
  79. data/test/jobs/github_sync_job_test.rb +2 -1
  80. data/test/models/commit_deployment_status_test.rb +3 -3
  81. data/test/models/commits_test.rb +2 -0
  82. data/test/models/deploy_spec_test.rb +7 -0
  83. data/test/models/deploys_test.rb +18 -0
  84. data/test/models/hook_test.rb +30 -1
  85. data/test/models/merge_request_test.rb +19 -4
  86. data/test/models/shipit/check_run_test.rb +124 -5
  87. data/test/models/shipit/review_stack_test.rb +38 -6
  88. data/test/models/shipit/stacks_test.rb +42 -4
  89. data/test/models/shipit/webhooks/handlers/pull_request/review_stack_adapter_test.rb +24 -0
  90. data/test/models/tasks_test.rb +22 -0
  91. data/test/test_helper.rb +15 -0
  92. data/test/unit/anonymous_user_serializer_test.rb +1 -1
  93. data/test/unit/command_test.rb +5 -0
  94. data/test/unit/commit_serializer_test.rb +1 -1
  95. data/test/unit/deploy_commands_test.rb +70 -14
  96. data/test/unit/deploy_serializer_test.rb +1 -1
  97. data/test/unit/github_app_test.rb +2 -3
  98. data/test/unit/github_apps_test.rb +416 -0
  99. data/test/unit/shipit_deployment_checks_test.rb +77 -0
  100. data/test/unit/shipit_test.rb +14 -0
  101. data/test/unit/user_serializer_test.rb +1 -1
  102. metadata +202 -191
@@ -31,7 +31,7 @@ module Shipit
31
31
  end
32
32
 
33
33
  delegate :pending?, :success?, :error?, :failure?, :unknown?, :missing?, :state, :simple_state,
34
- to: :significant_status
34
+ to: :significant_status
35
35
  delegate :each, :size, :map, to: :statuses
36
36
  delegate :required_statuses, to: :commit
37
37
 
@@ -39,7 +39,7 @@ module Shipit
39
39
  scope :last_seven_days, -> { where("created_at > ?", 7.days.ago) }
40
40
  scope :previous_seven_days, -> { where(created_at: 14.days.ago..7.days.ago) }
41
41
 
42
- scope :due_for_rollup, -> { completed.where(rolled_up: false).where('created_at <= ?', 1.hour.ago) }
42
+ scope :due_for_rollup, -> { not_active.where(rolled_up: false).where('created_at <= ?', 1.hour.ago) }
43
43
 
44
44
  after_save :record_status_change
45
45
  after_create :prevent_concurrency, unless: :allow_concurrency?
@@ -175,7 +175,7 @@ module Shipit
175
175
  end
176
176
 
177
177
  delegate :acquire_git_cache_lock, :async_refresh_deployed_revision, :async_update_estimated_deploy_duration,
178
- to: :stack
178
+ to: :stack
179
179
 
180
180
  delegate :checklist, to: :definition
181
181
 
@@ -283,6 +283,10 @@ module Shipit
283
283
  user || AnonymousUser.new
284
284
  end
285
285
 
286
+ def author=(user)
287
+ super(user.presence)
288
+ end
289
+
286
290
  def finished?
287
291
  !pending? && !running? && !aborting?
288
292
  end
@@ -83,19 +83,18 @@ module Shipit
83
83
  end
84
84
 
85
85
  def capture!(command)
86
- started_at = Time.now
86
+ started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
87
87
  command.start do
88
88
  @task.ping
89
89
  check_for_abort
90
90
  end
91
- @task.write("$ #{command}\npid: #{command.pid}\nstarted at: #{started_at}\n")
91
+ @task.write("\n$ #{command}\npid: #{command.pid}\n")
92
92
  @task.pid = command.pid
93
93
  command.stream! do |line|
94
94
  @task.write(line)
95
95
  end
96
- @task.write("\n")
97
- finished_at = Time.now
98
- @task.write("pid: #{command.pid}\nfinished at: #{finished_at}\nran in: #{finished_at - started_at} seconds\n")
96
+ finished_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
97
+ @task.write("pid: #{command.pid} finished in: #{finished_at - started_at} seconds\n")
99
98
  command.success?
100
99
  end
101
100
 
@@ -26,7 +26,8 @@ module Shipit
26
26
  end
27
27
 
28
28
  def find_team_on_github(organization, slug)
29
- teams = Shipit::OctokitIterator.new { Shipit.github.api.org_teams(organization, per_page: 100) }
29
+ gh_api = Shipit.github(organization: organization).api
30
+ teams = Shipit::OctokitIterator.new(github_api: gh_api) { gh_api.org_teams(organization, per_page: 100) }
30
31
  teams.find { |t| t.slug == slug }
31
32
  rescue Octokit::NotFound
32
33
  end
@@ -41,7 +42,8 @@ module Shipit
41
42
  end
42
43
 
43
44
  def refresh_members!
44
- github_members = Shipit::OctokitIterator.new(Shipit.github.api.get(api_url).rels[:members])
45
+ github_api = Shipit.github(organization: organization).api
46
+ github_members = Shipit::OctokitIterator.new(github_api.get(api_url).rels[:members])
45
47
  members = github_members.map { |u| User.find_or_create_from_github(u) }
46
48
  self.members = members
47
49
  save!
@@ -15,6 +15,8 @@ module Shipit
15
15
 
16
16
  def self.find_or_create_by_login!(login)
17
17
  find_or_create_by!(login: login) do |user|
18
+ # Users are global, any app can be used
19
+ # This will not work for users that only exist in an Enterprise install
18
20
  user.github_user = Shipit.github.api.user(login)
19
21
  end
20
22
  end
@@ -91,6 +93,8 @@ module Shipit
91
93
  end
92
94
 
93
95
  def refresh_from_github!
96
+ # Users are global, any app can be used
97
+ # This will not work for users that only exist in an Enterprise install
94
98
  update!(github_user: Shipit.github.api.user(github_id))
95
99
  rescue Octokit::NotFound
96
100
  identify_renamed_user!
@@ -44,8 +44,8 @@ module Shipit
44
44
  return unless stack.archived?
45
45
 
46
46
  stack.transaction do
47
- stack.unarchive!(*args, &block)
48
47
  Shipit::ReviewStackProvisioningQueue.add(stack)
48
+ stack.unarchive!(*args, &block)
49
49
  end
50
50
  end
51
51
 
@@ -7,7 +7,10 @@ module Shipit
7
7
  requires :ref
8
8
  end
9
9
  def process
10
- stacks.where(branch: branch).each(&:sync_github)
10
+ stacks
11
+ .not_archived
12
+ .where(branch: branch)
13
+ .find_each(&:sync_github)
11
14
  end
12
15
 
13
16
  private
@@ -8,7 +8,7 @@ module Shipit
8
8
  has_one :head, serializer: ShortCommitSerializer
9
9
 
10
10
  attributes :id, :number, :title, :github_id, :additions, :deletions, :state, :merge_status, :mergeable,
11
- :merge_requested_at, :rejection_reason, :html_url, :branch, :base_ref
11
+ :merge_requested_at, :rejection_reason, :html_url, :branch, :base_ref
12
12
 
13
13
  def html_url
14
14
  github_pull_request_url(object)
@@ -3,6 +3,6 @@ class SubsetValidator < ActiveModel::EachValidator
3
3
  def validate_each(record, attribute, value)
4
4
  superset = options[:of]
5
5
  rest = value - superset
6
- record.errors.add(attribute, :subset, options) unless rest.empty?
6
+ record.errors.add(attribute, :subset, **options) unless rest.empty?
7
7
  end
8
8
  end
@@ -4,7 +4,7 @@
4
4
  <%= stylesheet_link_tag *(params[:stylesheets] || []).select { |url| url.start_with?('https://assets-cdn.github.com/', 'https://github.githubassets.com/') } %>
5
5
  <%= stylesheet_link_tag 'merge_status' %>
6
6
  </head>
7
- <body>
7
+ <body data-color-mode="<%= params[:mode] %>">
8
8
  <div class="merge-status-container" data-layout-content><%= yield %></div>
9
9
  <%= javascript_include_tag 'merge_status' %>
10
10
  </body>
@@ -69,7 +69,8 @@
69
69
  <h2 class="banner__title">Continuous Delivery Delayed!</h2>
70
70
  <p class="banner__text">
71
71
  Continuous Delivery for this stack is currently paused because
72
- <%= link_to 'the pre-deploy checks failed', stack_commit_checks_path(stack, sha: stack.next_commit_to_deploy.sha) %>.
72
+
73
+ <%= link_to_if stack.deployment_checks_passed?, 'the pre-deploy checks failed', stack_commit_checks_path(stack, sha: stack.next_commit_to_deploy.sha) %>.
73
74
  You can either wait for them to pass, or trigger a deploy manually.
74
75
  </p>
75
76
  </div>
@@ -17,7 +17,7 @@
17
17
  </div>
18
18
  <div class="field-wrapper">
19
19
  <%= f.label :branch %>
20
- <%= f.text_field :branch, placeholder: 'master' %>
20
+ <%= f.text_field :branch, placeholder: '<default branch>' %>
21
21
  </div>
22
22
  <div class="field-wrapper">
23
23
  <%= f.label :environment %>
@@ -1,8 +1,10 @@
1
1
  host: 'localhost:3000'
2
2
  redis_url: 'redis://127.0.0.1:6379/0'
3
3
 
4
+ # For creating an app see: https://github.com/Shopify/shipit-engine/blob/master/docs/setup.md#creating-the-github-app
4
5
  # Can be obtained there: https://github.com/settings/apps
5
6
  # Set the "Authorization callback URL" as `<host>/github/auth/github/callback`
7
+
6
8
  github:
7
9
  app_id:
8
10
  installation_id:
@@ -12,3 +14,25 @@ github:
12
14
  id:
13
15
  secret:
14
16
  teams: # Optional
17
+
18
+ # Use this configuration schema if you are configuring multiple Github applications for different Github organizations
19
+
20
+ # github:
21
+ # somegithuborg:
22
+ # app_id:
23
+ # installation_id:
24
+ # webhook_secret: # nil
25
+ # private_key:
26
+ # oauth:
27
+ # id:
28
+ # secret:
29
+ # teams: # Optional
30
+ # someothergithuborg:
31
+ # app_id:
32
+ # installation_id:
33
+ # webhook_secret: # nil
34
+ # private_key:
35
+ # oauth:
36
+ # id:
37
+ # secret:
38
+ # teams: # Optional
@@ -1,13 +1,24 @@
1
1
  host: 'shipit-engine.myshopify.io'
2
2
  redis_url: 'redis://shipit-engine.railgun:6379'
3
3
 
4
- # TODO: document creating a dev app
4
+ # For creating an app see: https://github.com/Shopify/shipit-engine/blob/master/docs/setup.md#creating-the-github-app
5
+
5
6
  github:
6
- app_id:
7
- installation_id:
8
- webhook_secret: # nil
9
- private_key:
10
- oauth:
11
- id:
12
- secret:
13
- teams:
7
+ somegithuborg:
8
+ app_id:
9
+ installation_id:
10
+ webhook_secret: # nil
11
+ private_key:
12
+ oauth:
13
+ id:
14
+ secret:
15
+ teams:
16
+ someothergithuborg:
17
+ app_id:
18
+ installation_id:
19
+ webhook_secret: # nil
20
+ private_key:
21
+ oauth:
22
+ id:
23
+ secret:
24
+ teams:
@@ -0,0 +1,5 @@
1
+ class RemoveStacksBranchDefault < ActiveRecord::Migration[6.1]
2
+ def change
3
+ change_column_default(:stacks, :branch, from: 'master', to: nil)
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddGithubUpdatedAtToCheckRuns < ActiveRecord::Migration[6.1]
2
+ def change
3
+ add_column :check_runs, :github_updated_at, :datetime, default: 0
4
+ end
5
+ end
data/lib/shipit.rb CHANGED
@@ -24,7 +24,6 @@ require 'safe_yaml/load'
24
24
  require 'securecompare'
25
25
 
26
26
  require 'redis-objects'
27
- require 'redis-namespace'
28
27
 
29
28
  require 'octokit'
30
29
  require 'faraday-http-cache'
@@ -60,9 +59,12 @@ SafeYAML::OPTIONS[:deserialize_symbols] = false
60
59
  module Shipit
61
60
  extend self
62
61
 
62
+ GithubOrganizationUnknown = Class.new(StandardError)
63
+ TOP_LEVEL_GH_KEYS = [:app_id, :installation_id, :webhook_secret, :private_key, :oauth, :domain]
64
+
63
65
  delegate :table_name_prefix, to: :secrets
64
66
 
65
- attr_accessor :disable_api_authentication, :timeout_exit_codes
67
+ attr_accessor :disable_api_authentication, :timeout_exit_codes, :deployment_checks
66
68
  attr_writer(
67
69
  :internal_hook_receivers,
68
70
  :preferred_org_emails,
@@ -92,7 +94,7 @@ module Shipit
92
94
  secrets.redis_url.present? ? URI(secrets.redis_url) : nil
93
95
  end
94
96
 
95
- def redis(namespace = nil)
97
+ def redis
96
98
  @redis ||= Redis.new(
97
99
  url: redis_url.to_s.presence,
98
100
  logger: Rails.logger,
@@ -100,17 +102,41 @@ module Shipit
100
102
  reconnect_delay: 0.5,
101
103
  reconnect_delay_max: 1,
102
104
  )
103
- return @redis unless namespace
104
- Redis::Namespace.new(namespace, redis: @redis)
105
105
  end
106
106
 
107
- def github
108
- @github ||= GitHubApp.new(secrets.github)
107
+ def github(organization: github_default_organization)
108
+ # Backward compatibility
109
+ # nil signifies the single github app config schema is being used
110
+ if github_default_organization.nil?
111
+ config = secrets.github
112
+ else
113
+ config = github_app_config(organization)
114
+ raise GithubOrganizationUnknown, organization if config.nil?
115
+ end
116
+ @github ||= {}
117
+ @github[organization] ||= GitHubApp.new(organization, config)
118
+ end
119
+
120
+ def github_default_organization
121
+ return nil unless secrets&.github
122
+ org = secrets.github.keys.first
123
+ TOP_LEVEL_GH_KEYS.include?(org) ? nil : org
124
+ end
125
+
126
+ def github_organizations
127
+ return [nil] unless github_default_organization
128
+ secrets.github.keys
129
+ end
130
+
131
+ def github_app_config(organization)
132
+ github_config = secrets.github.deep_transform_keys(&:downcase)
133
+ github_organization = organization.downcase.to_sym
134
+ github_config[github_organization]
109
135
  end
110
136
 
111
137
  def legacy_github_api
112
138
  if secrets&.github_api.present?
113
- @legacy_github_api ||= github.new_client(access_token: secrets.github_api['access_token'])
139
+ @legacy_github_api ||= github.new_client(access_token: secrets.github_api[:access_token])
114
140
  end
115
141
  end
116
142
 
@@ -175,13 +201,11 @@ module Shipit
175
201
  end
176
202
 
177
203
  def revision
178
- @revision ||= begin
179
- if revision_file.exist?
180
- revision_file.read
181
- else
182
- %x(git rev-parse HEAD)
183
- end.strip
184
- end
204
+ @revision ||= if revision_file.exist?
205
+ revision_file.read
206
+ else
207
+ %x(git rev-parse HEAD)
208
+ end.strip
185
209
  end
186
210
 
187
211
  def default_inactivity_timeout
@@ -13,7 +13,8 @@ module Shipit
13
13
  Denied = Class.new(Error)
14
14
  TimedOut = Class.new(Error)
15
15
 
16
- BASE_ENV = Bundler.clean_env.merge((ENV.keys - Bundler.clean_env.keys).map { |k| [k, nil] }.to_h)
16
+ unbundled_env = Bundler.respond_to?(:unbundled_env) ? Bundler.unbundled_env : Bundler.clean_env
17
+ BASE_ENV = unbundled_env.merge((ENV.keys - unbundled_env.keys).map { |k| [k, nil] }.to_h)
17
18
 
18
19
  class Failed < Error
19
20
  attr_reader :exit_code
@@ -29,7 +30,7 @@ module Shipit
29
30
  def initialize(*args, default_timeout: Shipit.default_inactivity_timeout, env: {}, chdir:)
30
31
  @args, options = parse_arguments(args)
31
32
  @timeout = options['timeout'] || options[:timeout] || default_timeout
32
- @env = env
33
+ @env = env.transform_values { |v| v&.to_s }
33
34
  @chdir = chdir.to_s
34
35
  @timed_out = false
35
36
  end
@@ -86,7 +87,7 @@ module Shipit
86
87
  @out = @pid = nil
87
88
  FileUtils.mkdir_p(@chdir)
88
89
  begin
89
- @out, child_in, @pid = PTY.spawn(clean_env, *interpolated_arguments, chdir: @chdir)
90
+ @out, child_in, @pid = PTY.spawn(unbundled_env, *interpolated_arguments, chdir: @chdir)
90
91
  child_in.close
91
92
  rescue Errno::ENOENT
92
93
  raise NotFound, "#{Shellwords.split(interpolated_arguments.first).first}: command not found"
@@ -97,8 +98,8 @@ module Shipit
97
98
  self
98
99
  end
99
100
 
100
- def clean_env
101
- BASE_ENV.merge('PATH' => "#{ENV['PATH']}:#{Shipit.shell_paths.join(':')}").merge(@env.stringify_keys)
101
+ def unbundled_env
102
+ BASE_ENV.merge('PATH' => "#{Shipit.shell_paths.join(':')}:#{ENV['PATH']}").merge(@env.stringify_keys)
102
103
  end
103
104
 
104
105
  def stream(&block)
@@ -216,7 +217,7 @@ module Shipit
216
217
  argument
217
218
  end
218
219
  end
219
- [args, options]
220
+ [args.map(&:to_s), options]
220
221
  end
221
222
 
222
223
  def running?
@@ -21,8 +21,8 @@ module Shipit
21
21
 
22
22
  def env
23
23
  @env ||= Shipit.env.merge(
24
- 'GITHUB_DOMAIN' => Shipit.github.domain,
25
- 'GITHUB_TOKEN' => Shipit.github.token,
24
+ 'GITHUB_DOMAIN' => github.domain,
25
+ 'GITHUB_TOKEN' => github.token,
26
26
  'GIT_ASKPASS' => Shipit::Engine.root.join('lib', 'snippets', 'git-askpass').realpath.to_s,
27
27
  )
28
28
  end
@@ -30,5 +30,12 @@ module Shipit
30
30
  def git(*args)
31
31
  Command.new("git", *args)
32
32
  end
33
+ ruby2_keywords :git if respond_to?(:ruby2_keywords, true)
34
+
35
+ private
36
+
37
+ def github
38
+ Shipit.github
39
+ end
33
40
  end
34
41
  end
data/lib/shipit/engine.rb CHANGED
@@ -10,6 +10,8 @@ module Shipit
10
10
  Shipit::Engine.routes.default_url_options[:host] = Shipit.host
11
11
  Pubsubstub.redis_url = Shipit.redis_url.to_s
12
12
 
13
+ Rails.application.secrets.deep_symbolize_keys!
14
+
13
15
  app.config.assets.paths << Emoji.images_path
14
16
  app.config.assets.precompile += %w(
15
17
  favicon.ico
data/lib/shipit/flock.rb CHANGED
@@ -11,14 +11,21 @@ module Shipit
11
11
 
12
12
  def initialize(path)
13
13
  @path = Pathname.new(path)
14
+ @acquired = false
14
15
  end
15
16
 
16
17
  def lock(timeout:)
18
+ return yield if @acquired
17
19
  path.parent.mkpath
18
20
  path.open('w') do |file|
19
21
  if retrying(timeout: timeout) { file.flock(File::LOCK_EX | File::LOCK_NB) }
20
22
  file.write($PROCESS_ID.to_s)
21
- return yield
23
+ @acquired = true
24
+ begin
25
+ yield
26
+ ensure
27
+ @acquired = false
28
+ end
22
29
  else
23
30
  raise TimeoutError, "Couldn't acquire lock for #{path} in #{timeout} seconds"
24
31
  end