filedepot 0.2.0 → 0.2.2
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/README.md +50 -2
- data/lib/filedepot/cli.rb +99 -18
- data/lib/filedepot/storage/base.rb +25 -0
- data/lib/filedepot/storage_ssh.rb +23 -10
- data/lib/filedepot/version.rb +1 -1
- metadata +29 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2e67db1d2be17a3af457047eb147926143e85ce7a41f398ff818fc83c404dcfa
|
|
4
|
+
data.tar.gz: 3a6616e9a4925732d04d78f55fb716127c115d63220b673165795beba8b9d9c7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 42946b05448b6bf6c3db84b7a8c372a8f5e61688ed63defa263401ac67273d74ef647b0642e52277515d1ae2910c17fcda810b5f7da965eb5b2855bbf2913409
|
|
7
|
+
data.tar.gz: d11483ddbf5f480956a7ae334e5bd238ff701e77e198d259b825db077ae1961c844cbd71e50a8cc04ac6cb39899884ad37688e598fcd91bc534d9bc1baec6872
|
data/README.md
CHANGED
|
@@ -30,17 +30,65 @@ sources:
|
|
|
30
30
|
base_path: /Users/user/filedepot
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
Optional `public_base_path` for public URLs (shown in info and after push):
|
|
34
|
+
|
|
35
|
+
```yaml
|
|
36
|
+
sources:
|
|
37
|
+
- name: test
|
|
38
|
+
ssh: ssh
|
|
39
|
+
host: 127.0.0.1
|
|
40
|
+
base_path: /data/filedepot
|
|
41
|
+
public_base_path: https://example.com/files
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
When `default_source` does not match any source name, the first source is used.
|
|
45
|
+
|
|
33
46
|
## Commands
|
|
34
47
|
|
|
35
48
|
| Command | Description |
|
|
36
49
|
|---------|-------------|
|
|
37
50
|
| `filedepot` | Show current source and available commands |
|
|
38
51
|
| `filedepot config` | Open config file with $EDITOR |
|
|
39
|
-
| `filedepot push HANDLE` | Send file to current storage |
|
|
40
|
-
| `filedepot pull HANDLE [
|
|
52
|
+
| `filedepot push HANDLE FILE` | Send file to current storage |
|
|
53
|
+
| `filedepot pull HANDLE [--path PATH] [--version N]` | Get file from storage |
|
|
41
54
|
| `filedepot versions HANDLE` | List all versions of a handle |
|
|
55
|
+
| `filedepot info HANDLE` | Show info for a handle |
|
|
42
56
|
| `filedepot delete HANDLE [VERSION]` | Delete file(s) after confirmation |
|
|
43
57
|
|
|
58
|
+
### Push
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
filedepot push test test.txt
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Sends `test.txt` to storage with handle `test`. Each push creates a new version. When `public_base_path` is configured, the URL is shown after upload.
|
|
65
|
+
|
|
66
|
+
### Pull
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
filedepot pull test
|
|
70
|
+
filedepot pull test --path ./output/file.txt
|
|
71
|
+
filedepot pull test --version 2
|
|
72
|
+
filedepot pull test --version 2 --path ./output/file.txt
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Gets the latest version by default, or a specific version with `--version`. Use `--path` to specify the local destination. Prompts before creating directories or overwriting files.
|
|
76
|
+
|
|
77
|
+
### Versions
|
|
78
|
+
|
|
79
|
+
Lists versions in descending order with creation datetime. Shows at most 10, with a summary if more exist.
|
|
80
|
+
|
|
81
|
+
### Info
|
|
82
|
+
|
|
83
|
+
Shows handle, remote base path, current version, updated-at datetime, and latest version URL (when `public_base_path` is set).
|
|
84
|
+
|
|
85
|
+
## Testing
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
bundle install
|
|
89
|
+
bundle exec rake test
|
|
90
|
+
```
|
|
91
|
+
|
|
44
92
|
## License
|
|
45
93
|
|
|
46
94
|
MIT
|
data/lib/filedepot/cli.rb
CHANGED
|
@@ -35,19 +35,9 @@ module Filedepot
|
|
|
35
35
|
filedepot pull test --version 2
|
|
36
36
|
filedepot pull test --version 2 --path ./test/file.txt
|
|
37
37
|
HELP
|
|
38
|
-
versions:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
List all versions of a handle. Each version has an integer ID from 1 to n.
|
|
43
|
-
HELP
|
|
44
|
-
delete: <<~HELP
|
|
45
|
-
Usage:
|
|
46
|
-
filedepot delete HANDLE [VERSION]
|
|
47
|
-
|
|
48
|
-
After confirmation, deletes all versions of a file.
|
|
49
|
-
If VERSION is specified, deletes only that specific version.
|
|
50
|
-
HELP
|
|
38
|
+
versions: "Usage: filedepot versions HANDLE\n\nList all versions of a handle. Each version has an integer ID from 1 to n.",
|
|
39
|
+
delete: "Usage: filedepot delete HANDLE [VERSION]\n\nAfter confirmation, deletes all versions of a file.\nIf VERSION is specified, deletes only that specific version.",
|
|
40
|
+
info: "Usage: filedepot info HANDLE\n\nShow info for a handle: remote base path and current version."
|
|
51
41
|
}.freeze
|
|
52
42
|
|
|
53
43
|
desc "config", "Open the config file using $EDITOR"
|
|
@@ -60,6 +50,20 @@ module Filedepot
|
|
|
60
50
|
|
|
61
51
|
desc "push HANDLE FILE", "Send a file to the current storage with a specific handle"
|
|
62
52
|
def push(handle, file_path)
|
|
53
|
+
if handle.to_s.strip.empty?
|
|
54
|
+
puts "Error: Handle is required."
|
|
55
|
+
return
|
|
56
|
+
end
|
|
57
|
+
if file_path.to_s.strip.empty?
|
|
58
|
+
puts "Error: File path is required."
|
|
59
|
+
return
|
|
60
|
+
end
|
|
61
|
+
path = File.expand_path(file_path)
|
|
62
|
+
unless File.file?(path)
|
|
63
|
+
puts "Error: File not found: #{path}"
|
|
64
|
+
return
|
|
65
|
+
end
|
|
66
|
+
|
|
63
67
|
source = Config.current_source
|
|
64
68
|
if source.nil?
|
|
65
69
|
puts "Error: No storage source configured. Run 'filedepot config' to set up."
|
|
@@ -67,8 +71,13 @@ module Filedepot
|
|
|
67
71
|
end
|
|
68
72
|
|
|
69
73
|
storage = Storage::Base.for(source)
|
|
70
|
-
storage.push(handle,
|
|
71
|
-
|
|
74
|
+
storage.push(handle, path)
|
|
75
|
+
version = storage.current_version(handle)
|
|
76
|
+
puts "Pushed #{file_path} as #{handle} (version #{version})"
|
|
77
|
+
uploaded_url = storage.url(handle, version, File.basename(path))
|
|
78
|
+
puts uploaded_url if uploaded_url
|
|
79
|
+
rescue RuntimeError => e
|
|
80
|
+
puts "Error: #{e.message}"
|
|
72
81
|
end
|
|
73
82
|
|
|
74
83
|
desc "pull HANDLE", "Get file from storage"
|
|
@@ -93,6 +102,8 @@ module Filedepot
|
|
|
93
102
|
info = storage.pull_info(handle, version, local_path)
|
|
94
103
|
target_path = info[:target_path]
|
|
95
104
|
|
|
105
|
+
puts "Pulling #{handle} (version #{info[:version_num]})"
|
|
106
|
+
|
|
96
107
|
parent_dir = File.dirname(target_path)
|
|
97
108
|
unless parent_dir == "." || File.directory?(parent_dir)
|
|
98
109
|
return unless confirm?("create local directory #{parent_dir}, y/n?")
|
|
@@ -104,7 +115,9 @@ module Filedepot
|
|
|
104
115
|
end
|
|
105
116
|
|
|
106
117
|
storage.pull(handle, version, target_path)
|
|
107
|
-
puts "
|
|
118
|
+
puts "pulled to #{target_path}"
|
|
119
|
+
rescue RuntimeError => e
|
|
120
|
+
puts "Error: #{e.message}"
|
|
108
121
|
end
|
|
109
122
|
|
|
110
123
|
desc "versions HANDLE", "List all versions of a handle (each version has an integer from 1 to n)"
|
|
@@ -123,7 +136,7 @@ module Filedepot
|
|
|
123
136
|
storage = Storage::Base.for(source)
|
|
124
137
|
versions_list = storage.versions(handle)
|
|
125
138
|
if versions_list.empty?
|
|
126
|
-
puts "
|
|
139
|
+
puts "Error: Handle '#{handle}' not found."
|
|
127
140
|
else
|
|
128
141
|
max_display = 10
|
|
129
142
|
to_show = versions_list.first(max_display)
|
|
@@ -135,14 +148,81 @@ module Filedepot
|
|
|
135
148
|
end
|
|
136
149
|
end
|
|
137
150
|
|
|
151
|
+
desc "info HANDLE", "Show info for a handle"
|
|
152
|
+
def info(handle = nil)
|
|
153
|
+
if handle.nil?
|
|
154
|
+
puts COMMAND_HELP[:info]
|
|
155
|
+
return
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
source = Config.current_source
|
|
159
|
+
if source.nil?
|
|
160
|
+
puts "Error: No storage source configured. Run 'filedepot config' to set up."
|
|
161
|
+
return
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
storage = Storage::Base.for(source)
|
|
165
|
+
versions_list = storage.versions(handle)
|
|
166
|
+
if versions_list.empty?
|
|
167
|
+
puts "Error: Handle '#{handle}' not found."
|
|
168
|
+
return
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
data = storage.info(handle)
|
|
172
|
+
|
|
173
|
+
puts "handle: #{data[:handle]}"
|
|
174
|
+
puts "remote_base_path: #{data[:remote_base_path]}"
|
|
175
|
+
puts "current version: #{data[:current_version]}"
|
|
176
|
+
puts "updated at: #{data[:updated_at]}" if data[:updated_at]
|
|
177
|
+
puts "latest version url: #{data[:latest_version_url]}" if data[:latest_version_url]
|
|
178
|
+
end
|
|
179
|
+
|
|
138
180
|
desc "delete HANDLE [VERSION]", "After confirmation, delete all versions of a file; or only a specific version if specified"
|
|
139
181
|
def delete(handle = nil, version = nil)
|
|
140
182
|
if handle.nil?
|
|
141
183
|
puts COMMAND_HELP[:delete]
|
|
142
184
|
return
|
|
143
185
|
end
|
|
186
|
+
|
|
187
|
+
source = Config.current_source
|
|
188
|
+
if source.nil?
|
|
189
|
+
puts "Error: No storage source configured. Run 'filedepot config' to set up."
|
|
190
|
+
return
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
storage = Storage::Base.for(source)
|
|
194
|
+
versions_list = storage.versions(handle)
|
|
195
|
+
if versions_list.empty?
|
|
196
|
+
puts "Error: Handle '#{handle}' not found."
|
|
197
|
+
return
|
|
198
|
+
end
|
|
199
|
+
if version
|
|
200
|
+
version_nums = versions_list.map(&:first)
|
|
201
|
+
unless version_nums.include?(version.to_i)
|
|
202
|
+
puts "Error: Version #{version} not found for handle '#{handle}'."
|
|
203
|
+
return
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
144
207
|
version_str = version ? " version #{version}" : " all versions"
|
|
145
|
-
puts "
|
|
208
|
+
puts "This will delete '#{handle}'#{version_str}."
|
|
209
|
+
begin
|
|
210
|
+
print "Type the handle name to confirm: "
|
|
211
|
+
input = $stdin.gets&.strip
|
|
212
|
+
rescue Interrupt
|
|
213
|
+
puts
|
|
214
|
+
return
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
unless input == handle
|
|
218
|
+
puts "Aborted (handle name did not match)."
|
|
219
|
+
return
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
storage.delete(handle, version)
|
|
223
|
+
puts "Deleted #{handle}#{version ? " version #{version}" : ""}."
|
|
224
|
+
rescue RuntimeError => e
|
|
225
|
+
puts "Error: #{e.message}"
|
|
146
226
|
end
|
|
147
227
|
|
|
148
228
|
default_task :default
|
|
@@ -166,6 +246,7 @@ module Filedepot
|
|
|
166
246
|
puts " filedepot push HANDLE FILE Send file to current storage"
|
|
167
247
|
puts " filedepot pull HANDLE [--path PATH] [--version N] Get file from storage"
|
|
168
248
|
puts " filedepot versions HANDLE List all versions of a handle"
|
|
249
|
+
puts " filedepot info HANDLE Show info for a handle"
|
|
169
250
|
puts " filedepot delete HANDLE [VER] Delete file(s) after confirmation"
|
|
170
251
|
puts ""
|
|
171
252
|
puts "Use 'filedepot help COMMAND' for more information on a command."
|
|
@@ -52,10 +52,35 @@ module Filedepot
|
|
|
52
52
|
raise "not implemented"
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
+
def versions_data(handle)
|
|
56
|
+
raise "not implemented"
|
|
57
|
+
end
|
|
58
|
+
|
|
55
59
|
def delete(handle, version = nil)
|
|
56
60
|
raise "not implemented"
|
|
57
61
|
end
|
|
58
62
|
|
|
63
|
+
def info(handle)
|
|
64
|
+
data = versions_data(handle)
|
|
65
|
+
latest = data.first
|
|
66
|
+
result = {
|
|
67
|
+
handle: handle,
|
|
68
|
+
remote_base_path: remote_base_path,
|
|
69
|
+
current_version: current_version(handle)
|
|
70
|
+
}
|
|
71
|
+
result[:updated_at] = latest[:datetime] if latest
|
|
72
|
+
result[:latest_version_url] = latest[:url] if latest && latest[:url]
|
|
73
|
+
result
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def url(handle, version, filename)
|
|
77
|
+
base = @source["public_base_path"].to_s.sub(%r{/+$}, "")
|
|
78
|
+
return nil if base.empty? || filename.nil? || filename.empty?
|
|
79
|
+
|
|
80
|
+
path = [handle, version, filename].join("/")
|
|
81
|
+
"#{base}/#{path}"
|
|
82
|
+
end
|
|
83
|
+
|
|
59
84
|
protected
|
|
60
85
|
|
|
61
86
|
def remote_base_path
|
|
@@ -27,7 +27,7 @@ module Filedepot
|
|
|
27
27
|
|
|
28
28
|
def push(handle, local_path)
|
|
29
29
|
path = File.expand_path(local_path)
|
|
30
|
-
raise "File not found: #{
|
|
30
|
+
raise "File not found: #{path}" unless File.file?(path)
|
|
31
31
|
|
|
32
32
|
push_path = next_version_path(handle)
|
|
33
33
|
|
|
@@ -57,14 +57,14 @@ module Filedepot
|
|
|
57
57
|
def pull(handle, version = nil, local_path = nil)
|
|
58
58
|
ssh_session do |ssh|
|
|
59
59
|
versions_list = versions_for(ssh, handle)
|
|
60
|
-
raise "
|
|
60
|
+
raise "Handle '#{handle}' not found" if versions_list.empty?
|
|
61
61
|
|
|
62
62
|
version_num = version ? version.to_i : versions_list.max
|
|
63
|
-
raise "Version #{version} not found" unless versions_list.include?(version_num)
|
|
63
|
+
raise "Version #{version} not found for handle '#{handle}'" unless versions_list.include?(version_num)
|
|
64
64
|
|
|
65
65
|
version_dir = File.join(remote_handle_path(handle), version_num.to_s)
|
|
66
66
|
remote_file = first_file_in_dir(ssh, version_dir)
|
|
67
|
-
raise "No file found in version #{version_num}" if remote_file.nil?
|
|
67
|
+
raise "No file found in version #{version_num} for handle '#{handle}'" if remote_file.nil?
|
|
68
68
|
|
|
69
69
|
remote_filename = File.basename(remote_file)
|
|
70
70
|
target_path = resolve_local_path(local_path, remote_filename)
|
|
@@ -77,14 +77,14 @@ module Filedepot
|
|
|
77
77
|
def pull_info(handle, version = nil, local_path = nil)
|
|
78
78
|
ssh_session do |ssh|
|
|
79
79
|
versions_list = versions_for(ssh, handle)
|
|
80
|
-
raise "
|
|
80
|
+
raise "Handle '#{handle}' not found" if versions_list.empty?
|
|
81
81
|
|
|
82
82
|
version_num = version ? version.to_i : versions_list.max
|
|
83
|
-
raise "Version #{version} not found" unless versions_list.include?(version_num)
|
|
83
|
+
raise "Version #{version} not found for handle '#{handle}'" unless versions_list.include?(version_num)
|
|
84
84
|
|
|
85
85
|
version_dir = File.join(remote_handle_path(handle), version_num.to_s)
|
|
86
86
|
remote_file = first_file_in_dir(ssh, version_dir)
|
|
87
|
-
raise "No file found in version #{version_num}" if remote_file.nil?
|
|
87
|
+
raise "No file found in version #{version_num} for handle '#{handle}'" if remote_file.nil?
|
|
88
88
|
|
|
89
89
|
remote_filename = File.basename(remote_file)
|
|
90
90
|
target_path = resolve_local_path(local_path, remote_filename)
|
|
@@ -93,18 +93,31 @@ module Filedepot
|
|
|
93
93
|
end
|
|
94
94
|
end
|
|
95
95
|
|
|
96
|
-
def
|
|
96
|
+
def versions_data(handle)
|
|
97
97
|
ssh_session do |ssh|
|
|
98
98
|
versions_list = versions_for(ssh, handle).sort.reverse
|
|
99
99
|
versions_list.map do |v|
|
|
100
100
|
version_dir = File.join(remote_handle_path(handle), v.to_s)
|
|
101
101
|
epoch = stat_mtime(ssh, version_dir)
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
remote_file = first_file_in_dir(ssh, version_dir)
|
|
103
|
+
path = remote_file || version_dir
|
|
104
|
+
filename = remote_file ? File.basename(remote_file) : nil
|
|
105
|
+
{
|
|
106
|
+
version: v,
|
|
107
|
+
datetime: epoch ? Time.at(epoch) : nil,
|
|
108
|
+
path: path,
|
|
109
|
+
handle: handle,
|
|
110
|
+
filename: filename,
|
|
111
|
+
url: url(handle, v, filename)
|
|
112
|
+
}
|
|
104
113
|
end
|
|
105
114
|
end
|
|
106
115
|
end
|
|
107
116
|
|
|
117
|
+
def versions(handle)
|
|
118
|
+
versions_data(handle).map { |d| [d[:version], d[:datetime] ? d[:datetime].to_s : ""] }
|
|
119
|
+
end
|
|
120
|
+
|
|
108
121
|
def delete(handle, version = nil)
|
|
109
122
|
ssh_session do |ssh|
|
|
110
123
|
handle_dir = remote_handle_path(handle)
|
data/lib/filedepot/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: filedepot
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Filedepot
|
|
@@ -51,6 +51,34 @@ dependencies:
|
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
53
|
version: '1.3'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: minitest
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '5.0'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '5.0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: rake
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '13.0'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '13.0'
|
|
54
82
|
description: Command-line tool to sync files on remote storage via SSH
|
|
55
83
|
email:
|
|
56
84
|
- ''
|