shipit-engine 0.21.0 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -106
  3. data/app/assets/images/timedout.svg +14 -0
  4. data/app/assets/stylesheets/_pages/_commits.scss +11 -2
  5. data/app/controllers/shipit/stacks_controller.rb +1 -7
  6. data/app/controllers/shipit/webhooks_controller.rb +102 -66
  7. data/app/helpers/shipit/github_url_helper.rb +2 -2
  8. data/app/helpers/shipit/shipit_helper.rb +3 -31
  9. data/app/jobs/shipit/destroy_job.rb +9 -0
  10. data/app/jobs/shipit/github_sync_job.rb +1 -1
  11. data/app/jobs/shipit/setup_github_hook_job.rb +1 -3
  12. data/app/models/shipit/anonymous_user.rb +4 -1
  13. data/app/models/shipit/commit.rb +8 -8
  14. data/app/models/shipit/commit_deployment.rb +3 -3
  15. data/app/models/shipit/commit_deployment_status.rb +2 -2
  16. data/app/models/shipit/deploy.rb +3 -3
  17. data/app/models/shipit/deploy_spec/file_system.rb +3 -3
  18. data/app/models/shipit/deploy_spec/kubernetes_discovery.rb +10 -2
  19. data/app/models/shipit/github_hook.rb +2 -99
  20. data/app/models/shipit/github_status.rb +1 -1
  21. data/app/models/shipit/hook.rb +1 -1
  22. data/app/models/shipit/pull_request.rb +10 -10
  23. data/app/models/shipit/rollback.rb +1 -1
  24. data/app/models/shipit/stack.rb +27 -26
  25. data/app/models/shipit/task.rb +2 -2
  26. data/app/models/shipit/team.rb +4 -17
  27. data/app/models/shipit/user.rb +3 -3
  28. data/app/serializers/shipit/task_serializer.rb +2 -2
  29. data/app/serializers/shipit/user_serializer.rb +1 -1
  30. data/app/views/shipit/missing_settings.html.erb +5 -36
  31. data/app/views/shipit/stacks/new.html.erb +1 -1
  32. data/app/views/shipit/stacks/settings.html.erb +0 -4
  33. data/config/routes.rb +3 -13
  34. data/config/secrets.development.shopify.yml +10 -15
  35. data/config/secrets.development.yml +1 -1
  36. data/db/migrate/20180417130436_remove_all_github_hooks.rb +11 -0
  37. data/lib/shipit.rb +13 -56
  38. data/lib/shipit/command.rb +1 -1
  39. data/lib/shipit/engine.rb +2 -8
  40. data/lib/shipit/github_app.rb +122 -0
  41. data/lib/shipit/octokit_bot_users_patch.rb +25 -0
  42. data/lib/shipit/octokit_iterator.rb +2 -2
  43. data/lib/shipit/version.rb +1 -1
  44. data/lib/tasks/teams.rake +8 -24
  45. data/test/controllers/stacks_controller_test.rb +3 -29
  46. data/test/controllers/webhooks_controller_test.rb +29 -46
  47. data/test/dummy/config/secrets.yml +40 -10
  48. data/test/dummy/db/development.sqlite3 +0 -0
  49. data/test/dummy/db/schema.rb +1 -1
  50. data/test/dummy/db/seeds.rb +0 -1
  51. data/test/dummy/db/test.sqlite3 +0 -0
  52. data/test/fixtures/payloads/push_master.json +7 -6
  53. data/test/fixtures/payloads/push_not_master.json +7 -6
  54. data/test/fixtures/shipit/users.yml +2 -2
  55. data/test/helpers/hooks_helper.rb +1 -1
  56. data/test/helpers/payloads_helper.rb +1 -2
  57. data/test/jobs/destroy_stack_job_test.rb +1 -1
  58. data/test/models/commits_test.rb +5 -5
  59. data/test/models/deploy_spec_test.rb +17 -5
  60. data/test/models/github_hook_test.rb +1 -40
  61. data/test/models/pull_request_test.rb +11 -11
  62. data/test/models/stacks_test.rb +4 -10
  63. data/test/models/team_test.rb +3 -3
  64. data/test/models/users_test.rb +7 -7
  65. data/test/test_helper.rb +1 -1
  66. data/test/unit/github_app_test.rb +44 -0
  67. data/test/unit/shipit_test.rb +2 -49
  68. metadata +9 -3
  69. data/lib/tasks/webhook.rake +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f6940a4cd6a4608c60f6349020a01ef276049cf5aa78ff784abfe2cdf99a920f
4
- data.tar.gz: f7056eb044a29a5646a349bcfe9fc8c6614fcbd7a199797275657c6c3598bb2c
3
+ metadata.gz: '09b8ffb2b4130e05d8da6bfbdc22080becca47dd92ee5a06b4113d3729578226'
4
+ data.tar.gz: 2223bee54a1d2a084823763876a049668829af0725779b413f0ed6d665ed45d6
5
5
  SHA512:
6
- metadata.gz: 4d61eb2c4fe287478d9ef7589b59ac268abbac149dc1783f15cf98d22230463aaeea4caf13060da7f0b2038742585e5df00cd29d05edd571a2326c030bbefbc0
7
- data.tar.gz: 6d94a0ca00022f7727d7ecc9dd965f60ae9b92867c44434fa5057cd036a512ed04a685f0dd0e93a8886bd4df370a01682e6e2c213b5c8d65c1db4339efd79f2e
6
+ metadata.gz: 2a2bef860f0dc71622f450e4c52949680c7edca82c48462a48db34beb034b4b022e020228d160f5b86b7792d5938e0fdc09e6fb08e018c8a8e7f147bf22b60e7
7
+ data.tar.gz: 7cf318e67131f2a0a4bfbd316a8b732a5c8cd57fa49428d1f38a25e978a2e1535d0c5c8ca5e67bb83b4ad3c8f7dd07390e43832ef06ba697a9a2c5671e2df2d6
data/README.md CHANGED
@@ -20,7 +20,6 @@ This guide aims to help you [set up](#installation-and-setup), [use](#using-ship
20
20
  **I. INSTALLATION & SETUP**
21
21
 
22
22
  * [Installation](#installation)
23
- * [Configuring shipit.yml and secrets.yml](#configuring-ymls)
24
23
  * [Updating an existing installation](#updating-shipit)
25
24
 
26
25
  **II. USING SHIPIT**
@@ -43,21 +42,7 @@ This guide aims to help you [set up](#installation-and-setup), [use](#using-ship
43
42
 
44
43
  <h3 id="installation">Installation</h3>
45
44
 
46
- *Shipit requires a database (MySQL, PostgreSQL or SQLite3), Redis, and Ruby 2.1 or superior.*
47
-
48
- Shipit provides you with a Rails template. To bootstrap your Shipit installation:
49
-
50
- 1. If you don't have Rails installed, run this command: `gem install rails -v 5.1`
51
- 2. Run this command: `rails _5.1_ new shipit --skip-action-cable --skip-turbolinks --skip-action-mailer -m https://raw.githubusercontent.com/Shopify/shipit-engine/master/template.rb`
52
- 3. Enter your **Client ID**, **Client Secret**, and **GitHub API access token** when prompted. These can be found on your application's GitHub page.
53
- 4. To setup the database, run this command: `rake db:setup`
54
-
55
- <h3 id="configuring-ymls">Configuring <code>shipit.yml</code> and <code>secrets.yml</code></h3>
56
-
57
- Shipit should just work right out of the box &mdash; you probably won't need to alter its configuration files before getting it up and running. But if you want to customize Shipit for your own deployment environment, you'll need to edit the `shipit.yml` and `secrets.yml` files:
58
-
59
- * The settings in the `shipit.yml` file are related to the different things you can do within Shipit, such as handling deploys, performing custom tasks, and enforcing deployment checklists. If you want to edit these settings, [start here](#configuring-shipit).
60
- * The settings in the `secrets.yml` file are related to the ways that Shipit connects with GitHub. If you want to edit these settings, [start here](#configuring-secrets).
45
+ To create a new Shipit installation you can follow the [setup guide](docs/setup.md).
61
46
 
62
47
  <h3 id="updating-shipit">Updating an existing installation</h3>
63
48
 
@@ -65,6 +50,10 @@ Shipit should just work right out of the box &mdash; you probably won't need to
65
50
  2. Update the `shipit-engine` gem with `bundle update shipit-engine`.
66
51
  3. Install new migrations with `rake shipit:install:migrations db:migrate`.
67
52
 
53
+ <h3 id="special-update">Specific updates requiring more steps</h3>
54
+
55
+ If you are upgrading from `0.21` or older, you will have to update the configuration. Please follow [the dedicated upgrade guide](docs/updates/0.22.md)
56
+
68
57
  * * *
69
58
 
70
59
  <h2 id="using-shipit">II. USING SHIPIT</h2>
@@ -570,96 +559,6 @@ See also `commands_inactivity_timeout` in `secrets.yml` for a global timeout set
570
559
 
571
560
 
572
561
  ***
573
- <h2 id="configuring-secrets">Configuring <code>secrets.yml</code></h2>
574
-
575
- The settings in the `secrets.yml` file relate to the ways that GitHub connects with Shipit:
576
-
577
- **`secret_key_base`** is used to verify the integrity of signed cookies.
578
-
579
- For example:
580
-
581
- ```yml
582
- production:
583
- secret_key_base: s3cr3t # This needs to be a very long, fully random
584
- ```
585
- <br>
586
-
587
- **`github_oauth`** contains the settings required to authenticate users through GitHub.
588
-
589
- The value for `id` is your application's *Client ID*, and the value for `secret` is your application's *Client Secret* &mdash; both of these should appear on your application's GitHub page.
590
-
591
- Note: When setting up your application in Github, set the *Authorization callback URL* to `<yourdomain>/github/auth/github/callback`.
592
-
593
- The `teams` key is optional, and required only if you want to restrict access to a set of GitHub teams.
594
-
595
- If it's missing, the Shipit installation will be public unless you setup another authentication method.
596
-
597
- After you change the list of teams, you have to invoke `bin/rake teams:fetch` in production so that a webhook is setup to keep the list of members up to date.
598
-
599
- For example:
600
-
601
- ```yml
602
- production:
603
- github_oauth:
604
- id: (your application's Client ID)
605
- secret: (your application's Client Secret)
606
- teams:
607
- - Shipit/team
608
- - Shipit/another_team
609
- ```
610
- <br>
611
-
612
- **`github_api`** communicates with the GitHub API about the stacks and setup Hooks. It should reflect the guidelines at https://github.com/octokit/octokit.rb.
613
-
614
- If you specify an `access_token`, you don't need a `login` and `password`. The opposite is also true: if you specify a `login` and `password`, then you don't need an `access_token`.
615
-
616
- For example:
617
-
618
- ```yml
619
- production:
620
- github_api:
621
- access_token: 10da65c687f6degaf5475ce12a980d5vd8c44d2a
622
- ```
623
- <br>
624
-
625
- **`host`** is the host that hosts Shipit. It's used to generate URLs, and it's the host that GitHub will try to talk to.
626
-
627
- For example:
628
- ```yml
629
- production:
630
- host: 'http://localhost:3000'
631
- ```
632
- <br>
633
-
634
- **`redis_url`** is the URL of the redis instance that Shipit uses.
635
-
636
- For example:
637
-
638
- ```yml
639
- production:
640
- redis_url: "redis://127.0.0.1:6379/7"
641
- ```
642
-
643
- <br>
644
-
645
- If you use GitHub Enterprise, you must also specify the `github_domain`.
646
-
647
- For example:
648
- ```yml
649
- production:
650
- github_domain: "github.example.com"
651
-
652
- ```
653
-
654
- <br>
655
-
656
- **`commands_inactivity_timeout`** is the duration after which Shipit will terminate a command if no ouput was received. Default is `300` (5 minutes).
657
-
658
- For example:
659
- ```yml
660
- production:
661
- commands_inactivity_timeout: 900 # 15 minutes
662
- ```
663
562
 
664
563
  <h2 id="script-parameters">Script parameters</h2>
665
564
 
@@ -0,0 +1,14 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4
+ <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
5
+ viewBox="0 0 60.4 71.8" enable-background="new 0 0 60.4 71.8" xml:space="preserve">
6
+ <g>
7
+ <path d="M34.7,11.7V5.9h3.1c1.6,0,2.9-1.3,2.9-2.9c0-1.6-1.3-2.9-2.9-2.9H22.6c-1.6,0-2.9,1.3-2.9,2.9c0,1.6,1.3,2.9,2.9,2.9h3.1
8
+ v5.9C11.2,13.9,0,26.5,0,41.6c0,16.7,13.5,30.2,30.2,30.2s30.2-13.5,30.2-30.2C60.4,26.5,49.3,13.9,34.7,11.7z M30.2,64.3
9
+ c-12.5,0-22.7-10.2-22.7-22.7c0-12.5,10.2-22.7,22.7-22.7s22.7,10.2,22.7,22.7C52.9,54.1,42.7,64.3,30.2,64.3z"/>
10
+ <path d="M44.9,48.4l-12-8.1V26.4c0-0.3-0.1-0.7-0.4-0.9c-0.2-0.2-0.6-0.4-0.9-0.4h-3.3c-0.3,0-0.7,0.1-0.9,0.4
11
+ c-0.2,0.2-0.4,0.6-0.4,0.9v16.4c0,0.3,0.1,0.7,0.4,0.9c0.1,0.1,0.2,0.2,0.3,0.2l13.9,9.3c0.2,0.1,0.5,0.2,0.7,0.2
12
+ c0.1,0,0.2,0,0.3,0c0.3-0.1,0.6-0.3,0.8-0.6l1.8-2.8C45.6,49.5,45.4,48.8,44.9,48.4z"/>
13
+ </g>
14
+ </svg>
@@ -281,13 +281,22 @@
281
281
  }
282
282
 
283
283
  .status--error,
284
- [data-deploy-status='error'],
284
+ [data-deploy-status='error'] {
285
+ border-color: #333;
286
+
287
+ .status__icon {
288
+ background-image: asset-data-url("error.svg");
289
+ }
290
+ }
291
+
285
292
  .status--timedout,
286
293
  [data-deploy-status='timedout'] {
287
294
  border-color: #333;
288
295
 
289
296
  .status__icon {
290
- background-image: asset-data-url("error.svg");
297
+ background-image: asset-data-url("timedout.svg");
298
+ background-position: top 35% left 50%;
299
+ background-size: 55%;
291
300
  }
292
301
  }
293
302
 
@@ -1,6 +1,6 @@
1
1
  module Shipit
2
2
  class StacksController < ShipitController
3
- before_action :load_stack, only: %i(update destroy settings sync_webhooks clear_git_cache refresh)
3
+ before_action :load_stack, only: %i(update destroy settings clear_git_cache refresh)
4
4
 
5
5
  def new
6
6
  @stack = Stack.new
@@ -64,12 +64,6 @@ module Shipit
64
64
  redirect_to(params[:return_to].presence || stack_settings_path(@stack), options)
65
65
  end
66
66
 
67
- def sync_webhooks
68
- @stack.setup_hooks
69
- flash[:success] = 'Webhooks syncing scheduled'
70
- redirect_to stack_settings_path(@stack)
71
- end
72
-
73
67
  def clear_git_cache
74
68
  ClearGitCacheJob.perform_later(@stack)
75
69
  flash[:success] = 'Git Cache clearing scheduled'
@@ -4,102 +4,138 @@ module Shipit
4
4
 
5
5
  respond_to :json
6
6
 
7
- def push
8
- branch = params['ref'].gsub('refs/heads/', '')
7
+ class Handler
8
+ class << self
9
+ attr_reader :param_parser
9
10
 
10
- if branch == stack.branch
11
- GithubSyncJob.perform_later(stack_id: stack.id)
11
+ def params(&block)
12
+ @param_parser = ExplicitParameters::Parameters.define(&block)
13
+ end
12
14
  end
13
15
 
14
- head :ok
15
- end
16
+ attr_reader :params, :payload
16
17
 
17
- params do
18
- requires :sha, String
19
- requires :state, String
20
- accepts :description, String
21
- accepts :target_url, String
22
- accepts :context, String
23
- accepts :created_at, String
18
+ def initialize(payload)
19
+ @payload = payload
20
+ @params = self.class.param_parser.parse!(payload)
21
+ end
24
22
 
25
- accepts :branches, Array do
26
- requires :name, String
23
+ def process
24
+ raise NotImplementedError
27
25
  end
28
- end
29
- def state
30
- if commit = stack.commits.find_by(sha: params.sha)
31
- commit.create_status_from_github!(params)
26
+
27
+ private
28
+
29
+ def stacks
30
+ @stacks ||= Stack.repo(repository_name)
31
+ end
32
+
33
+ def repository_name
34
+ payload.dig('repository', 'full_name')
32
35
  end
33
- head :ok
34
36
  end
35
37
 
36
- params do
37
- requires :team do
38
- requires :id, Integer
39
- requires :name, String
40
- requires :slug, String
41
- requires :url, String
38
+ class PushHandler < Handler
39
+ params do
40
+ requires :ref
42
41
  end
43
- requires :organization do
44
- requires :login, String
42
+ def process
43
+ stacks.where(branch: branch).each(&:sync_github)
45
44
  end
46
- requires :member do
47
- requires :login, String
45
+
46
+ private
47
+
48
+ def branch
49
+ params.ref.gsub('refs/heads/', '')
48
50
  end
49
51
  end
50
- def membership
51
- team = find_or_create_team!
52
- member = User.find_or_create_by_login!(params.member.login)
53
-
54
- case membership_action
55
- when 'added'
56
- team.add_member(member)
57
- when 'removed'
58
- team.members.delete(member)
59
- else
60
- raise ArgumentError, "Don't know how to perform action: `#{params.action.inspect}`"
52
+
53
+ class StatusHandler < Handler
54
+ params do
55
+ requires :sha, String
56
+ requires :state, String
57
+ accepts :description, String
58
+ accepts :target_url, String
59
+ accepts :context, String
60
+ accepts :created_at, String
61
+
62
+ accepts :branches, Array do
63
+ requires :name, String
64
+ end
65
+ end
66
+ def process
67
+ Commit.where(sha: params.sha).each do |commit|
68
+ commit.create_status_from_github!(params)
69
+ end
61
70
  end
62
- head :ok
63
71
  end
64
72
 
65
- def index
66
- render text: "working"
73
+ class MembershipHandler < Handler
74
+ params do
75
+ requires :action, String
76
+ requires :team do
77
+ requires :id, Integer
78
+ requires :name, String
79
+ requires :slug, String
80
+ requires :url, String
81
+ end
82
+ requires :organization do
83
+ requires :login, String
84
+ end
85
+ requires :member do
86
+ requires :login, String
87
+ end
88
+ end
89
+ def process
90
+ team = find_or_create_team!
91
+ member = User.find_or_create_by_login!(params.member.login)
92
+
93
+ case params.action
94
+ when 'added'
95
+ team.add_member(member)
96
+ when 'removed'
97
+ team.members.delete(member)
98
+ else
99
+ raise ArgumentError, "Don't know how to perform action: `#{action.inspect}`"
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ def find_or_create_team!
106
+ Team.find_or_create_by!(github_id: params.team.id) do |team|
107
+ team.github_team = params.team
108
+ team.organization = params.organization.login
109
+ end
110
+ end
67
111
  end
68
112
 
69
- private
113
+ HANDLERS = {
114
+ 'push' => PushHandler,
115
+ 'status' => StatusHandler,
116
+ 'membership' => MembershipHandler,
117
+ }.freeze
70
118
 
71
- def find_or_create_team!
72
- Team.find_or_create_by!(github_id: params.team.id) do |team|
73
- team.github_team = params.team
74
- team.organization = params.organization.login
75
- team.automatically_setup_hooks = true
119
+ def create
120
+ if handler = HANDLERS[event]
121
+ handler.new(JSON.parse(request.raw_post)).process
76
122
  end
123
+
124
+ head :ok
77
125
  end
78
126
 
127
+ private
128
+
79
129
  def verify_signature
80
- head(422) unless webhook.verify_signature(request.headers['X-Hub-Signature'], request.raw_post)
130
+ head(422) unless Shipit.github.verify_webhook_signature(request.headers['X-Hub-Signature'], request.raw_post)
81
131
  end
82
132
 
83
133
  def check_if_ping
84
134
  head :ok if event == 'ping'
85
135
  end
86
136
 
87
- def webhook
88
- @webhook ||= if params[:stack_id]
89
- stack.github_hooks.find_by!(event: event)
90
- else
91
- GithubHook::Organization.find_by!(organization: params.organization.login, event: event)
92
- end
93
- end
94
-
95
137
  def event
96
- request.headers['X-Github-Event'] || action_name
97
- end
98
-
99
- def membership_action
100
- # GitHub send an `action` parameter that is shadowed by Rails url parameters
101
- # It's also impossible to pass an `action` parameters from a test case.
102
- request.request_parameters['action'] || params[:_action]
138
+ request.headers.fetch('X-Github-Event')
103
139
  end
104
140
 
105
141
  def stack
@@ -4,7 +4,7 @@ module Shipit
4
4
 
5
5
  def github_avatar(user, options = {})
6
6
  uri = user.avatar_uri
7
- attributes = options.slice(:class).merge(alt: user.try!(:name))
7
+ attributes = options.slice(:class).merge(alt: user&.name)
8
8
  if options[:size]
9
9
  uri.query ||= ''
10
10
  uri.query += "&s=#{options[:size]}"
@@ -21,7 +21,7 @@ module Shipit
21
21
  module_function :github_commit_range_url
22
22
 
23
23
  def github_user_url(user, *args)
24
- [Shipit.github_url, user, *args].join('/')
24
+ Shipit.github.url(user, *args)
25
25
  end
26
26
  module_function :github_user_url
27
27
 
@@ -41,38 +41,10 @@ module Shipit
41
41
  tags
42
42
  end
43
43
 
44
- def missing_github_oauth_message
44
+ def missing_github_app_message
45
+ # TODO: Document how to create an app
45
46
  <<-MESSAGE.html_safe
46
- Shipit requires a GitHub application to authenticate users.
47
- If you haven't created an application on GitHub yet, you can do so in the
48
- #{link_to 'Settings', Shipit.github_url('/settings/applications/new'), target: '_blank'}
49
- section of your profile. You can also create applications for organizations.
50
- When setting up your application in Github, set the Homepage URL to your domain
51
- and the Authorization callback URL to '<yourdomain>/github/auth/github/callback'.
52
- MESSAGE
53
- end
54
-
55
- def missing_github_oauth_id_message
56
- <<-MESSAGE.html_safe
57
- Copy the Client ID from your GitHub application,
58
- and paste it into the secrets.yml file under <code>github_oauth.id</code>.
59
- MESSAGE
60
- end
61
-
62
- def missing_github_oauth_secret_message
63
- <<-MESSAGE.html_safe
64
- Copy the Client Secret from your GitHub application,
65
- and paste it into the secrets.yml file under <code>github_oauth.secret</code>.
66
- MESSAGE
67
- end
68
-
69
- def missing_github_api_credentials_message
70
- <<-MESSAGE.html_safe
71
- Shipit needs API access to GitHub. You can
72
- #{link_to 'create an access token', Shipit.github_url('/settings/tokens'), target: '_blank'}
73
- with the following permissions:
74
- <code>admin:repo_hook</code>, <code>admin:org_hook</code> and <code>repo</code>
75
- and add it to the secrets.yml file under the key <code>github_api.access_token</code>.
47
+ Shipit requires a GitHub App to authenticate users and perform API calls.
76
48
  MESSAGE
77
49
  end
78
50