tap 0.7.9 → 0.8.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.
- data/History +28 -0
- data/MIT-LICENSE +1 -1
- data/README +71 -43
- data/Rakefile +81 -64
- data/Tutorial +235 -0
- data/bin/tap +80 -44
- data/lib/tap.rb +41 -12
- data/lib/tap/app.rb +243 -246
- data/lib/tap/file_task.rb +357 -118
- data/lib/tap/generator.rb +88 -29
- data/lib/tap/generator/generators/config/config_generator.rb +4 -2
- data/lib/tap/generator/generators/config/templates/config.erb +1 -2
- data/lib/tap/generator/generators/file_task/file_task_generator.rb +3 -18
- data/lib/tap/generator/generators/file_task/templates/task.erb +22 -15
- data/lib/tap/generator/generators/file_task/templates/test.erb +13 -2
- data/{test/test/inference_methods/test_assert_files_exist/input/input_1.txt → lib/tap/generator/generators/generator/USAGE} +0 -0
- data/lib/tap/generator/generators/generator/generator_generator.rb +21 -0
- data/lib/tap/generator/generators/generator/templates/generator.erb +23 -0
- data/lib/tap/generator/generators/generator/templates/usage.erb +1 -0
- data/{test/test/inference_methods/test_assert_files_exist/input/input_2.txt → lib/tap/generator/generators/package/USAGE} +0 -0
- data/lib/tap/generator/generators/package/package_generator.rb +38 -0
- data/lib/tap/generator/generators/package/templates/package.erb +186 -0
- data/lib/tap/generator/generators/root/root_generator.rb +14 -9
- data/lib/tap/generator/generators/root/templates/Rakefile +20 -14
- data/{test/test/inference_methods/test_infer_glob/expected/file.yml → lib/tap/generator/generators/root/templates/ReadMe.txt} +0 -0
- data/lib/tap/generator/generators/root/templates/tap.yml +82 -0
- data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +0 -1
- data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +2 -1
- data/{test/test/inference_methods/test_infer_glob/expected/file_1.txt → lib/tap/generator/generators/script/USAGE} +0 -0
- data/lib/tap/generator/generators/script/script_generator.rb +17 -0
- data/lib/tap/generator/generators/script/templates/script.erb +42 -0
- data/lib/tap/generator/generators/task/task_generator.rb +1 -1
- data/lib/tap/generator/generators/task/templates/task.erb +24 -16
- data/lib/tap/generator/generators/task/templates/test.erb +13 -17
- data/lib/tap/generator/generators/workflow/templates/task.erb +10 -10
- data/lib/tap/generator/generators/workflow/templates/test.erb +1 -1
- data/lib/tap/generator/generators/workflow/workflow_generator.rb +3 -18
- data/lib/tap/root.rb +108 -146
- data/lib/tap/script.rb +362 -0
- data/lib/tap/script/console.rb +28 -0
- data/lib/tap/script/destroy.rb +13 -1
- data/lib/tap/script/generate.rb +13 -1
- data/lib/tap/script/run.rb +100 -57
- data/lib/tap/support/batch_queue.rb +0 -3
- data/lib/tap/support/logger.rb +6 -3
- data/lib/tap/support/rake.rb +54 -0
- data/lib/tap/support/task_configuration.rb +169 -0
- data/lib/tap/support/tdoc.rb +198 -0
- data/lib/tap/support/tdoc/config_attr.rb +338 -0
- data/lib/tap/support/tdoc/tdoc_html_generator.rb +38 -0
- data/lib/tap/support/tdoc/tdoc_html_template.rb +42 -0
- data/lib/tap/support/versions.rb +33 -1
- data/lib/tap/task.rb +339 -227
- data/lib/tap/test.rb +86 -128
- data/lib/tap/test/env_vars.rb +16 -5
- data/lib/tap/test/file_methods.rb +373 -0
- data/lib/tap/test/subset_methods.rb +299 -180
- data/lib/tap/version.rb +2 -1
- data/lib/tap/workflow.rb +2 -0
- data/test/app/lib/app_test_task.rb +1 -0
- data/test/app_test.rb +327 -83
- data/test/check/binding_eval.rb +23 -0
- data/test/check/define_method_check.rb +22 -0
- data/test/check/dependencies_check.rb +175 -0
- data/test/check/inheritance_check.rb +22 -0
- data/test/file_task_test.rb +524 -291
- data/test/{test/inference_methods/test_infer_glob/expected/file_2.txt → root/glob/one.txt} +0 -0
- data/test/root/glob/two.txt +0 -0
- data/test/root_test.rb +330 -262
- data/test/script_test.rb +194 -0
- data/test/support/audit_test.rb +5 -2
- data/test/support/combinator_test.rb +10 -10
- data/test/support/rake_test.rb +35 -0
- data/test/support/task_configuration_test.rb +272 -0
- data/test/support/tdoc_test.rb +363 -0
- data/test/support/templater_test.rb +2 -2
- data/test/support/versions_test.rb +32 -0
- data/test/tap_test_helper.rb +39 -0
- data/test/task_base_test.rb +115 -0
- data/test/task_class_test.rb +56 -4
- data/test/task_execute_test.rb +29 -0
- data/test/task_test.rb +89 -70
- data/test/test/env_vars_test.rb +48 -0
- data/test/test/{inference_methods → file_methods}/test_assert_expected/expected/file.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_assert_expected/expected/folder/file.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_assert_expected/input/file.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_assert_expected/input/folder/file.txt +0 -0
- data/test/test/file_methods/test_assert_files_exist/input/input_1.txt +0 -0
- data/test/test/file_methods/test_assert_files_exist/input/input_2.txt +0 -0
- data/test/test/file_methods/test_assert_output_files_equal/expected/one.txt +1 -0
- data/test/test/file_methods/test_assert_output_files_equal/expected/two.txt +1 -0
- data/test/test/file_methods/test_assert_output_files_equal/input/one.txt +1 -0
- data/test/test/file_methods/test_assert_output_files_equal/input/two.txt +1 -0
- data/test/test/{inference_methods → file_methods}/test_file_compare/expected/output_1.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_file_compare/expected/output_2.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_file_compare/input/input_1.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_file_compare/input/input_2.txt +0 -0
- data/test/test/file_methods/test_infer_glob/expected/file.yml +0 -0
- data/test/test/file_methods/test_infer_glob/expected/file_1.txt +0 -0
- data/test/test/file_methods/test_infer_glob/expected/file_2.txt +0 -0
- data/test/test/file_methods/test_method_glob/expected/file.yml +0 -0
- data/test/test/file_methods/test_method_glob/expected/file_1.txt +0 -0
- data/test/test/file_methods/test_method_glob/expected/file_2.txt +0 -0
- data/test/test/{inference_methods → file_methods}/test_yml_compare/expected/output_1.yml +0 -0
- data/test/test/{inference_methods → file_methods}/test_yml_compare/expected/output_2.yml +0 -0
- data/test/test/{inference_methods → file_methods}/test_yml_compare/input/input_1.yml +0 -0
- data/test/test/{inference_methods → file_methods}/test_yml_compare/input/input_2.yml +0 -0
- data/test/test/file_methods_test.rb +204 -0
- data/test/test/subset_methods_test.rb +93 -33
- data/test/test/test_assert_expected_result_files/expected/task/name/a.txt +1 -0
- data/test/test/test_assert_expected_result_files/expected/task/name/b.txt +1 -0
- data/test/test/test_assert_expected_result_files/input/a.txt +1 -0
- data/test/test/test_assert_expected_result_files/input/b.txt +1 -0
- data/test/test/test_file_task_test/expected/one.txt +1 -0
- data/test/test/test_file_task_test/expected/two.txt +1 -0
- data/test/test/test_file_task_test/input/one.txt +1 -0
- data/test/test/test_file_task_test/input/two.txt +1 -0
- data/test/test_test.rb +143 -3
- data/test/workflow_test.rb +2 -0
- data/vendor/rails_generator.rb +56 -0
- data/vendor/rails_generator/base.rb +263 -0
- data/vendor/rails_generator/commands.rb +581 -0
- data/vendor/rails_generator/generated_attribute.rb +42 -0
- data/vendor/rails_generator/lookup.rb +209 -0
- data/vendor/rails_generator/manifest.rb +53 -0
- data/vendor/rails_generator/options.rb +143 -0
- data/vendor/rails_generator/scripts.rb +83 -0
- data/vendor/rails_generator/scripts/destroy.rb +7 -0
- data/vendor/rails_generator/scripts/generate.rb +7 -0
- data/vendor/rails_generator/scripts/update.rb +12 -0
- data/vendor/rails_generator/simple_logger.rb +46 -0
- data/vendor/rails_generator/spec.rb +44 -0
- metadata +180 -196
- data/lib/tap/generator/generators/root/templates/app.yml +0 -19
- data/lib/tap/generator/generators/root/templates/config/process_tap_request.yml +0 -4
- data/lib/tap/generator/generators/root/templates/lib/process_tap_request.rb +0 -26
- data/lib/tap/generator/generators/root/templates/public/images/nav.jpg +0 -0
- data/lib/tap/generator/generators/root/templates/public/stylesheets/color.css +0 -57
- data/lib/tap/generator/generators/root/templates/public/stylesheets/layout.css +0 -108
- data/lib/tap/generator/generators/root/templates/public/stylesheets/normalize.css +0 -40
- data/lib/tap/generator/generators/root/templates/public/stylesheets/typography.css +0 -21
- data/lib/tap/generator/generators/root/templates/server/config/environment.rb +0 -60
- data/lib/tap/generator/generators/root/templates/server/lib/tasks/clear_database_prerequisites.rake +0 -5
- data/lib/tap/generator/generators/root/templates/server/test/test_helper.rb +0 -53
- data/lib/tap/script/server.rb +0 -12
- data/lib/tap/support/rap.rb +0 -38
- data/lib/tap/test/inference_methods.rb +0 -298
- data/test/task/config/task_with_config.yml +0 -1
- data/test/test/inference_methods_test.rb +0 -311
data/lib/tap/file_task.rb
CHANGED
|
@@ -1,22 +1,111 @@
|
|
|
1
1
|
module Tap
|
|
2
2
|
|
|
3
|
-
# FileTask provides methods for
|
|
4
|
-
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
3
|
+
# FileTask provides methods for creating/modifying files such that you can
|
|
4
|
+
# rollback changes if an error occurs. In addition, FileTask provides a
|
|
5
|
+
# method to infer filepaths within the standard Tap directory structure.
|
|
6
|
+
#
|
|
7
|
+
# == Creating Files/Rolling Back Changes
|
|
8
|
+
#
|
|
9
|
+
# FileTask tracks which files to roll back using the added_files array
|
|
10
|
+
# and the backed_up_files hash. On an execute error, all added files are
|
|
11
|
+
# removed and then all backed up files (backed_up_files keys) are restored
|
|
12
|
+
# using the corrsponding backup files (backed_up_files values).
|
|
13
|
+
#
|
|
14
|
+
# For consistency, all filepaths in added_files and backed_up_files should
|
|
15
|
+
# be expanded using File.expand_path. The easiest way to ensure files are
|
|
16
|
+
# properly set up for rollback is to use prepare before working with files
|
|
17
|
+
# and to create directories with mkdir.
|
|
18
|
+
#
|
|
19
|
+
# # this file will be backed up and restored
|
|
20
|
+
# File.open("file.txt", "w") {|f| f << "original content"}
|
|
21
|
+
#
|
|
22
|
+
# t = FileTask.new do |task, inputs|
|
|
23
|
+
# task.mkdir("some/dir") # marked for rollback
|
|
24
|
+
# task.prepare("file.txt", "path/to/file.txt") # marked for rollback
|
|
25
|
+
#
|
|
26
|
+
# File.open("file.txt", "w") {|f| f << "new content"}
|
|
27
|
+
# File.touch("path/to/file.txt")
|
|
28
|
+
#
|
|
29
|
+
# # raise an error to start rollback
|
|
30
|
+
# raise "error!"
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# begin
|
|
34
|
+
# File.exists?("some/dir") # => false
|
|
35
|
+
# File.exists?("path/to/file.txt") # => false
|
|
36
|
+
# t.execute(nil)
|
|
37
|
+
# rescue
|
|
38
|
+
# $!.message # => "error!"
|
|
39
|
+
# File.exists?("some/dir") # => false
|
|
40
|
+
# File.exists?("path/to/file.txt") # => false
|
|
41
|
+
# File.read("file.txt") # => "original content"
|
|
42
|
+
# end
|
|
43
|
+
#
|
|
44
|
+
#--
|
|
45
|
+
# TODO - make cleanup into remove_backup_files and simply remove this
|
|
46
|
+
# configuration. People will figure it out themselves if they want that to happen.
|
|
47
|
+
#
|
|
48
|
+
# The FileTask configurations modify the default backup and restore behavior.
|
|
49
|
+
#
|
|
50
|
+
# backup_dir:: The app directory alias for backups (default :backup)
|
|
51
|
+
# backup_timestamp:: A strftime format string used as a timestamp for backup
|
|
52
|
+
# files (default "%Y%m%d_%H%M%S")
|
|
53
|
+
# rollback_on_error:: Controls whether or not added and backed up files are
|
|
54
|
+
# rolled back on error (default true)
|
|
55
|
+
# cleanup_after_execute:: If true, backed up files will be removed after a
|
|
56
|
+
# successful execution (default false)
|
|
57
|
+
#++
|
|
10
58
|
class FileTask < Task
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
:restore_on_error => true,
|
|
15
|
-
:remove_backed_up_files => false},
|
|
16
|
-
:make_accessors => true)
|
|
59
|
+
autoload(:FileUtils, "fileutils")
|
|
60
|
+
|
|
61
|
+
class << self
|
|
17
62
|
|
|
18
|
-
|
|
19
|
-
|
|
63
|
+
# A batch File.open method. If a block is given, each file in the list will be
|
|
64
|
+
# opened the open files passed to the block. Files are automatically closed when
|
|
65
|
+
# the block returns. If no block is given, the open files are returned.
|
|
66
|
+
#
|
|
67
|
+
# FileTask.open(["one.txt", "two.txt"], "w") do |one, two|
|
|
68
|
+
# one << "one"
|
|
69
|
+
# two << "two"
|
|
70
|
+
# end
|
|
71
|
+
#
|
|
72
|
+
# File.read("one.txt") # => "one"
|
|
73
|
+
# File.read("two.txt") # => "two"
|
|
74
|
+
#
|
|
75
|
+
# Note that open normally takes and passes a list (ie an Array). If you provide
|
|
76
|
+
# a single argument, it will be translated into an Array, and passed AS AN ARRAY
|
|
77
|
+
# to the block.
|
|
78
|
+
#
|
|
79
|
+
# FileTask.open("file.txt", "w") do |array|
|
|
80
|
+
# array.first << "content"
|
|
81
|
+
# end
|
|
82
|
+
#
|
|
83
|
+
# File.read("file.txt") # => "content"
|
|
84
|
+
def open(list, mode="rb")
|
|
85
|
+
open_files = []
|
|
86
|
+
begin
|
|
87
|
+
[list].flatten.map {|path| path.to_str }.each do |filepath|
|
|
88
|
+
open_files << File.open(filepath, mode)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
block_given? ? yield(open_files) : open_files
|
|
92
|
+
ensure
|
|
93
|
+
open_files.each {|file| file.close } if block_given?
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
write_inheritable_attribute(:backup_dir, :backup)
|
|
99
|
+
class_inheritable_accessor(:backup_dir)
|
|
100
|
+
|
|
101
|
+
write_inheritable_attribute(:backup_timestamp, "%Y%m%d_%H%M%S")
|
|
102
|
+
class_inheritable_accessor(:backup_timestamp)
|
|
103
|
+
|
|
104
|
+
write_inheritable_attribute(:rollback_on_error, true)
|
|
105
|
+
class_inheritable_accessor(:rollback_on_error)
|
|
106
|
+
|
|
107
|
+
attr_reader :inference_block, :backed_up_files, :added_files
|
|
108
|
+
attr_accessor :dirname, :backup_dir, :backup_timestamp, :rollback_on_error
|
|
20
109
|
|
|
21
110
|
def initialize(*args)
|
|
22
111
|
super
|
|
@@ -24,7 +113,10 @@ module Tap
|
|
|
24
113
|
batch.each do |task|
|
|
25
114
|
task.dirname = task.default_dirname
|
|
26
115
|
task.backed_up_files = {}
|
|
27
|
-
task.
|
|
116
|
+
task.added_files = []
|
|
117
|
+
task.backup_dir = self.class.backup_dir
|
|
118
|
+
task.backup_timestamp = self.class.backup_timestamp
|
|
119
|
+
task.rollback_on_error = self.class.rollback_on_error
|
|
28
120
|
end
|
|
29
121
|
end
|
|
30
122
|
|
|
@@ -43,32 +135,36 @@ module Tap
|
|
|
43
135
|
# t = FileTask.new
|
|
44
136
|
# t.app[:data] # => "/data"
|
|
45
137
|
# t.dirname # => "tap/file_task"
|
|
46
|
-
# t.
|
|
138
|
+
# t.filepath(:data, "result.txt") # => "/data/tap/file_task/result.txt"
|
|
47
139
|
#
|
|
48
140
|
# t.inference do |root, dir, path|
|
|
49
141
|
# File.join(root, dir, path.chomp(".txt") + ".yml")
|
|
50
142
|
# end
|
|
51
143
|
#
|
|
52
|
-
# t.
|
|
144
|
+
# t.filepath(:data, "result.txt") # => "/data/tap/file_task/result.yml"
|
|
53
145
|
#
|
|
54
|
-
def
|
|
146
|
+
def filepath(dir, *paths)
|
|
55
147
|
inference_block ?
|
|
56
148
|
inference_block.call(app[dir], dirname, *paths) :
|
|
57
149
|
app.filepath(dir, dirname, *paths)
|
|
58
150
|
end
|
|
59
151
|
|
|
60
|
-
# Makes a backup filepath
|
|
61
|
-
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
152
|
+
# Makes a backup filepath relative to backup_dir by translating the input
|
|
153
|
+
# filepath and inserting a timestamp formatted using backup_timestamp.
|
|
154
|
+
# The filepath used during translation will be the filepath relative
|
|
155
|
+
# to dirname (if the input filepath is relative to dirname) or just
|
|
156
|
+
# the basename of the filepath.
|
|
65
157
|
#
|
|
66
158
|
# t = FileTask.new("dir/name", :backup_dir => :backup, :backup_timestamp => "%Y%m%d")
|
|
67
|
-
# t.
|
|
68
|
-
#
|
|
159
|
+
# t.dirname # => "dir/name"
|
|
160
|
+
# t.app[:backup] # => "/backup"
|
|
161
|
+
# Date.today.to_s # => "2007-08-08"
|
|
69
162
|
#
|
|
70
|
-
#
|
|
71
|
-
# t.backup_filepath("dir/name/folder/file.txt")
|
|
163
|
+
# # uses path relative to dirname, if possible
|
|
164
|
+
# t.backup_filepath("dir/name/folder/file.txt") # => "/backup/folder/file_20070808.txt"
|
|
165
|
+
#
|
|
166
|
+
# # otherwise uses basename
|
|
167
|
+
# t.backup_filepath("path/to/folder/file.txt") # => "/backup/file_20070808.txt"
|
|
72
168
|
#
|
|
73
169
|
def backup_filepath(filepath)
|
|
74
170
|
extname = File.extname(filepath)
|
|
@@ -79,33 +175,18 @@ module Tap
|
|
|
79
175
|
backup_path[(split_index + dirname.length + 1)..-1] :
|
|
80
176
|
File.basename(backup_path)
|
|
81
177
|
|
|
82
|
-
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# A batch File.open method. If a block is given, each file in the list will be
|
|
86
|
-
# opened the open files passed to the block. Files are automatically closed when
|
|
87
|
-
# the block returns. If no block is given, the open files are returned.
|
|
88
|
-
def open(list, mode="rb")
|
|
89
|
-
open_files = []
|
|
90
|
-
begin
|
|
91
|
-
fu_list(list).each do |filepath|
|
|
92
|
-
open_files << File.open(filepath, mode)
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
block_given? ? yield(open_files) : open_files
|
|
96
|
-
ensure
|
|
97
|
-
open_files.each {|file| file.close } if block_given?
|
|
98
|
-
end
|
|
178
|
+
filepath(backup_dir, backup_path)
|
|
99
179
|
end
|
|
100
180
|
|
|
101
181
|
# Returns true if all of the targets are up to date relative to all of the sources
|
|
102
|
-
# AND the task config_file
|
|
103
|
-
# for both targets and sources.
|
|
182
|
+
# AND the task config_file, if it exists. Single values or arrays can be provided
|
|
183
|
+
# for both targets and sources. Used to check if any work needs to be done for
|
|
184
|
+
# a given set of sources and configurations.
|
|
104
185
|
#
|
|
105
|
-
# Returns false if +force?+ is true.
|
|
186
|
+
# Returns false (ie 'not up to date') if +force?+ is true.
|
|
106
187
|
def uptodate?(targets, sources=[])
|
|
107
188
|
if app.options.force
|
|
108
|
-
|
|
189
|
+
log_basename(:force, *targets)
|
|
109
190
|
false
|
|
110
191
|
else
|
|
111
192
|
targets = [targets] unless targets.kind_of?(Array)
|
|
@@ -119,12 +200,31 @@ module Tap
|
|
|
119
200
|
end
|
|
120
201
|
|
|
121
202
|
# Makes a backup of each file in list to backup_filepath(file) and registers
|
|
122
|
-
# the files so that they can be restored using restore. If
|
|
123
|
-
# files will be copied to backup_filepath, otherwise the file is
|
|
124
|
-
# backup_filepath. Raises an error if the file is already
|
|
203
|
+
# the files so that they can be restored using restore. If backup_using_copy
|
|
204
|
+
# is true, the files will be copied to backup_filepath, otherwise the file is
|
|
205
|
+
# moved to backup_filepath. Raises an error if the file is already listed
|
|
206
|
+
# in backed_up_files.
|
|
125
207
|
#
|
|
126
208
|
# Returns a list of the backup_filepaths.
|
|
127
|
-
|
|
209
|
+
#
|
|
210
|
+
# file = "file.txt"
|
|
211
|
+
# File.open(file, "w") {|f| f << "file content"}
|
|
212
|
+
#
|
|
213
|
+
# t = FileTask.new
|
|
214
|
+
# backed_up_file = t.backup(file).first
|
|
215
|
+
#
|
|
216
|
+
# File.exists?(file) # => false
|
|
217
|
+
# File.exists?(backed_up_file) # => true
|
|
218
|
+
# File.read(backed_up_file) # => "file content"
|
|
219
|
+
#
|
|
220
|
+
# File.open(file, "w") {|f| f << "new content"}
|
|
221
|
+
# t.restore(file)
|
|
222
|
+
#
|
|
223
|
+
# File.exists?(file) # => true
|
|
224
|
+
# File.exists?(backed_up_file) # => false
|
|
225
|
+
# File.read(file) # => "file content"
|
|
226
|
+
#
|
|
227
|
+
def backup(list, backup_using_copy=false)
|
|
128
228
|
fu_list(list).collect do |filepath|
|
|
129
229
|
next unless File.exists?(filepath)
|
|
130
230
|
|
|
@@ -137,7 +237,7 @@ module Tap
|
|
|
137
237
|
dir = File.dirname(target)
|
|
138
238
|
mkdir(dir)
|
|
139
239
|
|
|
140
|
-
if
|
|
240
|
+
if backup_using_copy
|
|
141
241
|
log :cp, "#{filepath} to #{target}", Logger::DEBUG
|
|
142
242
|
FileUtils.cp(filepath, target)
|
|
143
243
|
else
|
|
@@ -151,13 +251,33 @@ module Tap
|
|
|
151
251
|
end
|
|
152
252
|
end
|
|
153
253
|
|
|
154
|
-
# Restores
|
|
155
|
-
# directory
|
|
254
|
+
# Restores each file in the input list using the backup file from
|
|
255
|
+
# backed_up_files. The backup directory is removed if it is empty.
|
|
256
|
+
#
|
|
257
|
+
# Returns a list of the restored files.
|
|
258
|
+
#
|
|
259
|
+
# file = "file.txt"
|
|
260
|
+
# File.open(file, "w") {|f| f << "file content"}
|
|
261
|
+
#
|
|
262
|
+
# t = FileTask.new
|
|
263
|
+
# backed_up_file = t.backup(file).first
|
|
264
|
+
#
|
|
265
|
+
# File.exists?(file) # => true
|
|
266
|
+
# File.exists?(backed_up_file) # => true
|
|
267
|
+
# File.read(backed_up_file) # => "file content"
|
|
268
|
+
#
|
|
269
|
+
# File.open(file, "w") {|f| f << "new content"}
|
|
270
|
+
# t.restore(file)
|
|
271
|
+
#
|
|
272
|
+
# File.exists?(file) # => true
|
|
273
|
+
# File.exists?(backed_up_file) # => false
|
|
274
|
+
# File.read(file) # => "file content"
|
|
275
|
+
#
|
|
156
276
|
def restore(list)
|
|
157
277
|
fu_list(list).collect do |filepath|
|
|
158
278
|
filepath = File.expand_path(filepath)
|
|
159
|
-
|
|
160
|
-
|
|
279
|
+
next unless backed_up_files.has_key?(filepath)
|
|
280
|
+
|
|
161
281
|
target = backed_up_files.delete(filepath)
|
|
162
282
|
|
|
163
283
|
dir = File.dirname(filepath)
|
|
@@ -170,13 +290,36 @@ module Tap
|
|
|
170
290
|
rmdir(dir)
|
|
171
291
|
|
|
172
292
|
filepath
|
|
173
|
-
end
|
|
293
|
+
end.compact
|
|
174
294
|
end
|
|
175
295
|
|
|
176
296
|
# Creates the directories in list if they do not exist and adds
|
|
177
|
-
# them to
|
|
297
|
+
# them to added_files so they can be removed using rmdir. Creating
|
|
298
|
+
# directories in this way causes them to be rolled back upon an
|
|
299
|
+
# execution error.
|
|
178
300
|
#
|
|
179
301
|
# Returns the made directories.
|
|
302
|
+
#
|
|
303
|
+
# t = FileTask.new do |task, inputs|
|
|
304
|
+
# File.exists?("path") # => false
|
|
305
|
+
#
|
|
306
|
+
# task.mkdir("path/to/dir") # will be rolled back
|
|
307
|
+
# File.exists?("path/to/dir") # => true
|
|
308
|
+
#
|
|
309
|
+
# FileUtils.mkdir("path/to/another") # will not be rolled back
|
|
310
|
+
# File.exists?("path/to/another") # => true
|
|
311
|
+
#
|
|
312
|
+
# raise "error!"
|
|
313
|
+
# end
|
|
314
|
+
#
|
|
315
|
+
# begin
|
|
316
|
+
# t.execute(nil)
|
|
317
|
+
# rescue
|
|
318
|
+
# $!.message # => "error!"
|
|
319
|
+
# File.exists?("path/to/dir") # => false
|
|
320
|
+
# File.exists?("path/to/another") # => true
|
|
321
|
+
# end
|
|
322
|
+
#
|
|
180
323
|
def mkdir(list)
|
|
181
324
|
fu_list(list).each do |dir|
|
|
182
325
|
dir = File.expand_path(dir)
|
|
@@ -190,17 +333,28 @@ module Tap
|
|
|
190
333
|
make_paths.reverse_each do |dir|
|
|
191
334
|
log :mkdir, dir, Logger::DEBUG
|
|
192
335
|
FileUtils.mkdir(dir)
|
|
193
|
-
|
|
336
|
+
added_files << dir
|
|
194
337
|
end
|
|
195
338
|
end
|
|
196
339
|
end
|
|
197
340
|
|
|
198
341
|
# Removes each directory in the input list, provided the directory is in
|
|
199
|
-
#
|
|
342
|
+
# added_files and the directory is empty. When checking if the directory
|
|
200
343
|
# is empty, rmdir checks for regular files and hidden files. Removed
|
|
201
|
-
# directories are removed from
|
|
344
|
+
# directories are removed from added_files.
|
|
202
345
|
#
|
|
203
346
|
# Returns a list of the removed directories.
|
|
347
|
+
#
|
|
348
|
+
# t = FileTask.new
|
|
349
|
+
# File.exists?("path") # => false
|
|
350
|
+
# FileUtils.mkdir("path") # will not be removed
|
|
351
|
+
#
|
|
352
|
+
# t.mkdir("path/to/dir")
|
|
353
|
+
# File.exists?("path/to/dir") # => true
|
|
354
|
+
#
|
|
355
|
+
# t.rmdir("path/to/dir")
|
|
356
|
+
# File.exists?("path") # => true
|
|
357
|
+
# File.exists?("path/to") # => false
|
|
204
358
|
def rmdir(list)
|
|
205
359
|
removed = []
|
|
206
360
|
fu_list(list).each do |dir|
|
|
@@ -208,7 +362,7 @@ module Tap
|
|
|
208
362
|
|
|
209
363
|
# remove directories and parents until the
|
|
210
364
|
# directory was not made by the task
|
|
211
|
-
while
|
|
365
|
+
while added_files.include?(dir)
|
|
212
366
|
break unless Dir.entries(dir).delete_if {|d| d == "." || d == ".."}.empty?
|
|
213
367
|
|
|
214
368
|
if File.exists?(dir)
|
|
@@ -216,20 +370,45 @@ module Tap
|
|
|
216
370
|
FileUtils.rmdir(dir)
|
|
217
371
|
end
|
|
218
372
|
|
|
219
|
-
removed <<
|
|
373
|
+
removed << added_files.delete(dir)
|
|
220
374
|
dir = File.dirname(dir)
|
|
221
375
|
end
|
|
222
376
|
end
|
|
223
377
|
removed
|
|
224
378
|
end
|
|
225
379
|
|
|
226
|
-
#
|
|
227
|
-
# ensuring that the parent directory for the file exists, and
|
|
228
|
-
# to
|
|
229
|
-
#
|
|
380
|
+
# Prepares the input list of files by backing them up (if they exist),
|
|
381
|
+
# ensuring that the parent directory for the file exists, and adding
|
|
382
|
+
# each file to added_files. As a result the files can be removed
|
|
383
|
+
# using rm, restored using restore, and will be rolled back upon an
|
|
384
|
+
# execution error.
|
|
385
|
+
#
|
|
386
|
+
# Returns the prepared files.
|
|
387
|
+
#
|
|
388
|
+
# File.open("file.txt", "w") {|f| f << "original content"}
|
|
230
389
|
#
|
|
231
|
-
#
|
|
232
|
-
|
|
390
|
+
# t = FileTask.new do |task, inputs|
|
|
391
|
+
# File.exists?("path") # => false
|
|
392
|
+
#
|
|
393
|
+
# # backup... make parent dirs... prepare for restore
|
|
394
|
+
# task.prepare(["file.txt", "path/to/file.txt"])
|
|
395
|
+
#
|
|
396
|
+
# File.open("file.txt", "w") {|f| f << "new content"}
|
|
397
|
+
# File.touch("path/to/file.txt")
|
|
398
|
+
#
|
|
399
|
+
# raise "error!"
|
|
400
|
+
# end
|
|
401
|
+
#
|
|
402
|
+
# begin
|
|
403
|
+
# t.execute(nil)
|
|
404
|
+
# rescue
|
|
405
|
+
# $!.message # => "error!"
|
|
406
|
+
# File.exists?("file.txt") # => true
|
|
407
|
+
# File.read("file.txt") # => "original content"
|
|
408
|
+
# File.exists?("path") # => false
|
|
409
|
+
# end
|
|
410
|
+
#
|
|
411
|
+
def prepare(list, backup_using_copy=false)
|
|
233
412
|
list = fu_list(list)
|
|
234
413
|
existing_files, non_existant_files = list.partition do |filepath|
|
|
235
414
|
File.exists?(filepath)
|
|
@@ -237,8 +416,7 @@ module Tap
|
|
|
237
416
|
|
|
238
417
|
# backup existing files
|
|
239
418
|
existing_files.each do |filepath|
|
|
240
|
-
backup(filepath,
|
|
241
|
-
made_files << File.expand_path(filepath)
|
|
419
|
+
backup(filepath, backup_using_copy)
|
|
242
420
|
end
|
|
243
421
|
|
|
244
422
|
# ensure the parent directory exists
|
|
@@ -246,24 +424,37 @@ module Tap
|
|
|
246
424
|
non_existant_files.each do |filepath|
|
|
247
425
|
dir = File.dirname(filepath)
|
|
248
426
|
mkdir(dir)
|
|
249
|
-
made_files << File.expand_path(filepath)
|
|
250
427
|
end
|
|
251
|
-
|
|
252
|
-
yield list if block_given?
|
|
253
428
|
|
|
429
|
+
list.each do |filepath|
|
|
430
|
+
added_files << File.expand_path(filepath)
|
|
431
|
+
end
|
|
432
|
+
|
|
254
433
|
list
|
|
255
434
|
end
|
|
256
435
|
|
|
257
|
-
# Removes each file in the input list, provided the file is in
|
|
436
|
+
# Removes each file in the input list, provided the file is in added_files.
|
|
258
437
|
# The parent directory of each file is removed using rmdir. Removed files
|
|
259
|
-
# are removed from
|
|
438
|
+
# are removed from added_files.
|
|
260
439
|
#
|
|
261
440
|
# Returns the removed files and directories.
|
|
441
|
+
#
|
|
442
|
+
# t = FileTask.new
|
|
443
|
+
# File.exists?("path") # => false
|
|
444
|
+
# FileUtils.mkdir("path") # will not be removed
|
|
445
|
+
#
|
|
446
|
+
# t.make("path/to/file.txt")
|
|
447
|
+
# FileUtils.touch("path/to/file.txt")
|
|
448
|
+
# File.exists?("path/to/file.txt") # => true
|
|
449
|
+
#
|
|
450
|
+
# t.rm("path/to/file.txt")
|
|
451
|
+
# File.exists?("path") # => true
|
|
452
|
+
# File.exists?("path/to") # => false
|
|
262
453
|
def rm(list)
|
|
263
454
|
removed = []
|
|
264
455
|
fu_list(list).each do |filepath|
|
|
265
456
|
filepath = File.expand_path(filepath)
|
|
266
|
-
next unless
|
|
457
|
+
next unless added_files.include?(filepath)
|
|
267
458
|
|
|
268
459
|
# if the file exists, remove it
|
|
269
460
|
if File.exists?(filepath)
|
|
@@ -271,21 +462,87 @@ module Tap
|
|
|
271
462
|
FileUtils.rm(filepath, :force => true)
|
|
272
463
|
end
|
|
273
464
|
|
|
274
|
-
removed <<
|
|
465
|
+
removed << added_files.delete(filepath)
|
|
275
466
|
removed.concat rmdir(File.dirname(filepath))
|
|
276
467
|
end
|
|
277
468
|
removed
|
|
278
469
|
end
|
|
470
|
+
|
|
471
|
+
def rollback
|
|
472
|
+
added_files.dup.each do |filepath|
|
|
473
|
+
begin
|
|
474
|
+
case
|
|
475
|
+
when File.file?(filepath)
|
|
476
|
+
rm(filepath)
|
|
477
|
+
when File.directory?(filepath)
|
|
478
|
+
rmdir(filepath)
|
|
479
|
+
else
|
|
480
|
+
# assures non-existant files are cleared from added_files
|
|
481
|
+
# this is automatically done by rm and rmdir for existing files
|
|
482
|
+
added_files.delete(filepath)
|
|
483
|
+
end
|
|
484
|
+
rescue
|
|
485
|
+
yield $!
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
backed_up_files.keys.each do |filepath|
|
|
490
|
+
begin
|
|
491
|
+
restore(filepath)
|
|
492
|
+
rescue
|
|
493
|
+
yield $!
|
|
494
|
+
end
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
def cleanup(pattern=nil)
|
|
499
|
+
backed_up_files.each do |filepath, target|
|
|
500
|
+
next unless pattern == nil || target =~ pattern
|
|
501
|
+
|
|
502
|
+
# the filepath needs to be added to added_files
|
|
503
|
+
# before it can be removed by rm
|
|
504
|
+
added_files << target
|
|
505
|
+
rm(target)
|
|
506
|
+
backed_up_files.delete(filepath)
|
|
507
|
+
end
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
# Run the system command +cmd+. If multiple arguments are given the command
|
|
511
|
+
# is not run with the shell (same semantics as Kernel::exec and Kernel::system).
|
|
512
|
+
#
|
|
513
|
+
# Example:
|
|
514
|
+
# sh %{ls -ltr}
|
|
515
|
+
#
|
|
516
|
+
# sh 'ls', 'file with spaces'
|
|
517
|
+
#
|
|
518
|
+
# # check exit status after command runs
|
|
519
|
+
# sh %{grep pattern file} do |ok, res|
|
|
520
|
+
# if ! ok
|
|
521
|
+
# puts "pattern not found (status = #{res.exitstatus})"
|
|
522
|
+
# end
|
|
523
|
+
# end
|
|
524
|
+
#
|
|
525
|
+
def sh(*cmd, &block)
|
|
526
|
+
# based on sh from Rake
|
|
527
|
+
unless block_given?
|
|
528
|
+
block = lambda { |ok, status|
|
|
529
|
+
ok or raise "Command failed with status (#{status.exitstatus}): [#{cmd.join(" ")}]"
|
|
530
|
+
}
|
|
531
|
+
end
|
|
532
|
+
log :sh, cmd.join(" ")
|
|
533
|
+
res = system(*cmd)
|
|
534
|
+
block.call(res, $?)
|
|
535
|
+
end
|
|
279
536
|
|
|
280
537
|
# Logs the given action, with the basenames of the input filepaths.
|
|
281
|
-
def
|
|
538
|
+
def log_basename(action, filepaths, level=Logger::INFO)
|
|
282
539
|
msg = filepaths.collect {|filepath| File.basename(filepath) }.join(',')
|
|
283
|
-
log(action ,
|
|
540
|
+
log(action, msg, level)
|
|
284
541
|
end
|
|
285
542
|
|
|
286
543
|
protected
|
|
287
544
|
|
|
288
|
-
attr_writer :inference_block, :backed_up_files, :
|
|
545
|
+
attr_writer :inference_block, :backed_up_files, :added_files
|
|
289
546
|
|
|
290
547
|
# The default_dirname is based on the name of the task, and the
|
|
291
548
|
# index of the task in batch (if the task is batched):
|
|
@@ -300,51 +557,33 @@ module Tap
|
|
|
300
557
|
batched? ? "#{name}_#{batch_index}" : name
|
|
301
558
|
end
|
|
302
559
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
rm(filepath)
|
|
309
|
-
end if remove_backed_up_files
|
|
560
|
+
# Clears added_files and backed_up_files so that
|
|
561
|
+
# a failure will not affect previous executions
|
|
562
|
+
def before_execute
|
|
563
|
+
added_files.clear
|
|
564
|
+
backed_up_files.clear
|
|
310
565
|
end
|
|
311
566
|
|
|
567
|
+
# Removes made files/dirs and restores backed-up files upon
|
|
568
|
+
# an execute error. Collects any errors raised along the way
|
|
569
|
+
# and raises them in a Tap::Support::RunError.
|
|
312
570
|
def on_execute_error(original_error)
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
made_files.dup.each do |filepath|
|
|
317
|
-
begin
|
|
318
|
-
case
|
|
319
|
-
when File.file?(filepath)
|
|
320
|
-
rm(filepath)
|
|
321
|
-
when File.directory?(filepath)
|
|
322
|
-
rmdir(filepath)
|
|
323
|
-
end
|
|
324
|
-
rescue
|
|
325
|
-
restore_errors << $!
|
|
326
|
-
end
|
|
327
|
-
end
|
|
328
|
-
|
|
329
|
-
backed_up_files.keys.each do |filepath|
|
|
330
|
-
begin
|
|
331
|
-
restore(filepath)
|
|
332
|
-
rescue
|
|
333
|
-
restore_errors << $!
|
|
334
|
-
end
|
|
335
|
-
end
|
|
571
|
+
rollback_errors = []
|
|
572
|
+
if rollback_on_error
|
|
573
|
+
rollback {|error| rollback_errors << error}
|
|
336
574
|
end
|
|
337
575
|
|
|
338
|
-
# Re-raise
|
|
339
|
-
#
|
|
340
|
-
|
|
576
|
+
# Re-raise the error if no rollback errors occured,
|
|
577
|
+
# otherwise, raise a RunError tracking the restore
|
|
578
|
+
# errors.
|
|
579
|
+
if rollback_errors.empty?
|
|
341
580
|
raise original_error
|
|
342
581
|
else
|
|
343
|
-
raise Support::RunError.new(original_error,
|
|
582
|
+
raise Support::RunError.new(original_error, rollback_errors)
|
|
344
583
|
end
|
|
345
584
|
end
|
|
346
585
|
|
|
347
|
-
|
|
586
|
+
protected
|
|
348
587
|
|
|
349
588
|
# Lifted from FileUtils
|
|
350
589
|
def fu_list(arg)
|