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,44 @@
1
+ module Shipit
2
+ class Status < ActiveRecord::Base
3
+ STATES = %w(pending success failure error).freeze
4
+ enum state: STATES.zip(STATES).to_h
5
+
6
+ belongs_to :commit, touch: true
7
+
8
+ validates :state, inclusion: {in: STATES, allow_blank: true}, presence: true
9
+
10
+ after_create :enable_ci_on_stack
11
+ after_commit :schedule_continuous_delivery, :broadcast_update, on: :create
12
+ after_commit :touch_commit
13
+
14
+ delegate :broadcast_update, to: :commit
15
+
16
+ def self.replicate_from_github!(github_status)
17
+ find_or_create_by!(
18
+ state: github_status.state,
19
+ description: github_status.description,
20
+ target_url: github_status.rels.try(:[], :target).try(:href),
21
+ context: github_status.context,
22
+ created_at: github_status.created_at,
23
+ )
24
+ end
25
+
26
+ def group?
27
+ false
28
+ end
29
+
30
+ private
31
+
32
+ def enable_ci_on_stack
33
+ commit.stack.enable_ci!
34
+ end
35
+
36
+ def touch_commit
37
+ commit.touch
38
+ end
39
+
40
+ def schedule_continuous_delivery
41
+ commit.schedule_continuous_delivery
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,35 @@
1
+ module Shipit
2
+ class StatusGroup
3
+ STATES_PRIORITY = %w(failure error pending success).freeze
4
+
5
+ attr_reader :statuses, :significant_status
6
+
7
+ def initialize(significant_status, visible_statuses)
8
+ @significant_status = significant_status
9
+ @statuses = visible_statuses
10
+ end
11
+
12
+ delegate :success?, :state, to: :significant_status
13
+
14
+ def description
15
+ "#{success_count} / #{statuses.count} checks OK"
16
+ end
17
+
18
+ def target_url
19
+ end
20
+
21
+ def to_partial_path
22
+ 'statuses/group'
23
+ end
24
+
25
+ def group?
26
+ true
27
+ end
28
+
29
+ private
30
+
31
+ def success_count
32
+ @statuses.count { |s| s.state == 'success'.freeze }
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,201 @@
1
+ module Shipit
2
+ class Task < ActiveRecord::Base
3
+ belongs_to :deploy, foreign_key: :parent_id, required: false # required for fixtures
4
+
5
+ belongs_to :user
6
+ belongs_to :stack, touch: true, counter_cache: true
7
+ belongs_to :until_commit, class_name: 'Commit'
8
+ belongs_to :since_commit, class_name: 'Commit'
9
+
10
+ has_many :chunks, -> { order(:id) }, class_name: 'OutputChunk', dependent: :destroy
11
+
12
+ serialize :definition, TaskDefinition
13
+ serialize :env, Hash
14
+
15
+ scope :success, -> { where(status: 'success') }
16
+ scope :completed, -> { where(status: %w(success error failed flapping aborted)) }
17
+ scope :active, -> { where(status: %w(pending running aborting)) }
18
+
19
+ scope :due_for_rollup, -> { completed.where(rolled_up: false).where('created_at <= ?', 1.hour.ago) }
20
+
21
+ after_save :record_status_change
22
+ after_commit :emit_hooks
23
+
24
+ state_machine :status, initial: :pending do
25
+ after_transition any => %i(success failed error) do |task|
26
+ task.async_refresh_deployed_revision
27
+ end
28
+
29
+ after_transition any => :flapping do |task|
30
+ task.update!(confirmations: 0)
31
+ end
32
+
33
+ event :run do
34
+ transition pending: :running
35
+ end
36
+
37
+ event :failure do
38
+ transition %i(running flapping) => :failed
39
+ end
40
+
41
+ event :complete do
42
+ transition %i(running flapping) => :success
43
+ end
44
+
45
+ event :error do
46
+ transition all => :error
47
+ end
48
+
49
+ event :aborting do
50
+ transition all - %i(aborted) => :aborting
51
+ end
52
+
53
+ event :aborted do
54
+ transition aborting: :aborted
55
+ end
56
+
57
+ event :flap do
58
+ transition %i(failed error success) => :flapping
59
+ end
60
+
61
+ state :pending
62
+ state :running
63
+ state :failed
64
+ state :success
65
+ state :error
66
+ state :aborting
67
+ state :aborted
68
+ state :flapping
69
+ end
70
+
71
+ def report_failure!(_error)
72
+ reload
73
+ if aborting?
74
+ aborted!
75
+ else
76
+ failure!
77
+ end
78
+ end
79
+
80
+ def report_error!(error)
81
+ write("#{error.class}: #{error.message}\n\t#{error.backtrace.join("\t")}\n")
82
+ error!
83
+ end
84
+
85
+ delegate :acquire_git_cache_lock, :async_refresh_deployed_revision, to: :stack
86
+
87
+ delegate :checklist, to: :definition
88
+
89
+ def spec
90
+ @spec ||= DeploySpec::FileSystem.new(working_directory, stack.environment)
91
+ end
92
+
93
+ def enqueue
94
+ raise "only persisted jobs can be enqueued" unless persisted?
95
+ PerformTaskJob.perform_later(self)
96
+ end
97
+
98
+ def write(text)
99
+ chunks.create!(text: text)
100
+ end
101
+
102
+ def chunk_output
103
+ if rolled_up?
104
+ output
105
+ else
106
+ chunks.pluck(:text).join
107
+ end
108
+ end
109
+
110
+ def schedule_rollup_chunks
111
+ ChunkRollupJob.perform_later(self)
112
+ end
113
+
114
+ def rollup_chunks
115
+ ActiveRecord::Base.transaction do
116
+ self.output = chunk_output
117
+ chunks.delete_all
118
+ update_attribute(:rolled_up, true)
119
+ end
120
+ end
121
+
122
+ def output
123
+ gzip = self[:gzip_output]
124
+
125
+ if gzip.nil? || gzip.empty?
126
+ ''
127
+ else
128
+ ActiveSupport::Gzip.decompress(gzip)
129
+ end
130
+ end
131
+
132
+ def output=(string)
133
+ self[:gzip_output] = ActiveSupport::Gzip.compress(string)
134
+ end
135
+
136
+ def rollback?
137
+ false
138
+ end
139
+
140
+ def rollbackable?
141
+ false
142
+ end
143
+
144
+ def supports_rollback?
145
+ false
146
+ end
147
+
148
+ def author
149
+ user || AnonymousUser.new
150
+ end
151
+
152
+ def finished?
153
+ !pending? && !running? && !aborting?
154
+ end
155
+
156
+ def pid
157
+ pid = Shipit.redis.get("task:#{id}:pid")
158
+ pid.presence && pid.to_i
159
+ end
160
+
161
+ def pid=(pid)
162
+ Shipit.redis.set("task:#{id}:pid", pid, ex: 1.hour.to_i)
163
+ end
164
+
165
+ def abort!(rollback_once_aborted: false)
166
+ target_pid = pid
167
+ return write("\nAbort: failed, PID unknown\n") unless target_pid.present?
168
+
169
+ update!(rollback_once_aborted: rollback_once_aborted)
170
+ aborting!
171
+ write("\nAbort: sending SIGTERM to pid #{target_pid}\n")
172
+ Process.kill('TERM', target_pid)
173
+ rescue Errno::ESRCH
174
+ write("\nAbort: PID #{target_pid} ESRCH: No such process\n")
175
+ aborted!
176
+ true
177
+ end
178
+
179
+ def working_directory
180
+ File.join(stack.deploys_path, id.to_s)
181
+ end
182
+
183
+ def clear_working_directory
184
+ FileUtils.rm_rf(working_directory)
185
+ end
186
+
187
+ def record_status_change
188
+ @status_changed ||= status_changed?
189
+ end
190
+
191
+ def emit_hooks
192
+ return unless @status_changed
193
+ @status_changed = nil
194
+ Hook.emit(hook_event, stack, hook_event => self, status: status, stack: stack)
195
+ end
196
+
197
+ def hook_event
198
+ self.class.name.demodulize.underscore.to_sym
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,38 @@
1
+ module Shipit
2
+ class TaskDefinition
3
+ NotFound = Class.new(StandardError)
4
+
5
+ class << self
6
+ def load(payload)
7
+ return unless payload.present?
8
+ json = JSON.parse(payload)
9
+ new(json.delete('id'), json)
10
+ end
11
+
12
+ def dump(definition)
13
+ return unless definition.present?
14
+ JSON.dump(definition.as_json)
15
+ end
16
+ end
17
+
18
+ attr_reader :id, :action, :description, :steps, :checklist
19
+
20
+ def initialize(id, config)
21
+ @id = id
22
+ @action = config['action']
23
+ @description = config['description'] || ''
24
+ @steps = config['steps'] || []
25
+ @checklist = config['checklist'] || []
26
+ end
27
+
28
+ def as_json
29
+ {
30
+ id: id,
31
+ action: action,
32
+ description: description,
33
+ steps: steps,
34
+ checklist: checklist,
35
+ }
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,69 @@
1
+ module Shipit
2
+ class Team < ActiveRecord::Base
3
+ REQUIRED_HOOKS = %i(membership)
4
+
5
+ has_many :members, class_name: :User, through: :memberships, source: :user
6
+ has_many :memberships
7
+
8
+ has_many :github_hooks,
9
+ -> { where(event: REQUIRED_HOOKS) },
10
+ foreign_key: :organization,
11
+ primary_key: :organization,
12
+ class_name: 'GithubHook::Organization'
13
+
14
+ after_commit :setup_hooks, if: :automatically_setup_hooks?
15
+
16
+ class << self
17
+ def find_or_create_by_handle(handle)
18
+ organization, slug = handle.split('/').map(&:downcase)
19
+ find_by(organization: organization, slug: slug) || fetch_and_create_from_github(organization, slug)
20
+ end
21
+
22
+ def fetch_and_create_from_github(organization, slug)
23
+ if github_team = find_team_on_github(organization, slug)
24
+ create!(github_team: github_team, organization: organization)
25
+ end
26
+ end
27
+
28
+ def find_team_on_github(organization, slug)
29
+ teams = Shipit::OctokitIterator.new { Shipit.github_api.org_teams(organization, per_page: 100) }
30
+ teams.find { |t| t.slug == slug }
31
+ rescue Octokit::NotFound
32
+ end
33
+ end
34
+
35
+ def handle
36
+ "#{organization}/#{slug}"
37
+ end
38
+
39
+ def add_member(member)
40
+ members.append(member) unless members.include?(member)
41
+ end
42
+
43
+ attr_writer :automatically_setup_hooks
44
+ def automatically_setup_hooks?
45
+ @automatically_setup_hooks
46
+ end
47
+
48
+ def setup_hooks(async: true)
49
+ REQUIRED_HOOKS.each do |event|
50
+ hook = github_hooks.find_or_create_by!(event: event)
51
+ async ? hook.schedule_setup! : hook.setup!
52
+ end
53
+ end
54
+
55
+ def refresh_members!
56
+ github_members = Shipit::OctokitIterator.new(Shipit.github_api.get(api_url).rels[:members])
57
+ members = github_members.map { |u| User.find_or_create_from_github(u) }
58
+ self.members = members
59
+ save!
60
+ end
61
+
62
+ def github_team=(github_team)
63
+ self.name = github_team.name
64
+ self.slug = github_team.slug
65
+ self.api_url = github_team.url
66
+ self.github_id = github_team.id
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,43 @@
1
+ module Shipit
2
+ UnknownStatus = Struct.new(:commit) do
3
+ def state
4
+ 'unknown'
5
+ end
6
+
7
+ def pending?
8
+ false
9
+ end
10
+
11
+ def success?
12
+ false
13
+ end
14
+
15
+ def error?
16
+ false
17
+ end
18
+
19
+ def failure?
20
+ false
21
+ end
22
+
23
+ def group?
24
+ false
25
+ end
26
+
27
+ def target_url
28
+ nil
29
+ end
30
+
31
+ def description
32
+ ''
33
+ end
34
+
35
+ def context
36
+ 'ci/unknown'
37
+ end
38
+
39
+ def to_partial_path
40
+ 'shipit/statuses/status'
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,83 @@
1
+ module Shipit
2
+ class User < ActiveRecord::Base
3
+ DEFAULT_AVATAR = URI.parse('https://avatars.githubusercontent.com/u/583231?')
4
+
5
+ has_many :authored_commits, class_name: :Commit, foreign_key: :author_id, inverse_of: :author
6
+ has_many :commits, foreign_key: :committer_id, inverse_of: :committer
7
+ has_many :tasks
8
+
9
+ def self.find_or_create_by_login!(login)
10
+ find_or_create_by!(login: login) do |user|
11
+ user.github_user = Shipit.github_api.user(login)
12
+ end
13
+ end
14
+
15
+ def self.find_or_create_from_github(github_user)
16
+ find_from_github(github_user) || create_from_github!(github_user)
17
+ end
18
+
19
+ def self.find_from_github(github_user)
20
+ return unless github_user.id
21
+ find_by(github_id: github_user.id)
22
+ end
23
+
24
+ def self.create_from_github!(github_user)
25
+ create!(github_user: github_user)
26
+ end
27
+
28
+ def self.refresh_shard(shard_index, shards_count)
29
+ where.not(login: nil).where('id % ? = ?', shards_count, shard_index).find_each do |user|
30
+ RefreshGithubUserJob.perform_later(user)
31
+ end
32
+ end
33
+
34
+ def identifiers_for_ping
35
+ {github_id: github_id, name: name, email: email, github_login: login}
36
+ end
37
+
38
+ def logged_in?
39
+ true
40
+ end
41
+
42
+ def stacks_contributed_to
43
+ return [] unless id
44
+ Commit.where('author_id = :id or committer_id = :id', id: id).uniq.pluck(:stack_id)
45
+ end
46
+
47
+ def refresh_from_github!
48
+ update!(github_user: Shipit.github_api.user(login))
49
+ rescue Octokit::NotFound
50
+ identify_renamed_user!
51
+ end
52
+
53
+ def github_user=(github_user)
54
+ return unless github_user
55
+ github_user = github_user.rels[:self].get.data unless github_user.name
56
+ assign_attributes(
57
+ github_id: github_user.id,
58
+ name: github_user.name || github_user.login, # Name is not mandatory on GitHub
59
+ email: github_user.email,
60
+ login: github_user.login,
61
+ avatar_url: github_user.rels[:avatar].try(:href),
62
+ api_url: github_user.rels[:self].try(:href),
63
+ )
64
+ end
65
+
66
+ def avatar_uri
67
+ URI.parse(avatar_url)
68
+ rescue URI::InvalidURIError
69
+ DEFAULT_AVATAR.dup
70
+ end
71
+
72
+ private
73
+
74
+ def identify_renamed_user!
75
+ last_commit = commits.last
76
+ return unless last_commit
77
+ github_author = last_commit.github_commit.author
78
+ update!(github_user: github_author)
79
+ rescue Octokit::NotFound
80
+ false
81
+ end
82
+ end
83
+ end