shipit-engine 0.29.0 → 0.30.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +37 -2
  3. data/app/assets/images/archive-solid.svg +1 -0
  4. data/app/assets/stylesheets/_pages/_stacks.scss +76 -0
  5. data/app/controllers/shipit/api/stacks_controller.rb +20 -1
  6. data/app/controllers/shipit/api_clients_controller.rb +49 -0
  7. data/app/controllers/shipit/merge_status_controller.rb +8 -4
  8. data/app/controllers/shipit/stacks_controller.rb +58 -9
  9. data/app/controllers/shipit/webhooks_controller.rb +2 -130
  10. data/app/helpers/shipit/stacks_helper.rb +4 -0
  11. data/app/jobs/shipit/background_job/unique.rb +3 -1
  12. data/app/jobs/shipit/continuous_delivery_job.rb +1 -0
  13. data/app/jobs/shipit/destroy_stack_job.rb +2 -2
  14. data/app/models/shipit/commit.rb +21 -9
  15. data/app/models/shipit/commit_deployment.rb +15 -11
  16. data/app/models/shipit/commit_deployment_status.rb +6 -2
  17. data/app/models/shipit/deploy.rb +48 -7
  18. data/app/models/shipit/deploy_stats.rb +57 -0
  19. data/app/models/shipit/repository.rb +38 -0
  20. data/app/models/shipit/stack.rb +41 -34
  21. data/app/models/shipit/task.rb +26 -4
  22. data/app/models/shipit/webhooks.rb +32 -0
  23. data/app/models/shipit/webhooks/handlers/check_suite_handler.rb +19 -0
  24. data/app/models/shipit/webhooks/handlers/handler.rb +40 -0
  25. data/app/models/shipit/webhooks/handlers/membership_handler.rb +45 -0
  26. data/app/models/shipit/webhooks/handlers/push_handler.rb +20 -0
  27. data/app/models/shipit/webhooks/handlers/status_handler.rb +26 -0
  28. data/app/serializers/shipit/stack_serializer.rb +6 -1
  29. data/app/validators/ascii_only_validator.rb +3 -3
  30. data/app/views/layouts/_head.html.erb +0 -0
  31. data/app/views/layouts/shipit.html.erb +4 -2
  32. data/app/views/shipit/api_clients/index.html.erb +36 -0
  33. data/app/views/shipit/api_clients/new.html.erb +33 -0
  34. data/app/views/shipit/api_clients/show.html.erb +35 -0
  35. data/app/views/shipit/merge_status/logged_out.erb +1 -1
  36. data/app/views/shipit/stacks/_header.html.erb +12 -7
  37. data/app/views/shipit/stacks/_links.html.erb +1 -0
  38. data/app/views/shipit/stacks/index.html.erb +7 -2
  39. data/app/views/shipit/stacks/settings.html.erb +19 -0
  40. data/app/views/shipit/stacks/statistics.html.erb +82 -0
  41. data/config/locales/en.yml +14 -2
  42. data/config/routes.rb +4 -0
  43. data/db/migrate/20191209231045_create_shipit_repositories.rb +12 -0
  44. data/db/migrate/20191209231307_add_repository_reference_to_stacks.rb +15 -0
  45. data/db/migrate/20191216162728_backfill_repository_data.rb +22 -0
  46. data/db/migrate/20191216163010_remove_repository_information_from_stacks.rb +20 -0
  47. data/db/migrate/20191219205202_add_archived_since_to_stacks.rb +6 -0
  48. data/db/migrate/20200102175621_optional_task_commits.rb +6 -0
  49. data/db/migrate/20200109132519_add_sha_to_commit_deployments.rb +5 -0
  50. data/lib/shipit/github_app.rb +32 -3
  51. data/lib/shipit/task_commands.rb +10 -2
  52. data/lib/shipit/version.rb +1 -1
  53. data/test/controllers/api/ccmenu_controller_test.rb +1 -1
  54. data/test/controllers/api/stacks_controller_test.rb +14 -6
  55. data/test/controllers/api_clients_controller_test.rb +103 -0
  56. data/test/controllers/merge_status_controller_test.rb +21 -4
  57. data/test/controllers/stacks_controller_test.rb +35 -0
  58. data/test/controllers/webhooks_controller_test.rb +26 -0
  59. data/test/dummy/config/environments/development.rb +22 -4
  60. data/test/dummy/db/schema.rb +17 -6
  61. data/test/dummy/db/seeds.rb +20 -6
  62. data/test/fixtures/shipit/commit_deployment_statuses.yml +4 -4
  63. data/test/fixtures/shipit/commit_deployments.yml +8 -8
  64. data/test/fixtures/shipit/commits.yml +23 -0
  65. data/test/fixtures/shipit/repositories.yml +23 -0
  66. data/test/fixtures/shipit/stacks.yml +100 -16
  67. data/test/fixtures/shipit/tasks.yml +66 -3
  68. data/test/jobs/destroy_stack_job_test.rb +9 -0
  69. data/test/models/commit_deployment_status_test.rb +33 -4
  70. data/test/models/commit_deployment_test.rb +8 -11
  71. data/test/models/commits_test.rb +22 -2
  72. data/test/models/deploy_stats_test.rb +112 -0
  73. data/test/models/deploys_test.rb +55 -17
  74. data/test/models/pull_request_test.rb +1 -1
  75. data/test/models/shipit/repository_test.rb +76 -0
  76. data/test/models/shipit/wehbooks/handlers_test.rb +26 -0
  77. data/test/models/stacks_test.rb +44 -51
  78. data/test/models/undeployed_commits_test.rb +13 -0
  79. data/test/test_helper.rb +3 -1
  80. data/test/unit/deploy_commands_test.rb +9 -0
  81. data/test/unit/github_app_test.rb +136 -0
  82. metadata +161 -128
@@ -0,0 +1 @@
1
+ <%# Placeholder to be used by the host application %>
@@ -1,5 +1,5 @@
1
1
  <% content_for :page_title do %>
2
- <h1>Shipit</h1>
2
+ <h1><%= Shipit.app_name %></h1>
3
3
  <% end %>
4
4
  <% content_for :primary_navigation do %>
5
5
  <%= link_to 'Add a stack', new_stack_path, class: 'btn secondary' %>
@@ -18,7 +18,7 @@
18
18
  </ul>
19
19
  <ul class="stack-lst">
20
20
  <% @stacks.each do |stack| %>
21
- <li class="search-item <%= stack.undeployed_commits? ? 'undeployed' : '' %> <%= @user_stacks.include?(stack.id) ? 'contributor' : 'not-matching' %>" data-search="<%= stack.repo_name %> <%= stack.environment %> <%= stack.deploy_url %>" data-stack-id="<%= stack.id %>">
21
+ <li class="search-item <%= stack.archived? ? 'archived' : '' %> <%= stack.undeployed_commits? ? 'undeployed' : '' %> <%= @user_stacks.include?(stack.id) ? 'contributor' : 'not-matching' %>" data-search="<%= stack.repo_name %> <%= stack.environment %> <%= stack.deploy_url %>" data-stack-id="<%= stack.id %>">
22
22
  <%= link_to stack_path(stack), class: 'commits-path' do %>
23
23
  <span class="col"><%= stack.repo_name %></span>
24
24
  <small class="col"><%= stack.environment.capitalize %></small>
@@ -30,6 +30,11 @@
30
30
  </ul>
31
31
 
32
32
  <%= link_to 'show all stacks', '#', class: 'btn secondary show-all-stacks' %></p>
33
+ <% if @show_archived %>
34
+ <%= link_to 'hide archived stacks', '?', class: 'btn secondary' %></p>
35
+ <% else %>
36
+ <%= link_to 'show archived stacks', '?show_archived=1', class: 'btn secondary' %></p>
37
+ <% end %>
33
38
  </section>
34
39
 
35
40
  </div>
@@ -61,6 +61,7 @@
61
61
  <%- end -%>
62
62
  <% end %>
63
63
  </div>
64
+
64
65
  <div class="setting-section">
65
66
  <h5>Resynchronize this stack</h5>
66
67
  <table>
@@ -80,6 +81,24 @@
80
81
  <%= button_to "Fetch URL", "", class: 'btn', data: {remote: ccmenu_url_url(stack_id: @stack.to_param)} %>
81
82
  </div>
82
83
 
84
+ <div class="setting-section">
85
+ <% if @stack.archived? %>
86
+ <h5>Restore Stack</h5>
87
+ <p>This action will de-archive the Stack, restoring it in the stack list and unlocking it.</p>
88
+ <%= form_for @stack do |f| %>
89
+ <%= f.hidden_field :archived, value: false %>
90
+ <%= f.submit class: "btn", value: "Restore" %>
91
+ <% end %>
92
+ <% else %>
93
+ <h5>Archive Stack</h5>
94
+ <p>This action will archive the Stack, hiding it from the stack list and locking it. It can still be found if you know the URL and de-archived.</p>
95
+ <%= form_for @stack do |f| %>
96
+ <%= f.hidden_field :archived, value: true %>
97
+ <%= f.submit class: "btn", value: "Archive" %>
98
+ <% end %>
99
+ <% end %>
100
+ </div>
101
+
83
102
  <div class="setting-section">
84
103
  <h5>Delete this stack</h5>
85
104
  <p>This action will delete the stack from Ship it permanently. Be careful.</p>
@@ -0,0 +1,82 @@
1
+ <%= render partial: 'shipit/stacks/header', locals: {stack: @stack} %>
2
+ <%= render partial: 'shipit/stacks/banners', locals: {stack: @stack} %>
3
+
4
+ <div class="wrapper">
5
+ <section>
6
+ <header class="section-header">
7
+ <h2>Statistics &ndash; Past 7 Days</h2>
8
+ </header>
9
+
10
+ <div class="row">
11
+ <div class="box">
12
+ <div class="box__body">
13
+ <div class="stats">
14
+ <div class="stats__amount"><%= @deploy_stats.count %></div>
15
+ <div class="stats__caption">Deploys</div>
16
+ <div class="stats__change">
17
+ <div class="stats__value stats__value--<%=positive_negative_class(@diffs[:count])%>"><%= number_to_percentage(@diffs[:count], precision: 1) %></div>
18
+ <div class="stats__period">this week</div>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ </div>
23
+
24
+ <div class="box">
25
+ <div class="box__body">
26
+ <div class="stats">
27
+ <div class="stats__amount"><%= number_to_percentage(@deploy_stats.success_rate, precision: 1) %></div>
28
+ <div class="stats__caption">Deploys success rate</div>
29
+ </div>
30
+ </div>
31
+ </div>
32
+
33
+ <div class="box">
34
+ <div class="box__body">
35
+ <div class="stats">
36
+ <div class="stats__amount"><%= Shipit::Duration.new @deploy_stats.average_duration %></div>
37
+ <div class="stats__caption">Average deploy time</div>
38
+ <div class="stats__change">
39
+ <div class="stats__value stats__value--<%=positive_negative_class(@diffs[:average_duration])%>"><%= number_to_percentage(@diffs[:average_duration], precision: 1) %></div>
40
+ <div class="stats__period">this week</div>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ </div>
46
+
47
+ <div class="row">
48
+ <div class="box">
49
+ <div class="box__body">
50
+ <div class="stats">
51
+ <div class="stats__amount"><%= Shipit::Duration.new @deploy_stats.min_duration %></div>
52
+ <div class="stats__caption">Min deploy time</div>
53
+ </div>
54
+ </div>
55
+ </div>
56
+
57
+ <div class="box">
58
+ <div class="box__body">
59
+ <div class="stats">
60
+ <div class="stats__amount"><%= Shipit::Duration.new @deploy_stats.median_duration %></div>
61
+ <div class="stats__caption">Median deploy time</div>
62
+ <div class="stats__change">
63
+ <div class="stats__value stats__value--<%=positive_negative_class(@diffs[:median_duration])%>"><%= number_to_percentage(@diffs[:median_duration], precision: 1) %></div>
64
+ <div class="stats__period">this week</div>
65
+ </div>
66
+ </div>
67
+ </div>
68
+ </div>
69
+
70
+ <div class="box">
71
+ <div class="box__body">
72
+ <div class="stats">
73
+ <div class="stats__amount"><%= Shipit::Duration.new @deploy_stats.max_duration %></div>
74
+ <div class="stats__caption">Max deploy time</div>
75
+ </div>
76
+ </div>
77
+ </div>
78
+ </div>
79
+
80
+ </section>
81
+
82
+ </div>
@@ -20,6 +20,16 @@
20
20
  # available at http://guides.rubyonrails.org/i18n.html.
21
21
 
22
22
  en:
23
+ stack:
24
+ nav:
25
+ refresh: Refresh statuses & commits
26
+ commits: Commits & Deploys
27
+ settings: Settings
28
+ timeline: Timeline
29
+ statistics: Stats
30
+ merge_queue: "Merge Queue (%{count})"
31
+ view_on_github: View on GitHub
32
+ deploy_link: View website
23
33
  commit:
24
34
  lock: This commit is safe to deploy. Click to mark it as unsafe.
25
35
  unlock: This commit was marked as unsafe to deploy. Click to mark it as safe.
@@ -72,12 +82,14 @@ en:
72
82
  ascii: "contains non-ASCII characters"
73
83
  deployment_description:
74
84
  deploy:
75
- pending: "%{author} triggered the deploy of %{stack} to %{sha}"
85
+ in_progress: "%{author} triggered the deploy of %{stack} to %{sha}"
86
+ pending: "%{author} created the deploy of %{stack} to %{sha}"
76
87
  success: "%{author} deployed %{stack} to %{sha}"
77
88
  failure: "Deploy of %{stack} to %{sha} by %{author} failed"
78
89
  error: "Deploy of %{stack} to %{sha} by %{author} failed"
79
90
  rollback:
80
- pending: "%{author} triggered the rollback of %{stack} to %{sha}"
91
+ in_progress: "%{author} triggered the deploy of %{stack} to %{sha}"
92
+ pending: "%{author} created the rollback of %{stack} to %{sha}"
81
93
  success: "%{author} rolled back %{stack} to %{sha}"
82
94
  failure: "Rollback of %{stack} to %{sha} by %{author} failed"
83
95
  error: "Rollback of %{stack} to %{sha} by %{author} failed"
@@ -47,6 +47,7 @@ Shipit::Engine.routes.draw do
47
47
  delete '/merge_status/*stack_id/pull/:number', action: :dequeue, controller: :merge_status, id: stack_id_format, as: :dequeue_pull_request
48
48
 
49
49
  # Humans
50
+ resources :api_clients
50
51
  resources :stacks, only: %i(new create index)
51
52
 
52
53
  scope '/github/auth/github', as: :github_authentication, controller: :github_authentication do
@@ -61,6 +62,7 @@ Shipit::Engine.routes.draw do
61
62
  patch '/' => 'stacks#update'
62
63
  delete '/' => 'stacks#destroy'
63
64
  get :settings, controller: :stacks
65
+ get :statistics, controller: :stacks
64
66
  post :refresh, controller: :stacks
65
67
  get :refresh, controller: :stacks # For easier design, sorry :/
66
68
  post :clear_git_cache, controller: :stacks
@@ -74,6 +76,8 @@ Shipit::Engine.routes.draw do
74
76
  get '/commit/:sha/checks' => 'commit_checks#show', as: :commit_checks
75
77
  get '/commit/:sha/checks/tail' => 'commit_checks#tail', as: :tail_commit_checks, defaults: {format: :json}
76
78
 
79
+ get '/stats' => 'stats#show', as: :stats
80
+
77
81
  resources :rollbacks, only: %i(create)
78
82
  resources :commits, only: %i(update)
79
83
  resources :tasks, only: %i(show) do
@@ -0,0 +1,12 @@
1
+ class CreateShipitRepositories < ActiveRecord::Migration[6.0]
2
+ def change
3
+ create_table :repositories do |t|
4
+ t.string :owner, limit: 39, null: false
5
+ t.string :name, limit: 100, null: false
6
+
7
+ t.timestamps
8
+ end
9
+
10
+ add_index :repositories, ["owner", "name"], name: "repository_unicity", unique: true
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ class AddRepositoryReferenceToStacks < ActiveRecord::Migration[6.0]
2
+ def up
3
+ change_table(:stacks) do |t|
4
+ t.references :repository
5
+ end
6
+ end
7
+
8
+ def down
9
+ change_column :stacks, :repo_name, :string, null: false
10
+ change_column :stacks, :repo_owner, :string, null: false
11
+ change_table(:stacks) do |t|
12
+ t.remove_references :repository
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ class BackfillRepositoryData < ActiveRecord::Migration[6.0]
2
+ def up
3
+ repositories = {}
4
+ Shipit::Stack.all.each do |stack|
5
+ repo_owner = stack[:repo_owner].downcase
6
+ repo_name = stack[:repo_name].downcase
7
+
8
+ repository = (repositories[[repo_owner, repo_name]] ||= Shipit::Repository.create_or_find_by!(
9
+ owner: repo_owner,
10
+ name: repo_name,
11
+ ))
12
+
13
+ stack.update_column(:repository_id, repository.id)
14
+ end
15
+ end
16
+
17
+ def down
18
+ Shipit::Repository.find_each do |repository|
19
+ repository.stacks.update_all(repo_owner: repository.owner, repo_name: repository.name)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ class RemoveRepositoryInformationFromStacks < ActiveRecord::Migration[6.0]
2
+ def up
3
+ change_column_null :stacks, :repository_id, false
4
+ change_table(:stacks) do |t|
5
+ t.remove_index ["repo_owner", "repo_name", "environment"]
6
+ t.remove :repo_owner
7
+ t.remove :repo_name
8
+ t.index ["repository_id", "environment"], name: "stack_unicity", unique: true
9
+ end
10
+ end
11
+
12
+ def down
13
+ change_table(:stacks) do |t|
14
+ t.column :repo_name, :string, limit: 100
15
+ t.column :repo_owner, :string, limit: 39
16
+ t.remove_index ["repository_id", "environment"]
17
+ t.index ["repo_owner", "repo_name", "environment"], name: "stack_unicity", unique: true
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,6 @@
1
+ class AddArchivedSinceToStacks < ActiveRecord::Migration[6.0]
2
+ def change
3
+ add_column :stacks, :archived_since, :datetime
4
+ add_index :stacks, :archived_since
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class OptionalTaskCommits < ActiveRecord::Migration[6.0]
2
+ def change
3
+ change_column_null :tasks, :since_commit_id, true
4
+ change_column_null :tasks, :until_commit_id, true
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ class AddShaToCommitDeployments < ActiveRecord::Migration[6.0]
2
+ def change
3
+ add_column :commit_deployments, :sha, :string, limit: 40
4
+ end
5
+ end
@@ -9,6 +9,8 @@ module Shipit
9
9
  end
10
10
  end
11
11
 
12
+ attr_reader :expires_at, :refresh_at
13
+
12
14
  def to_s
13
15
  @token
14
16
  end
@@ -16,10 +18,16 @@ module Shipit
16
18
  def initialize(token, expires_at)
17
19
  @token = token
18
20
  @expires_at = expires_at
21
+
22
+ # This needs to be lower than the token's lifetime, but higher than the cache expiry setting.
23
+ @refresh_at = expires_at - GITHUB_TOKEN_REFRESH_WINDOW
19
24
  end
20
25
 
21
26
  def blank?
22
- @expires_at.past?
27
+ # Old tokens missing @refresh_at may be used upon deploy, so we should auto-correct for now.
28
+ # TODO: Remove this assignment at a later date.
29
+ @refresh_at ||= @expires_at - GITHUB_TOKEN_REFRESH_WINDOW
30
+ @refresh_at.past?
23
31
  end
24
32
  end
25
33
 
@@ -27,6 +35,10 @@ module Shipit
27
35
  AuthenticationFailed = Class.new(StandardError)
28
36
  API_STATUS_ID = 'brv1bkgrwx7q'.freeze
29
37
 
38
+ GITHUB_EXPECTED_TOKEN_LIFETIME = 60.minutes
39
+ GITHUB_TOKEN_RAILS_CACHE_LIFETIME = 50.minutes
40
+ GITHUB_TOKEN_REFRESH_WINDOW = GITHUB_EXPECTED_TOKEN_LIFETIME - GITHUB_TOKEN_RAILS_CACHE_LIFETIME - 2.minutes
41
+
30
42
  attr_reader :oauth_teams, :domain, :bot_login
31
43
 
32
44
  def initialize(config)
@@ -79,13 +91,21 @@ module Shipit
79
91
  end
80
92
 
81
93
  def fetch_new_token
82
- Rails.cache.fetch('github:integration:access-token', expires_in: 50.minutes, race_condition_ttl: 10.minutes) do
94
+ # Rails can add 5 minutes to the cache entry expiration time when any TTL is provided,
95
+ # so our TTL setting can be lower, and TTL + expires_in should be lower than the GitHub token expiration.
96
+ Rails.cache.fetch(
97
+ 'github:integration:access-token',
98
+ expires_in: GITHUB_TOKEN_RAILS_CACHE_LIFETIME,
99
+ race_condition_ttl: 4.minutes,
100
+ ) do
83
101
  response = new_client(bearer_token: authentication_payload).create_app_installation_access_token(
84
102
  installation_id,
85
103
  accept: 'application/vnd.github.machine-man-preview+json',
86
104
  )
87
105
  token = Token.from_github(response)
88
106
  raise AuthenticationFailed if token.blank?
107
+ Rails.logger.info("Created GitHub access token ending #{token.to_s[-5..-1]}, expires at #{token.expires_at}"\
108
+ " and will be refreshed at #{token&.refresh_at}")
89
109
  token
90
110
  end
91
111
  end
@@ -120,12 +140,21 @@ module Shipit
120
140
  url('/api/v3/') if enterprise?
121
141
  end
122
142
 
143
+ def web_endpoint
144
+ url if enterprise?
145
+ end
146
+
123
147
  def enterprise?
124
148
  domain != DOMAIN
125
149
  end
126
150
 
127
151
  def new_client(options = {})
128
- options.reverse_merge(api_endpoint: api_endpoint) if api_endpoint
152
+ if enterprise?
153
+ options = options.reverse_merge(
154
+ api_endpoint: api_endpoint,
155
+ web_endpoint: web_endpoint,
156
+ )
157
+ end
129
158
  client = Octokit::Client.new(options)
130
159
  client.middleware = faraday_stack
131
160
  client
@@ -28,19 +28,21 @@ module Shipit
28
28
  end
29
29
 
30
30
  def env
31
- normalized_name = ActiveSupport::Inflector.transliterate(@task.author.name)
32
31
  super.merge(
33
32
  'ENVIRONMENT' => @stack.environment,
34
33
  'BRANCH' => @stack.branch,
35
- 'SHIPIT_USER' => "#{@task.author.login} (#{normalized_name}) via Shipit",
34
+ 'SHIPIT_USER' => "#{@task.author.login} (#{normalized_author_name}) via Shipit",
36
35
  'EMAIL' => @task.author.email,
37
36
  'BUNDLE_PATH' => Rails.root.join('data', 'bundler').to_s,
38
37
  'SHIPIT_LINK' => @task.permalink,
39
38
  'LAST_DEPLOYED_SHA' => @stack.last_deployed_commit.sha,
40
39
  'TASK_ID' => @task.id.to_s,
40
+ 'DEPLOY_URL' => @stack.deploy_url,
41
41
  'IGNORED_SAFETIES' => @task.ignored_safeties? ? '1' : '0',
42
42
  'GIT_COMMITTER_NAME' => @task.user&.name || Shipit.committer_name,
43
43
  'GIT_COMMITTER_EMAIL' => @task.user&.email || Shipit.committer_email,
44
+ 'GITHUB_REPO_OWNER' => @stack.repository.owner,
45
+ 'GITHUB_REPO_NAME' => @stack.repository.name,
44
46
  ).merge(deploy_spec.machine_env).merge(@task.env)
45
47
  end
46
48
 
@@ -70,6 +72,12 @@ module Shipit
70
72
  FileUtils.rm_rf(@task.working_directory) if deploy_spec.clear_working_directory?
71
73
  end
72
74
 
75
+ private
76
+
77
+ def normalized_author_name
78
+ ActiveSupport::Inflector.transliterate(@task.author.name)
79
+ end
80
+
73
81
  protected
74
82
 
75
83
  def steps_directory
@@ -1,3 +1,3 @@
1
1
  module Shipit
2
- VERSION = '0.29.0'.freeze
2
+ VERSION = '0.30.0'.freeze
3
3
  end
@@ -43,7 +43,7 @@ module Shipit
43
43
  end
44
44
 
45
45
  test "stacks with no deploys render correctly" do
46
- stack = Stack.create!(repo_owner: 'foo', repo_name: 'bar')
46
+ stack = Stack.create!(repository: Repository.new(owner: "foo", name: "bar"))
47
47
  get :show, params: {stack_id: stack.to_param}
48
48
  assert_payload 'lastBuildStatus', 'Success'
49
49
  end
@@ -25,7 +25,7 @@ module Shipit
25
25
  post :create, params: {repo_owner: 'some', repo_name: 'owner/path'}
26
26
  end
27
27
  assert_response :unprocessable_entity
28
- assert_json 'errors', 'repo_name' => ['is invalid']
28
+ assert_json 'errors', 'repository' => ['is invalid']
29
29
  end
30
30
 
31
31
  test "#create creates a stack and renders it back" do
@@ -38,19 +38,27 @@ module Shipit
38
38
  end
39
39
 
40
40
  test "#create fails to create stack if it already exists" do
41
- Stack.create!(
42
- repo_name: 'rails',
43
- repo_owner: 'rails',
41
+ repository = shipit_repositories(:rails)
42
+ existing_stack = Stack.create!(
43
+ repository: repository,
44
44
  environment: 'staging',
45
45
  branch: 'staging',
46
46
  )
47
47
 
48
48
  assert_no_difference -> { Stack.count } do
49
- post :create, params: {repo_name: 'rails', repo_owner: 'rails', environment: 'staging', branch: 'staging'}
49
+ post :create,
50
+ params: {
51
+ repo_name: existing_stack.repo_name,
52
+ repo_owner: existing_stack.repo_owner,
53
+ environment: existing_stack.environment,
54
+ branch: existing_stack.branch,
55
+ }
50
56
  end
51
57
 
52
58
  assert_response :unprocessable_entity
53
- assert_json 'errors', 'repo_name' => ['cannot be used more than once with this environment']
59
+ assert_json 'errors', 'repository' => [
60
+ 'cannot be used more than once with this environment. Check archived stacks.',
61
+ ]
54
62
  end
55
63
 
56
64
  test "#index returns a list of stacks" do