shipit-engine 0.8.9 → 0.9.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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/shipit_bs.js.coffee +2 -0
  3. data/app/assets/javascripts/task.js.coffee +14 -5
  4. data/app/assets/javascripts/task/search_bar.js.coffee +52 -0
  5. data/app/assets/javascripts/task/stream.js.coffee +9 -2
  6. data/app/assets/javascripts/task/tty.js.coffee +75 -28
  7. data/app/assets/stylesheets/_base/_forms.scss +5 -0
  8. data/app/assets/stylesheets/_pages/_commits.scss +1 -1
  9. data/app/assets/stylesheets/_pages/_deploy.scss +56 -24
  10. data/app/assets/stylesheets/_pages/_settings.scss +5 -0
  11. data/app/assets/stylesheets/_structure/_layout.scss +1 -0
  12. data/app/assets/stylesheets/_structure/_main.scss +4 -0
  13. data/app/assets/stylesheets/shipit.scss +2 -0
  14. data/app/assets/stylesheets/shipit_bs.scss +22 -0
  15. data/app/controllers/shipit/deploys_controller.rb +5 -1
  16. data/app/controllers/shipit/shipit_controller.rb +10 -3
  17. data/app/controllers/shipit/stacks_controller.rb +12 -3
  18. data/app/controllers/shipit/tasks_controller.rb +4 -0
  19. data/app/helpers/shipit/shipit_helper.rb +18 -0
  20. data/app/helpers/shipit/stacks_helper.rb +1 -1
  21. data/app/jobs/shipit/cache_deploy_spec_job.rb +2 -0
  22. data/app/jobs/shipit/fetch_deployed_revision_job.rb +1 -0
  23. data/app/jobs/shipit/git_mirror_update_job.rb +2 -0
  24. data/app/jobs/shipit/perform_task_job.rb +1 -0
  25. data/app/models/shipit/commit.rb +2 -2
  26. data/app/models/shipit/deploy.rb +1 -1
  27. data/app/models/shipit/deploy_spec/bundler_discovery.rb +1 -1
  28. data/app/models/shipit/duration.rb +28 -0
  29. data/app/models/shipit/stack.rb +33 -11
  30. data/app/models/shipit/task.rb +26 -3
  31. data/app/serializers/shipit/task_serializer.rb +14 -1
  32. data/app/views/bootstrap/shipit/missing_settings.html.erb +97 -0
  33. data/app/views/bootstrap/shipit/stacks/new.html.erb +44 -0
  34. data/app/views/layouts/shipit.html.erb +1 -1
  35. data/app/views/layouts/shipit_bootstrap.html.erb +44 -0
  36. data/app/views/shipit/commits/_commit.html.erb +3 -2
  37. data/app/views/shipit/deploys/_deploy.html.erb +11 -2
  38. data/app/views/shipit/deploys/show.html.erb +1 -1
  39. data/app/views/shipit/stacks/new.html.erb +12 -12
  40. data/app/views/shipit/stacks/settings.html.erb +5 -0
  41. data/app/views/shipit/stacks/show.html.erb +1 -1
  42. data/app/views/shipit/tasks/_task.html.erb +10 -2
  43. data/app/views/shipit/tasks/_task_output.html.erb +11 -1
  44. data/app/views/shipit/tasks/show.html.erb +1 -1
  45. data/config/routes.rb +1 -0
  46. data/db/migrate/20160324155046_add_started_at_and_ended_at_on_tasks.rb +25 -0
  47. data/lib/shipit.rb +13 -0
  48. data/lib/shipit/command.rb +13 -9
  49. data/lib/shipit/engine.rb +8 -0
  50. data/lib/shipit/template_renderer_extension.rb +16 -0
  51. data/lib/shipit/version.rb +1 -1
  52. data/test/controllers/deploys_controller_test.rb +11 -0
  53. data/test/controllers/stacks_controller_test.rb +5 -0
  54. data/test/controllers/tasks_controller_test.rb +6 -0
  55. data/test/dummy/config/secrets.example.yml +4 -0
  56. data/test/dummy/config/secrets.yml +2 -0
  57. data/test/dummy/data/stacks/byroot/junk/production/deploys/83/bar.txt +2 -0
  58. data/test/dummy/data/stacks/byroot/junk/production/deploys/83/dkfdsf +0 -0
  59. data/test/dummy/data/stacks/byroot/junk/production/deploys/83/dskjfsd +0 -0
  60. data/test/dummy/data/stacks/byroot/junk/production/deploys/83/dslkjfjsdf +0 -0
  61. data/test/dummy/data/stacks/byroot/junk/production/deploys/83/plopfizz +0 -0
  62. data/test/dummy/data/stacks/byroot/junk/production/deploys/83/sd +0 -0
  63. data/test/dummy/data/stacks/byroot/junk/production/deploys/83/sdkfjsdf +1 -0
  64. data/test/dummy/data/stacks/byroot/junk/production/deploys/83/sdlfjsdfdsfj +0 -0
  65. data/test/dummy/data/stacks/byroot/junk/production/deploys/83/sdlkfjsdlkfjsdlkfjdsfsdfksdfjsldkfjsdlkfjsdf +0 -0
  66. data/test/dummy/data/stacks/byroot/junk/production/deploys/83/shipit.yml +32 -0
  67. data/test/dummy/data/stacks/byroot/junk/production/deploys/83/toto.txt +2 -0
  68. data/test/dummy/data/stacks/byroot/junk/production/git/bar.txt +1 -0
  69. data/test/dummy/data/stacks/byroot/junk/production/git/shipit.yml +6 -1
  70. data/test/dummy/data/stacks/byroot/test/production/git/README.md +1 -0
  71. data/test/dummy/db/development.sqlite3 +0 -0
  72. data/test/dummy/db/schema.rb +3 -1
  73. data/test/dummy/db/seeds.rb +6 -0
  74. data/test/dummy/db/test.sqlite3 +0 -0
  75. data/test/dummy/db/test.sqlite3-journal +0 -0
  76. data/test/fixtures/shipit/tasks.yml +11 -0
  77. data/test/models/commits_test.rb +1 -1
  78. data/test/models/deploys_test.rb +40 -0
  79. data/test/models/duration_test.rb +13 -0
  80. data/test/models/stacks_test.rb +3 -4
  81. data/test/unit/command_test.rb +14 -0
  82. data/vendor/assets/javascripts/clusterize.js +327 -0
  83. data/vendor/assets/javascripts/mousetrap-global-bind.js +43 -0
  84. data/vendor/assets/javascripts/mousetrap.js +1021 -0
  85. data/vendor/assets/javascripts/string_includes.js +14 -0
  86. data/vendor/assets/stylesheets/clusterize.css +27 -0
  87. metadata +100 -3
  88. data/app/assets/javascripts/task/sticky_element.js.coffee +0 -16
@@ -1,4 +1,6 @@
1
1
  //= require 'ansi_stream'
2
+ //= require 'clusterize'
3
+
2
4
  @import "_base/_media-queries";
3
5
  @import "_base/_utility";
4
6
  @import "_base/_colors";
@@ -0,0 +1,22 @@
1
+ // Custom bootstrap variables must be set or import before bootstrap itself.
2
+ @import "bootstrap";
3
+
4
+ body {
5
+ padding-top: 5rem;
6
+ }
7
+
8
+ .navbar-brand {
9
+ background: asset-data-url('anchor.svg') center center no-repeat;
10
+ display: block;
11
+ width: 40px;
12
+ height: 40px;
13
+ transition: transform .3s ease-in-out;
14
+ text-indent: -9999px;
15
+ top: 50%;
16
+ right: 100%;
17
+ margin-right: 1em;
18
+
19
+ &:hover {
20
+ transform: rotate(-25deg);
21
+ }
22
+ }
@@ -9,10 +9,14 @@ module Shipit
9
9
  def new
10
10
  @commit = @stack.commits.by_sha!(params[:sha])
11
11
  @commit.checks.schedule if @stack.checks?
12
- @deploy = @stack.deploys.new(until_commit: @commit, since_commit: @stack.last_deployed_commit)
12
+ @deploy = @stack.build_deploy(@commit, current_user)
13
13
  end
14
14
 
15
15
  def show
16
+ respond_to do |format|
17
+ format.html
18
+ format.text { render plain: @deploy.chunk_output }
19
+ end
16
20
  end
17
21
 
18
22
  def create
@@ -11,9 +11,12 @@ module Shipit
11
11
  helper Shipit::Engine.routes.url_helpers
12
12
  include Shipit::Engine.routes.url_helpers
13
13
 
14
- before_action :ensure_required_settings,
15
- :force_github_authentication,
16
- :set_variant
14
+ before_action(
15
+ :toogle_bootstrap_feature,
16
+ :ensure_required_settings,
17
+ :force_github_authentication,
18
+ :set_variant,
19
+ )
17
20
 
18
21
  # Respond to HTML by default
19
22
  respond_to :html
@@ -24,6 +27,10 @@ module Shipit
24
27
 
25
28
  private
26
29
 
30
+ def toogle_bootstrap_feature
31
+ prepend_view_path(Shipit.bootstrap_view_path) if Shipit.feature_bootstrap?
32
+ end
33
+
27
34
  def ensure_required_settings
28
35
  return if Shipit.all_settings_present?
29
36
 
@@ -24,8 +24,16 @@ module Shipit
24
24
  @commits = @commits.to_a
25
25
  end
26
26
 
27
+ def lookup
28
+ @stack = Stack.find(params[:id])
29
+ redirect_to stack_url(@stack)
30
+ end
31
+
27
32
  def create
28
- @stack = Stack.create(create_params)
33
+ @stack = Stack.new(create_params)
34
+ unless @stack.save
35
+ flash[:warning] = @stack.errors.full_messages.to_sentence
36
+ end
29
37
  respond_with(@stack)
30
38
  end
31
39
 
@@ -41,7 +49,7 @@ module Shipit
41
49
  RefreshStatusesJob.perform_later(stack_id: @stack.id)
42
50
  GithubSyncJob.perform_later(stack_id: @stack.id)
43
51
  flash[:success] = 'Refresh scheduled'
44
- redirect_to :back
52
+ redirect_to request.referer ? :back : stack_path(@stack)
45
53
  end
46
54
 
47
55
  def update
@@ -73,7 +81,8 @@ module Shipit
73
81
  end
74
82
 
75
83
  def update_params
76
- params.require(:stack).permit(:deploy_url, :lock_reason, :continuous_deployment, :ignore_ci).tap do |params|
84
+ params.require(:stack).permit(:deploy_url, :lock_reason, :environment,
85
+ :continuous_deployment, :ignore_ci).tap do |params|
77
86
  params[:lock_author_id] = params[:lock_reason].present? ? current_user.id : nil
78
87
  end
79
88
  end
@@ -19,6 +19,10 @@ module Shipit
19
19
 
20
20
  def show
21
21
  task
22
+ respond_to do |format|
23
+ format.html
24
+ format.text { render plain: @task.chunk_output }
25
+ end
22
26
  end
23
27
 
24
28
  def create
@@ -1,5 +1,23 @@
1
1
  module Shipit
2
2
  module ShipitHelper
3
+ def emojify(content)
4
+ h(content).to_str.gsub(/:([\w+-]+):/) do |match|
5
+ if emoji = Emoji.find_by_alias($1)
6
+ %(
7
+ <img
8
+ alt="##{$1}"
9
+ src="#{image_path("emoji/#{emoji.image_filename}")}"
10
+ style="vertical-align:middle"
11
+ width="20"
12
+ height="20"
13
+ />
14
+ )
15
+ else
16
+ match
17
+ end
18
+ end.html_safe if content.present?
19
+ end
20
+
3
21
  def include_plugins(stack)
4
22
  stack.plugins.flat_map do |plugin, config|
5
23
  plugin_tags(plugin, config)
@@ -37,7 +37,7 @@ module Shipit
37
37
 
38
38
  def render_commit_message(commit)
39
39
  message = commit.pull_request_title || commit.message
40
- content_tag(:span, message.truncate(COMMIT_TITLE_LENGTH), class: 'event-message')
40
+ content_tag(:span, emojify(message.truncate(COMMIT_TITLE_LENGTH)), class: 'event-message')
41
41
  end
42
42
 
43
43
  def render_commit_message_with_link(commit)
@@ -3,6 +3,8 @@ module Shipit
3
3
  include BackgroundJob::Unique
4
4
 
5
5
  def perform(stack)
6
+ return if stack.inaccessible?
7
+
6
8
  commands = Commands.for(stack)
7
9
  commands.with_temporary_working_directory(commit: stack.commits.last) do |path|
8
10
  stack.update!(cached_deploy_spec: DeploySpec::FileSystem.new(path, stack.environment))
@@ -4,6 +4,7 @@ module Shipit
4
4
 
5
5
  def perform(stack)
6
6
  return if stack.active_task?
7
+ return if stack.inaccessible?
7
8
 
8
9
  commands = StackCommands.new(stack)
9
10
 
@@ -3,6 +3,8 @@ module Shipit
3
3
  queue_as :default
4
4
 
5
5
  def perform(stack)
6
+ return if stack.inaccessible?
7
+
6
8
  commands = StackCommands.new(stack)
7
9
  stack.acquire_git_cache_lock do
8
10
  commands.fetch.run
@@ -20,6 +20,7 @@ module Shipit
20
20
  perform_task
21
21
  @task.complete!
22
22
  rescue Command::Error => error
23
+ @task.write("\n#{error.message}\n")
23
24
  @task.report_failure!(error)
24
25
  rescue StandardError => error
25
26
  @task.report_error!(error)
@@ -23,12 +23,12 @@ module Shipit
23
23
 
24
24
  def self.newer_than(commit)
25
25
  return all unless commit
26
- where('id > ?', commit.is_a?(Commit) ? commit.id : commit)
26
+ where('id > ?', commit.try(:id) || commit)
27
27
  end
28
28
 
29
29
  def self.until(commit)
30
30
  return all unless commit
31
- where('id <= ?', commit.is_a?(Commit) ? commit.id : commit)
31
+ where('id <= ?', commit.try(:id) || commit)
32
32
  end
33
33
 
34
34
  def self.successful
@@ -90,7 +90,7 @@ module Shipit
90
90
  end
91
91
 
92
92
  def currently_deployed?
93
- until_commit_id == stack.last_deployed_commit.try!(:id)
93
+ until_commit_id == stack.last_deployed_commit.id
94
94
  end
95
95
 
96
96
  def commits
@@ -26,7 +26,7 @@ module Shipit
26
26
  # Heroku apps often specify a ruby version.
27
27
  if /darwin/ =~ RUBY_PLATFORM
28
28
  # OSX is nitpicky about the -i.
29
- %q(sed -i '' '/^ruby\s/d' Gemfile)
29
+ %q(/usr/bin/sed -i '' '/^ruby\s/d' Gemfile)
30
30
  else
31
31
  %q(sed -i '/^ruby\s/d' Gemfile)
32
32
  end
@@ -0,0 +1,28 @@
1
+ module Shipit
2
+ class Duration
3
+ UNITS = {
4
+ 's' => :seconds,
5
+ 'm' => :minutes,
6
+ 'h' => :hours,
7
+ 'd' => :days,
8
+ }.freeze
9
+
10
+ def initialize(seconds)
11
+ @seconds = seconds
12
+ end
13
+
14
+ def to_i
15
+ @seconds.to_i
16
+ end
17
+
18
+ def to_s
19
+ seconds = to_i
20
+ days, seconds = seconds.divmod(1.day.to_i)
21
+ if days > 0
22
+ "#{days}d#{Time.at(seconds).utc.strftime('%Hh%Mm%Ss')}"
23
+ else
24
+ Time.at(seconds).utc.strftime('%Hh%Mm%Ss')[/[^0a-z]\w+/] || '0s'
25
+ end
26
+ end
27
+ end
28
+ end
@@ -2,6 +2,22 @@ require 'fileutils'
2
2
 
3
3
  module Shipit
4
4
  class Stack < ActiveRecord::Base
5
+ module NoDeployedCommit
6
+ extend self
7
+
8
+ def id
9
+ -1
10
+ end
11
+
12
+ def sha
13
+ ''
14
+ end
15
+
16
+ def blank?
17
+ true
18
+ end
19
+ end
20
+
5
21
  REPO_OWNER_MAX_SIZE = 39
6
22
  REPO_NAME_MAX_SIZE = 100
7
23
  ENVIRONMENT_MAX_SIZE = 50
@@ -36,7 +52,8 @@ module Shipit
36
52
  after_commit :setup_hooks, :sync_github, on: :create
37
53
  after_touch :clear_cache
38
54
 
39
- validates :repo_name, uniqueness: {scope: %i(repo_owner environment)}
55
+ validates :repo_name, uniqueness: {scope: %i(repo_owner environment),
56
+ message: 'cannot be used more than once with this environment'}
40
57
  validates :repo_owner, :repo_name, :environment, presence: true, ascii_only: true
41
58
  validates :repo_owner, format: {with: /\A[a-z0-9_\-\.]+\z/}, length: {maximum: REPO_OWNER_MAX_SIZE}
42
59
  validates :repo_name, format: {with: /\A[a-z0-9_\-\.]+\z/}, length: {maximum: REPO_NAME_MAX_SIZE}
@@ -57,7 +74,7 @@ module Shipit
57
74
  end
58
75
 
59
76
  def trigger_task(definition_id, user, env: nil)
60
- commit = last_deployed_commit
77
+ commit = last_deployed_commit.presence || commits.first
61
78
  task = tasks.create(
62
79
  user_id: user.id,
63
80
  definition: find_task_definition(definition_id),
@@ -69,15 +86,19 @@ module Shipit
69
86
  task
70
87
  end
71
88
 
72
- def trigger_deploy(until_commit, user, env: nil)
73
- since_commit = last_deployed_commit
74
-
75
- deploy = deploys.create(
89
+ def build_deploy(until_commit, user, env: nil)
90
+ since_commit = last_deployed_commit.presence || commits.first
91
+ deploys.build(
76
92
  user_id: user.id,
77
93
  until_commit: until_commit,
78
94
  since_commit: since_commit,
79
95
  env: filter_deploy_envs(env || {}),
80
96
  )
97
+ end
98
+
99
+ def trigger_deploy(*args)
100
+ deploy = build_deploy(*args)
101
+ deploy.save!
81
102
  deploy.enqueue
82
103
  deploy
83
104
  end
@@ -95,10 +116,11 @@ module Shipit
95
116
  end
96
117
 
97
118
  def update_deployed_revision(sha)
98
- return if active_task?
99
-
100
119
  last_deploy = deploys_and_rollbacks.last
101
- actual_deployed_commit = commits.reachable.by_sha!(sha)
120
+ return if last_deploy.try!(:active?)
121
+
122
+ actual_deployed_commit = commits.reachable.by_sha(sha)
123
+ return unless actual_deployed_commit
102
124
 
103
125
  if last_deploy && actual_deployed_commit == last_deploy.until_commit
104
126
  last_deploy.accept!
@@ -107,7 +129,7 @@ module Shipit
107
129
  else
108
130
  deploys.create!(
109
131
  until_commit: actual_deployed_commit,
110
- since_commit: last_deployed_commit,
132
+ since_commit: last_deployed_commit.presence || commits.first,
111
133
  status: 'success',
112
134
  )
113
135
  end
@@ -144,7 +166,7 @@ module Shipit
144
166
  if deploy = last_successful_deploy
145
167
  deploy.until_commit
146
168
  else
147
- commits.first
169
+ NoDeployedCommit
148
170
  end
149
171
  end
150
172
 
@@ -1,5 +1,8 @@
1
1
  module Shipit
2
2
  class Task < ActiveRecord::Base
3
+ ACTIVE_STATUSES = %w(pending running aborting).freeze
4
+ COMPLETED_STATUSES = %w(success error failed flapping aborted).freeze
5
+
3
6
  belongs_to :deploy, foreign_key: :parent_id, required: false # required for fixtures
4
7
 
5
8
  belongs_to :user
@@ -13,8 +16,8 @@ module Shipit
13
16
  serialize :env, Hash
14
17
 
15
18
  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)) }
19
+ scope :completed, -> { where(status: COMPLETED_STATUSES) }
20
+ scope :active, -> { where(status: ACTIVE_STATUSES) }
18
21
  scope :exclusive, -> { where(allow_concurrency: false) }
19
22
 
20
23
  scope :due_for_rollup, -> { completed.where(rolled_up: false).where('created_at <= ?', 1.hour.ago) }
@@ -23,6 +26,14 @@ module Shipit
23
26
  after_commit :emit_hooks
24
27
 
25
28
  state_machine :status, initial: :pending do
29
+ before_transition any => :running do |task|
30
+ task.started_at ||= Time.now.utc
31
+ end
32
+
33
+ before_transition any => %i(success failed error) do |task|
34
+ task.ended_at ||= Time.now.utc
35
+ end
36
+
26
37
  after_transition any => %i(success failed error) do |task|
27
38
  task.async_refresh_deployed_revision
28
39
  end
@@ -69,6 +80,10 @@ module Shipit
69
80
  state :flapping
70
81
  end
71
82
 
83
+ def active?
84
+ status.in?(ACTIVE_STATUSES)
85
+ end
86
+
72
87
  def report_failure!(_error)
73
88
  reload
74
89
  if aborting?
@@ -79,7 +94,7 @@ module Shipit
79
94
  end
80
95
 
81
96
  def report_error!(error)
82
- write("#{error.class}: #{error.message}\n\t#{error.backtrace.join("\t")}\n")
97
+ write("#{error.class}: #{error.message}\n\t#{error.backtrace.join("\n\t")}\n")
83
98
  error!
84
99
  end
85
100
 
@@ -87,6 +102,14 @@ module Shipit
87
102
 
88
103
  delegate :checklist, to: :definition
89
104
 
105
+ def duration?
106
+ started_at? && ended_at?
107
+ end
108
+
109
+ def duration
110
+ Duration.new(ended_at - started_at) if duration?
111
+ end
112
+
90
113
  def spec
91
114
  @spec ||= DeploySpec::FileSystem.new(working_directory, stack.environment)
92
115
  end
@@ -5,7 +5,20 @@ module Shipit
5
5
  has_one :author
6
6
  has_one :revision, serializer: ShortCommitSerializer
7
7
 
8
- attributes :id, :url, :html_url, :output_url, :type, :status, :action, :description, :updated_at, :created_at
8
+ attributes(*%i(
9
+ id
10
+ url
11
+ html_url
12
+ output_url
13
+ type
14
+ status
15
+ action
16
+ description
17
+ started_at
18
+ ended_at
19
+ updated_at
20
+ created_at
21
+ ))
9
22
 
10
23
  def revision
11
24
  object.until_commit