command_proposal 1.0.11 → 1.0.12
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/app/assets/javascripts/command_proposal/console.js +27 -1
- data/app/assets/javascripts/command_proposal/feed.js +1 -0
- data/app/assets/javascripts/command_proposal/terminal.js +3 -3
- data/app/assets/stylesheets/command_proposal/components.scss +6 -0
- data/app/assets/stylesheets/command_proposal/terminal.scss +2 -7
- data/app/controllers/command_proposal/iterations_controller.rb +0 -1
- data/app/controllers/command_proposal/runner_controller.rb +1 -0
- data/app/controllers/command_proposal/tasks_controller.rb +2 -0
- data/app/helpers/command_proposal/permissions_helper.rb +7 -3
- data/app/models/command_proposal/iteration.rb +7 -0
- data/app/models/command_proposal/task.rb +12 -2
- data/app/views/command_proposal/tasks/_console_lines.html.erb +16 -6
- data/app/views/command_proposal/tasks/_lines.html.erb +5 -1
- data/app/views/command_proposal/tasks/_task_detail_table.html.erb +1 -1
- data/lib/command_proposal/services/command_interpreter.rb +3 -2
- data/lib/command_proposal/services/runner.rb +2 -1
- data/lib/command_proposal/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab0271a9e0e80bf0df78d8284cc8e13d292ef2254ab57180b1434f50b351dcb7
|
4
|
+
data.tar.gz: 21b077ee6cfa9ede8ea83315523297ff9268bf5236de797c9f3acae9f1ef3910
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 52a46842e394c99d8dbb4527c222ab93e93dc437992996b3b9af3850a9bc6571bac4e472268c7f821d6b541c17074d21954d0b18e5e15d2109141a4f04179491
|
7
|
+
data.tar.gz: eb670b929efd2d6c612997e569276fac67aa494325db38d357a5589496bdba19b0d5335d68696d715022b45e9ed13eae4e2245d4ddc12f53371ad3d611e4987b
|
@@ -141,6 +141,16 @@ cmdDocReady(function() {
|
|
141
141
|
line.textContent = console_input.textContent
|
142
142
|
|
143
143
|
console_input.textContent = ""
|
144
|
+
|
145
|
+
var result = document.createElement("div")
|
146
|
+
result.classList.add("result")
|
147
|
+
|
148
|
+
var spinner = document.createElement("i")
|
149
|
+
spinner.className = "fa fa-circle-o-notch fa-spin cmd-icon-grey"
|
150
|
+
result.append(spinner)
|
151
|
+
|
152
|
+
line.appendChild(result)
|
153
|
+
|
144
154
|
lines.appendChild(line)
|
145
155
|
stored_entry = undefined
|
146
156
|
history_cmd_idx = undefined
|
@@ -167,6 +177,8 @@ cmdDocReady(function() {
|
|
167
177
|
done: function(res, status, req) {
|
168
178
|
if (status == 200) {
|
169
179
|
var json = JSON.parse(res)
|
180
|
+
line.querySelector(".result").remove()
|
181
|
+
|
170
182
|
var result = document.createElement("div")
|
171
183
|
result.classList.add("result")
|
172
184
|
|
@@ -174,7 +186,21 @@ cmdDocReady(function() {
|
|
174
186
|
result.classList.add("cmd-error")
|
175
187
|
result.textContent = json.error
|
176
188
|
} else {
|
177
|
-
|
189
|
+
var truncate = 2000
|
190
|
+
if (json.result.length > truncate-3) {
|
191
|
+
result.textContent = json.result.slice(0, truncate-3) + "..."
|
192
|
+
var encoded = encodeURIComponent(json.result)
|
193
|
+
|
194
|
+
var download = document.createElement("a")
|
195
|
+
download.classList.add("cmd-truncated-download")
|
196
|
+
download.setAttribute("href", "data:application/txt," + encoded)
|
197
|
+
download.setAttribute("download", "result.txt")
|
198
|
+
download.textContent = "Output truncated. Click here to download full result."
|
199
|
+
|
200
|
+
line.insertAdjacentElement("afterend", download)
|
201
|
+
} else {
|
202
|
+
result.textContent = json.result
|
203
|
+
}
|
178
204
|
}
|
179
205
|
|
180
206
|
line.appendChild(result)
|
@@ -29,6 +29,7 @@ cmdDocReady(function() {
|
|
29
29
|
}
|
30
30
|
document.querySelector("td[data-iteration-status]").innerText = json.status
|
31
31
|
document.querySelector("td[data-iteration-duration]").innerText = json.duration
|
32
|
+
document.querySelector("td[data-iteration-started]").innerText = json.started_at
|
32
33
|
|
33
34
|
if (continue_statuses.includes(json.status)) {
|
34
35
|
setTimeout(function() { pingFeed(terminal) }, 1000)
|
@@ -4,13 +4,13 @@ cmdDocReady(function() {
|
|
4
4
|
function setReadOnlyUI(cm) {
|
5
5
|
cm.getWrapperElement().classList.add("CodeMirror-readonly")
|
6
6
|
|
7
|
-
pencil = document.createElement("i")
|
7
|
+
var pencil = document.createElement("i")
|
8
8
|
pencil.className = "fa fa-pencil fa-stack-1x"
|
9
9
|
|
10
|
-
ban = document.createElement("i")
|
10
|
+
var ban = document.createElement("i")
|
11
11
|
ban.className = "fa fa-ban fa-stack-2x fa-flip-horizontal"
|
12
12
|
|
13
|
-
stack = document.createElement("span")
|
13
|
+
var stack = document.createElement("span")
|
14
14
|
stack.className = "fa-stack fa-2x"
|
15
15
|
stack.append(pencil)
|
16
16
|
stack.append(ban)
|
@@ -6,14 +6,12 @@
|
|
6
6
|
background: #112435;
|
7
7
|
padding: 10px 20px;
|
8
8
|
padding-left: 40px;
|
9
|
-
overflow-x: auto;
|
10
|
-
overflow-y: visible;
|
11
9
|
color: lime;
|
12
10
|
font-family: monospace;
|
13
11
|
text-align: left;
|
14
|
-
white-space: nowrap;
|
15
|
-
white-space: pre;
|
16
12
|
counter-reset: line-count;
|
13
|
+
white-space: pre-wrap;
|
14
|
+
word-wrap: break-word;
|
17
15
|
|
18
16
|
.line {
|
19
17
|
min-height: 18px;
|
@@ -67,6 +65,3 @@
|
|
67
65
|
color: black;
|
68
66
|
}
|
69
67
|
}
|
70
|
-
.cmd-terminal {
|
71
|
-
white-space: pre-wrap;
|
72
|
-
}
|
@@ -28,7 +28,6 @@ class ::CommandProposal::IterationsController < ::CommandProposal::EngineControl
|
|
28
28
|
|
29
29
|
return error!("Session has expired. Please start a new session.") if runner.nil?
|
30
30
|
|
31
|
-
|
32
31
|
@task.user = command_user # Separate from update to ensure it's set first
|
33
32
|
@task.update(code: params[:code]) # Creates a new iteration
|
34
33
|
@iteration = @task.current_iteration
|
@@ -65,6 +65,7 @@ class ::CommandProposal::RunnerController < ::CommandProposal::EngineController
|
|
65
65
|
result: @iteration.result,
|
66
66
|
status: @iteration.status,
|
67
67
|
duration: humanized_duration(@iteration.duration),
|
68
|
+
started_at: @iteration.started_at&.strftime("%b %-d '%y, %-l:%M%P")
|
68
69
|
}.tap do |response|
|
69
70
|
if @iteration.started?
|
70
71
|
response[:endpoint] = runner_url(@task, @iteration)
|
@@ -51,6 +51,7 @@ class ::CommandProposal::TasksController < ::CommandProposal::EngineController
|
|
51
51
|
def create
|
52
52
|
@task = ::CommandProposal::Task.new(task_params.except(:code))
|
53
53
|
@task.user = command_user
|
54
|
+
@task.skip_approval = true unless approval_required?
|
54
55
|
|
55
56
|
# Cannot create the iteration until the task is created, so save then update
|
56
57
|
if @task.save && @task.update(task_params)
|
@@ -69,6 +70,7 @@ class ::CommandProposal::TasksController < ::CommandProposal::EngineController
|
|
69
70
|
def update
|
70
71
|
@task = ::CommandProposal::Task.find_by!(friendly_id: params[:id])
|
71
72
|
@task.user = command_user
|
73
|
+
@task.skip_approval = true unless approval_required?
|
72
74
|
|
73
75
|
if @task.update(task_params)
|
74
76
|
redirect_to cmd_path(@task)
|
@@ -2,14 +2,14 @@ module CommandProposal
|
|
2
2
|
module PermissionsHelper
|
3
3
|
def can_command?(user=command_user)
|
4
4
|
return false unless permitted_to_use?
|
5
|
-
return true unless
|
5
|
+
return true unless approval_required?
|
6
6
|
|
7
7
|
command_user.try("#{cmd_config.role_scope}?")
|
8
8
|
end
|
9
9
|
|
10
10
|
def can_approve?(iteration)
|
11
11
|
return false unless permitted_to_use?
|
12
|
-
return true unless
|
12
|
+
return true unless approval_required?
|
13
13
|
return if iteration.nil?
|
14
14
|
|
15
15
|
command_user.try("#{cmd_config.role_scope}?") && !current_is_author?(iteration)
|
@@ -17,11 +17,15 @@ module CommandProposal
|
|
17
17
|
|
18
18
|
def has_approval?(task)
|
19
19
|
return false unless permitted_to_use?
|
20
|
-
return true unless
|
20
|
+
return true unless approval_required?
|
21
21
|
|
22
22
|
task&.approved?
|
23
23
|
end
|
24
24
|
|
25
|
+
def approval_required?
|
26
|
+
cmd_config.approval_required?
|
27
|
+
end
|
28
|
+
|
25
29
|
def current_is_author?(iteration)
|
26
30
|
return false unless permitted_to_use?
|
27
31
|
|
@@ -21,6 +21,9 @@ class ::CommandProposal::Iteration < ApplicationRecord
|
|
21
21
|
serialize :args, ::CommandProposal::Service::JSONWrapper
|
22
22
|
include ::CommandProposal::Service::ExternalBelong
|
23
23
|
|
24
|
+
TRUNCATE_COUNT = 2000
|
25
|
+
# Also hardcoded in JS: app/assets/javascripts/command_proposal/console.js
|
26
|
+
|
24
27
|
has_many :comments
|
25
28
|
belongs_to :task
|
26
29
|
external_belongs_to :requester
|
@@ -70,6 +73,10 @@ class ::CommandProposal::Iteration < ApplicationRecord
|
|
70
73
|
(completed_at || stopped_at || Time.current) - started_at
|
71
74
|
end
|
72
75
|
|
76
|
+
def end_time
|
77
|
+
completed_at || stopped_at || Time.current
|
78
|
+
end
|
79
|
+
|
73
80
|
def force_reset
|
74
81
|
# Debugging method. Should never actually be called.
|
75
82
|
update(status: :approved, result: nil, completed_at: nil, stopped_at: nil, started_at: nil)
|
@@ -7,7 +7,7 @@
|
|
7
7
|
|
8
8
|
class ::CommandProposal::Task < ApplicationRecord
|
9
9
|
self.table_name = :command_proposal_tasks
|
10
|
-
attr_accessor :user
|
10
|
+
attr_accessor :user, :skip_approval
|
11
11
|
|
12
12
|
has_many :iterations
|
13
13
|
has_many :ordered_iterations, -> { order(created_at: :desc) }, class_name: "CommandProposal::Iteration"
|
@@ -79,7 +79,17 @@ class ::CommandProposal::Task < ApplicationRecord
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def code=(new_code)
|
82
|
-
|
82
|
+
if skip_approval
|
83
|
+
iterations.create(
|
84
|
+
code: new_code,
|
85
|
+
requester: user,
|
86
|
+
status: :approved,
|
87
|
+
approver: user,
|
88
|
+
approved_at: Time.current
|
89
|
+
)
|
90
|
+
else
|
91
|
+
iterations.create(code: new_code, requester: user)
|
92
|
+
end
|
83
93
|
end
|
84
94
|
|
85
95
|
private
|
@@ -1,8 +1,18 @@
|
|
1
1
|
<% if lines.none? && !(skip_empty ||= false) -%><div class="line"></div><% end
|
2
2
|
-%><% lines.each do |iteration| -%>
|
3
|
-
<div class="line"><%= iteration.code
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
<div class="line"><%= iteration.code
|
4
|
+
-%><div class="result"><%=
|
5
|
+
truncate = ::CommandProposal::Iteration::TRUNCATE_COUNT
|
6
|
+
if iteration.result.present?
|
7
|
+
iteration.result.truncate(truncate)
|
8
|
+
elsif iteration.completed?
|
9
|
+
"Error: No response"
|
10
|
+
else
|
11
|
+
content_tag :i, nil, class: "fa fa-circle-o-notch fa-spin cmd-icon-grey"
|
12
|
+
end
|
13
|
+
%></div
|
14
|
+
></div><%=
|
15
|
+
if iteration.result.length > truncate
|
16
|
+
link_to("Output truncated. Click here to download full result.", "data:application/txt,#{ERB::Util.url_encode(iteration.result)}", class: "cmd-truncated-download", download: "result.txt")
|
17
|
+
end
|
18
|
+
%><% end -%>
|
@@ -1,4 +1,8 @@
|
|
1
1
|
<% if lines.blank? && !(skip_empty ||= false) -%><div class="line"></div><% end
|
2
2
|
-%><% lines&.split("\n").each do |line|
|
3
|
-
|
3
|
+
truncate = ::CommandProposal::Iteration::TRUNCATE_COUNT
|
4
|
+
-%><div class="line"><%= line.truncate(truncate) -%></div><%=
|
5
|
+
if line.length > truncate
|
6
|
+
link_to("Output truncated. Click here to download full result.", "data:application/txt,#{ERB::Util.url_encode(line)}", class: "cmd-truncated-download", download: "result.txt")
|
7
|
+
end -%><%
|
4
8
|
end -%>
|
@@ -10,7 +10,7 @@
|
|
10
10
|
<tr>
|
11
11
|
<td><%= @iteration&.requester_name.presence || "ID: #{@iteration&.requester_id}" if @iteration&.requester_id.present? %></td>
|
12
12
|
<td><%= @iteration&.approver_name.presence || "ID: #{@iteration&.approver_id}" if @iteration&.approver_id.present? %></td>
|
13
|
-
<td><%= @iteration&.started_at&.strftime("%b %-d '%y, %-l:%M%P") %></td>
|
13
|
+
<td data-iteration-started><%= @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>
|
16
16
|
</tr>
|
@@ -66,7 +66,7 @@ module CommandProposal
|
|
66
66
|
# Rollback the create/update if anything fails
|
67
67
|
ActiveRecord::Base.transaction do
|
68
68
|
command_request if @task.function? && @iteration.approved_at? && @iteration.complete?
|
69
|
-
@iteration.update(@params)
|
69
|
+
@iteration.update(@params.merge(requester: @user))
|
70
70
|
|
71
71
|
error!("Cannot run without approval.") unless has_approval?(@task)
|
72
72
|
end
|
@@ -91,7 +91,8 @@ module CommandProposal
|
|
91
91
|
if ::CommandProposal.sessions.key?("task:#{@task.id}")
|
92
92
|
@task.first_iteration.update(status: :success, completed_at: Time.current)
|
93
93
|
else
|
94
|
-
@task.
|
94
|
+
ended_at = @task.iterations.last&.end_time || Time.current
|
95
|
+
@task.first_iteration.update(status: :terminated, completed_at: ended_at)
|
95
96
|
end
|
96
97
|
::CommandProposal.sessions.delete("task:#{@task.id}")
|
97
98
|
end
|
@@ -90,7 +90,8 @@ module CommandProposal
|
|
90
90
|
begin
|
91
91
|
# Run bring functions in here so we can capture any string outputs
|
92
92
|
# OR! Run the full runner and instead of saving to an iteration, return the string for prepending here
|
93
|
-
@session.eval("_ = (#{@iteration.code})").inspect # rubocop:disable Security/Eval - Eval is scary, but in this case it's exactly what we need.
|
93
|
+
result = @session.eval("_ = (#{@iteration.code})").inspect # rubocop:disable Security/Eval - Eval is scary, but in this case it's exactly what we need.
|
94
|
+
result = nil unless @iteration.task.console? # Only store final result for consoles
|
94
95
|
status = :success
|
95
96
|
rescue Exception => e # rubocop:disable Lint/RescueException - Yes, rescue full Exception so that we can catch typos in evals as well
|
96
97
|
status = :failed
|
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.12
|
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-
|
11
|
+
date: 2021-10-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|