shipit-engine 0.5.2 → 0.6.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 (274) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/shipit/api/cacheable.rb +13 -0
  3. data/app/controllers/concerns/shipit/api/paginable.rb +37 -0
  4. data/app/controllers/concerns/shipit/api/rendering.rb +25 -0
  5. data/app/controllers/concerns/{api/paginable.rb → shipit/pagination.rb} +5 -13
  6. data/app/controllers/shipit/api/base_controller.rb +68 -0
  7. data/app/controllers/shipit/api/deploys_controller.rb +17 -0
  8. data/app/controllers/shipit/api/hooks_controller.rb +53 -0
  9. data/app/controllers/shipit/api/locks_controller.rb +32 -0
  10. data/app/controllers/shipit/api/outputs_controller.rb +17 -0
  11. data/app/controllers/shipit/api/stacks_controller.rb +21 -0
  12. data/app/controllers/shipit/api/tasks_controller.rb +20 -0
  13. data/app/controllers/shipit/commit_checks_controller.rb +26 -0
  14. data/app/controllers/shipit/deploys_controller.rb +47 -0
  15. data/app/controllers/shipit/github_authentication_controller.rb +27 -0
  16. data/app/controllers/shipit/rollbacks_controller.rb +26 -0
  17. data/app/controllers/shipit/shipit_controller.rb +62 -0
  18. data/app/controllers/shipit/stacks_controller.rb +81 -0
  19. data/app/controllers/shipit/status_controller.rb +7 -0
  20. data/app/controllers/shipit/tasks_controller.rb +48 -0
  21. data/app/controllers/shipit/webhooks_controller.rb +101 -0
  22. data/app/helpers/shipit/chunks_helper.rb +8 -0
  23. data/app/helpers/shipit/deploys_helper.rb +28 -0
  24. data/app/helpers/shipit/github_url_helper.rb +48 -0
  25. data/app/helpers/shipit/shipit_helper.rb +64 -0
  26. data/app/helpers/shipit/stacks_helper.rb +78 -0
  27. data/app/helpers/shipit/tasks_helper.rb +11 -0
  28. data/app/jobs/shipit/background_job.rb +24 -0
  29. data/app/jobs/shipit/background_job/unique.rb +28 -0
  30. data/app/jobs/shipit/cache_deploy_spec_job.rb +12 -0
  31. data/app/jobs/shipit/chunk_rollup_job.rb +21 -0
  32. data/app/jobs/shipit/clear_git_cache_job.rb +9 -0
  33. data/app/jobs/shipit/deliver_hook_job.rb +9 -0
  34. data/app/jobs/shipit/destroy_stack_job.rb +9 -0
  35. data/app/jobs/shipit/emit_event_job.rb +10 -0
  36. data/app/jobs/shipit/fetch_commit_stats_job.rb +9 -0
  37. data/app/jobs/shipit/fetch_deployed_revision_job.rb +23 -0
  38. data/app/jobs/shipit/git_mirror_update_job.rb +12 -0
  39. data/app/jobs/shipit/github_sync_job.rb +55 -0
  40. data/app/jobs/shipit/perform_commit_checks_job.rb +7 -0
  41. data/app/jobs/shipit/perform_task_job.rb +57 -0
  42. data/app/jobs/shipit/refresh_github_user_job.rb +9 -0
  43. data/app/jobs/shipit/refresh_statuses_job.rb +14 -0
  44. data/app/jobs/shipit/setup_github_hook_job.rb +11 -0
  45. data/app/models/shipit/anonymous_user.rb +43 -0
  46. data/app/models/shipit/api_client.rb +44 -0
  47. data/app/models/shipit/commit.rb +209 -0
  48. data/app/models/shipit/commit_checks.rb +90 -0
  49. data/app/models/shipit/delivery.rb +47 -0
  50. data/app/models/shipit/deploy.rb +153 -0
  51. data/app/models/shipit/deploy_spec.rb +150 -0
  52. data/app/models/shipit/deploy_spec/bundler_discovery.rb +61 -0
  53. data/app/models/shipit/deploy_spec/capistrano_discovery.rb +29 -0
  54. data/app/models/shipit/deploy_spec/file_system.rb +64 -0
  55. data/app/models/shipit/deploy_spec/pypi_discovery.rb +34 -0
  56. data/app/models/shipit/deploy_spec/rubygems_discovery.rb +34 -0
  57. data/app/models/shipit/github_hook.rb +148 -0
  58. data/app/models/shipit/hook.rb +86 -0
  59. data/app/models/shipit/membership.rb +8 -0
  60. data/app/models/shipit/missing_status.rb +21 -0
  61. data/app/models/shipit/output_chunk.rb +11 -0
  62. data/app/models/shipit/rollback.rb +31 -0
  63. data/app/models/shipit/stack.rb +308 -0
  64. data/app/models/shipit/status.rb +44 -0
  65. data/app/models/shipit/status_group.rb +35 -0
  66. data/app/models/shipit/task.rb +201 -0
  67. data/app/models/shipit/task_definition.rb +38 -0
  68. data/app/models/shipit/team.rb +69 -0
  69. data/app/models/shipit/unknown_status.rb +43 -0
  70. data/app/models/shipit/user.rb +83 -0
  71. data/app/models/shipit/variable_definition.rb +21 -0
  72. data/app/serializers/concerns/shipit/conditional_attributes.rb +22 -0
  73. data/app/serializers/shipit/anonymous_user_serializer.rb +4 -0
  74. data/app/serializers/shipit/commit_serializer.rb +8 -0
  75. data/app/serializers/shipit/deploy_serializer.rb +15 -0
  76. data/app/serializers/shipit/hook_serializer.rb +12 -0
  77. data/app/serializers/shipit/rollback_serializer.rb +7 -0
  78. data/app/serializers/shipit/short_commit_serializer.rb +9 -0
  79. data/app/serializers/shipit/stack_serializer.rb +33 -0
  80. data/app/serializers/shipit/tail_task_serializer.rb +39 -0
  81. data/app/serializers/shipit/task_serializer.rb +30 -0
  82. data/app/serializers/shipit/user_serializer.rb +5 -0
  83. data/app/views/{commits → shipit/commits}/_commit.html.erb +1 -1
  84. data/app/views/{commits → shipit/commits}/_commit_author.html.erb +0 -0
  85. data/app/views/{deploys → shipit/deploys}/_checklist.html.erb +0 -0
  86. data/app/views/{deploys → shipit/deploys}/_checks.html.erb +0 -0
  87. data/app/views/{deploys → shipit/deploys}/_concurrent_deploy_warning.html.erb +0 -0
  88. data/app/views/{deploys → shipit/deploys}/_deploy.html.erb +1 -1
  89. data/app/views/{deploys → shipit/deploys}/_monitoring.html.erb +0 -0
  90. data/app/views/{deploys → shipit/deploys}/_summary.html.erb +0 -0
  91. data/app/views/{deploys → shipit/deploys}/new.html.erb +3 -3
  92. data/app/views/{deploys → shipit/deploys}/rollback.html.erb +2 -2
  93. data/app/views/{deploys → shipit/deploys}/show.html.erb +1 -1
  94. data/app/views/{github_authentication → shipit/github_authentication}/failed.html.erb +0 -0
  95. data/app/views/{stacks → shipit/stacks}/_header.html.erb +0 -0
  96. data/app/views/{stacks → shipit/stacks}/index.html.erb +0 -0
  97. data/app/views/{stacks → shipit/stacks}/new.html.erb +0 -0
  98. data/app/views/{stacks → shipit/stacks}/settings.html.erb +1 -1
  99. data/app/views/{stacks → shipit/stacks}/show.html.erb +2 -2
  100. data/app/views/{statuses → shipit/statuses}/_group.html.erb +1 -1
  101. data/app/views/{statuses → shipit/statuses}/_status.html.erb +0 -0
  102. data/app/views/{tasks → shipit/tasks}/_task.html.erb +1 -1
  103. data/app/views/{tasks → shipit/tasks}/_task_output.html.erb +1 -1
  104. data/app/views/{tasks → shipit/tasks}/index.html.erb +1 -1
  105. data/app/views/{tasks → shipit/tasks}/new.html.erb +1 -1
  106. data/app/views/{tasks → shipit/tasks}/show.html.erb +1 -1
  107. data/db/migrate/20160104151742_increase_tasks_type_size_back.rb +5 -0
  108. data/db/migrate/20160104151833_convert_sti_columns.rb +10 -0
  109. data/lib/shipit.rb +11 -10
  110. data/lib/shipit/command.rb +171 -0
  111. data/lib/shipit/commands.rb +25 -0
  112. data/lib/shipit/deploy_commands.rb +21 -0
  113. data/lib/shipit/engine.rb +3 -0
  114. data/lib/shipit/rollback_commands.rb +7 -0
  115. data/lib/shipit/stack_commands.rb +60 -0
  116. data/lib/shipit/task_commands.rb +68 -0
  117. data/lib/shipit/version.rb +1 -1
  118. data/lib/tasks/cron.rake +3 -3
  119. data/test/controllers/api/base_controller_test.rb +18 -14
  120. data/test/controllers/api/deploys_controller_test.rb +56 -52
  121. data/test/controllers/api/hooks_controller_test.rb +62 -58
  122. data/test/controllers/api/locks_controller_test.rb +38 -34
  123. data/test/controllers/api/outputs_controller_test.rb +15 -11
  124. data/test/controllers/api/stacks_controller_test.rb +56 -52
  125. data/test/controllers/api/tasks_controller_test.rb +30 -26
  126. data/test/controllers/commit_checks_controller_test.rb +29 -27
  127. data/test/controllers/deploys_controller_test.rb +68 -66
  128. data/test/controllers/github_authentication_controller_test.rb +9 -7
  129. data/test/controllers/rollbacks_controller_test.rb +43 -41
  130. data/test/controllers/stacks_controller_test.rb +131 -128
  131. data/test/controllers/status_controller_test.rb +8 -6
  132. data/test/controllers/tasks_controller_test.rb +70 -68
  133. data/test/controllers/webhooks_controller_test.rb +127 -125
  134. data/test/dummy/db/development.sqlite3 +0 -0
  135. data/test/dummy/db/schema.rb +2 -2
  136. data/test/dummy/db/seeds.rb +133 -131
  137. data/test/dummy/db/test.sqlite3 +0 -0
  138. data/test/fixtures/{api_clients.yml → shipit/api_clients.yml} +0 -0
  139. data/test/fixtures/{commits.yml → shipit/commits.yml} +0 -0
  140. data/test/fixtures/{deliveries.yml → shipit/deliveries.yml} +0 -0
  141. data/test/fixtures/{github_hooks.yml → shipit/github_hooks.yml} +4 -4
  142. data/test/fixtures/{hooks.yml → shipit/hooks.yml} +0 -0
  143. data/test/fixtures/{memberships.yml → shipit/memberships.yml} +0 -0
  144. data/test/fixtures/{output_chunks.yml → shipit/output_chunks.yml} +0 -0
  145. data/test/fixtures/{stacks.yml → shipit/stacks.yml} +0 -0
  146. data/test/fixtures/{statuses.yml → shipit/statuses.yml} +0 -0
  147. data/test/fixtures/{tasks.yml → shipit/tasks.yml} +8 -8
  148. data/test/fixtures/{teams.yml → shipit/teams.yml} +0 -0
  149. data/test/fixtures/{users.yml → shipit/users.yml} +0 -0
  150. data/test/helpers/api_helper.rb +1 -1
  151. data/test/helpers/fixture_aliases_helper.rb +4 -4
  152. data/test/jobs/cache_deploy_spec_job_test.rb +15 -13
  153. data/test/jobs/chunk_rollup_job_test.rb +30 -28
  154. data/test/jobs/deliver_hook_job_test.rb +11 -9
  155. data/test/jobs/destroy_stack_job_test.rb +11 -9
  156. data/test/jobs/emit_event_job_test.rb +10 -8
  157. data/test/jobs/fetch_commit_stats_job_test.rb +10 -8
  158. data/test/jobs/fetch_deployed_revision_job_test.rb +24 -22
  159. data/test/jobs/github_sync_job_test.rb +51 -49
  160. data/test/jobs/perform_task_job_test.rb +78 -76
  161. data/test/jobs/refresh_github_user_job_test.rb +10 -8
  162. data/test/jobs/refresh_status_job_test.rb +14 -12
  163. data/test/jobs/unique_job_test.rb +18 -15
  164. data/test/models/api_client_test.rb +20 -18
  165. data/test/models/commit_checks_test.rb +63 -61
  166. data/test/models/commits_test.rb +317 -314
  167. data/test/models/delivery_test.rb +29 -27
  168. data/test/models/deploys_test.rb +289 -287
  169. data/test/models/github_hook_test.rb +45 -43
  170. data/test/models/hook_test.rb +44 -42
  171. data/test/models/membership_test.rb +9 -7
  172. data/test/models/missing_status_test.rb +16 -14
  173. data/test/models/output_chunk_test.rb +14 -12
  174. data/test/models/rollbacks_test.rb +14 -12
  175. data/test/models/stacks_test.rb +272 -270
  176. data/test/models/status_group_test.rb +18 -16
  177. data/test/models/status_test.rb +42 -40
  178. data/test/models/task_definitions_test.rb +27 -25
  179. data/test/models/team_test.rb +39 -37
  180. data/test/models/users_test.rb +61 -59
  181. data/test/unit/command_test.rb +43 -41
  182. data/test/unit/commands_test.rb +8 -6
  183. data/test/unit/csv_serializer_test.rb +28 -26
  184. data/test/unit/deploy_commands_test.rb +179 -176
  185. data/test/unit/deploy_spec_test.rb +237 -235
  186. data/test/unit/github_url_helper_test.rb +19 -17
  187. data/test/unit/shipit_test.rb +44 -42
  188. metadata +139 -137
  189. data/app/controllers/api/base_controller.rb +0 -66
  190. data/app/controllers/api/deploys_controller.rb +0 -15
  191. data/app/controllers/api/hooks_controller.rb +0 -51
  192. data/app/controllers/api/locks_controller.rb +0 -30
  193. data/app/controllers/api/outputs_controller.rb +0 -15
  194. data/app/controllers/api/stacks_controller.rb +0 -19
  195. data/app/controllers/api/tasks_controller.rb +0 -18
  196. data/app/controllers/commit_checks_controller.rb +0 -24
  197. data/app/controllers/concerns/api/cacheable.rb +0 -11
  198. data/app/controllers/concerns/api/rendering.rb +0 -23
  199. data/app/controllers/concerns/pagination.rb +0 -25
  200. data/app/controllers/deploys_controller.rb +0 -45
  201. data/app/controllers/github_authentication_controller.rb +0 -25
  202. data/app/controllers/rollbacks_controller.rb +0 -24
  203. data/app/controllers/shipit_controller.rb +0 -54
  204. data/app/controllers/stacks_controller.rb +0 -79
  205. data/app/controllers/status_controller.rb +0 -5
  206. data/app/controllers/tasks_controller.rb +0 -46
  207. data/app/controllers/webhooks_controller.rb +0 -99
  208. data/app/helpers/chunks_helper.rb +0 -6
  209. data/app/helpers/deploys_helper.rb +0 -26
  210. data/app/helpers/github_url_helper.rb +0 -46
  211. data/app/helpers/shipit_helper.rb +0 -62
  212. data/app/helpers/stacks_helper.rb +0 -76
  213. data/app/helpers/tasks_helper.rb +0 -9
  214. data/app/jobs/background_job.rb +0 -22
  215. data/app/jobs/background_job/unique.rb +0 -26
  216. data/app/jobs/cache_deploy_spec_job.rb +0 -10
  217. data/app/jobs/chunk_rollup_job.rb +0 -19
  218. data/app/jobs/clear_git_cache_job.rb +0 -7
  219. data/app/jobs/deliver_hook_job.rb +0 -7
  220. data/app/jobs/destroy_stack_job.rb +0 -7
  221. data/app/jobs/emit_event_job.rb +0 -8
  222. data/app/jobs/fetch_commit_stats_job.rb +0 -7
  223. data/app/jobs/fetch_deployed_revision_job.rb +0 -21
  224. data/app/jobs/git_mirror_update_job.rb +0 -10
  225. data/app/jobs/github_sync_job.rb +0 -53
  226. data/app/jobs/perform_commit_checks_job.rb +0 -5
  227. data/app/jobs/perform_task_job.rb +0 -55
  228. data/app/jobs/refresh_github_user_job.rb +0 -7
  229. data/app/jobs/refresh_statuses_job.rb +0 -12
  230. data/app/jobs/setup_github_hook_job.rb +0 -9
  231. data/app/models/anonymous_user.rb +0 -41
  232. data/app/models/api_client.rb +0 -42
  233. data/app/models/commit.rb +0 -207
  234. data/app/models/commit_checks.rb +0 -88
  235. data/app/models/delivery.rb +0 -45
  236. data/app/models/deploy.rb +0 -151
  237. data/app/models/deploy_spec.rb +0 -148
  238. data/app/models/deploy_spec/bundler_discovery.rb +0 -59
  239. data/app/models/deploy_spec/capistrano_discovery.rb +0 -27
  240. data/app/models/deploy_spec/file_system.rb +0 -62
  241. data/app/models/deploy_spec/pypi_discovery.rb +0 -32
  242. data/app/models/deploy_spec/rubygems_discovery.rb +0 -32
  243. data/app/models/github_hook.rb +0 -144
  244. data/app/models/hook.rb +0 -84
  245. data/app/models/membership.rb +0 -6
  246. data/app/models/missing_status.rb +0 -18
  247. data/app/models/output_chunk.rb +0 -9
  248. data/app/models/rollback.rb +0 -29
  249. data/app/models/stack.rb +0 -306
  250. data/app/models/status.rb +0 -42
  251. data/app/models/status_group.rb +0 -33
  252. data/app/models/task.rb +0 -197
  253. data/app/models/task_definition.rb +0 -36
  254. data/app/models/team.rb +0 -67
  255. data/app/models/unknown_status.rb +0 -41
  256. data/app/models/user.rb +0 -81
  257. data/app/models/variable_definition.rb +0 -19
  258. data/app/serializers/anonymous_user_serializer.rb +0 -2
  259. data/app/serializers/commit_serializer.rb +0 -6
  260. data/app/serializers/concerns/conditional_attributes.rb +0 -20
  261. data/app/serializers/deploy_serializer.rb +0 -13
  262. data/app/serializers/hook_serializer.rb +0 -10
  263. data/app/serializers/rollback_serializer.rb +0 -5
  264. data/app/serializers/short_commit_serializer.rb +0 -7
  265. data/app/serializers/stack_serializer.rb +0 -31
  266. data/app/serializers/tail_task_serializer.rb +0 -37
  267. data/app/serializers/task_serializer.rb +0 -28
  268. data/app/serializers/user_serializer.rb +0 -3
  269. data/lib/command.rb +0 -169
  270. data/lib/commands.rb +0 -23
  271. data/lib/deploy_commands.rb +0 -19
  272. data/lib/rollback_commands.rb +0 -5
  273. data/lib/stack_commands.rb +0 -58
  274. data/lib/task_commands.rb +0 -66
@@ -0,0 +1,34 @@
1
+ module Shipit
2
+ class DeploySpec
3
+ module PypiDiscovery
4
+ def discover_deploy_steps
5
+ discover_pypi || super
6
+ end
7
+
8
+ def discover_pypi
9
+ publish_egg if egg?
10
+ end
11
+
12
+ def discover_review_checklist
13
+ discover_pypi_checklist || super
14
+ end
15
+
16
+ def discover_pypi_checklist
17
+ [%(<strong>Don't forget to add a tag before deploying!</strong> You can do this with:
18
+ git tag -a -m "Version <strong>x.y.z</strong>" v<strong>x.y.z</strong> && git push --tags)] if egg?
19
+ end
20
+
21
+ def egg?
22
+ setup_dot_py.exist?
23
+ end
24
+
25
+ def setup_dot_py
26
+ file('setup.py')
27
+ end
28
+
29
+ def publish_egg
30
+ ["assert-egg-version-tag #{setup_dot_py}", 'python setup.py register sdist upload']
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ module Shipit
2
+ class DeploySpec
3
+ module RubygemsDiscovery
4
+ def discover_deploy_steps
5
+ discover_gem || super
6
+ end
7
+
8
+ def discover_gem
9
+ publish_gem if gem?
10
+ end
11
+
12
+ def discover_review_checklist
13
+ discover_gem_checklist || super
14
+ end
15
+
16
+ def discover_gem_checklist
17
+ [%(<strong>Don't forget to add a tag before deploying!</strong> You can do this with:
18
+ git tag -a -m "Version <strong>x.y.z</strong>" v<strong>x.y.z</strong> && git push --tags)] if gem?
19
+ end
20
+
21
+ def gem?
22
+ !!gemspec
23
+ end
24
+
25
+ def gemspec
26
+ Dir[file('*.gemspec').to_s].first
27
+ end
28
+
29
+ def publish_gem
30
+ ["assert-gem-version-tag #{gemspec}", 'bundle exec rake release']
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,148 @@
1
+ module Shipit
2
+ class GithubHook < ActiveRecord::Base
3
+ include SecureCompare
4
+
5
+ belongs_to :stack, required: false # Required for fixtures
6
+
7
+ before_create :generate_secret
8
+ before_destroy :teardown!
9
+
10
+ def verify_signature(signature, message)
11
+ algorithm, signature = signature.split("=", 2)
12
+ return false unless algorithm == 'sha1'
13
+
14
+ secure_compare(signature, OpenSSL::HMAC.hexdigest(algorithm, secret, message))
15
+ end
16
+
17
+ delegate :github_repo_name, to: :stack
18
+ def setup!
19
+ hook = already_setup? ? update_hook! : create_hook!
20
+ update!(github_id: hook.id, api_url: hook.rels[:self].href)
21
+ end
22
+
23
+ def schedule_setup!
24
+ SetupGithubHookJob.perform_later(self)
25
+ end
26
+
27
+ def teardown!
28
+ destroy_hook! if already_setup?
29
+ true
30
+ end
31
+
32
+ def event=(event)
33
+ super(event.to_s)
34
+ end
35
+
36
+ def already_setup?
37
+ github_id?
38
+ end
39
+
40
+ private
41
+
42
+ def update_hook!
43
+ edit_hook!
44
+ rescue Octokit::NotFound
45
+ create_hook!
46
+ end
47
+
48
+ private
49
+
50
+ def endpoint_url
51
+ raise NotImplementedError.new('Subclasses must implement a `endpoint_url` method')
52
+ end
53
+
54
+ def hook_properties
55
+ {url: endpoint_url, content_type: 'json', secret: secret}
56
+ end
57
+
58
+ def generate_secret
59
+ self.secret = SecureRandom.hex
60
+ end
61
+
62
+ def url_helpers
63
+ Shipit::Engine.routes.url_helpers
64
+ end
65
+
66
+ def host
67
+ Shipit.host
68
+ end
69
+
70
+ def api
71
+ Shipit.github_api
72
+ end
73
+
74
+ class Repo < GithubHook
75
+ belongs_to :stack, required: true
76
+
77
+ private
78
+
79
+ def create_hook!
80
+ api.create_hook(github_repo_name, 'web', properties, events: [event], active: true)
81
+ end
82
+
83
+ def edit_hook!
84
+ api.edit_hook(github_repo_name, github_id, 'web', properties, events: [event], active: true)
85
+ end
86
+
87
+ def destroy_hook!
88
+ api.remove_hook(github_repo_name, github_id)
89
+ rescue Octokit::NotFound
90
+ end
91
+
92
+ def properties
93
+ {
94
+ url: endpoint_url,
95
+ content_type: 'json',
96
+ secret: secret,
97
+ }
98
+ end
99
+
100
+ def endpoint_url
101
+ case event
102
+ when 'push'
103
+ url_helpers.push_stack_webhooks_url(stack_id, host: host)
104
+ when 'status'
105
+ url_helpers.state_stack_webhooks_url(stack_id, host: host)
106
+ else
107
+ raise ArgumentError, "Unknown GithubHook::Repo event: `#{event.inspect}`"
108
+ end
109
+ end
110
+ end
111
+
112
+ class Organization < GithubHook
113
+ validates :organization, presence: true
114
+
115
+ private
116
+
117
+ def create_hook!
118
+ api.create_org_hook(organization, properties, events: [event], active: true)
119
+ end
120
+
121
+ def edit_hook!
122
+ api.edit_org_hook(organization, github_id, properties, events: [event], active: true)
123
+ end
124
+
125
+ def destroy_hook!
126
+ api.remove_org_hook(organization, github_id)
127
+ rescue Octokit::NotFound
128
+ end
129
+
130
+ def properties
131
+ {
132
+ url: endpoint_url,
133
+ content_type: 'json',
134
+ secret: secret,
135
+ }
136
+ end
137
+
138
+ def endpoint_url
139
+ case event
140
+ when 'membership'
141
+ url_helpers.membership_webhooks_url(host: host)
142
+ else
143
+ raise ArgumentError, "Unknown GithubHook::Organization event: `#{event.inspect}`"
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,86 @@
1
+ module Shipit
2
+ class Hook < ActiveRecord::Base
3
+ default_scope { order :id }
4
+
5
+ CONTENT_TYPES = {
6
+ 'json' => 'application/json',
7
+ 'form' => 'application/x-www-form-urlencoded',
8
+ }.freeze
9
+
10
+ EVENTS = %w(
11
+ task
12
+ deploy
13
+ rollback
14
+ lock
15
+ commit_status
16
+ deployable_status
17
+ ).freeze
18
+
19
+ belongs_to :stack, required: false
20
+ has_many :deliveries
21
+
22
+ validates :url, presence: true, url: {no_local: true, allow_blank: true}
23
+ validates :content_type, presence: true, inclusion: {in: CONTENT_TYPES.keys}
24
+ validates :events, presence: true, subset: {of: EVENTS}
25
+
26
+ serialize :events, Shipit::CSVSerializer
27
+
28
+ scope :global, -> { where(stack_id: nil) }
29
+ scope :scoped_to, -> (stack) { where(stack_id: stack.id) }
30
+ scope :for_stack, -> (stack_id) { where(stack_id: [nil, stack_id]) }
31
+
32
+ class << self
33
+ def emit(event, stack, payload)
34
+ Shipit::EmitEventJob.perform_later(
35
+ event: event.to_s,
36
+ stack_id: stack.try!(:id),
37
+ payload: coerce_payload(payload),
38
+ )
39
+ end
40
+
41
+ def deliver(event, stack_id, payload)
42
+ for_stack(stack_id).listening_event(event).each do |hook|
43
+ hook.deliver!(event, payload)
44
+ end
45
+ end
46
+
47
+ def listening_event(event)
48
+ event = event.to_s
49
+ all.to_a.select { |h| h.events.include?(event) }
50
+ end
51
+
52
+ def coerce_payload(payload)
53
+ coerced_payload = payload.dup
54
+ payload.each do |key, value|
55
+ if serializer = ActiveModel::Serializer.serializer_for(value)
56
+ coerced_payload[key] = serializer.new(value)
57
+ end
58
+ end
59
+ coerced_payload.to_json
60
+ end
61
+ end
62
+
63
+ def scoped?
64
+ stack_id?
65
+ end
66
+
67
+ def deliver!(event, payload)
68
+ deliveries.create!(
69
+ event: event,
70
+ url: url,
71
+ content_type: CONTENT_TYPES[content_type],
72
+ payload: serialize_payload(payload),
73
+ ).schedule!
74
+ end
75
+
76
+ private
77
+
78
+ def serialize_payload(payload)
79
+ if content_type == 'form'
80
+ payload.to_query
81
+ else
82
+ JSON.pretty_generate(payload)
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,8 @@
1
+ module Shipit
2
+ class Membership < ActiveRecord::Base
3
+ belongs_to :team, required: true
4
+ belongs_to :user, required: true
5
+
6
+ validates :user_id, uniqueness: {scope: :team_id}
7
+ end
8
+ end
@@ -0,0 +1,21 @@
1
+ module Shipit
2
+ class MissingStatus < SimpleDelegator
3
+ def initialize(instance, missing_statuses)
4
+ @missing_statuses = missing_statuses
5
+ super(instance)
6
+ end
7
+
8
+ def state
9
+ 'missing'
10
+ end
11
+
12
+ def success?
13
+ false
14
+ end
15
+
16
+ def description
17
+ statuses = @missing_statuses.to_sentence
18
+ I18n.t('missing_status.description', missing_statuses: statuses, count: @missing_statuses.size)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ module Shipit
2
+ class OutputChunk < ActiveRecord::Base
3
+ belongs_to :task
4
+
5
+ scope :tail, -> (start) { order(id: :asc).where('id > ?', start || 0) }
6
+
7
+ def text=(string)
8
+ super(string.force_encoding(Encoding::UTF_8).scrub)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,31 @@
1
+ module Shipit
2
+ class Rollback < Deploy
3
+ belongs_to :deploy, foreign_key: :parent_id
4
+
5
+ def rollback?
6
+ true
7
+ end
8
+
9
+ def rollbackable?
10
+ false
11
+ end
12
+
13
+ def supports_rollback?
14
+ false
15
+ end
16
+
17
+ def commits
18
+ return Commit.none unless stack
19
+
20
+ @commits ||= stack.commits.reachable.newer_than(until_commit_id).until(since_commit_id).order(id: :asc)
21
+ end
22
+
23
+ def commit_range
24
+ [until_commit, since_commit]
25
+ end
26
+
27
+ def to_partial_path
28
+ 'deploys/deploy'
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,308 @@
1
+ require 'fileutils'
2
+
3
+ module Shipit
4
+ class Stack < ActiveRecord::Base
5
+ REPO_OWNER_MAX_SIZE = 39
6
+ REPO_NAME_MAX_SIZE = 100
7
+ ENVIRONMENT_MAX_SIZE = 50
8
+ REQUIRED_HOOKS = %i(push status)
9
+
10
+ has_many :commits, dependent: :destroy
11
+ has_many :tasks, dependent: :destroy
12
+ has_many :deploys
13
+ has_many :rollbacks
14
+ has_many :deploys_and_rollbacks, -> { where(type: %w(Shipit::Deploy Shipit::Rollback)) }, class_name: 'Task'
15
+ has_many :github_hooks, dependent: :destroy, class_name: 'Shipit::GithubHook::Repo'
16
+ has_many :hooks, dependent: :destroy
17
+ has_many :api_clients, dependent: :destroy
18
+ belongs_to :lock_author, class_name: :User
19
+
20
+ def lock_author(*)
21
+ super || AnonymousUser.new
22
+ end
23
+
24
+ def lock_author=(user)
25
+ super(user.try!(:logged_in?) ? user : nil)
26
+ end
27
+
28
+ before_validation :update_defaults
29
+ before_destroy :clear_local_files
30
+ after_commit :emit_hooks
31
+ after_commit :broadcast_update, on: :update
32
+ after_commit :setup_hooks, :sync_github, on: :create
33
+ after_touch :clear_cache
34
+
35
+ validates :repo_name, uniqueness: {scope: %i(repo_owner environment)}
36
+ validates :repo_owner, :repo_name, :environment, presence: true, ascii_only: true
37
+ validates :repo_owner, format: {with: /\A[a-z0-9_\-\.]+\z/}, length: {maximum: REPO_OWNER_MAX_SIZE}
38
+ validates :repo_name, format: {with: /\A[a-z0-9_\-\.]+\z/}, length: {maximum: REPO_NAME_MAX_SIZE}
39
+ validates :environment, format: {with: /\A[a-z0-9\-_\:]+\z/}, length: {maximum: ENVIRONMENT_MAX_SIZE}
40
+
41
+ validates :lock_reason, length: {maximum: 4096}
42
+
43
+ serialize :cached_deploy_spec, DeploySpec
44
+ delegate :find_task_definition, :supports_rollback?,
45
+ :supports_fetch_deployed_revision?, to: :cached_deploy_spec, allow_nil: true
46
+
47
+ def self.refresh_deployed_revisions
48
+ find_each.select(&:supports_fetch_deployed_revision?).each(&:async_refresh_deployed_revision)
49
+ end
50
+
51
+ def undeployed_commits?
52
+ undeployed_commits_count > 0
53
+ end
54
+
55
+ def trigger_task(definition_id, user)
56
+ commit = last_deployed_commit
57
+ task = tasks.create(
58
+ user_id: user.id,
59
+ definition: find_task_definition(definition_id),
60
+ until_commit_id: commit.id,
61
+ since_commit_id: commit.id,
62
+ )
63
+ task.enqueue
64
+ task
65
+ end
66
+
67
+ def trigger_deploy(until_commit, user, env: nil)
68
+ since_commit = last_deployed_commit
69
+
70
+ deploy = deploys.create(
71
+ user_id: user.id,
72
+ until_commit: until_commit,
73
+ since_commit: since_commit,
74
+ env: env || {},
75
+ )
76
+ deploy.enqueue
77
+ deploy
78
+ end
79
+
80
+ def async_refresh_deployed_revision
81
+ FetchDeployedRevisionJob.perform_later(self)
82
+ end
83
+
84
+ def update_deployed_revision(sha)
85
+ return if deploying?
86
+
87
+ last_deploy = deploys_and_rollbacks.last
88
+ actual_deployed_commit = commits.reachable.by_sha!(sha)
89
+
90
+ if last_deploy && actual_deployed_commit == last_deploy.until_commit
91
+ last_deploy.accept!
92
+ elsif last_deploy && actual_deployed_commit == last_deploy.since_commit
93
+ last_deploy.reject!
94
+ else
95
+ deploys.create!(
96
+ until_commit: actual_deployed_commit,
97
+ since_commit: last_deployed_commit,
98
+ status: 'success',
99
+ )
100
+ end
101
+ end
102
+
103
+ def head
104
+ commits.reachable.first.try!(:sha)
105
+ end
106
+
107
+ def status
108
+ return :deploying if deploying?
109
+ :default
110
+ end
111
+
112
+ def last_successful_deploy
113
+ deploys_and_rollbacks.success.order(created_at: :desc).first
114
+ end
115
+
116
+ def last_deployed_commit
117
+ if deploy = last_successful_deploy
118
+ deploy.until_commit
119
+ else
120
+ commits.first
121
+ end
122
+ end
123
+
124
+ def filter_visible_statuses(statuses)
125
+ statuses.reject { |s| hidden_statuses.include?(s.context) }
126
+ end
127
+
128
+ def filter_meaningful_statuses(statuses)
129
+ filter_visible_statuses(statuses).reject { |s| soft_failing_statuses.include?(s.context) }
130
+ end
131
+
132
+ def deployable?
133
+ !locked? && !deploying?
134
+ end
135
+
136
+ def repo_name=(name)
137
+ super(name.try(:downcase))
138
+ end
139
+
140
+ def repo_owner=(name)
141
+ super(name.try(:downcase))
142
+ end
143
+
144
+ def repo_http_url
145
+ Shipit.github_url("#{repo_owner}/#{repo_name}")
146
+ end
147
+
148
+ def repo_git_url
149
+ "git@#{Shipit.github_domain}:#{repo_owner}/#{repo_name}.git"
150
+ end
151
+
152
+ def base_path
153
+ Rails.root.join('data/stacks', repo_owner, repo_name, environment)
154
+ end
155
+
156
+ def deploys_path
157
+ File.join(base_path, "deploys")
158
+ end
159
+
160
+ def git_path
161
+ File.join(base_path, "git")
162
+ end
163
+
164
+ def acquire_git_cache_lock(timeout: 15, &block)
165
+ Redis::Lock.new(
166
+ "stack:#{id}:git-cache-lock",
167
+ Shipit.redis,
168
+ timeout: timeout,
169
+ expiration: 60,
170
+ ).lock(&block)
171
+ end
172
+
173
+ def clear_git_cache!
174
+ tmp_path = "#{git_path}-#{SecureRandom.hex}"
175
+ acquire_git_cache_lock do
176
+ return unless File.exist?(git_path)
177
+ File.rename(git_path, tmp_path)
178
+ end
179
+ FileUtils.rm_rf(tmp_path)
180
+ end
181
+
182
+ def github_repo_name
183
+ [repo_owner, repo_name].join('/')
184
+ end
185
+
186
+ def github_commits
187
+ Shipit.github_api.commits(github_repo_name, sha: branch)
188
+ end
189
+
190
+ def deploying?
191
+ !!active_deploy
192
+ end
193
+
194
+ def active_deploy
195
+ return @active_deploy if defined?(@active_deploy)
196
+ @active_deploy ||= deploys_and_rollbacks.active.last
197
+ end
198
+
199
+ def locked?
200
+ lock_reason.present?
201
+ end
202
+
203
+ def to_param
204
+ [repo_owner, repo_name, environment].join('/')
205
+ end
206
+
207
+ def self.from_param!(param)
208
+ repo_owner, repo_name, environment = param.split('/')
209
+ where(
210
+ repo_owner: repo_owner.downcase,
211
+ repo_name: repo_name.downcase,
212
+ environment: environment,
213
+ ).first!
214
+ end
215
+
216
+ delegate :plugins, :task_definitions, :hidden_statuses, :required_statuses, :soft_failing_statuses,
217
+ :deploy_variables, to: :cached_deploy_spec
218
+
219
+ def monitoring?
220
+ monitoring.present?
221
+ end
222
+
223
+ def monitoring
224
+ cached_deploy_spec.review_monitoring
225
+ end
226
+
227
+ def checklist
228
+ cached_deploy_spec.review_checklist
229
+ end
230
+
231
+ def checks?
232
+ cached_deploy_spec.review_checks.present?
233
+ end
234
+
235
+ def update_undeployed_commits_count(after_commit = nil)
236
+ after_commit ||= last_deployed_commit
237
+ undeployed_commits = commits.reachable.select('count(*) as count').where('id > ?', after_commit.id)
238
+ self.class.where(id: id).update_all("undeployed_commits_count = (#{undeployed_commits.to_sql})")
239
+ end
240
+
241
+ def broadcast_update
242
+ payload = {url: Shipit::Engine.routes.url_helpers.stack_path(self)}.to_json
243
+ event = Pubsubstub::Event.new(payload, name: "stack.update")
244
+ Pubsubstub::RedisPubSub.publish("stack.#{id}", event)
245
+ end
246
+
247
+ def setup_hooks
248
+ REQUIRED_HOOKS.each do |event|
249
+ hook = github_hooks.find_or_create_by!(event: event)
250
+ hook.schedule_setup!
251
+ end
252
+ end
253
+
254
+ def schedule_for_destroy!
255
+ DestroyStackJob.perform_later(self)
256
+ end
257
+
258
+ def ci_enabled?
259
+ Rails.cache.fetch(ci_enabled_cache_key) do
260
+ commits.joins(:statuses).any?
261
+ end
262
+ end
263
+
264
+ def enable_ci!
265
+ Rails.cache.write(ci_enabled_cache_key, true)
266
+ end
267
+
268
+ def mark_as_accessible!
269
+ update!(inaccessible_since: nil)
270
+ end
271
+
272
+ def mark_as_inaccessible!
273
+ update!(inaccessible_since: Time.now) unless inaccessible?
274
+ end
275
+
276
+ def inaccessible?
277
+ inaccessible_since?
278
+ end
279
+
280
+ private
281
+
282
+ def clear_cache
283
+ remove_instance_variable(:@active_deploy) if defined?(@active_deploy)
284
+ end
285
+
286
+ def sync_github
287
+ GithubSyncJob.perform_later(stack_id: id)
288
+ end
289
+
290
+ def clear_local_files
291
+ FileUtils.rm_rf(base_path.to_s)
292
+ end
293
+
294
+ def update_defaults
295
+ self.environment = 'production' if environment.blank?
296
+ self.branch = 'master' if branch.blank?
297
+ end
298
+
299
+ def emit_hooks
300
+ return unless previous_changes.include?('lock_reason')
301
+ Hook.emit(:lock, self, locked: locked?, stack: self)
302
+ end
303
+
304
+ def ci_enabled_cache_key
305
+ "stacks:#{id}:ci_enabled"
306
+ end
307
+ end
308
+ end