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.
- checksums.yaml +4 -4
- data/app/controllers/foreman_tasks/api/tasks_controller.rb +4 -4
- data/app/controllers/foreman_tasks/tasks_controller.rb +5 -4
- data/app/graphql/types/recurring_logic.rb +1 -0
- data/app/helpers/foreman_tasks/foreman_tasks_helper.rb +4 -1
- data/app/lib/actions/middleware/watch_delegated_proxy_sub_tasks.rb +2 -6
- data/app/lib/actions/trigger_proxy_batch.rb +79 -0
- data/app/models/foreman_tasks/recurring_logic.rb +8 -0
- data/app/models/foreman_tasks/task/dynflow_task.rb +8 -3
- data/app/models/foreman_tasks/task.rb +1 -0
- data/app/models/foreman_tasks/triggering.rb +12 -4
- data/app/views/foreman_tasks/api/tasks/show.json.rabl +1 -1
- data/app/views/foreman_tasks/layouts/react.html.erb +0 -1
- data/app/views/foreman_tasks/recurring_logics/index.html.erb +4 -2
- data/app/views/foreman_tasks/task_groups/recurring_logic_task_groups/_recurring_logic_task_group.html.erb +4 -0
- data/db/migrate/20210720115251_add_purpose_to_recurring_logic.rb +6 -0
- data/extra/foreman-tasks-cleanup.sh +127 -0
- data/extra/foreman-tasks-export.sh +117 -0
- data/lib/foreman_tasks/tasks/export_tasks.rake +92 -47
- data/lib/foreman_tasks/version.rb +1 -1
- data/test/controllers/api/tasks_controller_test.rb +29 -0
- data/test/controllers/tasks_controller_test.rb +19 -0
- data/test/unit/actions/trigger_proxy_batch_test.rb +59 -0
- data/test/unit/triggering_test.rb +22 -0
- data/webpack/ForemanTasks/Components/TaskActions/TaskActionHelpers.js +11 -4
- data/webpack/ForemanTasks/Components/TaskActions/TaskActionHelpers.test.js +27 -5
- data/webpack/ForemanTasks/Components/TasksTable/TasksTable.js +8 -0
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableActions.js +6 -1
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableHelpers.js +2 -1
- data/webpack/ForemanTasks/Components/TasksTable/TasksTablePage.js +22 -11
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableReducer.js +17 -16
- data/webpack/ForemanTasks/Components/TasksTable/TasksTableSelectors.js +3 -0
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableHelpers.test.js +1 -1
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/TasksTableReducer.test.js +3 -1
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTablePage.test.js.snap +12 -2
- data/webpack/ForemanTasks/Components/TasksTable/__tests__/__snapshots__/TasksTableReducer.test.js.snap +5 -0
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/__snapshots__/selectionHeaderCellFormatter.test.js.snap +1 -0
- data/webpack/ForemanTasks/Components/TasksTable/formatters/__test__/selectionHeaderCellFormatter.test.js +1 -1
- data/webpack/ForemanTasks/Components/TasksTable/formatters/selectionHeaderCellFormatter.js +1 -0
- data/webpack/ForemanTasks/Components/TasksTable/index.js +2 -0
- metadata +8 -5
- data/app/services/foreman_tasks/dashboard_table_filter.rb +0 -56
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f89f36fdd06c5a49c3c11fa09f203cb238402478c3aaa19f9e82c1227965186
|
4
|
+
data.tar.gz: dbc346e70a1a549eb9ba5b9a7096cdba03fb1f8687aebab4a5f5f575cd33015c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 =
|
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(:
|
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.
|
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
|
-
|
131
|
-
|
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
|
@@ -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.
|
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.
|
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
|
-
|
146
|
-
|
147
|
-
|
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 =>
|
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.
|
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 %>
|
@@ -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
|
+
|