command_proposal 1.0.11 → 1.0.12

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b612ae41c1fc8d8b0026ece90e73dae10192904a4ff739a8e11b2185e12c6f4
4
- data.tar.gz: ae0ff52ce13d86c9fa32210d1d0121ac52a891aa87e5a951ee6c3e7796fe1a06
3
+ metadata.gz: ab0271a9e0e80bf0df78d8284cc8e13d292ef2254ab57180b1434f50b351dcb7
4
+ data.tar.gz: 21b077ee6cfa9ede8ea83315523297ff9268bf5236de797c9f3acae9f1ef3910
5
5
  SHA512:
6
- metadata.gz: 0f5742d0fb9bbf6b642e978a3ce6f017dfdeca31b9c3776ee720b1b8e27d4ade589bc9f776ef4fb0a4c16891c4617aa5c21b375760d75e2d3ce3d1172261fca2
7
- data.tar.gz: 17a14923c1c3b23942005d8d448bb621e4aafa72c4bf4ae75a8a9bb863e682b05f2fd0d0caf55ca70b087cb376598615c81574bec62a96fdaac6f5afee018402
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
- result.textContent = json.result
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,6 +6,12 @@
6
6
  width: 100%;
7
7
  }
8
8
 
9
+ .cmd-truncated-download {
10
+ font-size: 12px;
11
+ color: grey;
12
+ text-decoration: underline;
13
+ }
14
+
9
15
  .cmd-flex-row {
10
16
  display: flex;
11
17
  }
@@ -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 cmd_config.approval_required?
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 cmd_config.approval_required?
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 cmd_config.approval_required?
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
- iterations.create(code: new_code, requester: user)
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
- if iteration.result.present?
5
- -%><div class="result"><%= iteration.result %></div><%
6
- end
7
- -%></div>
8
- <% end -%>
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
- -%><div class="line"><%= line -%></div><%
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.first_iteration.update(status: :terminated, completed_at: Time.current)
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
@@ -1,3 +1,3 @@
1
1
  module CommandProposal
2
- VERSION = "1.0.11"
2
+ VERSION = "1.0.12"
3
3
  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.11
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-09-25 00:00:00.000000000 Z
11
+ date: 2021-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails