shipit-engine 0.29.0 → 0.30.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 (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