foreman-tasks 5.0.0 → 5.1.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/foreman_tasks/api/tasks_controller.rb +4 -4
  3. data/app/controllers/foreman_tasks/tasks_controller.rb +5 -4
  4. data/app/graphql/types/recurring_logic.rb +1 -0
  5. data/app/helpers/foreman_tasks/foreman_tasks_helper.rb +4 -1
  6. data/app/lib/actions/middleware/watch_delegated_proxy_sub_tasks.rb +2 -6
  7. data/app/lib/actions/trigger_proxy_batch.rb +79 -0
  8. data/app/models/foreman_tasks/recurring_logic.rb +8 -0
  9. data/app/models/foreman_tasks/task/dynflow_task.rb +8 -3
  10. data/app/models/foreman_tasks/task.rb +1 -0
  11. data/app/models/foreman_tasks/triggering.rb +12 -4
  12. data/app/views/foreman_tasks/api/tasks/show.json.rabl +1 -1
  13. data/app/views/foreman_tasks/layouts/react.html.erb +0 -1
  14. data/app/views/foreman_tasks/recurring_logics/index.html.erb +4 -2
  15. data/app/views/foreman_tasks/task_groups/recurring_logic_task_groups/_recurring_logic_task_group.html.erb +4 -0
  16. data/db/migrate/20210720115251_add_purpose_to_recurring_logic.rb +6 -0
  17. data/extra/foreman-tasks-cleanup.sh +127 -0
  18. data/extra/foreman-tasks-export.sh +117 -0
  19. data/lib/foreman_tasks/tasks/export_tasks.rake +92 -47
  20. data/lib/foreman_tasks/version.rb +1 -1
  21. data/test/controllers/api/tasks_controller_test.rb +29 -0
  22. data/test/controllers/tasks_controller_test.rb +19 -0
  23. data/test/unit/actions/trigger_proxy_batch_test.rb +59 -0
  24. data/test/unit/triggering_test.rb +22 -0
  25. data/webpack/ForemanTasks/Components/TaskActions/TaskActionHelpers.js +11 -4
  26. data/webpack/ForemanTasks/Components/TaskActions/TaskActionHelpers.test.js +27 -5
  27. data/webpack/ForemanTasks/Components/TasksTable/TasksTable.js +8 -0
  28. data/webpack/ForemanTasks/Components/TasksTable/TasksTableActions.js +6 -1
  29. data/webpack/ForemanTasks/Components/TasksTable/TasksTableHelpers.js +2 -1
  30. data/webpack/ForemanTasks/Components/TasksTable/TasksTablePage.js +22 -11
  31. data/webpack/ForemanTasks/Components/TasksTable/TasksTableReducer.js +17 -16
  32. data/webpack/ForemanTasks/Components/TasksTable/TasksTableSelectors.js +3 -0
  33. data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableHelpers.test.js +1 -1
  34. data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableReducer.test.js +3 -1
  35. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTablePage.test.js.snap +12 -2
  36. data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTableReducer.test.js.snap +5 -0
  37. data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/__snapshots__/selectionHeaderCellFormatter.test.js.snap +1 -0
  38. data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/selectionHeaderCellFormatter.test.js +1 -1
  39. data/webpack/ForemanTasks/Components/TasksTable/formatters/selectionHeaderCellFormatter.js +1 -0
  40. data/webpack/ForemanTasks/Components/TasksTable/index.js +2 -0
  41. metadata +8 -5
  42. data/app/services/foreman_tasks/dashboard_table_filter.rb +0 -56
  43. data/test/unit/dashboard_table_filter_test.rb +0 -77
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3e48966ae6049eb85b3051151b7d15d48beb91a55b3580525fe114a3e045ad0f
4
- data.tar.gz: 93da446f348f1a27600a79223ba4daab0e5e83417db711919392cfab2eedf57a
3
+ metadata.gz: 1f89f36fdd06c5a49c3c11fa09f203cb238402478c3aaa19f9e82c1227965186
4
+ data.tar.gz: dbc346e70a1a549eb9ba5b9a7096cdba03fb1f8687aebab4a5f5f575cd33015c
5
5
  SHA512:
6
- metadata.gz: 740ff700f75e43154068d3838611dc7c7306d7f5da91e3d2d41a6fce5ea0ccde84bd5f027545f24874b8fb112d9848505ae2a535386ee2f356852695dacd61a2
7
- data.tar.gz: 8f61c3c868af4b7c3d3173b728858537c72ccf695e60b63971ec19f8a675a6ad9c19ecdd3dedb2549e2eb27b7cde62a2be67ead4d737bb43b774301abb303228
6
+ metadata.gz: 17dcdf97e44f9040693011a96060bcabca50b8e3e2af025354d925a63f8e0477ee3547e0af4c38ce052581f1066880337e7dbc020440bec4942aff997e56b32d
7
+ data.tar.gz: e84cc64acfcd8f54c7b029f1af3b0126bdb4ac90539c46dc072c33520d9e5aa8d0e020ea282a1d2bafc0766ac5f4ce4ccf1ac9cd962b52db13ced8c655475ec3
@@ -192,7 +192,7 @@ module ForemanTasks
192
192
  params[:order] = "#{ordering_params[:sort_by]} #{ordering_params[:sort_order]}"
193
193
  end
194
194
  params[:order] ||= 'started_at DESC'
195
- @tasks = DashboardTableFilter.new(resource_scope_for_index, params).scope.order(params[:order].to_s)
195
+ @tasks = resource_scope_for_index.order(params[:order].to_s)
196
196
  end
197
197
 
198
198
  def search_options
@@ -260,7 +260,7 @@ module ForemanTasks
260
260
  raise BadRequest,
261
261
  _('Resource search_params requires resource_type and resource_id to be specified')
262
262
  end
263
- scope.joins(:locks).where(foreman_tasks_locks:
263
+ scope.joins(:links).where(foreman_tasks_links:
264
264
  { resource_type: search_params[:resource_type],
265
265
  resource_id: search_params[:resource_id] })
266
266
  when 'task'
@@ -320,7 +320,7 @@ module ForemanTasks
320
320
  end
321
321
 
322
322
  def find_task
323
- @task = resource_scope.find(params[:id])
323
+ @task = resource_scope.with_duration.find(params[:id])
324
324
  end
325
325
 
326
326
  def resource_scope(_options = {})
@@ -330,7 +330,7 @@ module ForemanTasks
330
330
  end
331
331
 
332
332
  def resource_scope_for_index(*args)
333
- super.select("DISTINCT foreman_tasks_tasks.*, coalesce(ended_at, current_timestamp) - coalesce(coalesce(started_at, ended_at), current_timestamp) as duration")
333
+ super.with_duration.distinct
334
334
  end
335
335
 
336
336
  def controller_permission
@@ -99,8 +99,8 @@ module ForemanTasks
99
99
  private
100
100
 
101
101
  def respond_with_tasks(scope)
102
- @tasks = filter(scope, paginate: false)
103
- csv_response(@tasks, [:id, :action, :state, :result, 'started_at.in_time_zone', 'ended_at.in_time_zone', :username], ['Id', 'Action', 'State', 'Result', 'Started At', 'Ended At', 'User'])
102
+ @tasks = filter(scope, paginate: false).with_duration
103
+ csv_response(@tasks, [:id, :action, :state, :result, 'started_at.in_time_zone', 'ended_at.in_time_zone', :duration, :username], ['Id', 'Action', 'State', 'Result', 'Started At', 'Ended At', 'Duration', 'User'])
104
104
  end
105
105
 
106
106
  def controller_permission
@@ -127,8 +127,9 @@ module ForemanTasks
127
127
  end
128
128
 
129
129
  def filter(scope, paginate: true)
130
- scope = DashboardTableFilter.new(scope, params).scope
131
- scope = scope.search_for(search_query, order: params[:order])
130
+ search = current_taxonomy_search
131
+ search = [search, params[:search]].select(&:present?).join(' AND ')
132
+ scope = scope.search_for(search, order: params[:order])
132
133
  scope = scope.paginate(page: params[:page], per_page: params[:per_page]) if paginate
133
134
  scope.distinct
134
135
  end
@@ -9,6 +9,7 @@ module Types
9
9
  field :max_iteration, Integer
10
10
  field :iteration, Integer
11
11
  field :state, String
12
+ field :purpose, String
12
13
  belongs_to :triggering, Types::Triggering
13
14
 
14
15
  def self.graphql_definition
@@ -141,7 +141,10 @@ module ForemanTasks
141
141
  weekly_fieldset(f, triggering),
142
142
  time_picker_fieldset(f, triggering),
143
143
  ]
144
-
144
+ tags << text_f(f, :start_at_raw, :label => _('Start at'), :placeholder => 'YYYY-mm-dd HH:MM')
145
+ tags << text_f(f, :purpose,
146
+ :label => _('Purpose'),
147
+ :label_help => N_('A special label for tracking a recurring job. There can be only one active job with a given purpose at a time.'))
145
148
  content_tag(:fieldset, nil, :id => 'trigger_mode_recurring', :class => "trigger_mode_form #{'hidden' unless triggering.recurring?}") do
146
149
  tags.join.html_safe
147
150
  end
@@ -20,9 +20,7 @@ module Actions
20
20
  private
21
21
 
22
22
  def set_clock
23
- action.world.clock.ping action.send(:suspended_action),
24
- POLL_INTERVAL,
25
- CheckOnProxyActions
23
+ action.plan_event(CheckOnProxyActions, POLL_INTERVAL, optional: true)
26
24
  end
27
25
 
28
26
  def check_triggered
@@ -44,9 +42,7 @@ module Actions
44
42
 
45
43
  def notify(event, tasks)
46
44
  tasks.each do |task|
47
- action.world.event task.execution_plan_id,
48
- task.step_id,
49
- event
45
+ action.plan_event(event, execution_plan_id: task.execution_plan_id, step_id: task.step_id)
50
46
  end
51
47
  end
52
48
 
@@ -0,0 +1,79 @@
1
+ module Actions
2
+ # This action plans proxy tasks in batches.
3
+ # It needs to be manually notified about the next batch being available by sending a TriggerNextBatch event.
4
+ #
5
+ # The ProxyAction needs to be planned with `:use_batch_triggering => true` to activate the feature
6
+ class TriggerProxyBatch < Base
7
+ TriggerNextBatch = Algebrick.type do
8
+ fields! batches: Integer
9
+ end
10
+ TriggerLastBatch = Algebrick.atom
11
+
12
+ def run(event = nil)
13
+ case event
14
+ when nil
15
+ if output[:planned_count]
16
+ check_finish
17
+ else
18
+ init_counts and suspend
19
+ end
20
+ when TriggerNextBatch
21
+ trigger_remote_tasks_batches(event.batches) and suspend
22
+ when TriggerLastBatch
23
+ trigger_remote_tasks_batch and on_finish
24
+ when ::Dynflow::Action::Skip
25
+ # do nothing
26
+ end
27
+ end
28
+
29
+ def trigger_remote_tasks_batches(amount = 1)
30
+ amount.times { trigger_remote_tasks_batch }
31
+ end
32
+
33
+ def trigger_remote_tasks_batch
34
+ # Find the tasks in batches, order them by proxy_url so we get all tasks
35
+ # to a certain proxy "close to each other"
36
+ batch = remote_tasks.pending.order(:proxy_url, :id).first(batch_size)
37
+ # Group the tasks by operation, in theory there should be only one operation
38
+ batch.group_by(&:operation).each do |operation, group|
39
+ ForemanTasks::RemoteTask.batch_trigger(operation, group)
40
+ end
41
+ output[:planned_count] += batch.size
42
+ rescue => e
43
+ action_logger.warn "Could not trigger task on the smart proxy: #{e.message}"
44
+ batch.each { |remote_task| remote_task.update_from_batch_trigger({}) }
45
+ output[:failed_count] += batch.size
46
+ end
47
+
48
+ def init_counts
49
+ output[:planned_count] = 0
50
+ output[:failed_count] = 0
51
+ end
52
+
53
+ def check_finish
54
+ if output[:planned_count] + output[:failed_count] + batch_size >= input[:total_count]
55
+ trigger_remote_tasks_batch and on_finish
56
+ else
57
+ suspend
58
+ end
59
+ end
60
+
61
+ def done?
62
+ output[:planned_count] + output[:failed_count] >= input[:total_count]
63
+ end
64
+
65
+ def remote_tasks
66
+ task.remote_sub_tasks
67
+ end
68
+
69
+ def on_finish
70
+ # nothing for now
71
+ end
72
+
73
+ private
74
+
75
+ def batch_size
76
+ input[:batch_size] || Setting['foreman_tasks_proxy_batch_size']
77
+ end
78
+ end
79
+ end
@@ -17,6 +17,9 @@ module ForemanTasks
17
17
  scoped_search :on => :iteration, :complete_value => false
18
18
  scoped_search :on => :cron_line, :complete_value => true
19
19
  scoped_search :on => :state, :complete_value => true
20
+ scoped_search :on => :purpose, :complete_value => true
21
+
22
+ validate :valid_purpose
20
23
 
21
24
  before_create do
22
25
  task_group.save
@@ -169,6 +172,7 @@ module ForemanTasks
169
172
  ::ForemanTasks::RecurringLogic.new_from_cronline(cronline).tap do |manager|
170
173
  manager.end_time = triggering.end_time if triggering.end_time_limited.present?
171
174
  manager.max_iteration = triggering.max_iteration if triggering.max_iteration.present?
175
+ manager.purpose = triggering.purpose if triggering.purpose.present?
172
176
  manager.triggering = triggering
173
177
  end
174
178
  end
@@ -189,5 +193,9 @@ module ForemanTasks
189
193
  end
190
194
  hash.select { |key, _| allowed_keys.include? key }
191
195
  end
196
+
197
+ def valid_purpose?
198
+ !(purpose.present? && self.class.where(:purpose => purpose, :state => %w[active disabled]).any?)
199
+ end
192
200
  end
193
201
  end
@@ -140,12 +140,17 @@ module ForemanTasks
140
140
 
141
141
  def main_action
142
142
  return @main_action if defined?(@main_action)
143
+
144
+ @main_action = execution_plan && execution_plan.root_plan_step.try(:action, execution_plan)
143
145
  if active_job?
144
146
  job_data = active_job_data
145
- @main_action = active_job_action(job_data['job_class'], job_data['arguments'])
146
- else
147
- @main_action = execution_plan && execution_plan.root_plan_step.try(:action, execution_plan)
147
+ begin
148
+ @main_action = active_job_action(job_data['job_class'], job_data['arguments'])
149
+ rescue => e
150
+ Foreman::Logging.exception("Failed to load ActiveJob for task #{id}", e, :logger => 'foreman-tasks')
151
+ end
148
152
  end
153
+ @main_action
149
154
  end
150
155
 
151
156
  # The class for ActiveJob jobs in Dynflow, JobWrapper is not expected to
@@ -78,6 +78,7 @@ module ForemanTasks
78
78
  :"foreman_tasks_links.resource_type" => resource.class.name)
79
79
  end)
80
80
  scope :for_action_types, (->(action_types) { where('foreman_tasks_tasks.label IN (?)', Array(action_types)) })
81
+ scope :with_duration, -> { select("foreman_tasks_tasks.*, coalesce(ended_at, current_timestamp) - coalesce(coalesce(started_at, ended_at), current_timestamp) as duration") }
81
82
 
82
83
  apipie :class, "A class representing #{model_name.human} object" do
83
84
  name 'Task'
@@ -2,7 +2,7 @@ module ForemanTasks
2
2
  class Triggering < ApplicationRecord
3
3
  PARAMS = [:start_at_raw, :start_before_raw, :max_iteration, :input_type,
4
4
  :cronline, :days, :days_of_week, :time, :end_time_limited,
5
- :end_time].freeze
5
+ :end_time, :purpose].freeze
6
6
  attr_accessor(*PARAMS)
7
7
 
8
8
  graphql_type '::Types::Triggering'
@@ -30,7 +30,7 @@ module ForemanTasks
30
30
  validates :input_type, :if => :recurring?,
31
31
  :inclusion => { :in => ALLOWED_INPUT_TYPES,
32
32
  :message => _('%{value} is not allowed input type') }
33
- validates :start_at_raw, format: { :with => TIME_REGEXP, :if => :future?,
33
+ validates :start_at_raw, format: { :with => TIME_REGEXP, :if => ->(triggering) { triggering.future? || (triggering.recurring? && triggering.start_at_raw) },
34
34
  :message => _('%{value} is wrong format') }
35
35
  validates :start_before_raw, format: { :with => TIME_REGEXP, :if => :future?,
36
36
  :message => _('%{value} is wrong format'), :allow_blank => true }
@@ -70,7 +70,7 @@ module ForemanTasks
70
70
  delay_options,
71
71
  *args
72
72
  when :recurring
73
- recurring_logic.start(action, *args)
73
+ recurring_logic.start_after(action, delay_options[:start_at] || Time.zone.now, *args)
74
74
  end
75
75
  end
76
76
 
@@ -94,7 +94,13 @@ module ForemanTasks
94
94
  end
95
95
 
96
96
  def parse_start_at!
97
- self.start_at ||= Time.zone.parse(start_at_raw)
97
+ self.start_at ||= Time.zone.parse(start_at_raw) if start_at_raw.present?
98
+ end
99
+
100
+ def parse_start_at
101
+ self.start_at ||= Time.zone.parse(start_at_raw) if start_at_raw.present?
102
+ rescue ArgumentError
103
+ errors.add(:start_at, _('is not a valid format'))
98
104
  end
99
105
 
100
106
  def parse_start_before!
@@ -104,7 +110,9 @@ module ForemanTasks
104
110
  private
105
111
 
106
112
  def can_start_recurring
113
+ parse_start_at
107
114
  errors.add(:input_type, _('No task could be started')) unless recurring_logic.valid?
115
+ errors.add(:purpose, _('Active or disabled recurring logic with purpose %s already exists') % recurring_logic.purpose) unless recurring_logic.valid_purpose?
108
116
  errors.add(:cronline, _('%s is not valid format of cron line') % cronline) unless recurring_logic.valid_cronline?
109
117
  end
110
118
 
@@ -3,6 +3,6 @@ object @task if @task
3
3
  extends 'api/v2/layouts/permissions'
4
4
 
5
5
  attributes :id, :label, :pending, :action
6
- attributes :username, :started_at, :ended_at, :state, :result, :progress
6
+ attributes :username, :started_at, :ended_at, :duration, :state, :result, :progress
7
7
  attributes :input, :output, :humanized, :cli_example, :start_at
8
8
  node(:available_actions) { |t| { cancellable: t.execution_plan&.cancellable?, resumable: t.resumable? } }
@@ -6,7 +6,6 @@
6
6
  <% end %>
7
7
 
8
8
  <% content_for(:content) do %>
9
- <%= notifications %>
10
9
  <div id="organization-id" data-id="<%= Organization.current.id if Organization.current %>" ></div>
11
10
  <div id="user-id" data-id="<%= User.current.id if User.current %>" ></div>
12
11
  <%= react_component('ForemanTasks') %>
@@ -8,9 +8,9 @@
8
8
  <% if authorized_for(:permission => :edit_recurring_logics, :auth_object => @recurring_logics) %>
9
9
  <% title_actions link_to(_('Clear Cancelled'),
10
10
  clear_cancelled_foreman_tasks_recurring_logics_path,
11
- class: ['btn', 'btn-sm', 'btn-danger'],
11
+ class: ['btn', 'btn-sm', 'btn-danger'],
12
12
  :'data-toggle' => "modal",
13
- :'data-target' => "#clear_modal")
13
+ :'data-target' => "#clear_modal")
14
14
  %>
15
15
 
16
16
  <div class="modal fade" id="clear_modal" tabindex="-1" role="dialog" aria-labelledby="Deploy" aria-hidden="true">
@@ -47,6 +47,7 @@
47
47
  <th><%= N_("Iteration limit") %></th>
48
48
  <th><%= N_("Repeat until") %></th>
49
49
  <th><%= N_("State") %></th>
50
+ <th><%= N_("Purpose") %></th>
50
51
  <th/>
51
52
  </thead>
52
53
  <% @recurring_logics.each do |recurring_logic| %>
@@ -61,6 +62,7 @@
61
62
  <td><%= format_recurring_logic_limit recurring_logic.max_iteration %></td>
62
63
  <td><%= format_recurring_logic_limit recurring_logic.end_time.try(:in_time_zone) %></td>
63
64
  <td><%= recurring_logic_state(recurring_logic) %></td>
65
+ <td><%= recurring_logic.purpose %></td>
64
66
  <td><%= recurring_logic_action_buttons(recurring_logic) %></td>
65
67
  </tr>
66
68
  <% end %>
@@ -36,4 +36,8 @@
36
36
  <th><%= N_("State") %></th>
37
37
  <td><%= recurring_logic_state(recurring_logic) %></td>
38
38
  </tr>
39
+ <tr>
40
+ <th><%= N_("Purpose") %></th>
41
+ <td><%= recurring_logic.purpose %></td>
42
+ </tr>
39
43
  </table>
@@ -0,0 +1,6 @@
1
+ class AddPurposeToRecurringLogic < ActiveRecord::Migration[6.0]
2
+ def change
3
+ add_column :foreman_tasks_recurring_logics, :purpose, :string
4
+ add_index :foreman_tasks_recurring_logics, :purpose, unique: true, where: "state IN ('active', 'disabled')"
5
+ end
6
+ end
@@ -0,0 +1,127 @@
1
+ #!/bin/bash
2
+
3
+ PROGNAME="$0"
4
+ EXECUTE=0
5
+ RAKE_COMMAND="${RAKE_COMMAND:-"foreman-rake"}"
6
+
7
+ function die() {
8
+ local code="$1"
9
+ local message="$2"
10
+ echo "$message" >&2
11
+ exit $code
12
+ }
13
+
14
+ function build_rake() {
15
+ echo -n "$RAKE_COMMAND "
16
+ echo -n 'foreman_tasks:cleanup '
17
+ for env in AFTER TASK_BACKUP BATCH_SIZE NOOP TASK_SEARCH STATES VERBOSE; do
18
+ local value="${!env}"
19
+ [ -n "${value}" ] && echo -n "${env}=$(printf '%q' "$value") "
20
+ done
21
+ echo
22
+ }
23
+
24
+ function usage() {
25
+ cat << EOF
26
+ Usage: $PROGNAME [script_options...] [options...]
27
+
28
+ An interface script for setting environment variables properly
29
+ for foreman-tasks:cleanup rake task. By default only prints
30
+ the command to run, with -E|--execute flag performs the cleanup.
31
+
32
+ Environment variables:
33
+ RAKE_COMMAND: can be used to redefine path to rake, by default foreman-rake
34
+
35
+ Script options:
36
+ EOF
37
+ cat <<EOF | column -s\& -t
38
+ -E|--execute & execute the created rake command
39
+ -h|--help & show this output
40
+ EOF
41
+
42
+ echo
43
+ echo Cleanup options:
44
+ cat <<EOF | column -s\& -t
45
+ -B|--batch-size BATCH_SIZE & process tasks in batches of BATCH_SIZE, 1000 by default
46
+ -S|--states STATES & operate on tasks in STATES, comma separated list of states, set to all to operate on tasks in any state
47
+ -a|--after AGE & operate on tasks older than AGE. Expected format is a number followed by the time unit (s,h,m,y), such as '10d' for 10 days
48
+ -b|--backup & backup deleted tasks
49
+ -n|--noop & do a dry run, print what would be done
50
+ -s|--search QUERY & use QUERY in scoped search format to match tasks to delete
51
+ -v|--verbose & be verbose
52
+ EOF
53
+ }
54
+
55
+ SHORTOPTS="a:bB:Ehns:S:v"
56
+ LONGOPTS="after:,backup,batch-size:,execute,help,noop,search:,states:,verbose"
57
+
58
+ ARGS=$(getopt -s bash \
59
+ --options $SHORTOPTS \
60
+ --longoptions $LONGOPTS \
61
+ --name $PROGNAME \
62
+ -- "$@" )
63
+
64
+ if [ $? -gt 0 ]; then
65
+ die 1 "getopt failed"
66
+ fi
67
+
68
+ eval set -- "$ARGS"
69
+
70
+ while true; do
71
+ case $1 in
72
+ -a|--after)
73
+ shift
74
+ AFTER="$1"
75
+ ;;
76
+ -b|--backup)
77
+ TASK_BACKUP=true
78
+ ;;
79
+ -B|--batch-size)
80
+ shift
81
+ BATCH_SIZE="$1"
82
+ ;;
83
+ -n|--noop)
84
+ NOOP=1
85
+ ;;
86
+ -s|--search)
87
+ shift
88
+ TASK_SEARCH="$1"
89
+ ;;
90
+ -S|--states)
91
+ shift
92
+ if [ "$1" == "all" ]; then
93
+ STATES=","
94
+ else
95
+ STATES="$1"
96
+ fi
97
+ ;;
98
+ -v|--verbose)
99
+ VERBOSE=1
100
+ ;;
101
+ -h|--help)
102
+ usage
103
+ exit 0
104
+ ;;
105
+ -E|--execute)
106
+ EXECUTE=1
107
+ ;;
108
+ \?)
109
+ die 1 "Invalid option: -$OPTARG"
110
+ ;;
111
+ --)
112
+ ;;
113
+ *)
114
+ [ -n "$1" ] || break
115
+ die 1 "Unaccepted parameter: $1"
116
+ shift
117
+ ;;
118
+ esac
119
+ shift
120
+ done
121
+
122
+ if [ "$EXECUTE" -eq 1 ]; then
123
+ build_rake | sh
124
+ else
125
+ build_rake
126
+ fi
127
+