Class: RakeUi::RakeTask

Inherits:
Object
  • Object
show all
Defined in:
app/models/rake_ui/rake_task.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(task) ⇒ RakeTask

Returns a new instance of RakeTask.



49
50
51
# File 'app/models/rake_ui/rake_task.rb', line 49

def initialize(task)
  @task = task
end

Instance Attribute Details

#taskObject (readonly)

Returns the value of attribute task.



46
47
48
# File 'app/models/rake_ui/rake_task.rb', line 46

def task
  @task
end

Class Method Details

.allObject



25
26
27
28
29
# File 'app/models/rake_ui/rake_task.rb', line 25

def self.all
  self.load.map do |task|
    new(task)
  end
end

.find_by_id(id) ⇒ Object



37
38
39
40
41
42
43
44
# File 'app/models/rake_ui/rake_task.rb', line 37

def self.find_by_id(id)
  t = all
  i = from_safe_identifier(id)

  t.find do |task|
    task.name == i
  end
end

.from_safe_identifier(id) ⇒ Object



11
12
13
# File 'app/models/rake_ui/rake_task.rb', line 11

def self.from_safe_identifier(id)
  CGI.unescape(id)
end

.internalObject



31
32
33
34
35
# File 'app/models/rake_ui/rake_task.rb', line 31

def self.internal
  self.load.map { |task|
    new(task)
  }.select(&:is_internal_task)
end

.loadObject



15
16
17
18
19
20
21
22
23
# File 'app/models/rake_ui/rake_task.rb', line 15

def self.load
  # Enables 'desc' to show up as full_comments
  if Rake::TaskManager.respond_to? :record_task_metadata
    Rake::TaskManager. = true
  end

  Rails.application.load_tasks
  Rake::Task.tasks
end

.to_safe_identifier(id) ⇒ Object



7
8
9
# File 'app/models/rake_ui/rake_task.rb', line 7

def self.to_safe_identifier(id)
  CGI.escape(id)
end

Instance Method Details

#build_rake_command(args: nil, environment: nil) ⇒ Object

returns an invokable rake command FOO=bar rake create_something rake create_something rake create_something



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'app/models/rake_ui/rake_task.rb', line 112

def build_rake_command(args: nil, environment: nil)
  command = ""

  if environment
    # Safely escape environment variables to prevent shell injection
    # Only accept KEY=VALUE pairs; reject malicious tokens
    #
    # Shellwords.split raises ArgumentError on malformed input (e.g. an
    # unbalanced quote like FOO="bar). Treat that as no usable env tokens
    # rather than letting the exception bubble up as a 500.
    tokens = begin
      Shellwords.split(environment)
    rescue ArgumentError
      []
    end

    escaped_env = tokens.map do |token|
      next unless token.match?(/\A[A-Z_][A-Z0-9_]*=.*\z/i)

      # Valid KEY=VALUE pattern - escape only the value
      key, value = token.split("=", 2)
      "#{key}=#{Shellwords.escape(value)}"
    end.compact.join(" ")

    command += "#{escaped_env} " if escaped_env.present?
  end

  command += "rake #{name}"

  if args
    # Escape args to prevent shell injection
    command += "[#{Shellwords.escape(args)}]"
  end

  command
end

#call(args: nil, environment: nil) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'app/models/rake_ui/rake_task.rb', line 85

def call(args: nil, environment: nil)
  rake_command = build_rake_command(args: args, environment: environment)

  rake_task_log = RakeUi::RakeTaskLog.build_new_for_command(
    name: name,
    args: args,
    environment: environment,
    rake_command: rake_command,
    rake_definition_file: rake_definition_file,
    raker_id: id
  )

  puts "[rake_ui] [rake_task] [forked] #{rake_task_log.rake_command_with_logging}"

  fork do
    system(rake_task_log.rake_command_with_logging)

    system(rake_task_log.command_to_mark_log_finished)
  end

  rake_task_log
end

#idObject



53
54
55
# File 'app/models/rake_ui/rake_task.rb', line 53

def id
  RakeUi::RakeTask.to_safe_identifier(name)
end

#internal_task?Boolean

thinking this is the sanest way to discern application vs gem defined tasks (like rails, devise etc)

Returns:

  • (Boolean)


75
76
77
78
79
80
81
82
83
# File 'app/models/rake_ui/rake_task.rb', line 75

def internal_task?
  actions.any? { |a| !a.to_s.include? "/ruby/gems" }

  # this was my initial thought here, leaving for posterity in case we need to or the definition of custom
  # from initial investigation the actions seemed like the most consistent as locations is sometimes empty
  # locations.any? do |location|
  #   !location.match(/\/bundle\/gems/)
  # end
end

#is_internal_taskObject



70
71
72
# File 'app/models/rake_ui/rake_task.rb', line 70

def is_internal_task
  internal_task?
end

#rake_definition_fileObject



58
59
60
61
62
63
64
65
66
67
68
# File 'app/models/rake_ui/rake_task.rb', line 58

def rake_definition_file
  definition = actions.first || ""

  if definition.respond_to?(:source_location)
    definition.source_location.join(":")
  else
    definition
  end
rescue
  "unable_to_determine_defining_file"
end