s3p 3.5.4 → 3.6.1

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.
package/package.json CHANGED
@@ -5,24 +5,26 @@
5
5
  },
6
6
  "bugs": "https:/github.com/generalui/s3p/issues",
7
7
  "dependencies": {
8
- "@art-suite/cli": "^1.3.11",
8
+ "@art-suite/cli": "2.2",
9
9
  "@aws-sdk/client-s3": "^3.436.0",
10
+ "@aws-sdk/lib-storage": "^3.635.0",
10
11
  "art-class-system": "^1.11.13",
11
12
  "art-communication-status": "^1.6.0",
12
- "art-standard-lib": "^1.70.12",
13
+ "art-standard-lib": "^1.73.3",
13
14
  "caffeine-script-runtime": "^1.14.0",
15
+ "glob": "^11.0.0",
14
16
  "neptune-namespaces-runtime": "^3.2.9",
15
17
  "shell-escape": "^0.2.0"
16
18
  },
17
19
  "description": "S3P is a CLI and library that is 5x to 50x faster than aws-cli for bulk S3 operations including: summarize, compare, copy and sync.",
18
20
  "devDependencies": {
21
+ "@art-suite/standard-package-builder": "^1.2.6",
19
22
  "art-build-configurator": "^1.29.3",
20
23
  "art-testbench": "^3.2.2",
21
- "caffeine-script": "^0.74.0",
24
+ "caffeine-script": "^0.75.1",
22
25
  "jest": "^29.3.1",
23
26
  "mock-fs": "^5.0.0",
24
- "neptune-namespaces": "^4.2.9",
25
- "npm-watch": "^0.11.0"
27
+ "neptune-namespaces": "^4.2.9"
26
28
  },
27
29
  "homepage": "https://github.com/generalui/s3p",
28
30
  "jest": {
@@ -30,6 +32,10 @@
30
32
  "build/**/*.js",
31
33
  "!build/test/**/*.js"
32
34
  ],
35
+ "coveragePathIgnorePatterns": [
36
+ "/index.js$",
37
+ "/namespace.js$"
38
+ ],
33
39
  "testEnvironment": "node",
34
40
  "testPathIgnorePatterns": [
35
41
  "/node_modules/",
@@ -44,14 +50,15 @@
44
50
  "url": "https://github.com/generalui/s3p.git"
45
51
  },
46
52
  "scripts": {
47
- "build": "caf -m -c source -o build\nnpm run nn",
48
- "build-clean": "npm run clean; npm run build",
53
+ "build": "rsync -av --exclude='*.caf' ./source/ ./build/\ncaf -m -c source -o build\nnpm run nn",
54
+ "build-clean": "# BUILD CLEAN START\n# - building into: build-temp\n#\nrm -rf ./__build-old__\nrsync -av --exclude='*.caf' ./source/ ./build-temp/\ncaf -m -c source -o build-temp\nnpm run nn\n\nnn build-temp/*\n\n# BUILD CLEAN FINIALIZING\n# - replace old build in: build\n# - with new build from: build-temp\n#\nmv build __build-old__\nmv build-temp build\nrm -rf ./__build-old__",
55
+ "build-watch": "nodemon -e js,ts,jsx,tsx,caf,coffee,json -i \"build/**\" -x npm run build",
49
56
  "clean": "rm -rf build/*",
50
57
  "nn": "nn build/* ",
51
- "test": "npm run nn\njest",
52
- "watch": "npm-watch"
58
+ "test": "jest",
59
+ "test-watch": "jest --watch"
53
60
  },
54
- "version": "3.5.4",
61
+ "version": "3.6.1",
55
62
  "watch": {
56
63
  "build": {
57
64
  "extensions": [
@@ -1,22 +1,89 @@
1
1
  import &StandardImport, {} &path
2
2
  exec = &util.promisify &child_process.exec
3
3
 
4
- createS3Url: (bucket, folder, key) ->
5
- if folder
6
- path.join folder, key
7
- else
8
- "" s3://#{bucket}/#{key}
9
-
10
- shellExec: (command) ->
11
- exec command
12
- .then ({stdout}) -> stdout
13
-
14
- humanByteSize: (bytes, decimals = 2) ->
15
- switch
16
- when bytes < 1024 then "" #{bytes/1024**0 | 0}_B
17
- when bytes < 1024**2 then "" #{(bytes/1024**1).toFixed decimals}kB
18
- when bytes < 1024**3 then "" #{(bytes/1024**2).toFixed decimals}mB
19
- when bytes < 1024**4 then "" #{(bytes/1024**3).toFixed decimals}gB
20
- when bytes < 1024**5 then "" #{(bytes/1024**4).toFixed decimals}tB
21
- when bytes < 1024**6 then "" #{(bytes/1024**5).toFixed decimals}pB
22
- else "" #{(bytes/1024**6).toFixed decimals}eB
4
+ {}
5
+ createS3Url: (bucket, folder, key) ->
6
+ if folder
7
+ path.join folder, key
8
+ else
9
+ "" s3://#{bucket}/#{key}
10
+
11
+ shellExec: (command) ->
12
+ exec command
13
+ .then ({stdout}) -> stdout
14
+
15
+ humanByteSize: (bytes, decimals = 2) ->
16
+ switch
17
+ when bytes < 1024 then "" #{bytes/1024**0 | 0}_B
18
+ when bytes < 1024**2 then "" #{(bytes/1024**1).toFixed decimals}kB
19
+ when bytes < 1024**3 then "" #{(bytes/1024**2).toFixed decimals}mB
20
+ when bytes < 1024**4 then "" #{(bytes/1024**3).toFixed decimals}gB
21
+ when bytes < 1024**5 then "" #{(bytes/1024**4).toFixed decimals}tB
22
+ when bytes < 1024**6 then "" #{(bytes/1024**5).toFixed decimals}pB
23
+ else "" #{(bytes/1024**6).toFixed decimals}eB
24
+
25
+ s3UrlPattern = ///i ^s3://([^\/]+)\/?(.*)$
26
+
27
+ # https://mybucket.s3.amazonaws.com/prefix
28
+ # https://mybucket.s3-us-west-2.amazonaws.com/prefix
29
+ httpUrlPattern =
30
+ ///i
31
+ ^
32
+ https?://
33
+
34
+ ([^.]+) # bucket
35
+
36
+ \.s3
37
+
38
+ ([-.]([-a-z0-9]+))? # region
39
+
40
+ \.amazonaws\.com
41
+
42
+ (\/(.+))? # prefix
43
+ $
44
+
45
+ # https://s3-us-west-2.amazonaws.com/mybucket/prefix
46
+ regionFirstHttpUrlPattern =
47
+ ///i
48
+ ^
49
+ https?://s3[-.]
50
+
51
+ ([-a-z0-9]+) # region
52
+
53
+ \.amazonaws\.com
54
+
55
+ \/([^/]+) # bucket
56
+
57
+ (\/(.+))? # prefix
58
+ $
59
+
60
+ parseS3Url = (input) ->
61
+ if isString input
62
+ input = input.trim()
63
+
64
+ log parseS3Url: {} input
65
+
66
+ switch
67
+ when match = input.match s3UrlPattern
68
+ [_, bucket, prefix] = match
69
+
70
+ when match = input.match httpUrlPattern
71
+ [_, bucket, _, region, _, prefix] = match
72
+
73
+ when match = input.match regionFirstHttpUrlPattern
74
+ [_, region, bucket, _, prefix] = match
75
+
76
+ else
77
+ folder = input
78
+
79
+ {} bucket, prefix, region, folder
80
+ else {}
81
+
82
+ processArgsIntoOptions = (args = []) ->
83
+ [fromStr, toStr] = args
84
+ if fromStr
85
+ {bucket, prefix, region, folder} = parseS3Url fromStr
86
+ if toStr
87
+ {bucket: toBucket, prefix: toPrefix, region: toRegion, folder: tofolder} = parseS3Url toStr
88
+
89
+ merge {} bucket, prefix, region, folder, toBucket, toPrefix, toRegion, tofolder
@@ -0,0 +1,99 @@
1
+ import &StandardImport, &glob, {} &path, fs: &fs.promises
2
+
3
+ class LocalFileLister extends BaseClass
4
+
5
+ constructor: ({fromFolder, _allFiles, _getFileStat}) ->
6
+ @_fromFolder =
7
+ # ensure has trailing slash
8
+ path.join fromFolder, "aaa"
9
+ .replace /aaa$/, ''
10
+
11
+ @_custom_getFileStat = _getFileStat
12
+
13
+ @_fileStats = {}
14
+
15
+ @_allFilesPromise =
16
+ if _allFiles
17
+ Promise.resolve _allFiles
18
+ else
19
+ glob
20
+ path.join fromFolder, "**"
21
+ nodir: true
22
+
23
+ .then ({found}) -> found
24
+
25
+ .then (files) ->
26
+ array file in files.sort()
27
+ file = file.slice fromFolder.length + 1, file.length
28
+
29
+ @getter files: -> @_allFilesPromise
30
+ @getter :fromFolder
31
+
32
+ ## list
33
+ IN:
34
+ limit: maximum number of files to return
35
+ startAfter: if present, returns files strictly greater than startAfter
36
+ stopAt: if present, returns files up to and including stopAt
37
+
38
+ OUT:
39
+ []
40
+ Key:
41
+ LastModified:
42
+ Size
43
+ Owner
44
+ list: ({limit=1000, startAfter, stopAt}) ->
45
+ if present startAfter then @_findStartIndex startAfter
46
+ else Promise.deepAll {} @files, startIndex: 0
47
+ .then ({files, startIndex}) ->
48
+ out = []
49
+ i = startIndex
50
+ tilIndex = min files.length, startIndex + limit
51
+ stopAt ?= peek files
52
+ while i < tilIndex && files[i] <= stopAt
53
+ out.push @_getFileStat files[i]
54
+ i++
55
+
56
+ Promise.all out
57
+
58
+ #################################################
59
+ # PRIVATE
60
+ #################################################
61
+ ##
62
+ IN: file name
63
+ OUT: []
64
+ Key: file
65
+ LastModified: stats.mtime
66
+ Size: stats.size
67
+ Owner:
68
+ DisplayName: stats.uid.toString()
69
+ ID: stats.uid.toString()
70
+ _getFileStat: (file) ->
71
+ if @_custom_getFileStat
72
+ @_custom_getFileStat file
73
+ else
74
+ if f = @_fileStats[file]
75
+ Promise.resolve f
76
+ else
77
+ filePath = path.join @fromFolder, file
78
+ fs.stat path.join @fromFolder, file
79
+ .then (stats) -> @_fileStats[file] =
80
+ Key: file
81
+ LastModified: stats.mtime
82
+ Size: stats.size
83
+ Owner:
84
+ DisplayName: stats.uid.toString()
85
+ ID: stats.uid.toString()
86
+
87
+ _findStartIndex: (startAfter) ->
88
+ @files
89
+ .then (files) ->
90
+ j = 0
91
+ k = files.length
92
+ while j < k
93
+ m = (j + k) / 2 | 0
94
+ pivot = files[m]
95
+ switch
96
+ when startAfter == pivot then k = j = m
97
+ when startAfter < pivot then k = m
98
+ else j = m + 1
99
+ {} files, startIndex: j
@@ -1,22 +1,14 @@
1
- import &StandardImport, &FsEasy, &LibMisc, &@aws-sdk/client-s3, {} &shellEscape, &util.promisify, &fs, &path
1
+ import &StandardImport, &FsEasy, &LibMisc, &@aws-sdk/client-s3, &@aws-sdk/lib-storage, {} &shellEscape, &util.promisify, &path
2
2
  escape = (single) -> shellEscape [] single
3
3
 
4
+ fs = merge
5
+ {createReadStream} = &fs
6
+ {copyFile} = &fs.promises
7
+
4
8
  # &@aws-sdk/client-s3.config.setPromisesDependency &bluebird
5
9
 
6
10
  class S3 extends BaseClass
7
11
 
8
- @awsSdkS3 = new S3Client merge region: process.env.AWS_REGION # useAccelerateEndpoint: true
9
- # https://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration-examples.html
10
-
11
- @classGetter
12
- s3: ->
13
- listBuckets: (options = {}) -> Promise.resolve @awsSdkS3.send new ListBucketsCommand options
14
- listObjectsV2: (options = {}) -> Promise.resolve @awsSdkS3.send new ListObjectsV2Command options
15
- getObject: (options = {}) -> Promise.resolve @awsSdkS3.send new GetObjectCommand options
16
- copyObject: (options = {}) -> Promise.resolve @awsSdkS3.send new CopyObjectCommand options
17
- deleteObject: (options = {}) -> Promise.resolve @awsSdkS3.send new DeleteObjectCommand options
18
- headObject: (options = {}) -> Promise.resolve @awsSdkS3.send new HeadObjectCommand options
19
-
20
12
  @commonCopyOptionsSdkKeys =
21
13
  :ACL
22
14
  :CacheControl
@@ -30,12 +22,41 @@ class S3 extends BaseClass
30
22
 
31
23
  @commonSdkCopyOptionsByLowerCamelCaseKeys = object k from @commonCopyOptionsSdkKeys with-key lowerCamelCase k
32
24
 
33
- @listBuckets: =>
25
+ ## constructor
26
+ IN: options:
27
+ region: string, default: process.env.AWS_REGION
28
+ useAccelerateEndpoint: boolean, default: false
29
+
30
+ constructor: (options = []) ->
31
+ @awsSdkS3 = new S3Client merge
32
+ region: process.env.AWS_REGION
33
+ {region, useAccelerateEndpoint} = options
34
+
35
+ @getter
36
+ s3: ->
37
+ listBuckets: (options = {}) -> Promise.resolve @awsSdkS3.send new ListBucketsCommand options
38
+ listObjectsV2: (options = {}) -> Promise.resolve @awsSdkS3.send new ListObjectsV2Command options
39
+ getObject: (options = {}) -> Promise.resolve @awsSdkS3.send new GetObjectCommand options
40
+ copyObject: (options = {}) -> Promise.resolve @awsSdkS3.send new CopyObjectCommand options
41
+ deleteObject: (options = {}) -> Promise.resolve @awsSdkS3.send new DeleteObjectCommand options
42
+ headObject: (options = {}) -> Promise.resolve @awsSdkS3.send new HeadObjectCommand options
43
+
44
+
45
+ listBuckets: ->
34
46
  @s3.listBuckets()
35
47
  .then ({Buckets}) ->
36
48
  object {Name, CreationDate} from Buckets with-key Name with CreationDate
37
49
 
38
- @list: ({bucket, prefix, limit=1000, fetchOwner, startAfter}) =>
50
+ ## list
51
+ OUT:
52
+ []
53
+ Key:
54
+ LastModified:
55
+ Size
56
+ ETag
57
+ StorageClass
58
+ Owner
59
+ list: ({bucket, prefix, limit=1000, fetchOwner, startAfter, stopAt}) ->
39
60
  startTime = currentSecond()
40
61
  @s3.listObjectsV2
41
62
  Bucket: bucket
@@ -43,6 +64,7 @@ class S3 extends BaseClass
43
64
  MaxKeys: limit
44
65
  StartAfter: startAfter
45
66
  FetchOwner: fetchOwner
67
+
46
68
  .tapCatch (error) ->
47
69
  log.error S3.list-error: {} bucket, prefix, startAfter, limit, error
48
70
 
@@ -56,14 +78,12 @@ class S3 extends BaseClass
56
78
  else if duration > 60
57
79
  log.warn S3.list-slow: {} bucket, prefix, startAfter, limit, duration, results: merge results, Contents: "Array #{results.Contents.length}"
58
80
 
59
- results.Contents
81
+ if present stopAt
82
+ array item in-array results.Contents when item.Key <= stopAt
83
+ else
84
+ results.Contents
60
85
 
61
- ##
62
- TODO:
63
- For large files (>5GB), we need to just use the CLI:
64
- aws s3 cp s3://resbio-fastq-drop-internal/180316_NB501104_0354_AHM2L2BGX5/Undetermined/Undetermined_S0_L001_R1_001.fastq.gz s3://genui-dynamodb-migration-scratch/large-test.gz
65
- We can add --quiet (probably)
66
- Further, that should use acceleration, if enabled - still need to test that.
86
+ ## copy
67
87
  IN:
68
88
  options:
69
89
  size: Bytes (optional) if provided and if bigger than the max size allowed by copyObject, largeCopy is used
@@ -73,25 +93,28 @@ class S3 extends BaseClass
73
93
  key: <String>
74
94
  fromKey: from-key (default: key)
75
95
  toKey: to-key (default: key)
96
+ fromFolder: string
76
97
 
77
98
  toFolder: local folder
78
99
  OUT:
79
100
  Promise.then -> see https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#copyObject-property > Callback > Parameters
80
101
  {} CopyObjectResult, ...
81
- @copy: (options) =>
102
+ copy: (options) ->
82
103
  if options extract largeCopyThreshold, scratchState
83
104
  copyScratchState = scratchState.copyScratchState ?= {}
84
105
 
85
106
  @_normalizeCopyOptions(options) extract
86
- fromBucket, toBucket, fromKey, toKey, size
87
- toFolder
88
- pretend
89
- verbose
107
+ fromBucket, fromFolder, fromKey
108
+ toBucket, toFolder, toKey
109
+ pretend, verbose
110
+ size
90
111
 
91
112
  if size >= largeCopyThreshold
92
113
  @largeCopy {}
93
114
  commonCopyOptions: @extractCommonCopyOptions options
94
- fromBucket, toBucket, toFolder, fromKey, toKey, pretend, verbose
115
+ fromBucket, fromFolder, fromKey
116
+ toBucket, toFolder, toKey
117
+ pretend, verbose
95
118
 
96
119
  else
97
120
  copyOptions = merge
@@ -116,14 +139,30 @@ class S3 extends BaseClass
116
139
  if pretend
117
140
  timeout 1 -> pretend: true
118
141
 
142
+ else if fromFolder
143
+ fromFilePath = path.join fromFolder, fromKey
144
+ if toBucket
145
+ new Upload
146
+ client: @s3
147
+ params:
148
+ Bucket: toBucket
149
+ Key: toKey
150
+ Body: fs.createReadStream fromFilePath
151
+
152
+ .done()
153
+
154
+ else
155
+ createParentDirs toLocalFile, copyScratchState
156
+ .then -> fs.copyFile fromFilePath, toLocalFile
157
+
119
158
  else if toFolder
120
159
  if /\/$/.test(toKey) && size == 0 # this is a folder
121
- createParentDirs toLocalFile + "ignored-file-name"
122
- else
123
- createWriteStreamSafe
124
- toLocalFile
160
+ createParentDirs
161
+ path.join toLocalFile, "ignored-file-name"
125
162
  copyScratchState
126
163
 
164
+ else
165
+ createWriteStreamSafe toLocalFile, copyScratchState
127
166
  .then (writeStream) -> new Promise (resolve, reject) ->
128
167
  @s3.getObject copyOptions
129
168
  .then ({Body}) ->
@@ -135,12 +174,12 @@ class S3 extends BaseClass
135
174
  else
136
175
  @s3.copyObject copyOptions
137
176
 
138
- @delete: (options) =>
177
+ delete: (options) ->
139
178
  @s3.deleteObject
140
179
  Bucket: options.bucket
141
180
  Key: options.key
142
181
 
143
- @largeCopy: (options) =>
182
+ largeCopy: (options) ->
144
183
  options extract commonCopyOptions
145
184
  @_normalizeCopyOptions(options) extract
146
185
  fromBucket,
@@ -170,13 +209,13 @@ class S3 extends BaseClass
170
209
  else
171
210
  shellExec command
172
211
 
173
- # get object's metdata
174
- @headObject: ({bucket, key}) =>
212
+ # get object's metadata
213
+ headObject: ({bucket, key}) ->
175
214
  @s3.headObject
176
215
  Bucket: bucket
177
216
  Key: key
178
217
 
179
- @_normalizeCopyOptions: (options) ->
218
+ _normalizeCopyOptions: (options) ->
180
219
  options extract
181
220
  bucket, key
182
221
  fromBucket = bucket
@@ -192,15 +231,15 @@ class S3 extends BaseClass
192
231
  throw new Error "" Missing one of: fromBucket, toBucket, fromKey, toKey or bucket or key as a default
193
232
  merge options, {} fromBucket, fromKey, toBucket, toKey
194
233
 
195
- @_getCommonCopyOptionsForSdk: (options) =>
196
- object sdkKey, lowerCamelCasedKey from @commonSdkCopyOptionsByLowerCamelCaseKeys with-key sdkKey
234
+ _getCommonCopyOptionsForSdk: (options) ->
235
+ object sdkKey, lowerCamelCasedKey from S3.commonSdkCopyOptionsByLowerCamelCaseKeys with-key sdkKey
197
236
  options[lowerCamelCasedKey]
198
237
 
199
- @extractCommonCopyOptions: (options) =>
200
- object v, k from @commonSdkCopyOptionsByLowerCamelCaseKeys
238
+ extractCommonCopyOptions: (options) ->
239
+ object v, k from S3.commonSdkCopyOptionsByLowerCamelCaseKeys
201
240
  options[k]
202
241
 
203
- @shouldSyncObjects: (options) ->
242
+ shouldSyncObjects: (options) ->
204
243
  @_normalizeCopyOptions(options) extract fromBucket, toBucket, fromKey, toKey
205
244
  Promise.then ->
206
245
  if options.size < 1024**2 || !options.size
@@ -216,7 +255,7 @@ class S3 extends BaseClass
216
255
  OUT:
217
256
  copied: Bool
218
257
  status: if not copied, explains why
219
- @syncObject: (options) =>
258
+ syncObject: (options) ->
220
259
  @shouldSyncObjects options
221
260
  .then (shouldSync) ->
222
261
  if shouldSync
@@ -1,4 +1,4 @@
1
- import &path, &ArtStandardLib, &ArtClassSystem, &Lib, {} &colors
1
+ import &path, &ArtStandardLib, &ArtClassSystem, &Lib
2
2
 
3
3
  class S3Comprehensions
4
4
 
@@ -210,7 +210,7 @@ class S3Comprehensions
210
210
  While it returns true, it will be called once/second/parallel-branch until it returns false.
211
211
 
212
212
  COMPREHENSION
213
- with: (Item) -> ignored - alais of `map`
213
+ with: (Item) -> ignored - alias of `map`
214
214
  when: (Item) -> T/F - if true, pass it on to `map`, else skip
215
215
  returning: if present, return this value instead of stats
216
216
  into: <ALIAS: returning>
@@ -234,6 +234,7 @@ class S3Comprehensions
234
234
  startAfter
235
235
  stopAt
236
236
  filter
237
+ fromFolder
237
238
 
238
239
  # compare-to options
239
240
  compare
@@ -243,7 +244,7 @@ class S3Comprehensions
243
244
  # limits
244
245
  limit = 1000
245
246
  maxListRequests
246
- listConcurrency = 100 # max open reqeusts
247
+ listConcurrency = 100 # max open requests
247
248
 
248
249
  # actions
249
250
  returnValue
@@ -257,10 +258,16 @@ class S3Comprehensions
257
258
  throttle
258
259
  stats
259
260
 
261
+ # s3 options
262
+ region
263
+ useAccelerateEndpoint
264
+
260
265
  # internal use only
261
- s3 = &Lib/S3
266
+ s3
262
267
  pwp = new PromiseWorkerPool listConcurrency # internal use only
263
268
 
269
+ s3 ?= new &Lib/S3 {} region, useAccelerateEndpoint: !!useAccelerateEndpoint
270
+
264
271
  if compare && map && toBucket
265
272
  throw new Error "cannot use both `compare` and `map` - use mapList instead"
266
273
 
@@ -303,7 +310,7 @@ class S3Comprehensions
303
310
  ## applyF
304
311
  Apply the custom map or mapList functions
305
312
  IN:
306
- items: list of S3 items in the source bucekt as returned by the AwsSdk s3 list call
313
+ items: list of S3 items in the source bucket as returned by the AwsSdk s3 list call
307
314
  compareItems: (optional, if compare is set), list of items in the toBucket
308
315
  compareStartAfter
309
316
  compareStopAt
@@ -373,8 +380,8 @@ class S3Comprehensions
373
380
  maxOutstanding = Math.max maxOutstanding, outstanding += 2
374
381
 
375
382
  Promise.all []
376
- pwp.queue -> s3.list {} bucket, limit, fetchOwner, startAfter
377
- pwp.queue -> s3.list {} bucket, limit, fetchOwner, startAfter: middleKey
383
+ pwp.queue -> s3.list {} bucket, limit, fetchOwner, startAfter, stopAt: middleKey
384
+ pwp.queue -> s3.list {} bucket, limit, fetchOwner, stopAt, startAfter: middleKey
378
385
 
379
386
  .then ([rawLeftItems, rawRightItems]) ->
380
387
  rawLeftCount = rawLeftItems?.length
@@ -1,4 +1,5 @@
1
- import &path, &ArtStandardLib, &ArtClassSystem, &Lib, {} &colors
1
+ import &path, &ArtStandardLib, &ArtClassSystem, &Lib
2
+ colors = terminalColors
2
3
 
3
4
  S3C = &S3Comprehensions
4
5
 
@@ -304,7 +305,7 @@ class S3P
304
305
  @_copyWrapper: (options, eachFunction) =>
305
306
  options = S3C.normalizeOptions options
306
307
  options extract
307
- s3 = &Lib/S3
308
+ s3
308
309
  toKey
309
310
  stats
310
311
  pretend
@@ -316,6 +317,11 @@ class S3P
316
317
  largeCopyPwp
317
318
  largeCopyThreshold
318
319
 
320
+ # s3 options
321
+ region
322
+ useAccelerateEndpoint
323
+ s3 ?= new &Lib/S3 {} region, useAccelerateEndpoint
324
+
319
325
  stats ?= {}
320
326
  stats.copiedBytes = 0
321
327
  stats.copiedBytesPerSecond = 0
@@ -1,5 +1,15 @@
1
1
  import &StandardImport, &Lib
2
2
 
3
+ standardFromArgs =
4
+ description: "Specify the bucket and optionally a prefix. Supported forms: local/file/path, s3://bucket/prefix, https://s3.region.amazonaws.com/bucket/prefix and https://bucket.s3.amazonaws.com/prefix"
5
+ argument: "[bucket/prefix]"
6
+ argsToOptions: processArgsIntoOptions
7
+
8
+ standardFromToArgs =
9
+ description: "Specify where 'from' and where 'to' do the operation. Supported forms: local/file/path, s3://bucket/prefix, https://s3.region.amazonaws.com/bucket/prefix and https://bucket.s3.amazonaws.com/prefix"
10
+ argument: "[from [to]]"
11
+ argsToOptions: processArgsIntoOptions
12
+
3
13
  allCommandOptions =
4
14
  quiet: "" no output
5
15
  verbose: "" extra output
@@ -12,6 +22,7 @@ allCommandOptions =
12
22
  start-after: :key "" Start iteratating after this key. If 'prefix' is also specified, the set-intersection of the two will be used.
13
23
  stop-at: :key "" Iterate up to, and including, this key. If 'prefix' is also specified, the set-intersection of the two will be used.
14
24
  fetch-owner: "" If you want to filter on the Owner property of items, you'll need to add this option to fetch that property. Note: this adds about 10% overhead for listing items.
25
+ region: "" The AWS region of the bucket. Will use AWS_REGION environment variable if not specified.
15
26
 
16
27
  pattern:
17
28
  "" string OR js:/^any-javascript-regexp/i
@@ -70,7 +81,7 @@ writeOptions =
70
81
  OUTPOSTS
71
82
 
72
83
  toBucketOptions =
73
- to-bucket: argument: :bucket-name required: true description: "" The target bucket. It can be the same bucket.
84
+ to-bucket: argument: :bucket-name description: "" The target bucket. It can be the same bucket.
74
85
  to-prefix: :key-prefix "" If 'prefix' is specified, the target key will REPLACE its source prefix with toPrefix Otherwise, this is the same as add-prefix.
75
86
  add-prefix: :key-prefix "" The source key is prepended with this string for the target bucket.
76
87
  to-key: '"js:(key) => key"' "" Provide an arbitrary JavaScript function for re-keying keys.
@@ -106,12 +117,14 @@ cliCommands =
106
117
  options: merge allCommandOptions, advancedOptionsForAll,
107
118
  long: "" output item size and date as well as key
108
119
  raw: "" output one S3 item per line as JSON with all properties return from listObjects
120
+ args: standardFromArgs
109
121
 
110
122
  summarize:
111
123
  description: "" Scan all items in one bucket and produce a summary of all the items. Uses s3.listObjectsV2.
112
124
  options: merge allCommandOptions,
113
125
  summarize-folders: "" show count and size of each folder
114
126
  advancedOptionsForAll
127
+ args: standardFromArgs
115
128
  examples:
116
129
  bucket: :my-bucket
117
130
  "" get a detailed summary of item counts and sizes in my-bucket
@@ -122,6 +135,7 @@ cliCommands =
122
135
  compare:
123
136
  description: "" Compare two buckets and produce a summary of their differences. Uses s3.listObjectsV2.
124
137
  options: merge allCommandOptions, toBucketOptions, advancedOptionsForAll
138
+ args: standardFromToArgs
125
139
  examples:
126
140
  bucket: :my-bucket to-bucket: :my-to-bucket
127
141
  "" Compare items from my-bucket with my-to-bucket. Shows how many items exist in both, only one, or are difference sizes.
@@ -133,6 +147,7 @@ cliCommands =
133
147
 
134
148
  NOTE: This overwrites existing files in the target bucket. Try the 'sync' command for smarter copies when some of the files have already been copied.
135
149
  options: merge allCommandOptions, toBucketOptions, writeOptions, advancedOptionsForCopy, toFolderOptions
150
+ args: standardFromToArgs
136
151
  examples:
137
152
  bucket: :my-bucket to-bucket: :my-to-bucket
138
153
  "" Copy everything from my-bucket to my-to-bucket
@@ -165,6 +180,7 @@ cliCommands =
165
180
  description: "" Only copy files which do not exist in the target bucket. Uses s3.listObjectsV2, s3.copyObject and shell-exec 'aws s3 cp'.
166
181
  options: merge allCommandOptions, toBucketOptions, writeOptions, advancedOptionsForCopy,
167
182
  overwrite: "" If set, sync will overwrite existing files with different file sizes.
183
+ args: standardFromToArgs
168
184
 
169
185
  examples:
170
186
  bucket: :my-bucket to-bucket: :my-to-bucket
@@ -175,6 +191,7 @@ cliCommands =
175
191
  options: merge allCommandOptions, advancedOptionsForAll,
176
192
  map: :function "" This gets called for each item found. A javascript function of the form (item) => ...
177
193
  map-list: :function "" This gets called with an array of items (length between 1 and 1000). A javascript function of the form (itemList) => ...
194
+ args: standardFromArgs
178
195
  examples: []
179
196
  bucket: :my-bucket
180
197
  map: "" "js:(item) => console.log(item)"
@@ -204,6 +221,7 @@ cliCommands =
204
221
  ""
205
222
  If present, this function will be applied to produce the final result after the last call to reduce.
206
223
  Form: (finalReduceResult) -> ...
224
+ args: standardFromArgs
207
225
 
208
226
  examples:
209
227
  bucket: :my-bucket