houston-core 0.7.0.beta4 → 0.7.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.
- checksums.yaml +4 -4
- data/.gitignore +4 -2
- data/.travis.yml +50 -0
- data/Gemfile +1 -2
- data/Gemfile.lock +1 -7
- data/README.md +21 -9
- data/app/assets/stylesheets/houston/application/actions.scss +25 -4
- data/app/assets/stylesheets/houston/application/sprint.scss +1 -1
- data/app/assets/stylesheets/houston/application/test.scss +1 -1
- data/app/assets/stylesheets/houston/application/test_run.scss +2 -2
- data/app/assets/stylesheets/houston/application/timeline.scss +1 -1
- data/app/assets/stylesheets/houston/core/roboto.scss.erb +2 -2
- data/app/controllers/actions_controller.rb +13 -5
- data/app/controllers/errors_controller.rb +2 -2
- data/app/controllers/releases_controller.rb +1 -1
- data/app/controllers/triggers_controller.rb +1 -1
- data/app/helpers/actions_helper.rb +36 -1
- data/app/helpers/navigation_helper.rb +6 -1
- data/app/models/github/pull_request.rb +13 -4
- data/app/models/task.rb +1 -1
- data/app/presenters/task_presenter.rb +6 -6
- data/app/views/actions/_actions.html.erb +12 -0
- data/app/views/actions/index.html.erb +19 -12
- data/app/views/actions/running.html.erb +47 -0
- data/app/views/actions/show.html.erb +13 -14
- data/app/views/errors/_actions.html.erb +1 -1
- data/app/views/errors/index.html.erb +5 -1
- data/app/views/layouts/_navigation.html.erb +10 -5
- data/config/initializers/add_navigation_renderers.rb +8 -6
- data/config/initializers/houston_deliver.rb +16 -0
- data/config/routes.rb +1 -0
- data/db/structure.sql +1 -1
- data/houston-core.gemspec +1 -1
- data/lib/generators/module_generator.rb +5 -1
- data/lib/houston/boot/actions.rb +4 -1
- data/lib/houston/boot/configuration.rb +8 -1
- data/lib/houston/boot/extensions.rb +35 -9
- data/lib/houston/boot/serializer.rb +6 -1
- data/lib/houston/version.rb +1 -1
- data/script/cibuild +2 -2
- data/templates/new-module/lib/houston/%name%.rb +30 -7
- data/test/acceptance/creating_a_release_test.rb +14 -21
- data/test/data/bare_repo.git/FETCH_HEAD +1 -1
- data/test/data/bare_repo.git/objects/60/3a4970ec7ed5eb3f7a578850c21b7162424465 +1 -0
- data/test/data/bare_repo.git/objects/b9/1a4fe778398bc87b29842ca84a2727b4763c9d +1 -0
- data/test/data/bare_repo.git/objects/ba/b88c00ec62b5f30bf720ab1136401d1f9d2310 +2 -0
- data/test/data/bare_repo.git/refs/heads/master +1 -1
- data/test/integration/commits_api_test.rb +1 -0
- data/test/test_helper.rb +2 -2
- data/test/unit/models/sprint_test.rb +1 -1
- data/tmp/.keep +0 -0
- metadata +16 -5
data/app/models/task.rb
CHANGED
@@ -22,13 +22,13 @@ class TaskPresenter
|
|
22
22
|
project = ticket.project
|
23
23
|
{ id: task.id,
|
24
24
|
|
25
|
-
projectId: project.id,
|
26
|
-
projectSlug: project.slug,
|
27
|
-
projectTitle: project.name,
|
28
|
-
projectColor: project.color,
|
25
|
+
projectId: project && project.id,
|
26
|
+
projectSlug: project && project.slug,
|
27
|
+
projectTitle: project && project.name,
|
28
|
+
projectColor: project && project.color,
|
29
29
|
|
30
|
-
ticketSystem: project.ticket_tracker_name,
|
31
|
-
ticketUrl: ticket.ticket_tracker_ticket_url,
|
30
|
+
ticketSystem: project && project.ticket_tracker_name,
|
31
|
+
ticketUrl: project && ticket.ticket_tracker_ticket_url,
|
32
32
|
ticketNumber: ticket.number,
|
33
33
|
ticketType: ticket.type.to_s.downcase.dasherize,
|
34
34
|
ticketSequence: ticket.extended_attributes["sequence"], # <-- embeds knowledge of Houston::Scheduler
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<% @actions.each do |action| %>
|
2
|
+
<tr class="action" data-timestamp="<%= action.started_at.iso8601 %>">
|
3
|
+
<td class="table-margin"></td>
|
4
|
+
<td class="action-time"><%= format_time action.started_at %></td>
|
5
|
+
<td class="action-duration"><%= format_duration action.duration %></td>
|
6
|
+
<td class="action-trigger"><%= action.trigger %></td>
|
7
|
+
<td class="action-params"><%= format_action_params action.params %></td>
|
8
|
+
<td class="action-succeded"><%= format_action_state action %></td>
|
9
|
+
<td class="action-exception"><%= action.error.message if action.error %><td>
|
10
|
+
<td class="table-margin"></td>
|
11
|
+
</tr>
|
12
|
+
<% end %>
|
@@ -16,10 +16,12 @@
|
|
16
16
|
<tr>
|
17
17
|
<td class="table-margin"></td>
|
18
18
|
<th class="action-name">Name</th>
|
19
|
+
<th class="action-params">Params</th>
|
19
20
|
<th class="action-last">Last Run</th>
|
20
21
|
<th class="action-reliability">Reliability</th>
|
22
|
+
<th class="action-count">Count</th>
|
21
23
|
<th class="action-duration">Duration</th>
|
22
|
-
|
24
|
+
<td class="action-run-now"></td>
|
23
25
|
<td class="table-margin"></td>
|
24
26
|
</tr>
|
25
27
|
</thead>
|
@@ -28,24 +30,28 @@
|
|
28
30
|
<tr class="action">
|
29
31
|
<td class="table-margin"></td>
|
30
32
|
<td class="action-name"><%= link_to action[:name], action_path(slug: action[:name]) %></td>
|
33
|
+
<td class="action-params"><%= action[:required_params].join(", ") %></td>
|
31
34
|
<% if action[:last] %>
|
32
35
|
<td class="action-last" data-timestamp="<%= action[:last].started_at.iso8601 %>">
|
33
36
|
<%= format_time action[:last].started_at, today: false %>
|
34
37
|
<%= format_action_state action[:last] %>
|
35
38
|
</td>
|
36
|
-
<td class="action-reliability">
|
39
|
+
<td class="action-reliability" data-position="<%= action[:successful_runs].to_f / action[:runs] %>">
|
37
40
|
<%= number_to_percentage 100.0 * action[:successful_runs].to_f / action[:runs], precision: 1 %>
|
38
|
-
<span class="action-success-rate">/<%= action[:runs] %></span>
|
39
41
|
</td>
|
42
|
+
<td class="action-count"><%= action[:runs] %></td>
|
40
43
|
<td class="action-duration" data-position="<%= action[:avg_duration] %>"><%= format_duration action[:avg_duration] %></td>
|
41
44
|
<% else %>
|
42
|
-
<td class="action-last">—</td>
|
43
|
-
<td class="action-reliability">—</td>
|
44
|
-
<td class="action-
|
45
|
+
<td class="action-last" data-timestamp="0">—</td>
|
46
|
+
<td class="action-reliability" data-position="0">—</td>
|
47
|
+
<td class="action-count">0</td>
|
48
|
+
<td class="action-duration" data-position="0">—</td>
|
45
49
|
<% end %>
|
46
|
-
|
47
|
-
|
48
|
-
|
50
|
+
<td class="action-run-now">
|
51
|
+
<% if action[:required_params].empty? %>
|
52
|
+
<%= button_to "Run now", run_action_path(slug: action[:name]), :class => "btn btn-default" %>
|
53
|
+
<% end %>
|
54
|
+
</td>
|
49
55
|
<td class="table-margin"></td>
|
50
56
|
</tr>
|
51
57
|
<% end %>
|
@@ -59,9 +65,10 @@
|
|
59
65
|
$(function() {
|
60
66
|
$('#actions').tablesorter({
|
61
67
|
headers: {
|
62
|
-
|
63
|
-
|
64
|
-
|
68
|
+
3: { sorter: 'timestamp' },
|
69
|
+
4: { sorter: 'property' },
|
70
|
+
5: { sorter: 'integer' },
|
71
|
+
6: { sorter: 'property' }
|
65
72
|
}
|
66
73
|
});
|
67
74
|
});
|
@@ -0,0 +1,47 @@
|
|
1
|
+
<% content_for :title do %>
|
2
|
+
<h1 class="project-banner space-below">
|
3
|
+
Running Actions
|
4
|
+
</h1>
|
5
|
+
<% end %>
|
6
|
+
|
7
|
+
<div class="nomargin">
|
8
|
+
<table id="actions" class="table table-sortable table-striped">
|
9
|
+
<thead>
|
10
|
+
<tr>
|
11
|
+
<td class="table-margin"></td>
|
12
|
+
<th class="action-time">Started</th>
|
13
|
+
<th class="action-duration">Duration</th>
|
14
|
+
<th class="action-name">Action</th>
|
15
|
+
<th class="action-trigger">Trigger</th>
|
16
|
+
<th class="action-params">Params</th>
|
17
|
+
<td class="table-margin"></td>
|
18
|
+
</tr>
|
19
|
+
</thead>
|
20
|
+
<tbody>
|
21
|
+
<% @actions.each do |action| %>
|
22
|
+
<tr class="action">
|
23
|
+
<td class="table-margin"></td>
|
24
|
+
<td class="action-time"><%= format_time action.started_at %></td>
|
25
|
+
<td class="action-duration"><%= format_time_ago action.started_at %></td>
|
26
|
+
<td class="action-name"><%= link_to action[:name], action_path(slug: action[:name]) %></td>
|
27
|
+
<td class="action-trigger"><%= action.trigger %></td>
|
28
|
+
<td class="action-params"><%= format_action_params action.params %></td>
|
29
|
+
<td class="table-margin"></td>
|
30
|
+
</tr>
|
31
|
+
<% end %>
|
32
|
+
</tbody>
|
33
|
+
</table>
|
34
|
+
</div>
|
35
|
+
|
36
|
+
|
37
|
+
<% content_for :javascripts do %>
|
38
|
+
<script type="text/javascript">
|
39
|
+
$(function() {
|
40
|
+
$('#actions')
|
41
|
+
.tablesorter()
|
42
|
+
.on('click', '.action-params-short', function() {
|
43
|
+
$(this).hide().next().show();
|
44
|
+
});
|
45
|
+
});
|
46
|
+
</script>
|
47
|
+
<% end %>
|
@@ -19,19 +19,8 @@
|
|
19
19
|
<td class="table-margin"></td>
|
20
20
|
</tr>
|
21
21
|
</thead>
|
22
|
-
<tbody>
|
23
|
-
|
24
|
-
<tr class="action">
|
25
|
-
<td class="table-margin"></td>
|
26
|
-
<td class="action-time"><%= format_time action.started_at %></td>
|
27
|
-
<td class="action-duration"><%= format_duration action.duration %></td>
|
28
|
-
<td class="action-trigger"><%= action.trigger %></td>
|
29
|
-
<td class="action-params"><%= format_action_params action.params %></td>
|
30
|
-
<td class="action-succeded"><%= format_action_state action %></td>
|
31
|
-
<td class="action-exception"><%= action.error.message if action.error %><td>
|
32
|
-
<td class="table-margin"></td>
|
33
|
-
</tr>
|
34
|
-
<% end %>
|
22
|
+
<tbody class="infinite-scroll">
|
23
|
+
<%= render "actions/actions" %>
|
35
24
|
</tbody>
|
36
25
|
</table>
|
37
26
|
</div>
|
@@ -39,7 +28,17 @@
|
|
39
28
|
<% content_for :javascripts do %>
|
40
29
|
<script type="text/javascript">
|
41
30
|
$(function() {
|
42
|
-
$('#actions')
|
31
|
+
$('#actions')
|
32
|
+
.tablesorter()
|
33
|
+
.on('click', '.action-params-short', function() {
|
34
|
+
$(this).hide().next().show();
|
35
|
+
});
|
36
|
+
});
|
37
|
+
new InfiniteScroll({
|
38
|
+
load: function($tbody) {
|
39
|
+
var timestamp = $tbody.find('.action:last').attr('data-timestamp');
|
40
|
+
return $.get(window.location.pathname, {before: timestamp});
|
41
|
+
}
|
43
42
|
});
|
44
43
|
</script>
|
45
44
|
<% end %>
|
@@ -4,7 +4,7 @@
|
|
4
4
|
<td class="action-time"><%= format_time action.finished_at %></td>
|
5
5
|
<td class="action-name"><%= link_to action[:name], action_path(slug: action[:name]) %></td>
|
6
6
|
<td class="action-trigger"><%= action.trigger %></td>
|
7
|
-
<td class="action-params"
|
7
|
+
<td class="action-params"><%= format_action_params action.params %></td>
|
8
8
|
<td class="action-error-message"><%= action.error.message %></td>
|
9
9
|
<td class="table-margin"></td>
|
10
10
|
</tr>
|
@@ -27,7 +27,11 @@
|
|
27
27
|
<% content_for :javascripts do %>
|
28
28
|
<script type="text/javascript">
|
29
29
|
$(function() {
|
30
|
-
$('#
|
30
|
+
$('#actions')
|
31
|
+
.tablesorter()
|
32
|
+
.on('click', '.action-params-short', function() {
|
33
|
+
$(this).hide().next().show();
|
34
|
+
});
|
31
35
|
});
|
32
36
|
new InfiniteScroll({
|
33
37
|
load: function($tbody) {
|
@@ -5,14 +5,19 @@
|
|
5
5
|
<ul class="nav pull-right">
|
6
6
|
<% if current_user -%>
|
7
7
|
|
8
|
-
<% if
|
8
|
+
<% if can?(:manage, User) || can?(:read, Action) %>
|
9
9
|
<li class="dropdown">
|
10
10
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-cog"></i> <b class="caret"></b></a>
|
11
11
|
<ul class="dropdown-menu">
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
<% if can?(:manage, User) %>
|
13
|
+
<li><%= link_to "Users", main_app.users_path %></li>
|
14
|
+
<% end %>
|
15
|
+
<% if can?(:read, Action) %>
|
16
|
+
<li><%= link_to "Actions", main_app.actions_path %></li>
|
17
|
+
<li><%= link_to "Running Actions", main_app.running_actions_path %></li>
|
18
|
+
<li><%= link_to "Triggers", main_app.triggers_path %></li>
|
19
|
+
<li><%= link_to "Errors", main_app.errors_path %></li>
|
20
|
+
<% end %>
|
16
21
|
</ul>
|
17
22
|
</li>
|
18
23
|
<% end %>
|
@@ -1,13 +1,15 @@
|
|
1
1
|
Houston.add_navigation_renderer :sprint do
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
name "Sprint"
|
3
|
+
icon "fa-burndown"
|
4
|
+
path { Houston::Application.routes.url_helpers.current_sprint_path }
|
5
|
+
ability { |ability| ability.can?(:read, Sprint) }
|
5
6
|
end
|
6
7
|
|
7
8
|
Houston.add_navigation_renderer :pulls do
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
name "Pulls"
|
10
|
+
icon "octokit-pull-request"
|
11
|
+
path { Houston::Application.routes.url_helpers.pulls_path }
|
12
|
+
ability { |ability| ability.can?(:read, Github::PullRequest) }
|
11
13
|
end
|
12
14
|
|
13
15
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Houston
|
2
|
+
|
3
|
+
def self.deliver!(message)
|
4
|
+
Houston.try({max_tries: 3},
|
5
|
+
Errno::ECONNRESET,
|
6
|
+
Errno::EPIPE,
|
7
|
+
Errno::ETIMEDOUT,
|
8
|
+
Net::OpenTimeout,
|
9
|
+
Net::ReadTimeout,
|
10
|
+
Net::SMTPServerBusy,
|
11
|
+
EOFError) do
|
12
|
+
message.deliver_now!
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
data/config/routes.rb
CHANGED
@@ -237,6 +237,7 @@ Rails.application.routes.draw do
|
|
237
237
|
# Actions
|
238
238
|
|
239
239
|
get "actions", to: "actions#index", as: :actions
|
240
|
+
get "actions/running", to: "actions#running", as: :running_actions
|
240
241
|
get "actions/:slug", to: "actions#show", as: :action, constraints: { slug: /[^\/]+/ }
|
241
242
|
post "actions/:slug", to: "actions#run", as: :run_action, constraints: { slug: /[^\/]+/ }
|
242
243
|
|
data/db/structure.sql
CHANGED
data/houston-core.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["bob.lailfamily@gmail.com"]
|
11
11
|
|
12
12
|
spec.summary = %q{Mission Control for your projects and teams}
|
13
|
-
spec.homepage = "https://github.com/houston/houston"
|
13
|
+
spec.homepage = "https://github.com/houston/houston-core"
|
14
14
|
|
15
15
|
spec.files = `git ls-files -z`.split("\x0")
|
16
16
|
spec.executables = ["houston"]
|
@@ -13,7 +13,6 @@ module Generators
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def config
|
16
|
-
template "config/initializers/add_navigation_renderer.rb"
|
17
16
|
template "config/database.yml"
|
18
17
|
template "config/routes.rb"
|
19
18
|
empty_directory_with_keep_file "db"
|
@@ -41,6 +40,11 @@ module Generators
|
|
41
40
|
# do nothing
|
42
41
|
end
|
43
42
|
|
43
|
+
def assets_manifest
|
44
|
+
# do nothing
|
45
|
+
# added in Rails 5; Houston requires ~> 4.2.7
|
46
|
+
end
|
47
|
+
|
44
48
|
def readme
|
45
49
|
template "README.md"
|
46
50
|
end
|
data/lib/houston/boot/actions.rb
CHANGED
@@ -2,6 +2,9 @@ require "thread_safe"
|
|
2
2
|
|
3
3
|
module Houston
|
4
4
|
class Actions
|
5
|
+
class ExecutionContext < ReadonlyHash
|
6
|
+
end
|
7
|
+
|
5
8
|
|
6
9
|
def initialize
|
7
10
|
@actions = ThreadSafe::Hash.new
|
@@ -90,7 +93,7 @@ module Houston
|
|
90
93
|
private
|
91
94
|
|
92
95
|
def run!(params, options={})
|
93
|
-
params =
|
96
|
+
params = ExecutionContext.new(params)
|
94
97
|
trigger = options.fetch(:trigger, "manual")
|
95
98
|
|
96
99
|
::Action.record name, params, trigger do
|
@@ -147,7 +147,8 @@ module_function
|
|
147
147
|
|
148
148
|
def navigation(*args)
|
149
149
|
@navigation = args if args.any?
|
150
|
-
@navigation
|
150
|
+
return Houston.available_navigation_renderers unless @navigation
|
151
|
+
@navigation & Houston.available_navigation_renderers
|
151
152
|
end
|
152
153
|
|
153
154
|
def project_features(*args)
|
@@ -380,6 +381,12 @@ module_function
|
|
380
381
|
def on(*args, &block)
|
381
382
|
event_name, action_name = extract_trigger_and_action!(args)
|
382
383
|
event = Houston.get_registered_event(event_name)
|
384
|
+
|
385
|
+
unless event
|
386
|
+
puts "\e[31mUnregistered event: \e[1m#{event_name}\e[0;90m\n#{caller[0]}\e[0m\n\n"
|
387
|
+
return
|
388
|
+
end
|
389
|
+
|
383
390
|
action = assert_action! action_name, event.params, &block
|
384
391
|
action.assert_required_params! event.params
|
385
392
|
|
@@ -7,14 +7,26 @@ module Houston
|
|
7
7
|
|
8
8
|
|
9
9
|
|
10
|
-
def
|
11
|
-
@navigation_renderers
|
10
|
+
def available_navigation_renderers
|
11
|
+
@navigation_renderers.keys
|
12
12
|
end
|
13
13
|
|
14
14
|
def get_navigation_renderer(name)
|
15
15
|
@navigation_renderers.fetch(name)
|
16
16
|
end
|
17
17
|
|
18
|
+
def add_navigation_renderer(slug, &block)
|
19
|
+
dsl = FeatureDsl.new(GlobalFeature.new)
|
20
|
+
dsl.instance_eval(&block)
|
21
|
+
feature = dsl.feature
|
22
|
+
feature.slug = slug
|
23
|
+
raise ArgumentError, "Renderer must supply name, but #{slug.inspect} doesn't" unless feature.name
|
24
|
+
raise ArgumentError, "Renderer must supply icon, but #{slug.inspect} doesn't" unless feature.icon
|
25
|
+
raise ArgumentError, "Renderer must supply path lambda, but #{slug.inspect} doesn't" unless feature.path_block
|
26
|
+
|
27
|
+
@navigation_renderers[slug] = feature
|
28
|
+
end
|
29
|
+
|
18
30
|
|
19
31
|
|
20
32
|
|
@@ -23,11 +35,11 @@ module Houston
|
|
23
35
|
end
|
24
36
|
|
25
37
|
def get_project_feature(slug)
|
26
|
-
@available_project_features
|
38
|
+
@available_project_features.fetch(slug)
|
27
39
|
end
|
28
40
|
|
29
41
|
def add_project_feature(slug, &block)
|
30
|
-
dsl =
|
42
|
+
dsl = FeatureDsl.new(ProjectFeature.new)
|
31
43
|
dsl.instance_eval(&block)
|
32
44
|
feature = dsl.feature
|
33
45
|
feature.slug = slug
|
@@ -105,8 +117,21 @@ module Houston
|
|
105
117
|
|
106
118
|
private
|
107
119
|
|
108
|
-
class
|
109
|
-
attr_accessor :name, :slug, :icon, :path_block, :ability_block
|
120
|
+
class GlobalFeature
|
121
|
+
attr_accessor :name, :slug, :icon, :path_block, :ability_block
|
122
|
+
|
123
|
+
def path
|
124
|
+
path_block.call
|
125
|
+
end
|
126
|
+
|
127
|
+
def permitted?(ability)
|
128
|
+
return true if ability_block.nil?
|
129
|
+
ability_block.call ability
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class ProjectFeature < GlobalFeature
|
134
|
+
attr_accessor :fields
|
110
135
|
|
111
136
|
def initialize
|
112
137
|
self.fields = []
|
@@ -115,6 +140,7 @@ module Houston
|
|
115
140
|
def project_path(project)
|
116
141
|
path_block.call project
|
117
142
|
end
|
143
|
+
alias :path :project_path
|
118
144
|
|
119
145
|
def permitted?(ability, project)
|
120
146
|
return true if ability_block.nil?
|
@@ -122,11 +148,11 @@ module Houston
|
|
122
148
|
end
|
123
149
|
end
|
124
150
|
|
125
|
-
class
|
151
|
+
class FeatureDsl
|
126
152
|
attr_reader :feature
|
127
153
|
|
128
|
-
def initialize
|
129
|
-
@feature =
|
154
|
+
def initialize(feature)
|
155
|
+
@feature = feature
|
130
156
|
end
|
131
157
|
|
132
158
|
def name(value)
|