command_proposal 1.0.0 → 1.0.4
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/README.md +2 -2
- data/app/assets/javascripts/command_proposal/_codemirror.js +1 -1
- data/app/assets/javascripts/command_proposal/_helpers.js +62 -1
- data/app/assets/javascripts/command_proposal/console.js +24 -29
- data/app/assets/javascripts/command_proposal/feed.js +27 -30
- data/app/assets/javascripts/command_proposal/terminal.js +1 -1
- data/app/assets/stylesheets/command_proposal/tables.scss +2 -0
- data/app/assets/stylesheets/command_proposal/terminal.scss +2 -2
- data/app/controllers/command_proposal/runner_controller.rb +7 -2
- data/app/helpers/command_proposal/application_helper.rb +26 -20
- data/app/helpers/command_proposal/icons_helper.rb +1 -0
- data/app/helpers/command_proposal/permissions_helper.rb +12 -1
- data/app/models/command_proposal/comment.rb +1 -1
- data/app/models/command_proposal/iteration.rb +8 -3
- data/app/models/command_proposal/service/proposal_presenter.rb +18 -2
- data/app/models/command_proposal/task.rb +3 -3
- data/app/views/command_proposal/tasks/_console_lines.html.erb +8 -0
- data/app/views/command_proposal/tasks/_console_show.html.erb +3 -3
- data/app/views/command_proposal/tasks/_function_show.html.erb +43 -30
- data/app/views/command_proposal/tasks/_lines.html.erb +4 -8
- data/app/views/command_proposal/tasks/_module_show.html.erb +3 -6
- data/app/views/command_proposal/tasks/_past_iterations_list.html.erb +8 -1
- data/app/views/command_proposal/tasks/_task_detail_table.html.erb +3 -3
- data/app/views/command_proposal/tasks/_task_show.html.erb +31 -29
- data/app/views/command_proposal/tasks/form.html.erb +7 -7
- data/app/views/command_proposal/tasks/index.html.erb +3 -2
- data/lib/command_proposal/configuration.rb +1 -1
- data/lib/command_proposal/services/command_interpreter.rb +3 -1
- data/lib/command_proposal/services/runner.rb +25 -9
- data/lib/command_proposal/services/shut_down.rb +25 -0
- data/lib/command_proposal/version.rb +6 -1
- data/lib/command_proposal.rb +1 -0
- data/lib/generators/command_proposal/install/templates/initializer.rb +12 -5
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4d0b2ef7f0cf5f9044adaab75cb1b28b762086ff77ce659d461b976dd0533e3d
|
4
|
+
data.tar.gz: c4c50205f7e2260a9d7659e7c6938808366c07aa00f0e85511fd1269de768e95
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ee3a6d919ea22545e62488fc0291d476b168b26412433288b6acde185d96bba8e4f9df313d13596faf508f7faabcea1d5d8539e7794a47d897eb117d4fe70e1
|
7
|
+
data.tar.gz: faca82d2f612e2a0ec8b78ac09655d794b9e04b108b93b075b2097aff0dd0959dc75c21410fa7a6be4d71fac93f52428d0e497298a66c71b4b7caa6d6930329a
|
data/README.md
CHANGED
@@ -59,7 +59,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
59
59
|
|
60
60
|
## Contributing
|
61
61
|
|
62
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
62
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/rockster160/command_proposal. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/rockster160/command_proposal/blob/master/CODE_OF_CONDUCT.md).
|
63
63
|
|
64
64
|
## TODO
|
65
65
|
|
@@ -74,4 +74,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
74
74
|
|
75
75
|
## Code of Conduct
|
76
76
|
|
77
|
-
Everyone interacting in the CommandProposal project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
77
|
+
Everyone interacting in the CommandProposal project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/rockster160/command_proposal/blob/master/CODE_OF_CONDUCT.md).
|
@@ -5279,7 +5279,7 @@
|
|
5279
5279
|
}
|
5280
5280
|
|
5281
5281
|
// Possibly split or suppress the update based on the presence
|
5282
|
-
// of
|
5282
|
+
// of readonly spans in its range.
|
5283
5283
|
var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
|
5284
5284
|
if (split) {
|
5285
5285
|
for (var i = split.length - 1; i >= 0; --i)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
function
|
1
|
+
function cmdDocReady(fn) {
|
2
2
|
// see if DOM is already available
|
3
3
|
if (document.readyState === "complete" || document.readyState === "interactive") {
|
4
4
|
// call on next available tick
|
@@ -7,3 +7,64 @@ function docReady(fn) {
|
|
7
7
|
document.addEventListener("DOMContentLoaded", fn)
|
8
8
|
}
|
9
9
|
}
|
10
|
+
|
11
|
+
function HttpClient() {
|
12
|
+
this.request = function(method, url, opts) {
|
13
|
+
var req = new XMLHttpRequest()
|
14
|
+
req.onreadystatechange = function() {
|
15
|
+
if (req.status != 0 && req.readyState == 4 && opts.done) {
|
16
|
+
opts.done(req.responseText, req.status, req)
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
req.open(method, url, true)
|
21
|
+
if (opts.headers) {
|
22
|
+
Object.keys(opts.headers).forEach(function(key) {
|
23
|
+
req.setRequestHeader(key, opts.headers[key])
|
24
|
+
})
|
25
|
+
}
|
26
|
+
req.send(opts.body)
|
27
|
+
}
|
28
|
+
|
29
|
+
this.get = function(url, opts) {
|
30
|
+
this.request("GET", url, opts)
|
31
|
+
}
|
32
|
+
|
33
|
+
this.post = function(url, opts) {
|
34
|
+
this.request("POST", url, opts)
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
function CommandQueue() {
|
39
|
+
this.queue = []
|
40
|
+
this.eventCurrentlyRunning = false
|
41
|
+
this.runningQueue = null
|
42
|
+
|
43
|
+
this.run = function() {
|
44
|
+
if (!this.eventCurrentlyRunning) {
|
45
|
+
if (this.queue.length == 0) {
|
46
|
+
clearInterval(this.runningQueue)
|
47
|
+
this.runningQueue = null
|
48
|
+
return
|
49
|
+
}
|
50
|
+
var nextEvent = this.queue.shift()
|
51
|
+
this.eventCurrentlyRunning = true
|
52
|
+
nextEvent(this)
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
this.add = function(queued_function) {
|
57
|
+
this.queue.push(queued_function)
|
58
|
+
this.process()
|
59
|
+
}
|
60
|
+
|
61
|
+
this.finish = function(ms) {
|
62
|
+
this.eventCurrentlyRunning = false
|
63
|
+
}
|
64
|
+
|
65
|
+
this.process = function() {
|
66
|
+
if (this.runningQueue) { return }
|
67
|
+
var q = this
|
68
|
+
this.runningQueue = setInterval(function() { q.run() }, 1)
|
69
|
+
}
|
70
|
+
}
|
@@ -1,11 +1,11 @@
|
|
1
1
|
// Add tab / shift-tab for changing indents
|
2
|
-
|
2
|
+
cmdDocReady(function() {
|
3
3
|
var cmdconsole = document.querySelector(".cmd-console")
|
4
4
|
|
5
5
|
if (cmdconsole) {
|
6
6
|
var console_input = document.querySelector(".cmd-console .cmd-entry")
|
7
7
|
var lines = document.querySelector(".cmd-console .lines")
|
8
|
-
var queue =
|
8
|
+
var queue = new CommandQueue
|
9
9
|
var history_cmd_idx = undefined
|
10
10
|
var stored_entry = undefined
|
11
11
|
var commands = getPrevCommands()
|
@@ -152,43 +152,38 @@ docReady(function() {
|
|
152
152
|
if (/^[\s\n]*$/.test(line.textContent)) { return }
|
153
153
|
|
154
154
|
commands.push(line.textContent)
|
155
|
-
queue
|
155
|
+
queue.add(function(evt) {
|
156
156
|
$.rails.refreshCSRFTokens()
|
157
157
|
|
158
158
|
var params = { code: line.textContent, task_id: cmdconsole.dataset.task }
|
159
159
|
|
160
|
-
var
|
161
|
-
|
160
|
+
var client = new HttpClient()
|
161
|
+
client.post(cmdconsole.dataset.exeUrl, {
|
162
162
|
headers: {
|
163
163
|
"Content-Type": "application/json",
|
164
164
|
"X-CSRF-Token": $.rails.csrfToken()
|
165
165
|
},
|
166
|
-
body: JSON.stringify(params)
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
166
|
+
body: JSON.stringify(params),
|
167
|
+
done: function(res, status, req) {
|
168
|
+
if (status == 200) {
|
169
|
+
var json = JSON.parse(res)
|
170
|
+
var result = document.createElement("div")
|
171
|
+
result.classList.add("result")
|
172
|
+
|
173
|
+
if (json.error) {
|
174
|
+
result.classList.add("cmd-error")
|
175
|
+
result.textContent = json.error
|
176
|
+
} else {
|
177
|
+
result.textContent = json.result
|
178
|
+
}
|
179
|
+
|
180
|
+
line.appendChild(result)
|
181
|
+
} else {
|
182
|
+
console.log("Error: ", res, req);
|
183
|
+
}
|
184
|
+
evt.finish()
|
176
185
|
}
|
177
186
|
})
|
178
|
-
|
179
|
-
var json = await res
|
180
|
-
|
181
|
-
var result = document.createElement("div")
|
182
|
-
result.classList.add("result")
|
183
|
-
|
184
|
-
if (json.error) {
|
185
|
-
result.classList.add("cmd-error")
|
186
|
-
result.textContent = json.error
|
187
|
-
} else {
|
188
|
-
result.textContent = json.result
|
189
|
-
}
|
190
|
-
|
191
|
-
line.appendChild(result)
|
192
187
|
})
|
193
188
|
}
|
194
189
|
}
|
@@ -1,6 +1,6 @@
|
|
1
|
-
|
1
|
+
cmdDocReady(function() {
|
2
2
|
var terminals = document.querySelectorAll("[data-feed]")
|
3
|
-
var queue =
|
3
|
+
var queue = new CommandQueue
|
4
4
|
var continue_statuses = ["started", "approved", "cancelling"]
|
5
5
|
|
6
6
|
terminals.forEach(function(terminal) {
|
@@ -10,42 +10,39 @@ docReady(function() {
|
|
10
10
|
})
|
11
11
|
|
12
12
|
function pingFeed(terminal) {
|
13
|
-
queue
|
13
|
+
queue.add(function(evt) {
|
14
14
|
$.rails.refreshCSRFTokens()
|
15
15
|
|
16
|
-
var
|
17
|
-
|
16
|
+
var client = new HttpClient()
|
17
|
+
client.get(terminal.dataset.feed, {
|
18
18
|
headers: {
|
19
19
|
"Content-Type": "text/html",
|
20
20
|
"X-CSRF-Token": $.rails.csrfToken()
|
21
21
|
},
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
var json = await res
|
22
|
+
done: function(res, status, req) {
|
23
|
+
if (status == 200) {
|
24
|
+
var json = JSON.parse(res)
|
25
|
+
if (terminal.nextElementSibling && terminal.nextElementSibling.CodeMirror) {
|
26
|
+
terminal.nextElementSibling.CodeMirror.doc.setValue(json.result || "")
|
27
|
+
} else {
|
28
|
+
terminal.innerHTML = json.result_html
|
29
|
+
}
|
30
|
+
document.querySelector("td[data-iteration-status]").innerText = json.status
|
31
|
+
document.querySelector("td[data-iteration-duration]").innerText = json.duration
|
33
32
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
if (document.querySelector(".cancel-btn")) {
|
46
|
-
document.querySelector(".cancel-btn").remove()
|
33
|
+
if (continue_statuses.includes(json.status)) {
|
34
|
+
setTimeout(function() { pingFeed(terminal) }, 1000)
|
35
|
+
} else {
|
36
|
+
if (document.querySelector(".cancel-btn")) {
|
37
|
+
document.querySelector(".cancel-btn").remove()
|
38
|
+
}
|
39
|
+
}
|
40
|
+
} else {
|
41
|
+
console.log("Error: ", res, req);
|
42
|
+
}
|
43
|
+
evt.finish()
|
47
44
|
}
|
48
|
-
}
|
45
|
+
})
|
49
46
|
})
|
50
47
|
}
|
51
48
|
})
|
@@ -18,7 +18,7 @@
|
|
18
18
|
.line {
|
19
19
|
min-height: 18px;
|
20
20
|
counter-increment: line-count;
|
21
|
-
font-size:
|
21
|
+
font-size: 14px;
|
22
22
|
padding-left: 5px;
|
23
23
|
position: relative;
|
24
24
|
|
@@ -26,7 +26,7 @@
|
|
26
26
|
content: "[" counter(line-count) "]>";
|
27
27
|
position: absolute;
|
28
28
|
left: -40px;
|
29
|
-
top:
|
29
|
+
top: 2px;
|
30
30
|
width: 40px;
|
31
31
|
font-size: 10px;
|
32
32
|
vertical-align: middle;
|
@@ -67,13 +67,18 @@ class ::CommandProposal::RunnerController < ::CommandProposal::EngineController
|
|
67
67
|
duration: humanized_duration(@iteration.duration),
|
68
68
|
}.tap do |response|
|
69
69
|
if @iteration.started?
|
70
|
-
response[:endpoint] =
|
70
|
+
response[:endpoint] = runner_url(@task, @iteration)
|
71
71
|
end
|
72
72
|
if @task.console?
|
73
73
|
response[:result_html] = ApplicationController.render(
|
74
|
-
partial: "command_proposal/tasks/
|
74
|
+
partial: "command_proposal/tasks/console_lines",
|
75
75
|
locals: { lines: @task.lines }
|
76
76
|
)
|
77
|
+
else
|
78
|
+
response[:result_html] = ApplicationController.render(
|
79
|
+
partial: "command_proposal/tasks/lines",
|
80
|
+
locals: { lines: @iteration.result }
|
81
|
+
)
|
77
82
|
end
|
78
83
|
end
|
79
84
|
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
module CommandProposal
|
2
2
|
module ApplicationHelper
|
3
|
+
include ActionDispatch::Routing::PolymorphicRoutes
|
4
|
+
include Rails.application.routes.url_helpers
|
3
5
|
# In order to keep the regular app's routes working in the base template, we have to manually
|
4
6
|
# render the engine routes. Built a helper for this because it's long and nasty otherwise.
|
5
|
-
def
|
7
|
+
def cmd_url(*args)
|
6
8
|
return string_path(*args) if args.first.is_a?(String)
|
7
9
|
model_names = [:tasks, :iterations, :comments, :task, :iteration, :comment]
|
8
10
|
host = nil
|
@@ -19,39 +21,43 @@ module CommandProposal
|
|
19
21
|
args << { host: host, port: nil } if host.present?
|
20
22
|
|
21
23
|
begin
|
22
|
-
|
24
|
+
command_proposal_engine.url_for(args.compact)
|
23
25
|
rescue NoMethodError => e
|
24
|
-
raise "Error generating route! Please make sure `default_url_options` are set."
|
26
|
+
raise "Error generating route! Please make sure `config.action_mailer.default_url_options` are set."
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
30
|
+
def cmd_path(*args)
|
31
|
+
args.tap { |arg_list|
|
32
|
+
if arg_list.last.is_a?(Hash)
|
33
|
+
arg_list.last.merge!(only_path: true)
|
34
|
+
else
|
35
|
+
arg_list << { only_path: true }
|
36
|
+
end
|
37
|
+
}
|
38
|
+
|
39
|
+
cmd_url(*args)
|
40
|
+
end
|
41
|
+
|
28
42
|
def string_path(*args)
|
29
|
-
[
|
43
|
+
[command_proposal_engine.command_proposal_tasks_url + args.shift, args.to_param.presence].compact.join("?")
|
30
44
|
end
|
31
45
|
|
32
46
|
# Runner controller doesn't map to a model, so needs special handling
|
33
47
|
def runner_path(task, iteration=nil)
|
34
48
|
if iteration.present?
|
35
|
-
|
49
|
+
command_proposal_engine.command_proposal_task_runner_path(task, iteration)
|
36
50
|
else
|
37
|
-
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def router
|
42
|
-
@@router ||= begin
|
43
|
-
routes = ::CommandProposal::Engine.routes
|
44
|
-
routes.default_url_options = rails_default_url_options
|
45
|
-
routes.url_helpers
|
51
|
+
command_proposal_engine.command_proposal_task_runner_index_path(task)
|
46
52
|
end
|
47
53
|
end
|
48
54
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
+
# Runner controller doesn't map to a model, so needs special handling
|
56
|
+
def runner_url(task, iteration=nil)
|
57
|
+
if iteration.present?
|
58
|
+
command_proposal_engine.command_proposal_task_runner_url(task, iteration)
|
59
|
+
else
|
60
|
+
command_proposal_engine.command_proposal_task_runner_index_url(task)
|
55
61
|
end
|
56
62
|
end
|
57
63
|
end
|
@@ -7,6 +7,7 @@ module CommandProposal
|
|
7
7
|
started: "cmd-icon-grey fa fa-clock-o",
|
8
8
|
cancelling: "cmd-icon-yellow fa fa-hourglass-half",
|
9
9
|
cancelled: "cmd-icon-yellow fa fa-stop-circle",
|
10
|
+
terminated: "cmd-icon-red fa fa-stop-circle",
|
10
11
|
failed: "cmd-icon-red fa fa-times-circle",
|
11
12
|
success: "cmd-icon-green fa fa-check-circle",
|
12
13
|
}[status&.to_sym] || "cmd-icon-yellow fa fa-question"
|
@@ -1,12 +1,14 @@
|
|
1
1
|
module CommandProposal
|
2
2
|
module PermissionsHelper
|
3
3
|
def can_command?(user=command_user)
|
4
|
+
return false unless permitted_to_use?
|
4
5
|
return true unless cmd_config.approval_required?
|
5
6
|
|
6
7
|
command_user.try("#{cmd_config.role_scope}?")
|
7
8
|
end
|
8
9
|
|
9
10
|
def can_approve?(iteration)
|
11
|
+
return false unless permitted_to_use?
|
10
12
|
return true unless cmd_config.approval_required?
|
11
13
|
return if iteration.nil?
|
12
14
|
|
@@ -14,15 +16,24 @@ module CommandProposal
|
|
14
16
|
end
|
15
17
|
|
16
18
|
def has_approval?(task)
|
19
|
+
return false unless permitted_to_use?
|
17
20
|
return true unless cmd_config.approval_required?
|
18
21
|
|
19
22
|
task&.approved?
|
20
23
|
end
|
21
24
|
|
22
25
|
def current_is_author?(iteration)
|
26
|
+
return false unless permitted_to_use?
|
27
|
+
|
23
28
|
command_user&.id == iteration&.requester&.id
|
24
29
|
end
|
25
30
|
|
31
|
+
def permitted_to_use?
|
32
|
+
return true if cmd_config.controller_var.blank?
|
33
|
+
|
34
|
+
command_user&.send("#{cmd_config.role_scope}?")
|
35
|
+
end
|
36
|
+
|
26
37
|
def command_user(user=nil)
|
27
38
|
@command_user ||= begin
|
28
39
|
if user.present?
|
@@ -30,7 +41,7 @@ module CommandProposal
|
|
30
41
|
elsif cmd_config.controller_var.blank?
|
31
42
|
nil
|
32
43
|
else
|
33
|
-
|
44
|
+
send(cmd_config.controller_var)
|
34
45
|
end
|
35
46
|
end
|
36
47
|
end
|
@@ -13,8 +13,8 @@
|
|
13
13
|
|
14
14
|
# ADD: iteration_count?
|
15
15
|
|
16
|
-
|
17
|
-
|
16
|
+
require_dependency "command_proposal/service/external_belong"
|
17
|
+
require_dependency "command_proposal/service/json_wrapper"
|
18
18
|
|
19
19
|
class ::CommandProposal::Iteration < ApplicationRecord
|
20
20
|
self.table_name = :command_proposal_iterations
|
@@ -34,6 +34,7 @@ class ::CommandProposal::Iteration < ApplicationRecord
|
|
34
34
|
cancelling: 4, # Running, but told to stop
|
35
35
|
cancelled: 5,
|
36
36
|
success: 6,
|
37
|
+
terminated: 7, # Closed via server restart
|
37
38
|
}
|
38
39
|
|
39
40
|
delegate :name, to: :task
|
@@ -52,13 +53,17 @@ class ::CommandProposal::Iteration < ApplicationRecord
|
|
52
53
|
end
|
53
54
|
|
54
55
|
def complete?
|
55
|
-
success? || failed? || cancelled?
|
56
|
+
success? || failed? || cancelled? || terminated?
|
56
57
|
end
|
57
58
|
|
58
59
|
def pending?
|
59
60
|
created?
|
60
61
|
end
|
61
62
|
|
63
|
+
def running?
|
64
|
+
started? || cancelling?
|
65
|
+
end
|
66
|
+
|
62
67
|
def duration
|
63
68
|
return unless started_at?
|
64
69
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module CommandProposal
|
2
2
|
module Service
|
3
3
|
class ProposalPresenter
|
4
|
+
include Rails.application.routes.url_helpers
|
4
5
|
include ::CommandProposal::ApplicationHelper
|
5
6
|
attr_accessor :iteration
|
6
7
|
|
@@ -19,8 +20,9 @@ module CommandProposal
|
|
19
20
|
delegate :stopped_at, to: :iteration
|
20
21
|
delegate :duration, to: :iteration
|
21
22
|
|
22
|
-
def url
|
23
|
-
|
23
|
+
def url
|
24
|
+
path = ::CommandProposal::Engine.routes.url_helpers.command_proposal_task_path(@iteration.task)
|
25
|
+
"#{base_path}#{path}"
|
24
26
|
end
|
25
27
|
|
26
28
|
def requester
|
@@ -34,6 +36,20 @@ module CommandProposal
|
|
34
36
|
def type
|
35
37
|
@iteration.session_type
|
36
38
|
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def base_path
|
43
|
+
url_opts = Rails.application.config.action_mailer.default_url_options || {}
|
44
|
+
url_opts.tap do |opts|
|
45
|
+
opts[:protocol] ||= "http"
|
46
|
+
opts[:host] ||= "localhost"
|
47
|
+
opts[:port] ||= 3000
|
48
|
+
end
|
49
|
+
|
50
|
+
port_str = url_opts[:host] == "localhost" ? ":#{url_opts[:port]}" : ""
|
51
|
+
"#{url_opts[:protocol] || 'http'}://#{url_opts[:host]}#{port_str}"
|
52
|
+
end
|
37
53
|
end
|
38
54
|
end
|
39
55
|
end
|
@@ -19,10 +19,10 @@ class ::CommandProposal::Task < ApplicationRecord
|
|
19
19
|
enum session_type: {
|
20
20
|
# Task will have multiple iterations that are all essentially the same just with code changes
|
21
21
|
task: 0,
|
22
|
-
# Console iterations are actually line by line, so order matters
|
23
|
-
console: 1,
|
24
22
|
# Function iterations are much like tasks
|
25
|
-
function:
|
23
|
+
function: 1,
|
24
|
+
# Console iterations are actually line by line, so order matters
|
25
|
+
console: 2,
|
26
26
|
# Modules are included in tasks and not run independently
|
27
27
|
module: 3,
|
28
28
|
}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<% if lines.none? && !(skip_empty ||= false) -%><div class="line"></div><% end
|
2
|
+
-%><% lines.each do |iteration| -%>
|
3
|
+
<div class="line"><%= iteration.code -%><%
|
4
|
+
if iteration.result.present?
|
5
|
+
-%><div class="result"><%= iteration.result %></div><%
|
6
|
+
end
|
7
|
+
-%></div>
|
8
|
+
<% end -%>
|
@@ -7,7 +7,7 @@
|
|
7
7
|
<% when :approved, :started %>
|
8
8
|
<%# Closing > offset to get rid of spacing issues. %>
|
9
9
|
<div class="cmd-console" data-task="<%= @task.id %>" data-exe-url="<%= cmd_path(@task, :task_iterations) %>"
|
10
|
-
><div class="lines"><%= render partial: "
|
10
|
+
><div class="lines"><%= render partial: "console_lines", locals: { lines: @lines, skip_empty: true }
|
11
11
|
%></div
|
12
12
|
><div contenteditable="true" autofocus=true class="line cmd-entry"></div
|
13
13
|
></div>
|
@@ -26,7 +26,7 @@
|
|
26
26
|
<% if @task.first_iteration.success? %>
|
27
27
|
<p>Session closed.</p>
|
28
28
|
<% end %>
|
29
|
-
<div class="cmd-console"
|
29
|
+
<div class="cmd-console" readonly=true><%= render partial: "console_lines", locals: { lines: @lines } %></div>
|
30
30
|
<% end %>
|
31
31
|
|
32
32
|
<% else %>
|
@@ -38,7 +38,7 @@
|
|
38
38
|
<% if @task.first_iteration.success? %>
|
39
39
|
<p>Session closed.</p>
|
40
40
|
<% end %>
|
41
|
-
<div class="cmd-console"
|
41
|
+
<div class="cmd-console" readonly=true data-status="<%= @iteration.status %>" data-feed="<%= runner_path(@task, @iteration) %>"><%= render partial: "console_lines", locals: { lines: @lines } %></div>
|
42
42
|
|
43
43
|
<% end %>
|
44
44
|
<% end %>
|
@@ -1,54 +1,67 @@
|
|
1
1
|
<%= render partial: "task_detail_table" %>
|
2
2
|
|
3
|
-
<textarea class="cmd-terminal" readonly=true><%= @iteration.code %></textarea>
|
4
|
-
|
5
3
|
<%= form_for @iteration, url: cmd_path(@iteration), html: { id: "edit-form-1" } do |f| %>
|
6
|
-
<%
|
4
|
+
<% @iteration.brings.each do |bring_module| %>
|
5
|
+
<% needs_approval_str = " -- Module needs approval before running Task" unless bring_module.approved? %>
|
6
|
+
<%= link_to "Module: #{bring_module.name}#{needs_approval_str}", cmd_path("#{bring_module.friendly_id}") %>
|
7
|
+
<br>
|
8
|
+
<% end %>
|
9
|
+
<% if @task.approved? && @iteration.params.any? %>
|
7
10
|
<div class="form-field">
|
8
11
|
<% @iteration.params.each do |param_key| %>
|
9
|
-
<label for="
|
10
|
-
<
|
12
|
+
<label for="command_proposal_iteration[args][<%= param_key %>]"><%= param_key %></label>
|
13
|
+
<br>
|
14
|
+
<input type="text" name="command_proposal_iteration[args][<%= param_key %>]" value="">
|
15
|
+
<br>
|
11
16
|
<% end %>
|
12
17
|
</div>
|
13
18
|
<% end %>
|
14
19
|
|
15
|
-
<% if @iteration.approved_at? %>
|
20
|
+
<% if @iteration.approved_at? && !params.key?(:iteration) %>
|
16
21
|
<%= f.hidden_field :command, value: :run %>
|
17
22
|
<%= f.submit "Execute" %>
|
23
|
+
<br>
|
18
24
|
<% end %>
|
19
25
|
<% end %>
|
20
26
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
<% end %>
|
27
|
+
<% unless params.key?(:iteration) %>
|
28
|
+
<%= form_for @iteration, url: cmd_path(@iteration), html: { id: "edit-form-2" } do |f| %>
|
29
|
+
<% if current_is_author?(@iteration) %>
|
25
30
|
|
26
|
-
|
31
|
+
<% case @iteration.status&.to_sym %>
|
32
|
+
<% when :created, :failed, :cancelled, :terminated %>
|
33
|
+
<% unless @iteration.approved_at? %>
|
34
|
+
<% if can_approve?(@iteration) %>
|
35
|
+
<%= f.hidden_field :command, value: :approve %>
|
36
|
+
<%= f.submit "Approve!" %>
|
37
|
+
<% else %>
|
38
|
+
<%= f.hidden_field :command, value: :request %>
|
39
|
+
<%= f.submit "Request Access" %>
|
40
|
+
<% end %>
|
41
|
+
<% end %>
|
42
|
+
<% when :started %>
|
43
|
+
<%= f.hidden_field :command, value: :cancel %>
|
44
|
+
<%= f.submit "CANCEL!", class: "cancel-btn", data: { confirm: "WARNING: Cancelling a command mid-process can be dangerous. Any processes that have already run will not be rolled back. Do you wish to continue?" } %>
|
45
|
+
<% end %>
|
46
|
+
|
47
|
+
<% elsif can_approve?(@iteration) %>
|
27
48
|
|
28
|
-
|
29
|
-
|
30
|
-
<%
|
49
|
+
<% if @iteration.approved? %>
|
50
|
+
<p>Approved. Ready to run.</p>
|
51
|
+
<% elsif @iteration.pending? %>
|
31
52
|
<%= f.hidden_field :command, value: :approve %>
|
32
53
|
<%= f.submit "Approve!" %>
|
33
|
-
<% else %>
|
34
|
-
<%= f.hidden_field :command, value: :request %>
|
35
|
-
<%= f.submit "Request Access" %>
|
36
54
|
<% end %>
|
37
|
-
<% when :started %>
|
38
|
-
<%= f.hidden_field :command, value: :cancel %>
|
39
|
-
<%= f.submit "CANCEL!", class: "cancel-btn", data: { confirm: "WARNING: Cancelling a command mid-process can be dangerous. Any processes that have already run will not be rolled back. Do you wish to continue?" } %>
|
40
|
-
<% end %>
|
41
55
|
|
42
|
-
<% elsif can_approve?(@iteration) %>
|
43
|
-
|
44
|
-
<% if @iteration.approved? %>
|
45
|
-
<p>Approved. Ready to run.</p>
|
46
|
-
<% elsif @iteration.pending? %>
|
47
|
-
<%= f.hidden_field :command, value: :approve %>
|
48
|
-
<%= f.submit "Approve!" %>
|
49
56
|
<% end %>
|
50
|
-
|
51
57
|
<% end %>
|
52
58
|
<% end %>
|
53
59
|
|
54
|
-
<%
|
60
|
+
<% if @iteration&.started_at? %>
|
61
|
+
<div class="cmd-console" readonly=true data-status="<%= @iteration.status %>" data-feed="<%= runner_path(@task, @iteration) %>"><%= render partial: "lines", locals: { lines: @iteration&.result } %></div>
|
62
|
+
<% end %>
|
63
|
+
|
64
|
+
<br>
|
65
|
+
<textarea class="cmd-terminal" readonly=true><%= @iteration.code %></textarea>
|
66
|
+
|
67
|
+
<%= render partial: "past_iterations_list" %>
|
@@ -1,8 +1,4 @@
|
|
1
|
-
<% if lines.
|
2
|
-
-%><% lines.each do |
|
3
|
-
|
4
|
-
|
5
|
-
-%><div class="result"><%= iteration.result %></div><%
|
6
|
-
end
|
7
|
-
-%></div>
|
8
|
-
<% end -%>
|
1
|
+
<% if lines.blank? && !(skip_empty ||= false) -%><div class="line"></div><% end
|
2
|
+
-%><% lines.split("\n").each do |line|
|
3
|
+
-%><div class="line"><%= line -%></div><%
|
4
|
+
end -%>
|
@@ -12,15 +12,11 @@
|
|
12
12
|
<%= f.hidden_field :command, value: :request %>
|
13
13
|
<%= f.submit "Request Access" %>
|
14
14
|
<% end %>
|
15
|
-
<% when :approved %>
|
16
|
-
<p>Include this module in other commands by using <code>bring :<%= @task.friendly_id %></code> at the top of the file.</p>
|
17
15
|
<% end %>
|
18
16
|
|
19
17
|
<% elsif can_approve?(@iteration) %>
|
20
18
|
|
21
|
-
<% if @iteration.
|
22
|
-
<p>Approved. Ready to include.</p>
|
23
|
-
<% elsif @iteration.pending? %>
|
19
|
+
<% if @iteration.pending? %>
|
24
20
|
<%= f.hidden_field :command, value: :approve %>
|
25
21
|
<%= f.submit "Approve!" %>
|
26
22
|
<% end %>
|
@@ -28,6 +24,7 @@
|
|
28
24
|
<% end %>
|
29
25
|
<% end %>
|
30
26
|
|
27
|
+
<p>Include this module in other commands by using <code>bring :<%= @task.friendly_id %></code> at the top of the file.</p>
|
31
28
|
<textarea class="cmd-terminal" readonly=true><%= @iteration&.code %></textarea>
|
32
29
|
|
33
|
-
|
30
|
+
<%= render partial: "past_iterations_list" %>
|
@@ -7,9 +7,16 @@
|
|
7
7
|
<!-- <th>Diff</th> -->
|
8
8
|
</thead>
|
9
9
|
<tbody>
|
10
|
+
<% primary_iteration = @task.primary_iteration %>
|
10
11
|
<% @task.iterations.where.not(id: @iteration&.id).order(created_at: :desc).each do |iteration| %>
|
11
12
|
<tr>
|
12
|
-
<td
|
13
|
+
<td>
|
14
|
+
<% if iteration.id == primary_iteration&.id %>
|
15
|
+
<%= link_to iteration.created_at.strftime("%b %-d, %Y at %H:%M"), cmd_path(@task) %>
|
16
|
+
<% else %>
|
17
|
+
<%= link_to iteration.created_at.strftime("%b %-d, %Y at %H:%M"), cmd_path(@task, iteration: iteration.id) %>
|
18
|
+
<% end %>
|
19
|
+
</td>
|
13
20
|
<td><%= iteration.status.capitalize %></td>
|
14
21
|
<!-- <td><%= iteration.comments.count %></td> -->
|
15
22
|
<!-- <td><%= link_to "Diff", cmd_path(@task, iteration: @iteration.id, diff: iteration.id) %></td> -->
|
@@ -8,8 +8,8 @@
|
|
8
8
|
</thead>
|
9
9
|
<tbody>
|
10
10
|
<tr>
|
11
|
-
<td><%= @iteration&.requester_name %></td>
|
12
|
-
<td><%= @iteration&.approver_name %></td>
|
11
|
+
<td><%= @iteration&.requester_name.presence || "ID: #{@iteration&.requester_id}" if @iteration&.requester_id.present? %></td>
|
12
|
+
<td><%= @iteration&.approver_name.presence || "ID: #{@iteration&.approver_id}" if @iteration&.approver_id.present? %></td>
|
13
13
|
<td><%= @iteration&.started_at&.strftime("%b %-d '%y, %-l:%M%P") %></td>
|
14
14
|
<td data-iteration-status><%= @iteration&.status&.capitalize %></td>
|
15
15
|
<td data-iteration-duration><%= humanized_duration(@iteration&.duration) %></td>
|
@@ -22,7 +22,7 @@
|
|
22
22
|
<tbody>
|
23
23
|
<% @iteration.args.each do |arg_k, arg_v| %>
|
24
24
|
<tr>
|
25
|
-
<th><%= arg_k %></th>
|
25
|
+
<th class="cmd-text-right"><%= arg_k %></th>
|
26
26
|
<td><%= arg_v %></td>
|
27
27
|
</tr>
|
28
28
|
<% end %>
|
@@ -8,43 +8,45 @@
|
|
8
8
|
<br>
|
9
9
|
<% end %>
|
10
10
|
|
11
|
-
<%
|
12
|
-
|
13
|
-
|
11
|
+
<% unless params.key?(:iteration) %>
|
12
|
+
<% if current_is_author?(@iteration) %>
|
13
|
+
|
14
|
+
<% case @iteration.status&.to_sym %>
|
15
|
+
<% when :created, :cancelled, :terminated %>
|
16
|
+
<% if can_approve?(@iteration) %>
|
17
|
+
<%= f.hidden_field :command, value: :approve %>
|
18
|
+
<%= f.submit "Approve!" %>
|
19
|
+
<% else %>
|
20
|
+
<%= f.hidden_field :command, value: :request %>
|
21
|
+
<%= f.submit "Request Access" %>
|
22
|
+
<% end %>
|
23
|
+
<% when :failed %>
|
24
|
+
<p>Edit task to attempt to run again.</p>
|
25
|
+
<% when :approved %>
|
26
|
+
<%= f.hidden_field :command, value: :run %>
|
27
|
+
<%= f.submit "Execute" %>
|
28
|
+
<% when :started %>
|
29
|
+
<%= f.hidden_field :command, value: :cancel %>
|
30
|
+
<%= f.submit "CANCEL!", class: "cancel-btn", data: { confirm: "WARNING: Cancelling a command mid-process can be dangerous. Any processes that have already run will not be rolled back. Do you wish to continue?" } %>
|
31
|
+
<% when :success %>
|
32
|
+
<%= f.hidden_field :command, value: :request %>
|
33
|
+
<%= f.submit "Request Re-Run Access" %>
|
34
|
+
<% end %>
|
14
35
|
|
15
|
-
|
36
|
+
<% elsif can_approve?(@iteration) %>
|
16
37
|
|
17
|
-
|
18
|
-
|
19
|
-
<%
|
38
|
+
<% if @iteration.approved? %>
|
39
|
+
<p>Approved. Ready to run.</p>
|
40
|
+
<% elsif @iteration.pending? %>
|
20
41
|
<%= f.hidden_field :command, value: :approve %>
|
21
42
|
<%= f.submit "Approve!" %>
|
22
|
-
<% else %>
|
23
|
-
<%= f.hidden_field :command, value: :request %>
|
24
|
-
<%= f.submit "Request Access" %>
|
25
43
|
<% end %>
|
26
|
-
<% when :failed %>
|
27
|
-
<p>Edit task to attempt to run again.</p>
|
28
|
-
<% when :approved %>
|
29
|
-
<%= f.hidden_field :command, value: :run %>
|
30
|
-
<%= f.submit "Execute" %>
|
31
|
-
<% when :started %>
|
32
|
-
<%= f.hidden_field :command, value: :cancel %>
|
33
|
-
<%= f.submit "CANCEL!", class: "cancel-btn", data: { confirm: "WARNING: Cancelling a command mid-process can be dangerous. Any processes that have already run will not be rolled back. Do you wish to continue?" } %>
|
34
|
-
<% when :success %>
|
35
|
-
<%= f.hidden_field :command, value: :request %>
|
36
|
-
<%= f.submit "Request Re-Run Access" %>
|
37
|
-
<% end %>
|
38
|
-
|
39
|
-
<% elsif can_approve?(@iteration) %>
|
40
44
|
|
41
|
-
<% if @iteration.approved? %>
|
42
|
-
<p>Approved. Ready to run.</p>
|
43
|
-
<% elsif @iteration.pending? %>
|
44
|
-
<%= f.hidden_field :command, value: :approve %>
|
45
|
-
<%= f.submit "Approve!" %>
|
46
45
|
<% end %>
|
46
|
+
<% end %>
|
47
47
|
|
48
|
+
<% if @iteration&.started_at? %>
|
49
|
+
<div class="cmd-console" readonly=true data-status="<%= @iteration.status %>" data-feed="<%= runner_path(@task, @iteration) %>"><%= render partial: "lines", locals: { lines: @iteration&.result } %></div>
|
48
50
|
<% end %>
|
49
51
|
<% end %>
|
50
52
|
<% end %>
|
@@ -9,7 +9,7 @@
|
|
9
9
|
|
10
10
|
<%= form_for(@task, url: cmd_path(@task)) do |f| %>
|
11
11
|
<div class="cmd-field">
|
12
|
-
<%= f.text_field :name, placeholder: "Title", class: "cmd-input" %>
|
12
|
+
<%= f.text_field :name, placeholder: "Title", class: "cmd-input", required: true %>
|
13
13
|
</div>
|
14
14
|
|
15
15
|
<div class="cmd-field">
|
@@ -24,18 +24,18 @@
|
|
24
24
|
Task - Batch of code that typically only needs to be executed once. Requires approval for every new run.
|
25
25
|
<% end %>
|
26
26
|
</p>
|
27
|
-
<p>
|
28
|
-
<%= f.radio_button :session_type, :console %>
|
29
|
-
<%= f.label :session_type, value: :console do %>
|
30
|
-
Console - Starts a session that allows line-by-line commands to be executed. Requires approval only to begin session.
|
31
|
-
<% end %>
|
32
|
-
</p>
|
33
27
|
<p>
|
34
28
|
<%= f.radio_button :session_type, :function %>
|
35
29
|
<%= f.label :session_type, value: :function do %>
|
36
30
|
Function - Reusable batch of code, can accept arguments. Only requires approval on code changes.
|
37
31
|
<% end %>
|
38
32
|
</p>
|
33
|
+
<p>
|
34
|
+
<%= f.radio_button :session_type, :console %>
|
35
|
+
<%= f.label :session_type, value: :console do %>
|
36
|
+
Console - Starts a session that allows line-by-line commands to be executed. Requires approval only to begin session.
|
37
|
+
<% end %>
|
38
|
+
</p>
|
39
39
|
<p>
|
40
40
|
<%= f.radio_button :session_type, :module %>
|
41
41
|
<%= f.label :session_type, value: :module do %>
|
@@ -1,7 +1,8 @@
|
|
1
1
|
<div class="cmd-wrapper">
|
2
|
-
<%= link_to "New Command", cmd_path(:new, :task, session_type: params[:filter] || :task) %> <br>
|
2
|
+
<%= link_to "New #{params[:filter].presence&.capitalize || 'Command'}", cmd_path(:new, :task, session_type: params[:filter] || :task) %> <br>
|
3
3
|
<br>
|
4
|
-
|
4
|
+
<%= link_to "All", cmd_path(:tasks, current_params.except(:filter)), class: "cmd-tab #{:active unless params.key?(:filter)}"
|
5
|
+
%><% ::CommandProposal::Task.session_types.each_with_index do |(session_type, _session_enum), idx| %><%=
|
5
6
|
selected = params[:filter] == session_type.to_s
|
6
7
|
# Offset closing RB tags to fix spacing issues
|
7
8
|
link_to session_type.capitalize, toggled_param(filter: session_type), class: "cmd-tab #{:active if selected}"
|
@@ -25,7 +25,6 @@ module CommandProposal
|
|
25
25
|
when :approve then command_approve
|
26
26
|
when :run then command_run
|
27
27
|
when :cancel then command_cancel
|
28
|
-
when :cancel then command_cancel
|
29
28
|
when :close then command_close
|
30
29
|
end
|
31
30
|
|
@@ -78,6 +77,9 @@ module CommandProposal
|
|
78
77
|
return if @iteration.complete?
|
79
78
|
|
80
79
|
@iteration.update(status: :cancelling)
|
80
|
+
return if ::CommandProposal.sessions.key?("task:#{@task.id}")
|
81
|
+
|
82
|
+
::CommandProposal::Services::ShutDown.terminate(@iteration)
|
81
83
|
end
|
82
84
|
|
83
85
|
def command_close
|
@@ -4,23 +4,33 @@ module CommandProposal
|
|
4
4
|
attr_accessor :session
|
5
5
|
# Add expiration and things like that...
|
6
6
|
|
7
|
+
def self.execute(friendly_id)
|
8
|
+
task = ::CommandProposal::Task.find_by!(friendly_id: friendly_id)
|
9
|
+
|
10
|
+
new.execute(task.primary_iteration)
|
11
|
+
end
|
12
|
+
|
7
13
|
def initialize
|
8
14
|
@session = session
|
9
15
|
end
|
10
16
|
|
11
|
-
def execute(iteration
|
17
|
+
def execute(iteration)
|
12
18
|
@iteration = iteration
|
13
|
-
@inline = inline
|
14
19
|
prepare
|
15
20
|
|
16
21
|
run
|
17
22
|
|
18
23
|
complete
|
24
|
+
proposal = ::CommandProposal::Service::ProposalPresenter.new(@iteration)
|
19
25
|
@iteration = nil
|
26
|
+
proposal
|
20
27
|
end
|
21
28
|
|
22
|
-
def quick_run(
|
23
|
-
|
29
|
+
def quick_run(friendly_id)
|
30
|
+
task = ::CommandProposal::Task.module.find_by!(friendly_id: friendly_id)
|
31
|
+
iteration = task&.primary_iteration
|
32
|
+
|
33
|
+
raise CommandProposal::Error, ":#{friendly_id} does not have approval to run." unless iteration&.approved?
|
24
34
|
|
25
35
|
@session.eval(iteration.code)
|
26
36
|
end
|
@@ -49,14 +59,16 @@ module CommandProposal
|
|
49
59
|
stored_stdout = $stdout
|
50
60
|
$stdout = StringIO.new
|
51
61
|
result = nil # Init var for scope
|
62
|
+
status = nil
|
52
63
|
|
53
64
|
running_thread = Thread.new do
|
54
65
|
begin
|
55
66
|
# Run bring functions in here so we can capture any string outputs
|
56
67
|
# OR! Run the full runner and instead of saving to an iteration, return the string for prepending here
|
57
68
|
result = @session.eval("_ = (#{@iteration.code})").inspect # rubocop:disable Security/Eval - Eval is scary, but in this case it's exactly what we need.
|
69
|
+
status = :success
|
58
70
|
rescue Exception => e # rubocop:disable Lint/RescueException - Yes, rescue full Exception so that we can catch typos in evals as well
|
59
|
-
|
71
|
+
status = :failed
|
60
72
|
|
61
73
|
result = results_from_exception(e)
|
62
74
|
end
|
@@ -69,9 +81,12 @@ module CommandProposal
|
|
69
81
|
@iteration.update(result: $stdout.try(:string).dup)
|
70
82
|
end
|
71
83
|
|
72
|
-
|
84
|
+
if @iteration.cancelling?
|
85
|
+
running_thread.exit
|
86
|
+
status = :cancelled
|
87
|
+
end
|
73
88
|
|
74
|
-
sleep
|
89
|
+
sleep 0.4
|
75
90
|
end
|
76
91
|
|
77
92
|
output = $stdout.try(:string)
|
@@ -79,11 +94,12 @@ module CommandProposal
|
|
79
94
|
# Not using presence because we want to maintain other empty objects such as [] and {}
|
80
95
|
|
81
96
|
$stdout = stored_stdout
|
97
|
+
@iteration.status = status
|
82
98
|
@iteration.result = [output, "#{result || 'nil'}"].compact.join("\n")
|
83
99
|
end
|
84
100
|
|
85
101
|
def bring_function
|
86
|
-
"def bring(*func_names); func_names.each { |f| self.quick_run(
|
102
|
+
"def bring(*func_names); func_names.each { |f| self.quick_run(f) }; end"
|
87
103
|
end
|
88
104
|
|
89
105
|
def complete
|
@@ -91,7 +107,7 @@ module CommandProposal
|
|
91
107
|
if @iteration.cancelling? || @iteration.cancelled?
|
92
108
|
@iteration.result += "\n\n~~~~~ CANCELLED ~~~~~"
|
93
109
|
@iteration.status = :cancelled
|
94
|
-
elsif @iteration.failed
|
110
|
+
elsif @iteration.status&.to_sym == :failed
|
95
111
|
# No-op
|
96
112
|
else
|
97
113
|
@iteration.status = :success
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module CommandProposal
|
2
|
+
module Services
|
3
|
+
module ShutDown
|
4
|
+
module_function
|
5
|
+
|
6
|
+
def reset_all
|
7
|
+
pending = ::CommandProposal::Iteration.where(status: [:started, :cancelling])
|
8
|
+
pending.find_each do |iteration|
|
9
|
+
terminate(iteration)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def terminate(iteration)
|
14
|
+
return unless iteration.running?
|
15
|
+
|
16
|
+
terminated_result = iteration.result + "\n\n~~~~~ TERMINATED ~~~~~"
|
17
|
+
iteration.update(
|
18
|
+
status: :terminated,
|
19
|
+
result: terminated_result,
|
20
|
+
completed_at: Time.current
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/command_proposal.rb
CHANGED
@@ -1,4 +1,9 @@
|
|
1
1
|
::CommandProposal.configure do |config|
|
2
|
+
# Determines if a user needs a different user to approve their commands.
|
3
|
+
# Defaults to true, the recommended value.
|
4
|
+
# However, disabling in development could help with testing.
|
5
|
+
# config.approval_required = !Rails.env.development?
|
6
|
+
|
2
7
|
# Change if your base user class has a different model name
|
3
8
|
config.user_class_name = "User"
|
4
9
|
|
@@ -8,6 +13,8 @@
|
|
8
13
|
# Scope for your user class that determines users who are permitted to interact with commands
|
9
14
|
# It is highly recommended to make this very exclusive, as any users in this scope will be able
|
10
15
|
# to interact with your database directly.
|
16
|
+
# Expected that the class will respond to `#{role_scope}` and
|
17
|
+
# instances of the class respond to `#{role_scope}?`
|
11
18
|
config.role_scope = :admin
|
12
19
|
|
13
20
|
# Method called to display a user's name
|
@@ -34,14 +41,14 @@
|
|
34
41
|
|
35
42
|
# Called when a command is proposed for review
|
36
43
|
config.proposal_callback = Proc.new { |proposal|
|
37
|
-
# Slack.notify("#{proposal.requester} has proposed #{proposal.name}.\n
|
44
|
+
# Slack.notify("#{proposal.requester} has proposed #{proposal.name}.\n<#{proposal.url}|Click Here> to view this proposal and approve.")
|
38
45
|
}
|
39
46
|
# Called when a command runs and completes successfully
|
40
|
-
config.success_callback = Proc.new { |
|
41
|
-
# Slack.notify("The task #{proposal.name} has completed in #{proposal.duration}s.\n
|
47
|
+
config.success_callback = Proc.new { |proposal|
|
48
|
+
# Slack.notify("The task #{proposal.name} has completed in #{proposal.duration}s.\n<#{proposal.url}|Click Here> to view the results.")
|
42
49
|
}
|
43
50
|
# Called when a command runs but fails to complete
|
44
|
-
config.failed_callback = Proc.new { |
|
45
|
-
# Slack.notify("The task #{proposal.name} has failed!\n
|
51
|
+
config.failed_callback = Proc.new { |proposal|
|
52
|
+
# Slack.notify("The task #{proposal.name} has failed!\n<#{proposal.url}|Click Here> to see what went wrong.")
|
46
53
|
}
|
47
54
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: command_proposal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rocco Nicholls
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-08-
|
11
|
+
date: 2021-08-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -116,6 +116,7 @@ files:
|
|
116
116
|
- app/models/command_proposal/service/json_wrapper.rb
|
117
117
|
- app/models/command_proposal/service/proposal_presenter.rb
|
118
118
|
- app/models/command_proposal/task.rb
|
119
|
+
- app/views/command_proposal/tasks/_console_lines.html.erb
|
119
120
|
- app/views/command_proposal/tasks/_console_show.html.erb
|
120
121
|
- app/views/command_proposal/tasks/_function_show.html.erb
|
121
122
|
- app/views/command_proposal/tasks/_lines.html.erb
|
@@ -133,6 +134,7 @@ files:
|
|
133
134
|
- lib/command_proposal/engine.rb
|
134
135
|
- lib/command_proposal/services/command_interpreter.rb
|
135
136
|
- lib/command_proposal/services/runner.rb
|
137
|
+
- lib/command_proposal/services/shut_down.rb
|
136
138
|
- lib/command_proposal/version.rb
|
137
139
|
- lib/generators/command_proposal/install/install_generator.rb
|
138
140
|
- lib/generators/command_proposal/install/templates/initializer.rb
|