command_proposal 1.0.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +77 -0
- data/Rakefile +18 -0
- data/app/assets/config/command_proposal_manifest.js +1 -0
- data/app/assets/javascripts/command_proposal/_codemirror.js +9814 -0
- data/app/assets/javascripts/command_proposal/_helpers.js +9 -0
- data/app/assets/javascripts/command_proposal/codemirror-addon-searchcursor.js +296 -0
- data/app/assets/javascripts/command_proposal/codemirror-keymap-sublime.js +720 -0
- data/app/assets/javascripts/command_proposal/codemirror-mode-ruby.js +303 -0
- data/app/assets/javascripts/command_proposal/console.js +195 -0
- data/app/assets/javascripts/command_proposal/feed.js +51 -0
- data/app/assets/javascripts/command_proposal/terminal.js +40 -0
- data/app/assets/javascripts/command_proposal.js +1 -0
- data/app/assets/stylesheets/command_proposal/_variables.scss +0 -0
- data/app/assets/stylesheets/command_proposal/codemirror-rubyblue.scss +27 -0
- data/app/assets/stylesheets/command_proposal/codemirror.scss +367 -0
- data/app/assets/stylesheets/command_proposal/command_proposal.scss +1 -0
- data/app/assets/stylesheets/command_proposal/components.scss +31 -0
- data/app/assets/stylesheets/command_proposal/containers.scss +4 -0
- data/app/assets/stylesheets/command_proposal/icons.scss +12 -0
- data/app/assets/stylesheets/command_proposal/tables.scss +76 -0
- data/app/assets/stylesheets/command_proposal/terminal.scss +72 -0
- data/app/assets/stylesheets/command_proposal.scss +5 -0
- data/app/controllers/command_proposal/engine_controller.rb +6 -0
- data/app/controllers/command_proposal/iterations_controller.rb +83 -0
- data/app/controllers/command_proposal/runner_controller.rb +86 -0
- data/app/controllers/command_proposal/tasks_controller.rb +97 -0
- data/app/helpers/command_proposal/application_helper.rb +58 -0
- data/app/helpers/command_proposal/icons_helper.rb +15 -0
- data/app/helpers/command_proposal/params_helper.rb +63 -0
- data/app/helpers/command_proposal/permissions_helper.rb +42 -0
- data/app/jobs/command_proposal/application_job.rb +4 -0
- data/app/jobs/command_proposal/command_runner_job.rb +11 -0
- data/app/models/command_proposal/comment.rb +14 -0
- data/app/models/command_proposal/iteration.rb +78 -0
- data/app/models/command_proposal/service/external_belong.rb +48 -0
- data/app/models/command_proposal/service/json_wrapper.rb +18 -0
- data/app/models/command_proposal/service/proposal_presenter.rb +39 -0
- data/app/models/command_proposal/task.rb +106 -0
- data/app/views/command_proposal/tasks/_console_show.html.erb +44 -0
- data/app/views/command_proposal/tasks/_function_show.html.erb +54 -0
- data/app/views/command_proposal/tasks/_lines.html.erb +8 -0
- data/app/views/command_proposal/tasks/_module_show.html.erb +33 -0
- data/app/views/command_proposal/tasks/_past_iterations_list.html.erb +20 -0
- data/app/views/command_proposal/tasks/_task_detail_table.html.erb +31 -0
- data/app/views/command_proposal/tasks/_task_show.html.erb +55 -0
- data/app/views/command_proposal/tasks/error.html.erb +4 -0
- data/app/views/command_proposal/tasks/form.html.erb +64 -0
- data/app/views/command_proposal/tasks/index.html.erb +44 -0
- data/app/views/command_proposal/tasks/show.html.erb +10 -0
- data/config/routes.rb +11 -0
- data/lib/command_proposal/configuration.rb +41 -0
- data/lib/command_proposal/engine.rb +6 -0
- data/lib/command_proposal/services/command_interpreter.rb +108 -0
- data/lib/command_proposal/services/runner.rb +157 -0
- data/lib/command_proposal/version.rb +3 -0
- data/lib/command_proposal.rb +27 -0
- data/lib/generators/command_proposal/install/install_generator.rb +28 -0
- data/lib/generators/command_proposal/install/templates/initializer.rb +47 -0
- data/lib/generators/command_proposal/install/templates/install_command_proposal.rb +40 -0
- data/lib/tasks/command_proposal_tasks.rake +4 -0
- metadata +167 -0
@@ -0,0 +1,108 @@
|
|
1
|
+
# require_relative "command_proposal/permissions_helper"
|
2
|
+
|
3
|
+
module CommandProposal
|
4
|
+
module Services
|
5
|
+
class CommandInterpreter
|
6
|
+
class Error < StandardError; end
|
7
|
+
include ::CommandProposal::PermissionsHelper
|
8
|
+
|
9
|
+
def self.command(iteration, command, user, params={})
|
10
|
+
new(iteration, command, user, params).command
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(iteration, command, user, params={})
|
14
|
+
@iteration = iteration
|
15
|
+
@task = iteration.task
|
16
|
+
@command = command.to_s.to_sym
|
17
|
+
@user = user
|
18
|
+
@params = params
|
19
|
+
command_user(@user) if @user.present?
|
20
|
+
end
|
21
|
+
|
22
|
+
def command
|
23
|
+
case @command
|
24
|
+
when :request then command_request
|
25
|
+
when :approve then command_approve
|
26
|
+
when :run then command_run
|
27
|
+
when :cancel then command_cancel
|
28
|
+
when :cancel then command_cancel
|
29
|
+
when :close then command_close
|
30
|
+
end
|
31
|
+
|
32
|
+
@iteration
|
33
|
+
end
|
34
|
+
|
35
|
+
def command_request
|
36
|
+
check_can_command?
|
37
|
+
if @iteration.complete? && (@task.task? || @task.function?)
|
38
|
+
previous_iteration = @iteration
|
39
|
+
# Creates a new iteration with the same code so we don't lose results
|
40
|
+
@task.user = @user # Sets the task user to assign as the requester
|
41
|
+
@task.update(code: @iteration.code)
|
42
|
+
@iteration = @task.current_iteration
|
43
|
+
|
44
|
+
if @task.function? && previous_iteration.approved_at?
|
45
|
+
@params.merge!(previous_iteration.attributes.slice("approved_at", "approver_id"))
|
46
|
+
@params.merge!(status: :approved)
|
47
|
+
return # Don't trigger the callback
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
proposal = ::CommandProposal::Service::ProposalPresenter.new(@iteration)
|
52
|
+
::CommandProposal.configuration.proposal_callback&.call(proposal)
|
53
|
+
end
|
54
|
+
|
55
|
+
def command_approve
|
56
|
+
error!("Command is not ready for approval.") unless @iteration.pending?
|
57
|
+
check_can_command? && check_can_approve?
|
58
|
+
|
59
|
+
@iteration.update(status: :approved, approver: @user, approved_at: Time.current)
|
60
|
+
end
|
61
|
+
|
62
|
+
def command_run
|
63
|
+
check_can_command?
|
64
|
+
|
65
|
+
# Rollback the create/update if anything fails
|
66
|
+
ActiveRecord::Base.transaction do
|
67
|
+
command_request if @task.function? && @iteration.approved_at? && @iteration.complete?
|
68
|
+
@iteration.update(@params)
|
69
|
+
|
70
|
+
error!("Cannot run without approval.") unless has_approval?(@task)
|
71
|
+
end
|
72
|
+
|
73
|
+
::CommandProposal::CommandRunnerJob.perform_later(@iteration.id)
|
74
|
+
end
|
75
|
+
|
76
|
+
def command_cancel
|
77
|
+
check_can_command?
|
78
|
+
return if @iteration.complete?
|
79
|
+
|
80
|
+
@iteration.update(status: :cancelling)
|
81
|
+
end
|
82
|
+
|
83
|
+
def command_close
|
84
|
+
check_can_command?
|
85
|
+
return unless @iteration.task.console?
|
86
|
+
|
87
|
+
@task.first_iteration.update(status: :success, completed_at: Time.current)
|
88
|
+
::CommandProposal.sessions.delete("task:#{@task.id}")
|
89
|
+
end
|
90
|
+
|
91
|
+
def check_can_command?
|
92
|
+
return true if can_command?
|
93
|
+
|
94
|
+
error!("Sorry, you do not have permission to do this.")
|
95
|
+
end
|
96
|
+
|
97
|
+
def check_can_approve?
|
98
|
+
return true if can_approve?(@iteration)
|
99
|
+
|
100
|
+
error!("You cannot approve your own command.")
|
101
|
+
end
|
102
|
+
|
103
|
+
def error!(msg)
|
104
|
+
raise ::CommandProposal::Services::CommandInterpreter::Error.new(msg)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
module CommandProposal
|
2
|
+
module Services
|
3
|
+
class Runner
|
4
|
+
attr_accessor :session
|
5
|
+
# Add expiration and things like that...
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@session = session
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute(iteration, inline=false)
|
12
|
+
@iteration = iteration
|
13
|
+
@inline = inline
|
14
|
+
prepare
|
15
|
+
|
16
|
+
run
|
17
|
+
|
18
|
+
complete
|
19
|
+
@iteration = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def quick_run(iteration)
|
23
|
+
raise CommandProposal::Error, ":#{iteration.task.friendly_id} does not have approval to run." unless iteration.approved?
|
24
|
+
|
25
|
+
@session.eval(iteration.code)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def session
|
31
|
+
binding
|
32
|
+
end
|
33
|
+
|
34
|
+
def prepare
|
35
|
+
raise CommandProposal::Error, "Cannot run task without approval" unless @iteration.approved?
|
36
|
+
raise CommandProposal::Error, "Modules cannot be run independently" if @iteration.task.module?
|
37
|
+
|
38
|
+
@iteration.task.update(last_executed_at: Time.current)
|
39
|
+
@iteration.update(started_at: Time.current, status: :started)
|
40
|
+
end
|
41
|
+
|
42
|
+
def run
|
43
|
+
begin
|
44
|
+
@session.eval("#{bring_function};params = #{@iteration.args || {}}.with_indifferent_access")
|
45
|
+
rescue Exception => e # rubocop:disable Lint/RescueException - Yes, rescue full Exception so that we can catch typos in evals as well
|
46
|
+
return @iteration.result = results_from_exception(e)
|
47
|
+
end
|
48
|
+
|
49
|
+
stored_stdout = $stdout
|
50
|
+
$stdout = StringIO.new
|
51
|
+
result = nil # Init var for scope
|
52
|
+
|
53
|
+
running_thread = Thread.new do
|
54
|
+
begin
|
55
|
+
# Run bring functions in here so we can capture any string outputs
|
56
|
+
# OR! Run the full runner and instead of saving to an iteration, return the string for prepending here
|
57
|
+
result = @session.eval("_ = (#{@iteration.code})").inspect # rubocop:disable Security/Eval - Eval is scary, but in this case it's exactly what we need.
|
58
|
+
rescue Exception => e # rubocop:disable Lint/RescueException - Yes, rescue full Exception so that we can catch typos in evals as well
|
59
|
+
@iteration.status = :failed
|
60
|
+
|
61
|
+
result = results_from_exception(e)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
while running_thread.status.present?
|
66
|
+
@iteration.reload
|
67
|
+
|
68
|
+
if $stdout.try(:string) != @iteration.result
|
69
|
+
@iteration.update(result: $stdout.try(:string).dup)
|
70
|
+
end
|
71
|
+
|
72
|
+
running_thread.exit if @iteration.cancelling?
|
73
|
+
|
74
|
+
sleep 1
|
75
|
+
end
|
76
|
+
|
77
|
+
output = $stdout.try(:string)
|
78
|
+
output = nil if output == ""
|
79
|
+
# Not using presence because we want to maintain other empty objects such as [] and {}
|
80
|
+
|
81
|
+
$stdout = stored_stdout
|
82
|
+
@iteration.result = [output, "#{result || 'nil'}"].compact.join("\n")
|
83
|
+
end
|
84
|
+
|
85
|
+
def bring_function
|
86
|
+
"def bring(*func_names); func_names.each { |f| self.quick_run(::CommandProposal::Task.module.find_by!(friendly_id: f).current_iteration) }; end"
|
87
|
+
end
|
88
|
+
|
89
|
+
def complete
|
90
|
+
@iteration.completed_at = Time.current
|
91
|
+
if @iteration.cancelling? || @iteration.cancelled?
|
92
|
+
@iteration.result += "\n\n~~~~~ CANCELLED ~~~~~"
|
93
|
+
@iteration.status = :cancelled
|
94
|
+
elsif @iteration.failed?
|
95
|
+
# No-op
|
96
|
+
else
|
97
|
+
@iteration.status = :success
|
98
|
+
end
|
99
|
+
@iteration.save!
|
100
|
+
|
101
|
+
return if @iteration.task.console? # Don't notify for every console entry
|
102
|
+
proposal = ::CommandProposal::Service::ProposalPresenter.new(@iteration)
|
103
|
+
if @iteration.success?
|
104
|
+
::CommandProposal.configuration.success_callback&.call(proposal)
|
105
|
+
else
|
106
|
+
::CommandProposal.configuration.failed_callback&.call(proposal)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def results_from_exception(exc)
|
111
|
+
klass = exc.class
|
112
|
+
msg = exc.try(:message) || exc.try(:body) || exc.to_s
|
113
|
+
# Remove proposal context
|
114
|
+
msg.gsub!(/ for \#\<CommandProposal.*/, "")
|
115
|
+
msg.gsub!(/(::)?CommandProposal::Services::Runner(::)?/, "")
|
116
|
+
# Remove gem lines
|
117
|
+
msg.gsub!(/\/?((\w|(\\ ))*\/)*command_proposal\/services(\/(\w|(\\ ))*)*\.\w+\:\d+\: /, "")
|
118
|
+
info = gather_exception_info(exc)
|
119
|
+
|
120
|
+
["#{klass}: #{msg}", info.presence].compact.join("\n")
|
121
|
+
end
|
122
|
+
|
123
|
+
def gather_exception_info(exception)
|
124
|
+
error_info = []
|
125
|
+
backtrace = full_trace_from_exception(exception)
|
126
|
+
|
127
|
+
eval_trace = backtrace.select { |row| row.include?("(eval)") }.presence || []
|
128
|
+
eval_trace = eval_trace.map do |row|
|
129
|
+
eval_row_number = row[/\(eval\)\:\d+/].to_s[7..-1]
|
130
|
+
next if eval_row_number.blank?
|
131
|
+
|
132
|
+
error_line = @iteration.code.split("\n")[eval_row_number.to_i - 1]
|
133
|
+
"#{eval_row_number}: #{error_line}" if error_line.present?
|
134
|
+
end.compact
|
135
|
+
error_info += ["\n>> Command Trace"] + eval_trace if eval_trace.any?
|
136
|
+
|
137
|
+
app_trace = backtrace.select { |row|
|
138
|
+
row.include?("/app/") && !row.match?(/command_proposal\/(lib|app)/)
|
139
|
+
}.presence || []
|
140
|
+
error_info += ["\n>> App Trace"] + app_trace if app_trace.any?
|
141
|
+
|
142
|
+
error_info.uniq.join("\n")
|
143
|
+
end
|
144
|
+
|
145
|
+
def full_trace_from_exception(exception)
|
146
|
+
trace = exception.try(:backtrace).presence
|
147
|
+
return trace if trace.present?
|
148
|
+
|
149
|
+
trace = @session.send(:caller).dup
|
150
|
+
return trace if trace.present?
|
151
|
+
|
152
|
+
trace = caller.dup
|
153
|
+
trace
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "command_proposal/configuration"
|
2
|
+
require "command_proposal/version"
|
3
|
+
require "command_proposal/engine"
|
4
|
+
require "command_proposal/services/runner"
|
5
|
+
|
6
|
+
module CommandProposal
|
7
|
+
class Error < StandardError; end
|
8
|
+
def self.sessions
|
9
|
+
@sessions ||= {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.clear_sessions
|
13
|
+
@sessions = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.configuration
|
17
|
+
@configuration ||= ::CommandProposal::Configuration.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.reset
|
21
|
+
@configuration = ::CommandProposal::Configuration.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.configure
|
25
|
+
yield(configuration)
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "rails/generators/migration"
|
2
|
+
|
3
|
+
module CommandProposal
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < ::Rails::Generators::Base
|
6
|
+
include Rails::Generators::Migration
|
7
|
+
source_root File.expand_path("../templates", __FILE__)
|
8
|
+
desc "add the migrations"
|
9
|
+
|
10
|
+
def self.next_migration_number(path)
|
11
|
+
unless @prev_migration_nr
|
12
|
+
@prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
|
13
|
+
else
|
14
|
+
@prev_migration_nr += 1
|
15
|
+
end
|
16
|
+
@prev_migration_nr.to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
def copy_migrations
|
20
|
+
migration_template "install_command_proposal.rb", "db/migrate/install_command_proposal.rb"
|
21
|
+
end
|
22
|
+
|
23
|
+
def create_initializer_file
|
24
|
+
copy_file "initializer.rb", "config/initializers/command_proposal.rb"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
::CommandProposal.configure do |config|
|
2
|
+
# Change if your base user class has a different model name
|
3
|
+
config.user_class_name = "User"
|
4
|
+
|
5
|
+
# Helper method used by controllers to identify the currently logged in account.
|
6
|
+
config.controller_var = :current_user
|
7
|
+
|
8
|
+
# Scope for your user class that determines users who are permitted to interact with commands
|
9
|
+
# It is highly recommended to make this very exclusive, as any users in this scope will be able
|
10
|
+
# to interact with your database directly.
|
11
|
+
config.role_scope = :admin
|
12
|
+
|
13
|
+
# Method called to display a user's name
|
14
|
+
config.user_name = :name
|
15
|
+
|
16
|
+
# Callbacks for proposal state changes
|
17
|
+
# `proposal` is the current proposal
|
18
|
+
# Methods available:
|
19
|
+
# `proposal.url`
|
20
|
+
# `proposal.type`
|
21
|
+
# `proposal.name`
|
22
|
+
# `proposal.description`
|
23
|
+
# `proposal.args`
|
24
|
+
# `proposal.code`
|
25
|
+
# `proposal.result`
|
26
|
+
# `proposal.status`
|
27
|
+
# `proposal.requester`
|
28
|
+
# `proposal.approver`
|
29
|
+
# `proposal.approved_at`
|
30
|
+
# `proposal.started_at`
|
31
|
+
# `proposal.completed_at`
|
32
|
+
# `proposal.stopped_at`
|
33
|
+
# `proposal.duration`
|
34
|
+
|
35
|
+
# Called when a command is proposed for review
|
36
|
+
config.proposal_callback = Proc.new { |proposal|
|
37
|
+
# Slack.notify("#{proposal.requester} has proposed #{proposal.name}.\n<Click Here|#{proposal.url}> to view this proposal and approve.")
|
38
|
+
}
|
39
|
+
# Called when a command runs and completes successfully
|
40
|
+
config.success_callback = Proc.new { |iteration|
|
41
|
+
# Slack.notify("The task #{proposal.name} has completed in #{proposal.duration}s.\n<Click Here|#{proposal.url}> to view the results.")
|
42
|
+
}
|
43
|
+
# Called when a command runs but fails to complete
|
44
|
+
config.failed_callback = Proc.new { |iteration|
|
45
|
+
# Slack.notify("The task #{proposal.name} has failed!\n<Click Here|#{proposal.url}> to see what went wrong.")
|
46
|
+
}
|
47
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class InstallCommandProposal < ActiveRecord::Migration[5.0]
|
2
|
+
def change
|
3
|
+
create_table :command_proposal_tasks do |t|
|
4
|
+
# has_many :iterations
|
5
|
+
t.text :name
|
6
|
+
t.text :friendly_id
|
7
|
+
t.text :description
|
8
|
+
t.integer :session_type, default: 0 # [task, console, function]
|
9
|
+
t.datetime :last_executed_at
|
10
|
+
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
|
14
|
+
create_table :command_proposal_iterations do |t|
|
15
|
+
# has_many :comments
|
16
|
+
t.belongs_to :task
|
17
|
+
t.text :args
|
18
|
+
t.text :code
|
19
|
+
t.text :result
|
20
|
+
t.integer :status, default: 0 # [created approved started failed success]
|
21
|
+
t.belongs_to :requester
|
22
|
+
t.belongs_to :approver
|
23
|
+
t.datetime :approved_at
|
24
|
+
t.datetime :started_at
|
25
|
+
t.datetime :completed_at
|
26
|
+
t.datetime :stopped_at
|
27
|
+
|
28
|
+
t.timestamps
|
29
|
+
end
|
30
|
+
|
31
|
+
create_table :command_proposal_comments do |t|
|
32
|
+
t.belongs_to :iteration
|
33
|
+
t.integer :line_number
|
34
|
+
t.belongs_to :author
|
35
|
+
t.text :body
|
36
|
+
|
37
|
+
t.timestamps
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
metadata
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: command_proposal
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Rocco Nicholls
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-08-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: font-awesome-rails
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec-rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: activejob
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Rather than creating rake tasks, which have to go through CI/CD, then
|
70
|
+
some deploy process, eventually make it to production, only to be run once, and
|
71
|
+
then never get deleted and litter your code base- this gem allows you to create
|
72
|
+
command proposals via a UI that can be immediately reviewed by your peers and then
|
73
|
+
run- keeping a history of what happened, when, and what the results were.
|
74
|
+
email:
|
75
|
+
- rocco11nicholls@gmail.com
|
76
|
+
executables: []
|
77
|
+
extensions: []
|
78
|
+
extra_rdoc_files: []
|
79
|
+
files:
|
80
|
+
- MIT-LICENSE
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- app/assets/config/command_proposal_manifest.js
|
84
|
+
- app/assets/javascripts/command_proposal.js
|
85
|
+
- app/assets/javascripts/command_proposal/_codemirror.js
|
86
|
+
- app/assets/javascripts/command_proposal/_helpers.js
|
87
|
+
- app/assets/javascripts/command_proposal/codemirror-addon-searchcursor.js
|
88
|
+
- app/assets/javascripts/command_proposal/codemirror-keymap-sublime.js
|
89
|
+
- app/assets/javascripts/command_proposal/codemirror-mode-ruby.js
|
90
|
+
- app/assets/javascripts/command_proposal/console.js
|
91
|
+
- app/assets/javascripts/command_proposal/feed.js
|
92
|
+
- app/assets/javascripts/command_proposal/terminal.js
|
93
|
+
- app/assets/stylesheets/command_proposal.scss
|
94
|
+
- app/assets/stylesheets/command_proposal/_variables.scss
|
95
|
+
- app/assets/stylesheets/command_proposal/codemirror-rubyblue.scss
|
96
|
+
- app/assets/stylesheets/command_proposal/codemirror.scss
|
97
|
+
- app/assets/stylesheets/command_proposal/command_proposal.scss
|
98
|
+
- app/assets/stylesheets/command_proposal/components.scss
|
99
|
+
- app/assets/stylesheets/command_proposal/containers.scss
|
100
|
+
- app/assets/stylesheets/command_proposal/icons.scss
|
101
|
+
- app/assets/stylesheets/command_proposal/tables.scss
|
102
|
+
- app/assets/stylesheets/command_proposal/terminal.scss
|
103
|
+
- app/controllers/command_proposal/engine_controller.rb
|
104
|
+
- app/controllers/command_proposal/iterations_controller.rb
|
105
|
+
- app/controllers/command_proposal/runner_controller.rb
|
106
|
+
- app/controllers/command_proposal/tasks_controller.rb
|
107
|
+
- app/helpers/command_proposal/application_helper.rb
|
108
|
+
- app/helpers/command_proposal/icons_helper.rb
|
109
|
+
- app/helpers/command_proposal/params_helper.rb
|
110
|
+
- app/helpers/command_proposal/permissions_helper.rb
|
111
|
+
- app/jobs/command_proposal/application_job.rb
|
112
|
+
- app/jobs/command_proposal/command_runner_job.rb
|
113
|
+
- app/models/command_proposal/comment.rb
|
114
|
+
- app/models/command_proposal/iteration.rb
|
115
|
+
- app/models/command_proposal/service/external_belong.rb
|
116
|
+
- app/models/command_proposal/service/json_wrapper.rb
|
117
|
+
- app/models/command_proposal/service/proposal_presenter.rb
|
118
|
+
- app/models/command_proposal/task.rb
|
119
|
+
- app/views/command_proposal/tasks/_console_show.html.erb
|
120
|
+
- app/views/command_proposal/tasks/_function_show.html.erb
|
121
|
+
- app/views/command_proposal/tasks/_lines.html.erb
|
122
|
+
- app/views/command_proposal/tasks/_module_show.html.erb
|
123
|
+
- app/views/command_proposal/tasks/_past_iterations_list.html.erb
|
124
|
+
- app/views/command_proposal/tasks/_task_detail_table.html.erb
|
125
|
+
- app/views/command_proposal/tasks/_task_show.html.erb
|
126
|
+
- app/views/command_proposal/tasks/error.html.erb
|
127
|
+
- app/views/command_proposal/tasks/form.html.erb
|
128
|
+
- app/views/command_proposal/tasks/index.html.erb
|
129
|
+
- app/views/command_proposal/tasks/show.html.erb
|
130
|
+
- config/routes.rb
|
131
|
+
- lib/command_proposal.rb
|
132
|
+
- lib/command_proposal/configuration.rb
|
133
|
+
- lib/command_proposal/engine.rb
|
134
|
+
- lib/command_proposal/services/command_interpreter.rb
|
135
|
+
- lib/command_proposal/services/runner.rb
|
136
|
+
- lib/command_proposal/version.rb
|
137
|
+
- lib/generators/command_proposal/install/install_generator.rb
|
138
|
+
- lib/generators/command_proposal/install/templates/initializer.rb
|
139
|
+
- lib/generators/command_proposal/install/templates/install_command_proposal.rb
|
140
|
+
- lib/tasks/command_proposal_tasks.rake
|
141
|
+
homepage: https://github.com/Rockster160/command_proposal
|
142
|
+
licenses:
|
143
|
+
- MIT
|
144
|
+
metadata:
|
145
|
+
homepage_uri: https://github.com/Rockster160/command_proposal
|
146
|
+
source_code_uri: https://github.com/Rockster160/command_proposal
|
147
|
+
changelog_uri: https://github.com/Rockster160/command_proposal/blob/master/README.md
|
148
|
+
post_install_message:
|
149
|
+
rdoc_options: []
|
150
|
+
require_paths:
|
151
|
+
- lib
|
152
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
153
|
+
requirements:
|
154
|
+
- - ">="
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
version: '0'
|
157
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
158
|
+
requirements:
|
159
|
+
- - ">="
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
version: '0'
|
162
|
+
requirements: []
|
163
|
+
rubygems_version: 3.1.4
|
164
|
+
signing_key:
|
165
|
+
specification_version: 4
|
166
|
+
summary: Gives the ability to run approved commands through a UI in your browser
|
167
|
+
test_files: []
|