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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d3adae8af29b5bf23fca9832843d431f9ecac1a3a185309185858109de26c9ea
4
- data.tar.gz: 180afd3e6ad41a51a63c3ddd8ecfa5920519c9135180c74ea1cc10de631dd629
3
+ metadata.gz: 2e67db1d2be17a3af457047eb147926143e85ce7a41f398ff818fc83c404dcfa
4
+ data.tar.gz: 3a6616e9a4925732d04d78f55fb716127c115d63220b673165795beba8b9d9c7
5
5
  SHA512:
6
- metadata.gz: 2324d4048ddc0d01ad140393850b36c4615cf6104d60251d868563171499b6f5505f761ed1b225a0aa9851fcfadc7b9c6aed1bc9b6ddb068f51d4b9eee6c4d40
7
- data.tar.gz: aea6ac1df0926cff12f09f9547126cb6c449343e62c91f40c1615397396c80608c531c4c2a02faf957f885e18357c92982abf9510f8e7a22c8e3589a3d41627c
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 [VERSION]` | Get file from storage |
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: <<~HELP,
39
- Usage:
40
- filedepot versions HANDLE
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, file_path)
71
- puts "Pushed #{file_path} as #{handle} (version #{storage.current_version(handle)})"
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 "Pulled #{handle} (version #{info[:version_num]}) to #{target_path}"
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 "No versions found for handle: #{handle}"
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 "delete: would delete file '#{handle}'#{version_str} after confirmation (not implemented)"
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: #{local_path}" unless File.file?(path)
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 "No versions found for handle: #{handle}" if versions_list.empty?
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 "No versions found for handle: #{handle}" if versions_list.empty?
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 versions(handle)
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
- date_str = epoch ? Time.at(epoch).to_s : ""
103
- [v, date_str]
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)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Filedepot
4
- VERSION = "0.2.0"
4
+ VERSION = "0.2.2"
5
5
  end
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.0
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
  - ''