shipit-engine 0.6.4 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -2
  3. data/app/assets/javascripts/shipit/checklist.js.coffee +2 -2
  4. data/app/controllers/shipit/api/hooks_controller.rb +2 -2
  5. data/app/controllers/shipit/shipit_controller.rb +4 -3
  6. data/app/models/shipit/hook.rb +3 -2
  7. data/app/models/shipit/stack.rb +18 -2
  8. data/app/models/shipit/user.rb +2 -0
  9. data/app/serializers/shipit/hook_serializer.rb +9 -1
  10. data/app/serializers/shipit/stack_serializer.rb +1 -1
  11. data/app/views/shipit/deploys/_checklist.html.erb +1 -1
  12. data/app/views/shipit/deploys/_concurrent_deploy_warning.html.erb +4 -0
  13. data/app/views/shipit/deploys/new.html.erb +3 -5
  14. data/app/views/shipit/deploys/rollback.html.erb +3 -5
  15. data/config/routes.rb +29 -29
  16. data/db/migrate/20160122165559_rename_hooks_url_in_delivery_url.rb +5 -0
  17. data/lib/shipit.rb +3 -2
  18. data/lib/shipit/version.rb +1 -1
  19. data/test/controllers/api/hooks_controller_test.rb +19 -11
  20. data/test/controllers/stacks_controller_test.rb +7 -3
  21. data/test/dummy/config/secrets.example.yml +6 -2
  22. data/test/dummy/db/development.sqlite3 +0 -0
  23. data/test/dummy/db/schema.rb +2 -4
  24. data/test/dummy/db/test.sqlite3 +0 -0
  25. data/test/fixtures/shipit/hooks.yml +5 -5
  26. data/test/helpers/hooks_helper.rb +36 -0
  27. data/test/helpers/payloads_helper.rb +0 -1
  28. data/test/jobs/perform_task_job_test.rb +0 -1
  29. data/test/models/commits_test.rb +27 -16
  30. data/test/models/deploys_test.rb +12 -12
  31. data/test/models/hook_test.rb +5 -5
  32. data/test/models/stacks_test.rb +15 -7
  33. data/test/test_helper.rb +9 -5
  34. data/test/unit/shipit_test.rb +16 -0
  35. metadata +5 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7e59f9e4da5a153b86f3a4e001457f0ca9fa43f9
4
- data.tar.gz: fb56ea8afef030852bb331ce12a8fb5db9a40234
3
+ metadata.gz: 13126d19eca4966f56856152cf632751bf1ba9d1
4
+ data.tar.gz: 665a8bce4eb15e193e55b0da971a5840b7888bf6
5
5
  SHA512:
6
- metadata.gz: 505e87e04f14fbfb8403dfd95a868a536d6666e8fd32fc88894cd51d3b720a7a37cff6de7bb36290438d6ecf8b7795abc0317b8594d070b8adf837cae50b2259
7
- data.tar.gz: 331f978493b326520897b4ebeb29eb23ce75e8224586dfc7a48dd643e56f8405ee825ca956d0f9a1796cf8f14e2ffac40c15b80501f97462ee784fc3d36d2d1e
6
+ metadata.gz: 87cf3290c48593691fec2c278607cc1783206af426ca3dbefcab269b639614d98e5e8632cd1bc4fa27842f70615886f9b7c3914191d99dfc8168d43ab61ffcae
7
+ data.tar.gz: 12077a22444887bb58eb55faed2b395e257d97f2a762fc64164ef216b61921c6606d8261de6f202192324b17e461e4b3c9f008d4278503c7631a41513a309ce2
data/README.md CHANGED
@@ -316,7 +316,7 @@ development:
316
316
 
317
317
  The value for `id` is your application's *Client ID*, and the value for `secret` is your application's *Client Secret* — both of these should appear on your application's GitHub page.
318
318
 
319
- The `team` is optional, and required only if you want to specify a team that has access to the stack in Shipit.
319
+ The `teams` key is optional, and required only if you want to specify some teams which have access to the stack in Shipit.
320
320
 
321
321
  For example:
322
322
 
@@ -325,7 +325,9 @@ development:
325
325
  github_oauth:
326
326
  id: (your application's Client ID)
327
327
  secret: (your application's Client Secret)
328
- team: Shipit/team
328
+ teams:
329
+ - Shipit/team
330
+ - Shipit/another_team
329
331
  ```
330
332
  <br>
331
333
 
@@ -1,9 +1,9 @@
1
1
  $document = $(document)
2
2
 
3
3
  toggleDeployButton = ->
4
- $('.trigger-deploy').toggleClass('disabled btn--disabled', !!$(':checkbox[name=checklist]:not(:checked)').length)
4
+ $('.trigger-deploy').toggleClass('disabled btn--disabled', !!$(':checkbox.required:not(:checked)').length)
5
5
 
6
- $document.on('change', ':checkbox[name=checklist]', toggleDeployButton)
6
+ $document.on('change', ':checkbox.required', toggleDeployButton)
7
7
 
8
8
  jQuery ($) ->
9
9
  toggleDeployButton()
@@ -13,7 +13,7 @@ module Shipit
13
13
  end
14
14
 
15
15
  params do
16
- requires :url, String
16
+ requires :delivery_url, String
17
17
  requires :events, Array[String]
18
18
  accepts :content_type, String
19
19
  end
@@ -22,7 +22,7 @@ module Shipit
22
22
  end
23
23
 
24
24
  params do
25
- accepts :url, String
25
+ accepts :delivery_url, String
26
26
  accepts :events, Array[String]
27
27
  accepts :content_type, String
28
28
  end
@@ -32,9 +32,10 @@ module Shipit
32
32
 
33
33
  def force_github_authentication
34
34
  if current_user.logged_in?
35
- team = Shipit.github_team
36
- if team && !current_user.in?(team.members)
37
- render text: "You must be a member of #{team.handle} to access this application.", status: :forbidden
35
+ teams = Shipit.github_teams
36
+ unless teams.empty? || current_user.teams.where(id: teams).exists?
37
+ team_list = teams.map(&:handle).to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')
38
+ render text: "You must be a member of #{team_list} to access this application.", status: :forbidden
38
39
  end
39
40
  else
40
41
  redirect_to Shipit::Engine.routes.url_helpers.github_authentication_path(origin: request.original_url)
@@ -8,6 +8,7 @@ module Shipit
8
8
  }.freeze
9
9
 
10
10
  EVENTS = %w(
11
+ stack
11
12
  task
12
13
  deploy
13
14
  rollback
@@ -19,7 +20,7 @@ module Shipit
19
20
  belongs_to :stack, required: false
20
21
  has_many :deliveries
21
22
 
22
- validates :url, presence: true, url: {no_local: true, allow_blank: true}
23
+ validates :delivery_url, presence: true, url: {no_local: true, allow_blank: true}
23
24
  validates :content_type, presence: true, inclusion: {in: CONTENT_TYPES.keys}
24
25
  validates :events, presence: true, subset: {of: EVENTS}
25
26
 
@@ -67,7 +68,7 @@ module Shipit
67
68
  def deliver!(event, payload)
68
69
  deliveries.create!(
69
70
  event: event,
70
- url: url,
71
+ url: delivery_url,
71
72
  content_type: CONTENT_TYPES[content_type],
72
73
  payload: serialize_payload(payload),
73
74
  ).schedule!
@@ -27,7 +27,10 @@ module Shipit
27
27
 
28
28
  before_validation :update_defaults
29
29
  before_destroy :clear_local_files
30
- after_commit :emit_hooks
30
+ after_commit :emit_lock_hooks
31
+ after_commit :emit_added_hooks, on: :create
32
+ after_commit :emit_updated_hooks, on: :update
33
+ after_commit :emit_removed_hooks, on: :destroy
31
34
  after_commit :broadcast_update, on: :update
32
35
  after_commit :setup_hooks, :sync_github, on: :create
33
36
  after_touch :clear_cache
@@ -296,11 +299,24 @@ module Shipit
296
299
  self.branch = 'master' if branch.blank?
297
300
  end
298
301
 
299
- def emit_hooks
302
+ def emit_lock_hooks
300
303
  return unless previous_changes.include?('lock_reason')
301
304
  Hook.emit(:lock, self, locked: locked?, stack: self)
302
305
  end
303
306
 
307
+ def emit_added_hooks
308
+ Hook.emit(:stack, self, action: :added, stack: self)
309
+ end
310
+
311
+ def emit_updated_hooks
312
+ changed = !(previous_changes.keys - %w(updated_at)).empty?
313
+ Hook.emit(:stack, self, action: :updated, stack: self) if changed
314
+ end
315
+
316
+ def emit_removed_hooks
317
+ Hook.emit(:stack, self, action: :removed, stack: self)
318
+ end
319
+
304
320
  def ci_enabled_cache_key
305
321
  "stacks:#{id}:ci_enabled"
306
322
  end
@@ -2,6 +2,8 @@ module Shipit
2
2
  class User < ActiveRecord::Base
3
3
  DEFAULT_AVATAR = URI.parse('https://avatars.githubusercontent.com/u/583231?')
4
4
 
5
+ has_many :teams, through: :memberships
6
+ has_many :memberships
5
7
  has_many :authored_commits, class_name: :Commit, foreign_key: :author_id, inverse_of: :author
6
8
  has_many :commits, foreign_key: :committer_id, inverse_of: :committer
7
9
  has_many :tasks
@@ -3,7 +3,15 @@ module Shipit
3
3
  include ConditionalAttributes
4
4
 
5
5
  has_one :stack
6
- attributes :id, :url, :content_type, :events, :insecure_ssl, :created_at, :updated_at
6
+ attributes :id, :url, :delivery_url, :content_type, :events, :insecure_ssl, :created_at, :updated_at
7
+
8
+ def url
9
+ if object.scoped?
10
+ api_stack_hook_url(object.stack, object)
11
+ else
12
+ api_hook_url(object)
13
+ end
14
+ end
7
15
 
8
16
  def include_stack?
9
17
  object.scoped?
@@ -3,7 +3,7 @@ module Shipit
3
3
  include ConditionalAttributes
4
4
 
5
5
  has_one :lock_author
6
- attributes :id, :repo_owner, :repo_name, :environment, :html_url, :url, :tasks_url, :deploy_spec,
6
+ attributes :id, :repo_owner, :repo_name, :environment, :html_url, :url, :tasks_url, :deploy_url, :deploy_spec,
7
7
  :undeployed_commits_count, :is_locked, :lock_reason, :continuous_deployment, :created_at, :updated_at
8
8
 
9
9
  def url
@@ -6,7 +6,7 @@
6
6
  <ul class="deploy-checklist">
7
7
  <%- checklist.each_with_index do |check, index| -%>
8
8
  <li class="deploy-checklist__item">
9
- <%= check_box_tag :checklist, nil, false, class: 'deploy-checklist__item__checkbox', id: "checkbox_#{index}" %>
9
+ <%= check_box_tag :checklist, nil, false, class: 'deploy-checklist__item__checkbox required', id: "checkbox_#{index}" %>
10
10
  <label class="deploy-checklist__item__label" for="checkbox_<%= index %>">
11
11
  <%= sanitize(check, tags: %w(a strong), attributes: %w(href target)) %>
12
12
  </label>
@@ -2,5 +2,9 @@
2
2
  <h2><%= render_github_user(@stack.active_deploy.author) %> is already deploying!</h2>
3
3
  <ul>
4
4
  <li>Are you sure you want to deploy concurrently?</li>
5
+ <li>
6
+ <%= check_box_tag :force, true, false, class: 'required' %>
7
+ Yes I know what I'm doing.
8
+ </li>
5
9
  </ul>
6
10
  </section>
@@ -1,8 +1,6 @@
1
1
  <%= render partial: 'shipit/stacks/header', locals: { stack: @stack } %>
2
2
 
3
3
  <div class="wrapper">
4
- <%= render 'concurrent_deploy_warning' if @stack.deploying? %>
5
-
6
4
  <section>
7
5
  <header class="section-header">
8
6
  <h2>Commits included in this deploy (<%= link_to_github_deploy(@deploy) %>)</h2>
@@ -45,10 +43,10 @@
45
43
  <% end %>
46
44
  </section>
47
45
  <% end %>
46
+
47
+ <%= render 'concurrent_deploy_warning' if @stack.deploying? %>
48
+
48
49
  <section class="submit-section">
49
- <% if @stack.deploying? %>
50
- <%= hidden_field_tag :force, value: true %>
51
- <% end %>
52
50
  <%= f.hidden_field :until_commit_id %>
53
51
  <%= f.submit class: 'btn btn--primary btn--large trigger-deploy' %>
54
52
  </section>
@@ -9,8 +9,6 @@
9
9
  </ul>
10
10
  </section>
11
11
 
12
- <%= render 'concurrent_deploy_warning' if @stack.deploying? %>
13
-
14
12
  <section>
15
13
  <header class="section-header">
16
14
  <h2>Commits included in this rollback</h2>
@@ -41,10 +39,10 @@
41
39
  <% end %>
42
40
  </section>
43
41
  <% end %>
42
+
43
+ <%= render 'concurrent_deploy_warning' if @stack.deploying? %>
44
+
44
45
  <section class="submit-section">
45
- <% if @stack.deploying? %>
46
- <%= hidden_field_tag :force, value: true %>
47
- <% end %>
48
46
  <%= f.hidden_field :parent_id %>
49
47
  <%= f.submit 'Rollback', :class => ['btn', 'rollback', 'trigger-rollback'], data: {confirm: "Are you really sure it's safe?"} %>
50
48
  </section>
data/config/routes.rb CHANGED
@@ -5,16 +5,9 @@ Shipit::Engine.routes.draw do
5
5
 
6
6
  mount Pubsubstub::StreamAction.new, at: "/events", as: :events
7
7
 
8
+ # Robots
8
9
  get '/status/version' => 'status#version', as: :version
9
10
 
10
- scope '/github/auth/github', as: :github_authentication, controller: :github_authentication do
11
- get '/', action: :request
12
- post :callback
13
- get :callback
14
- get :logout
15
- end
16
-
17
- # Robots
18
11
  resources :stacks, only: %i(new create index) do
19
12
  resource :webhooks, only: [] do
20
13
  post :push, :state
@@ -28,7 +21,35 @@ Shipit::Engine.routes.draw do
28
21
  end
29
22
  end
30
23
 
24
+ # API
25
+ namespace :api do
26
+ root to: 'base#index'
27
+ resources :stacks, only: :index
28
+ scope '/stacks/*id', id: stack_id_format, as: :stack do
29
+ get '/' => 'stacks#show'
30
+ end
31
+
32
+ scope '/stacks/*stack_id', stack_id: stack_id_format, as: :stack do
33
+ resource :lock, only: %i(create update destroy)
34
+ resources :tasks, only: %i(index show) do
35
+ resource :output, only: :show
36
+ end
37
+ resources :deploys, only: %i(create)
38
+ post '/task/:task_name' => 'tasks#trigger', as: :trigger_task
39
+ resources :hooks, only: %i(index create show update destroy)
40
+ end
41
+
42
+ resources :hooks, only: %i(index create show update destroy)
43
+ end
44
+
31
45
  # Humans
46
+ scope '/github/auth/github', as: :github_authentication, controller: :github_authentication do
47
+ get '/', action: :request
48
+ post :callback
49
+ get :callback
50
+ get :logout
51
+ end
52
+
32
53
  scope '/*id', id: stack_id_format, as: :stack do
33
54
  get '/' => 'stacks#show'
34
55
  patch '/' => 'stacks#update'
@@ -64,25 +85,4 @@ Shipit::Engine.routes.draw do
64
85
  end
65
86
  end
66
87
  end
67
-
68
- # API
69
- namespace :api do
70
- root to: 'base#index'
71
- resources :stacks, only: :index
72
- scope '/stacks/*id', id: stack_id_format, as: :stack do
73
- get '/' => 'stacks#show'
74
- end
75
-
76
- scope '/stacks/*stack_id', stack_id: stack_id_format, as: :stack do
77
- resource :lock, only: %i(create update destroy)
78
- resources :tasks, only: %i(index show) do
79
- resource :output, only: :show
80
- end
81
- resources :deploys, only: %i(create)
82
- post '/task/:task_name' => 'tasks#trigger', as: :trigger_task
83
- resources :hooks, only: %i(index create show update destroy)
84
- end
85
-
86
- resources :hooks, only: %i(index create show update destroy)
87
- end
88
88
  end
@@ -0,0 +1,5 @@
1
+ class RenameHooksUrlInDeliveryUrl < ActiveRecord::Migration
2
+ def change
3
+ rename_column :hooks, :url, :delivery_url
4
+ end
5
+ end
data/lib/shipit.rb CHANGED
@@ -103,8 +103,9 @@ module Shipit
103
103
  secrets.host.presence
104
104
  end
105
105
 
106
- def github_team
107
- @github_team ||= github_oauth_credentials['team'] && Team.find_or_create_by_handle(github_oauth_credentials['team'])
106
+ def github_teams
107
+ teams = (Array(github_oauth_credentials['team']) + Array(github_oauth_credentials['teams'])).sort.uniq
108
+ @github_teams ||= teams.map { |t| Team.find_or_create_by_handle(t) }
108
109
  end
109
110
 
110
111
  def github_oauth_id
@@ -1,3 +1,3 @@
1
1
  module Shipit
2
- VERSION = '0.6.4'
2
+ VERSION = '0.7.0'
3
3
  end
@@ -8,13 +8,17 @@ module Shipit
8
8
  @stack = shipit_stacks(:shipit)
9
9
  end
10
10
 
11
+ test "the route has priority over stacks one" do
12
+ assert_recognizes({controller: 'shipit/api/hooks', action: 'show', id: '42'}, '/api/hooks/42')
13
+ end
14
+
11
15
  test "#index without a stack_id returns the list of global hooks" do
12
16
  hook = Hook.global.first
13
17
 
14
18
  get :index
15
19
  assert_response :ok
16
20
  assert_json '0.id', hook.id
17
- assert_json '0.url', hook.url
21
+ assert_json '0.delivery_url', hook.delivery_url
18
22
  assert_json '0.content_type', hook.content_type
19
23
  assert_no_json '0.stack'
20
24
  end
@@ -25,7 +29,7 @@ module Shipit
25
29
  get :index, stack_id: @stack.to_param
26
30
  assert_response :ok
27
31
  assert_json '0.id', hook.id
28
- assert_json '0.url', hook.url
32
+ assert_json '0.delivery_url', hook.delivery_url
29
33
  assert_json '0.content_type', hook.content_type
30
34
  assert_json '0.stack.id', @stack.id
31
35
  end
@@ -37,35 +41,39 @@ module Shipit
37
41
  assert_response :ok
38
42
 
39
43
  assert_json 'id', hook.id
40
- assert_json 'url', hook.url
44
+ assert_json 'delivery_url', hook.delivery_url
41
45
  assert_json 'content_type', hook.content_type
42
46
  assert_json 'stack.id', @stack.id
43
47
  end
44
48
 
45
49
  test "#create adds a new hook" do
46
50
  assert_difference -> { Hook.count }, 1 do
47
- post :create, url: 'https://example.com/hook', events: %w(deploy rollback)
51
+ post :create, delivery_url: 'https://example.com/hook', events: %w(deploy rollback)
48
52
  end
49
- assert_json 'url', 'https://example.com/hook'
50
- assert_json 'id', Hook.last.id
53
+ hook = Hook.last
54
+ assert_json 'delivery_url', 'https://example.com/hook'
55
+ assert_json 'url', "http://shipit.com/api/hooks/#{hook.id}"
56
+ assert_json 'id', hook.id
51
57
  end
52
58
 
53
59
  test "#create do not allow to set protected attributes" do
54
- post :create, url: 'https://example.com/hook', events: %w(deploy rollback), created_at: 2.months.ago.to_s(:db)
60
+ post :create, delivery_url: 'https://example.com/hook',
61
+ events: %w(deploy rollback),
62
+ created_at: 2.months.ago.to_s(:db)
55
63
  Hook.last.created_at > 2.seconds.ago
56
64
  end
57
65
 
58
66
  test "#create returns validation errors" do
59
- post :create, url: '../etc/passwd', events: %w(deploy)
67
+ post :create, delivery_url: '../etc/passwd', events: %w(deploy)
60
68
  assert_response :unprocessable_entity
61
- assert_json 'errors', 'url' => ['is not a valid URL']
69
+ assert_json 'errors', 'delivery_url' => ['is not a valid URL']
62
70
  end
63
71
 
64
72
  test "#update changes an existing hook" do
65
73
  hook = Hook.global.first
66
- patch :update, id: hook.id, url: 'https://shipit.com/'
74
+ patch :update, id: hook.id, delivery_url: 'https://shipit.com/'
67
75
  assert_response :ok
68
- assert_json 'url', 'https://shipit.com/'
76
+ assert_json 'delivery_url', 'https://shipit.com/'
69
77
  end
70
78
 
71
79
  test "#destroy removes an existing hook" do
@@ -54,11 +54,15 @@ module Shipit
54
54
  assert_redirected_to '/github/auth/github?origin=http%3A%2F%2Ftest.host%2F'
55
55
  end
56
56
 
57
- test "current_user must be a member of Shipit.github_team" do
58
- Shipit.stubs(:github_team).returns(shipit_teams(:cyclimse_cooks))
57
+ test "current_user must be a member of at least a Shipit.github_teams" do
58
+ session[:user_id] = shipit_users(:bob).id
59
+ Shipit.stubs(:github_teams).returns([shipit_teams(:cyclimse_cooks), shipit_teams(:shopify_developers)])
59
60
  get :index
60
61
  assert_response :forbidden
61
- assert_equal 'You must be a member of cyclimse/cooks to access this application.', response.body
62
+ assert_equal(
63
+ 'You must be a member of cyclimse/cooks or shopify/developers to access this application.',
64
+ response.body,
65
+ )
62
66
  end
63
67
 
64
68
  test "#show is success" do
@@ -3,7 +3,9 @@ development:
3
3
  github_oauth:
4
4
  # id:
5
5
  # secret:
6
- # team:
6
+ # teams:
7
+ # -
8
+ # -
7
9
  github_api:
8
10
  # access_token:
9
11
  # login:
@@ -23,5 +25,7 @@ test:
23
25
  github_oauth:
24
26
  id: 1d
25
27
  secret: s3cr37
26
- # team:
28
+ # teams:
29
+ # -
30
+ # -
27
31
  redis_url: "redis://127.0.0.1:6379/7"
Binary file
@@ -11,7 +11,7 @@
11
11
  #
12
12
  # It's strongly recommended that you check this file into your version control system.
13
13
 
14
- ActiveRecord::Schema.define(version: 20160104151833) do
14
+ ActiveRecord::Schema.define(version: 20160122165559) do
15
15
 
16
16
  create_table "api_clients", force: :cascade do |t|
17
17
  t.text "permissions", limit: 65535
@@ -76,7 +76,7 @@ ActiveRecord::Schema.define(version: 20160104151833) do
76
76
 
77
77
  create_table "hooks", force: :cascade do |t|
78
78
  t.integer "stack_id", limit: 4
79
- t.string "url", limit: 4096, null: false
79
+ t.string "delivery_url", limit: 4096, null: false
80
80
  t.string "content_type", limit: 4, default: "json", null: false
81
81
  t.string "secret", limit: 255
82
82
  t.string "events", limit: 255, default: "", null: false
@@ -161,8 +161,6 @@ ActiveRecord::Schema.define(version: 20160104151833) do
161
161
  add_index "tasks", ["rolled_up", "created_at", "status"], name: "index_tasks_on_rolled_up_and_created_at_and_status"
162
162
  add_index "tasks", ["since_commit_id"], name: "index_tasks_on_since_commit_id"
163
163
  add_index "tasks", ["stack_id"], name: "index_tasks_on_stack_id"
164
- add_index "tasks", ["type", "stack_id", "parent_id"], name: "index_tasks_by_stack_and_parent"
165
- add_index "tasks", ["type", "stack_id", "status"], name: "index_tasks_by_stack_and_status"
166
164
  add_index "tasks", ["until_commit_id"], name: "index_tasks_on_until_commit_id"
167
165
  add_index "tasks", ["user_id"], name: "index_tasks_on_user_id"
168
166
 
Binary file
@@ -1,28 +1,28 @@
1
1
  shipit_deploys:
2
2
  stack: shipit
3
3
  events: 'deploy,rollback'
4
- url: https://example.com/events/deploy
4
+ delivery_url: https://example.com/events/deploy
5
5
  content_type: json
6
6
 
7
7
  all_deploys:
8
8
  events: 'deploy,rollback'
9
- url: https://example.com/events/deploy
9
+ delivery_url: https://example.com/events/deploy
10
10
  content_type: form
11
11
 
12
12
  shipit_tasks:
13
13
  stack: shipit
14
14
  events: 'task'
15
- url: https://example.com/events/deploy
15
+ delivery_url: https://example.com/events/deploy
16
16
  content_type: json
17
17
 
18
18
  shipit_commit_status:
19
19
  stack: shipit
20
20
  events: 'commit_status'
21
- url: https://example.com/events/commit_status
21
+ delivery_url: https://example.com/events/commit_status
22
22
  content_type: json
23
23
 
24
24
  cyclimse_deploy_status:
25
25
  stack: cyclimse
26
26
  events: 'deploy_status'
27
- url: https://example.com/events/deploy_status
27
+ delivery_url: https://example.com/events/deploy_status
28
28
  content_type: json
@@ -0,0 +1,36 @@
1
+ module HooksHelper
2
+ def expect_hook(event, stack = nil, payload = nil)
3
+ spy_on_hook
4
+ yield
5
+ assert_received_with(Shipit::Hook, :emit) do |call|
6
+ if call.args.first == event && (stack.nil? || call.args.second == stack)
7
+ if payload.respond_to?(:call)
8
+ payload.call(call.args.third)
9
+ elsif payload
10
+ payload == call.args.third
11
+ else
12
+ true
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ def expect_no_hook(*args)
19
+ spy_on_hook
20
+ yield
21
+ spy = Spy::Subroutine.get(Shipit::Hook, :emit)
22
+ called = spy.calls.find do |call|
23
+ args.map.with_index.all? { |value, index| value == call.args[index] }
24
+ end
25
+ matcher = args.map(&:inspect).join(', ')
26
+ got = called && called.args.map(&:inspect).join(', ')
27
+ refute called, "Expected no hook matching: (#{matcher})\n got: (#{got})"
28
+ end
29
+
30
+ private
31
+
32
+ def spy_on_hook
33
+ Spy.on(Shipit::Hook, :emit).and_call_through
34
+ rescue Spy::AlreadyHookedError
35
+ end
36
+ end
@@ -4,4 +4,3 @@ module PayloadsHelper
4
4
  JSON.parse(file.read)
5
5
  end
6
6
  end
7
-
@@ -29,7 +29,6 @@ module Shipit
29
29
  DeployCommands.any_instance.expects(:perform).returns([])
30
30
  @job.stubs(:capture)
31
31
 
32
- Hook.expects(:emit).twice
33
32
  assert_enqueued_with(job: FetchDeployedRevisionJob, args: [@deploy.stack]) do
34
33
  @job.perform(@deploy)
35
34
  end
@@ -309,14 +309,14 @@ module Shipit
309
309
  assert_equal initial_state, commit.state
310
310
 
311
311
  expected_status_attributes = {state: new_state, description: initial_state, context: 'ci/travis'}
312
- if should_fire
313
- expect_hook_emit(commit, :deployable_status, expected_status_attributes)
314
- else
315
- Hook.expects(:emit).never
312
+ add_status = -> { commit.add_status(expected_status_attributes.merge(created_at: 1.day.ago.to_s(:db))) }
313
+ expect_hook_emit(commit, :commit_status, expected_status_attributes) do
314
+ if should_fire
315
+ expect_hook_emit(commit, :deployable_status, expected_status_attributes, &add_status)
316
+ else
317
+ expect_no_hook(:deployable_status, &add_status)
318
+ end
316
319
  end
317
- expect_hook_emit(commit, :commit_status, expected_status_attributes)
318
-
319
- commit.add_status(expected_status_attributes.merge(created_at: 1.day.ago.to_s(:db)))
320
320
  end
321
321
  end
322
322
  end
@@ -324,15 +324,24 @@ module Shipit
324
324
  test "#add_status does not fire webhooks for invisible statuses" do
325
325
  commit = shipit_commits(:second)
326
326
  assert commit.stack.hooks.where(events: ['commit_status']).size >= 1
327
- Hook.expects(:emit).never
328
- commit.add_status(state: 'failure', description: 'Sad', context: 'ci/hidden', created_at: 1.day.ago.to_s(:db))
327
+
328
+ expect_no_hook(:deployable_status) do
329
+ commit.add_status(state: 'failure', description: 'Sad', context: 'ci/hidden', created_at: 1.day.ago.to_s(:db))
330
+ end
329
331
  end
330
332
 
331
333
  test "#add_status does not fire webhooks for non-meaningful statuses" do
332
334
  commit = shipit_commits(:second)
333
335
  assert commit.stack.hooks.where(events: ['commit_status']).size >= 1
334
- Hook.expects(:emit).never
335
- commit.add_status(state: 'failure', description: 'Sad', context: 'ci/ok_to_fail', created_at: 1.day.ago.to_s(:db))
336
+
337
+ expect_no_hook(:deployable_status) do
338
+ commit.add_status(
339
+ state: 'failure',
340
+ description: 'Sad',
341
+ context: 'ci/ok_to_fail',
342
+ created_at: 1.day.ago.to_s(:db),
343
+ )
344
+ end
336
345
  end
337
346
 
338
347
  test "#visible_statuses forward the last_statuses to the stack" do
@@ -397,11 +406,13 @@ module Shipit
397
406
  end
398
407
  end
399
408
 
400
- def expect_hook_emit(commit, event, status_attributes)
401
- matchers = status_attributes.to_a.map { |pair| responds_with(pair.first, pair.second) }
402
- Hook.expects(:emit).with(event, commit.stack, has_entries(commit: commit, stack: commit.stack,
403
- event => all_of(*matchers),
404
- status: status_attributes[:state]))
409
+ def expect_hook_emit(commit, event, status_attributes, &block)
410
+ matches = lambda do |payload|
411
+ assert_equal commit, payload[:commit]
412
+ assert_equal commit.stack, payload[:stack]
413
+ assert_equal status_attributes[:state], payload[:status]
414
+ end
415
+ expect_hook(event, commit.stack, matches, &block)
405
416
  end
406
417
  end
407
418
  end
@@ -96,37 +96,41 @@ module Shipit
96
96
  test "transitioning to success causes an event to be broadcasted" do
97
97
  deploy = shipit_deploys(:shipit_pending)
98
98
 
99
- expect_hook(:deploy, deploy.stack, status: 'success', deploy: deploy, stack: deploy.stack)
100
99
  expect_event(deploy)
101
100
  deploy.status = 'running'
102
- deploy.complete!
101
+ expect_hook(:deploy, deploy.stack, status: 'success', deploy: deploy, stack: deploy.stack) do
102
+ deploy.complete!
103
+ end
103
104
  end
104
105
 
105
106
  test "transitioning to failed causes an event to be broadcasted" do
106
107
  deploy = shipit_deploys(:shipit_pending)
107
108
 
108
- expect_hook(:deploy, deploy.stack, status: 'failed', deploy: deploy, stack: deploy.stack)
109
109
  expect_event(deploy)
110
110
  deploy.status = 'running'
111
- deploy.failure!
111
+ expect_hook(:deploy, deploy.stack, status: 'failed', deploy: deploy, stack: deploy.stack) do
112
+ deploy.failure!
113
+ end
112
114
  end
113
115
 
114
116
  test "transitioning to error causes an event to be broadcasted" do
115
117
  deploy = shipit_deploys(:shipit_pending)
116
118
 
117
- expect_hook(:deploy, deploy.stack, status: 'error', deploy: deploy, stack: deploy.stack)
118
119
  expect_event(deploy)
119
120
  deploy.status = 'running'
120
- deploy.error!
121
+ expect_hook(:deploy, deploy.stack, status: 'error', deploy: deploy, stack: deploy.stack) do
122
+ deploy.error!
123
+ end
121
124
  end
122
125
 
123
126
  test "transitioning to running causes an event to be broadcasted" do
124
127
  deploy = shipit_deploys(:shipit_pending)
125
128
 
126
- expect_hook(:deploy, deploy.stack, status: 'running', deploy: deploy, stack: deploy.stack)
127
129
  expect_event(deploy)
128
130
  deploy.status = 'pending'
129
- deploy.run!
131
+ expect_hook(:deploy, deploy.stack, status: 'running', deploy: deploy, stack: deploy.stack) do
132
+ deploy.run!
133
+ end
130
134
  end
131
135
 
132
136
  test "creating a deploy causes an event to be broadcasted" do
@@ -386,9 +390,5 @@ module Shipit
386
390
  channel == "stack.#{deploy.stack.id}" && data['url'] == "/#{deploy.stack.to_param}"
387
391
  end
388
392
  end
389
-
390
- def expect_hook(event, stack, payload)
391
- Hook.expects(:emit).with(event, stack, payload)
392
- end
393
393
  end
394
394
  end
@@ -8,15 +8,15 @@ module Shipit
8
8
  end
9
9
 
10
10
  test "#url must be valid" do
11
- @hook.url = 'file:/ad"fa/adfa'
11
+ @hook.delivery_url = 'file:/ad"fa/adfa'
12
12
  refute @hook.valid?
13
- assert_equal ['Url is not a valid URL'], @hook.errors.full_messages
13
+ assert_equal ['Delivery url is not a valid URL'], @hook.errors.full_messages
14
14
  end
15
15
 
16
16
  test "#url must not be localhost" do
17
- @hook.url = 'file:///etc/passwd'
17
+ @hook.delivery_url = 'file:///etc/passwd'
18
18
  refute @hook.valid?
19
- assert_equal ['Url is not a valid URL'], @hook.errors.full_messages
19
+ assert_equal ['Delivery url is not a valid URL'], @hook.errors.full_messages
20
20
  end
21
21
 
22
22
  test "#events is accessible as an array" do
@@ -42,7 +42,7 @@ module Shipit
42
42
 
43
43
  delivery = Delivery.last
44
44
 
45
- assert_equal @hook.url, delivery.url
45
+ assert_equal @hook.delivery_url, delivery.url
46
46
  assert_equal 'application/x-www-form-urlencoded', delivery.content_type
47
47
  assert_equal 'foo=42', delivery.payload
48
48
  assert_equal 'scheduled', delivery.status
@@ -284,14 +284,16 @@ module Shipit
284
284
  end
285
285
 
286
286
  test "locking the stack triggers a webhook" do
287
- expect_hook(:lock, @stack, locked: true, stack: @stack)
288
- @stack.update(lock_reason: "Just for fun", lock_author: shipit_users(:walrus))
287
+ expect_hook(:lock, @stack, locked: true, stack: @stack) do
288
+ @stack.update(lock_reason: "Just for fun", lock_author: shipit_users(:walrus))
289
+ end
289
290
  end
290
291
 
291
292
  test "unlocking the stack triggers a webhook" do
292
293
  @stack.update(lock_reason: "Just for fun", lock_author: shipit_users(:walrus))
293
- expect_hook(:lock, @stack, locked: false, stack: @stack)
294
- @stack.update(lock_reason: nil)
294
+ expect_hook(:lock, @stack, locked: false, stack: @stack) do
295
+ @stack.update(lock_reason: nil)
296
+ end
295
297
  end
296
298
 
297
299
  test "the git cache lock prevent concurrent access to the git cache" do
@@ -347,10 +349,16 @@ module Shipit
347
349
  assert_equal [commit1, commit2], stack.filter_meaningful_statuses([soft_fail, commit1, commit2])
348
350
  end
349
351
 
350
- private
352
+ test "updating the stack emit a hook" do
353
+ expect_hook(:stack, @stack, action: :updated, stack: @stack) do
354
+ @stack.update(repo_name: 'foo')
355
+ end
356
+ end
351
357
 
352
- def expect_hook(event, stack, payload)
353
- Hook.expects(:emit).with(event, stack, payload)
358
+ test "updating the stack doesn't emit a hook if only `updated_at` is changed" do
359
+ expect_no_hook(:stack) do
360
+ @stack.update(updated_at: Time.zone.now)
361
+ end
354
362
  end
355
363
  end
356
364
  end
data/test/test_helper.rb CHANGED
@@ -6,11 +6,14 @@ SimpleCov.start 'rails'
6
6
  require 'fakeweb'
7
7
  FakeWeb.allow_net_connect = false
8
8
 
9
- require File.expand_path("../../test/dummy/config/environment.rb", __FILE__)
10
- ActiveRecord::Migrator.migrations_paths = [File.expand_path("../../test/dummy/db/migrate", __FILE__)]
11
- ActiveRecord::Migrator.migrations_paths << File.expand_path('../../db/migrate', __FILE__)
12
- require "rails/test_help"
13
- require "mocha/mini_test"
9
+ require File.expand_path('../../test/dummy/config/environment.rb', __FILE__)
10
+ ActiveRecord::Migrator.migrations_paths = [
11
+ File.expand_path('../../test/dummy/db/migrate', __FILE__),
12
+ File.expand_path('../../db/migrate', __FILE__),
13
+ ]
14
+ require 'rails/test_help'
15
+ require 'mocha/mini_test'
16
+ require 'spy/integration'
14
17
 
15
18
  # Load fixtures from the engine
16
19
  if ActiveSupport::TestCase.respond_to?(:fixture_path=)
@@ -34,6 +37,7 @@ class ActiveSupport::TestCase
34
37
  include JSONHelper
35
38
  include LinksHelper
36
39
  include ApiHelper
40
+ include HooksHelper
37
41
  include ActiveJob::TestHelper
38
42
 
39
43
  setup do
@@ -44,5 +44,21 @@ module Shipit
44
44
  refute Shipit.github_enterprise?
45
45
  assert_equal({}, Shipit.github_oauth_options)
46
46
  end
47
+
48
+ test ".github_teams returns an empty array if there's no team" do
49
+ assert_equal([], Shipit.github_teams)
50
+ end
51
+
52
+ test ".github_teams returns the team key as an array" do
53
+ Rails.application.secrets.stubs(:github_oauth).returns('team' => 'shopify/developers')
54
+ assert_equal(['shopify/developers'], Shipit.github_teams.map(&:handle))
55
+ end
56
+
57
+ test ".github_teams merges the teams and team keys in a single array" do
58
+ Rails.application.secrets.stubs(:github_oauth).returns(
59
+ 'team' => 'shopify/developers',
60
+ 'teams' => ['shopify/developers', 'cyclimse/cooks'])
61
+ assert_equal(['cyclimse/cooks', 'shopify/developers'], Shipit.github_teams.map(&:handle))
62
+ end
47
63
  end
48
64
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shipit-engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.4
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jean Boussier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-20 00:00:00.000000000 Z
11
+ date: 2016-02-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -531,6 +531,7 @@ files:
531
531
  - db/migrate/20151113151323_improve_indexes_on_tasks.rb
532
532
  - db/migrate/20160104151742_increase_tasks_type_size_back.rb
533
533
  - db/migrate/20160104151833_convert_sti_columns.rb
534
+ - db/migrate/20160122165559_rename_hooks_url_in_delivery_url.rb
534
535
  - db/schema.rb
535
536
  - lib/shipit-engine.rb
536
537
  - lib/shipit.rb
@@ -632,6 +633,7 @@ files:
632
633
  - test/fixtures/shipit/users.yml
633
634
  - test/helpers/api_helper.rb
634
635
  - test/helpers/fixture_aliases_helper.rb
636
+ - test/helpers/hooks_helper.rb
635
637
  - test/helpers/json_helper.rb
636
638
  - test/helpers/links_helper.rb
637
639
  - test/helpers/payloads_helper.rb
@@ -772,6 +774,7 @@ test_files:
772
774
  - test/fixtures/shipit/users.yml
773
775
  - test/helpers/api_helper.rb
774
776
  - test/helpers/fixture_aliases_helper.rb
777
+ - test/helpers/hooks_helper.rb
775
778
  - test/helpers/json_helper.rb
776
779
  - test/helpers/links_helper.rb
777
780
  - test/helpers/payloads_helper.rb