safe-rm 2.0.0 → 3.0.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.
- package/.eslintrc.js +1 -0
- package/{sample.safe-rm.conf → .safe-rm/config} +8 -12
- package/README.md +65 -5
- package/bin/rm.sh +98 -11
- package/package.json +1 -1
- package/test/cases.js +70 -26
- package/test/helper.js +19 -14
- package/test/rm.test.js +5 -2
- package/test/safe-rm-as.test.js +6 -3
- package/test/safe-rm.test.js +4 -2
- package/test/code.js +0 -56
package/.eslintrc.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
|
|
3
3
|
# This is a sample configuration file for safe-rm.
|
|
4
|
-
# By default, you could copy this file to ~/.safe-rm
|
|
4
|
+
# By default, you could copy this file to ~/.safe-rm/config
|
|
5
5
|
|
|
6
6
|
#
|
|
7
7
|
# If you want to use a trash directory other than the default system trash,
|
|
8
|
-
# uncomment the line after divider
|
|
8
|
+
# uncomment the line after divider `-----`
|
|
9
9
|
# and set the path to the desired trash directory.
|
|
10
10
|
# By default, safe-rm will use the system trash.
|
|
11
11
|
# -----------------------------------------------
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
#
|
|
15
15
|
# If you want to permanently delete files that are already in the trash,
|
|
16
|
-
# uncomment the line after divider
|
|
16
|
+
# uncomment the line after divider `-----`
|
|
17
17
|
# It defaults to 'no'.
|
|
18
18
|
# Pay attention that any value that starts with 'y' or 'Y' will be treated as 'yes'.
|
|
19
19
|
# -----------------------------------------------
|
|
@@ -21,19 +21,15 @@
|
|
|
21
21
|
|
|
22
22
|
#
|
|
23
23
|
# If you want to disable the use of AppleScript for moving files to the trash,
|
|
24
|
-
# uncomment the line after divider
|
|
24
|
+
# uncomment the line after divider `-----`
|
|
25
25
|
# It defaults to 'yes'.
|
|
26
26
|
# By setting it to 'no', the "put-back" feature will be disabled for MacOS.
|
|
27
27
|
# -----------------------------------------------
|
|
28
28
|
# export SAFE_RM_USE_APPLESCRIPT=no
|
|
29
29
|
|
|
30
30
|
#
|
|
31
|
-
#
|
|
31
|
+
# If you want to change the configuration root directory of safe-rm, in order to
|
|
32
|
+
# - put the .gitignore file in a different directory other than `~/.safe-rm/`
|
|
33
|
+
# Uncomment the line after divider `-----`
|
|
32
34
|
# -----------------------------------------------
|
|
33
|
-
# export
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
# Everything inside the following directories, excluding those directories,
|
|
37
|
-
# will be protected and cannot be removed.
|
|
38
|
-
# -----------------------------------------------
|
|
39
|
-
# export SAFE_RM_PROTECTED_DIRECTORIES=/path/to/dir1:/path/to/dir2
|
|
35
|
+
# export SAFE_RM_CONFIG_ROOT=/usr/local/etc/safe-rm
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
[](https://github.com/kaelzhang/shell-safe-rm/actions/workflows/nodejs.yml)
|
|
14
14
|
|
|
15
|
-
[Safe-rm][safe-rm], a much safer replacement of [`rm`][rm] with **ALMOST FULL** features of the
|
|
15
|
+
[Safe-rm][safe-rm], a drop-in and much safer replacement of the unix [`rm`][rm] command with **ALMOST FULL** features of the original [`rm`][rm].
|
|
16
16
|
|
|
17
17
|
The project was initially developed for Mac OS X, and then tested on Linux.
|
|
18
18
|
|
|
@@ -115,25 +115,85 @@ sudo sh uninstall.sh
|
|
|
115
115
|
|
|
116
116
|
## Configuration
|
|
117
117
|
|
|
118
|
-
Since
|
|
118
|
+
Since 3.0.0, you could create a configuration file located at `~/.safe-rm/config` in your `$HOME` directory, to support
|
|
119
119
|
- defining your custom trash directory
|
|
120
120
|
- allowing `safe-rm` to permanently delete files and directories that are already in the trash
|
|
121
121
|
- disallowing `safe-rm` to use [AppleScript][applescript]
|
|
122
122
|
|
|
123
|
-
For the description of each config, you could refer to the sample file [here](
|
|
123
|
+
For the description of each config, you could refer to the sample file [here](./.safe-rm/config)
|
|
124
|
+
|
|
125
|
+
```sh
|
|
126
|
+
# You could
|
|
127
|
+
cp -r ./.safe-rm ~/
|
|
128
|
+
```
|
|
124
129
|
|
|
125
130
|
If you want to use a custom configuration file
|
|
126
131
|
|
|
127
132
|
```sh
|
|
128
|
-
alias="
|
|
133
|
+
alias="SAFE_RM_CONFIG=/path/to/safe-rm.conf /path/to/shell-safe-rm/bin/rm.sh"
|
|
129
134
|
```
|
|
130
135
|
|
|
131
136
|
Or if it is installed by npm:
|
|
132
137
|
|
|
133
138
|
```sh
|
|
134
|
-
alias="
|
|
139
|
+
alias="SAFE_RM_CONFIG=/path/to/safe-rm.conf safe-rm"
|
|
135
140
|
```
|
|
136
141
|
|
|
142
|
+
### Disable `Put-back` Functionality on MacOS (MacOS only)
|
|
143
|
+
|
|
144
|
+
In `~/.safe-rm/config`
|
|
145
|
+
|
|
146
|
+
```sh
|
|
147
|
+
export SAFE_RM_USE_APPLESCRIPT=no
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Change the Default Trach Bin Other Than System Default
|
|
151
|
+
|
|
152
|
+
```sh
|
|
153
|
+
export SAFE_RM_TRASH=/path/to/trash
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Permanent Delete Files or Directories that Are Already in the Trash
|
|
157
|
+
|
|
158
|
+
```sh
|
|
159
|
+
export SAFE_RM_PERM_DEL_FILES_IN_TRASH=yes
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Protect Files And Directories From Deleting
|
|
163
|
+
|
|
164
|
+
If you want to protect some certain files or directories from deleting by mistake, you could create a `.gitignore` file under the `"~/.safe-rm/"` directory, you could write [.gitignore rules](https://git-scm.com/docs/gitignore) inside the file.
|
|
165
|
+
|
|
166
|
+
If a path is matched by the rules that defined in `~/.safe-rm/.gitignore`, the path will be protected and could not be deleted by `safe-rm`
|
|
167
|
+
|
|
168
|
+
For example, in the `~/.safe-rm/.gitignore`
|
|
169
|
+
|
|
170
|
+
```.gitignore
|
|
171
|
+
/path/to/be/protected
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
And when executing
|
|
175
|
+
|
|
176
|
+
```sh
|
|
177
|
+
$ safe-rm /path/to/be/protected # or
|
|
178
|
+
$ safe-rm /path/to/be/protected/foo # or
|
|
179
|
+
$ safe-rm -rf /path/to/be/protected/bar
|
|
180
|
+
|
|
181
|
+
# An error will occur
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
But pay attention that, by adding the protected pattern above, if we:
|
|
185
|
+
|
|
186
|
+
```sh
|
|
187
|
+
$ safe-rm -rf /path/to
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
To keep the performance of `safe-rm` and avoid conducting unnecessary file system traversing, this would not prevent `/path/to/be/protected/foo` from removing.
|
|
191
|
+
|
|
192
|
+
Pay **ATTENTION** that:
|
|
193
|
+
- Before adding protected rules, i.e. placing the `.gitignore` inside the `"~/.safe-rm/"` directory, it requires `git` to be installed in your environment
|
|
194
|
+
- The protected rules file applies to the root directory (/), which This means that the patterns defined within it need to be relative to the root directory.
|
|
195
|
+
- Avoid adding `/` in the protected rules file, or everything will be protected
|
|
196
|
+
|
|
137
197
|
|
|
138
198
|
[applescript]: https://en.wikipedia.org/wiki/AppleScript
|
|
139
199
|
[rm]: https://en.wikipedia.org/wiki/Rm_(Unix)
|
package/bin/rm.sh
CHANGED
|
@@ -3,18 +3,19 @@
|
|
|
3
3
|
# Basic configuration
|
|
4
4
|
# ------------------------------------------------------------------------------
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
DEFAULT_SAFE_RM_CONFIG="$HOME/.safe-rm/config"
|
|
7
7
|
|
|
8
8
|
# You could modify the location of the configuration file by using
|
|
9
9
|
# ```sh
|
|
10
|
-
# $ export
|
|
10
|
+
# $ export SAFE_RM_CONFIG=/path/to/safe-rm.conf
|
|
11
11
|
# ```
|
|
12
|
-
|
|
12
|
+
SAFE_RM_CONFIG=${SAFE_RM_CONFIG:="$DEFAULT_SAFE_RM_CONFIG"}
|
|
13
13
|
|
|
14
|
-
if [[ -f "$
|
|
15
|
-
source "$
|
|
14
|
+
if [[ -f "$SAFE_RM_CONFIG" ]]; then
|
|
15
|
+
source "$SAFE_RM_CONFIG"
|
|
16
16
|
fi
|
|
17
17
|
|
|
18
|
+
SAFE_RM_CONFIG_ROOT=${SAFE_RM_CONFIG_ROOT:="$HOME/.safe-rm"}
|
|
18
19
|
|
|
19
20
|
# Print debug info or not
|
|
20
21
|
SAFE_RM_DEBUG=${SAFE_RM_DEBUG:=}
|
|
@@ -78,7 +79,7 @@ SAFE_RM_TRASH=${SAFE_RM_TRASH:="$DEFAULT_TRASH"}
|
|
|
78
79
|
|
|
79
80
|
if [[ "$OS_TYPE" == "MacOS" ]]; then
|
|
80
81
|
if command -v osascript &> /dev/null; then
|
|
81
|
-
# `SAFE_RM_USE_APPLESCRIPT=no` in your
|
|
82
|
+
# `SAFE_RM_USE_APPLESCRIPT=no` in your SAFE_RM_CONFIG file
|
|
82
83
|
# to disable AppleScript
|
|
83
84
|
if [[ "$SAFE_RM_USE_APPLESCRIPT" == "no" ]]; then
|
|
84
85
|
debug "$LINENO: applescript disabled by conf"
|
|
@@ -110,6 +111,37 @@ else
|
|
|
110
111
|
fi
|
|
111
112
|
fi
|
|
112
113
|
|
|
114
|
+
|
|
115
|
+
SAFE_RM_PROTECTED_RULES="${SAFE_RM_CONFIG_ROOT}/.gitignore"
|
|
116
|
+
|
|
117
|
+
debug $SAFE_RM_PROTECTED_RULES
|
|
118
|
+
|
|
119
|
+
# But if it is not a file
|
|
120
|
+
if [[ -f "$SAFE_RM_PROTECTED_RULES" ]]; then
|
|
121
|
+
if command -v git &> /dev/null; then
|
|
122
|
+
debug "$LINENO: protected rules enabled: $SAFE_RM_PROTECTED_RULES"
|
|
123
|
+
|
|
124
|
+
if git -C "$SAFE_RM_CONFIG_ROOT" rev-parse --is-inside-work-tree &> /dev/null; then
|
|
125
|
+
:
|
|
126
|
+
else
|
|
127
|
+
error "[WARNING] safe-rm requires a git repository to use protected rules"
|
|
128
|
+
error "Initializing a git repository in \"$SAFE_RM_CONFIG_ROOT\" ..."
|
|
129
|
+
|
|
130
|
+
git -C "$SAFE_RM_CONFIG_ROOT" init -q
|
|
131
|
+
|
|
132
|
+
error "Success"
|
|
133
|
+
fi
|
|
134
|
+
else
|
|
135
|
+
error "[WARNING] safe-rm requires git installed to use protected rules"
|
|
136
|
+
error " please install git"
|
|
137
|
+
error " or remove the file \"$SAFE_RM_PROTECTED_RULES\""
|
|
138
|
+
SAFE_RM_PROTECTED_RULES=
|
|
139
|
+
fi
|
|
140
|
+
else
|
|
141
|
+
SAFE_RM_PROTECTED_RULES=
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
|
|
113
145
|
# ------------------------------------------------------------------------------
|
|
114
146
|
|
|
115
147
|
# Simple basename: /bin/rm -> rm
|
|
@@ -145,6 +177,7 @@ if [[ "$#" == 0 ]]; then
|
|
|
145
177
|
usage
|
|
146
178
|
fi
|
|
147
179
|
|
|
180
|
+
|
|
148
181
|
ARG_END=
|
|
149
182
|
FILE_NAME=
|
|
150
183
|
ARG=
|
|
@@ -299,6 +332,14 @@ if [[ ! -e $SAFE_RM_TRASH ]]; then
|
|
|
299
332
|
fi
|
|
300
333
|
|
|
301
334
|
|
|
335
|
+
check_return_status(){
|
|
336
|
+
local status=$?
|
|
337
|
+
if [[ $status -ne "0" ]]; then
|
|
338
|
+
debug "$LINENO: last command returned status $status"
|
|
339
|
+
EXIT_CODE=$status
|
|
340
|
+
fi
|
|
341
|
+
}
|
|
342
|
+
|
|
302
343
|
# try to remove a file or directory
|
|
303
344
|
remove(){
|
|
304
345
|
local file=$1
|
|
@@ -355,7 +396,7 @@ remove(){
|
|
|
355
396
|
# The file
|
|
356
397
|
# - is a symbolic link
|
|
357
398
|
# - is a file
|
|
358
|
-
# - does not
|
|
399
|
+
# - does not exist
|
|
359
400
|
trash "$file"
|
|
360
401
|
debug "$LINENO: trash returned status $?"
|
|
361
402
|
fi
|
|
@@ -405,18 +446,61 @@ trash(){
|
|
|
405
446
|
else
|
|
406
447
|
do_trash "$target"
|
|
407
448
|
fi
|
|
449
|
+
|
|
450
|
+
check_return_status
|
|
408
451
|
}
|
|
409
452
|
|
|
410
453
|
|
|
411
454
|
do_trash(){
|
|
412
|
-
|
|
455
|
+
local target=$1
|
|
456
|
+
|
|
457
|
+
debug "$LINENO: trash $target"
|
|
458
|
+
|
|
459
|
+
if is_protected "$target"; then
|
|
460
|
+
error "\"$target\" is protected by your configuration"
|
|
461
|
+
return 1
|
|
462
|
+
fi
|
|
413
463
|
|
|
414
464
|
if [[ -n $SAFE_RM_USE_APPLESCRIPT ]]; then
|
|
415
|
-
applescript_trash "$
|
|
465
|
+
applescript_trash "$target"
|
|
416
466
|
elif [[ "$OS_TYPE" == "MacOS" ]]; then
|
|
417
|
-
mac_trash "$
|
|
467
|
+
mac_trash "$target"
|
|
468
|
+
else
|
|
469
|
+
linux_trash "$target"
|
|
470
|
+
fi
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
get_absolute_path(){
|
|
475
|
+
echo $(cd "$(dirname "$1")" && pwd)/$(basename "$1")
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
# Returns
|
|
480
|
+
# - 0: the target is protected
|
|
481
|
+
# - 1: the target is not protected
|
|
482
|
+
is_protected(){
|
|
483
|
+
if [[ ! -n $SAFE_RM_PROTECTED_RULES ]]; then
|
|
484
|
+
# If no protected rules are set, the target is not protected
|
|
485
|
+
return 1
|
|
486
|
+
fi
|
|
487
|
+
|
|
488
|
+
local target=$1
|
|
489
|
+
local abs_path=$(get_absolute_path "$target")
|
|
490
|
+
|
|
491
|
+
# /path/to/foo -> path/to/foo
|
|
492
|
+
local rel_path=${abs_path#/}
|
|
493
|
+
|
|
494
|
+
debug "$LINENO: check whether $rel_path is protected"
|
|
495
|
+
|
|
496
|
+
local ignored=$(git -C "$SAFE_RM_CONFIG_ROOT" check-ignore -v --no-index "$rel_path")
|
|
497
|
+
|
|
498
|
+
debug "$LINENO: git check-ignore result: $ignored"
|
|
499
|
+
|
|
500
|
+
if [[ -n "$ignored" ]]; then
|
|
501
|
+
return 0
|
|
418
502
|
else
|
|
419
|
-
|
|
503
|
+
return 1
|
|
420
504
|
fi
|
|
421
505
|
}
|
|
422
506
|
|
|
@@ -523,6 +607,9 @@ mac_trash(){
|
|
|
523
607
|
}
|
|
524
608
|
|
|
525
609
|
|
|
610
|
+
# Check if the base name already exists in the trash
|
|
611
|
+
# If it does, foo -> foo.1
|
|
612
|
+
# If foo.1 exists, foo.1 -> foo.2
|
|
526
613
|
check_linux_trash_base(){
|
|
527
614
|
local base=$1
|
|
528
615
|
local trash="$SAFE_RM_TRASH/files"
|
package/package.json
CHANGED
package/test/cases.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
2
|
const {v4: uuid} = require('uuid')
|
|
3
3
|
const delay = require('delay')
|
|
4
|
-
const log = require('util').debuglog('safe-rm')
|
|
5
4
|
|
|
6
5
|
const {
|
|
7
6
|
generateContextMethods,
|
|
@@ -9,19 +8,30 @@ const {
|
|
|
9
8
|
IS_MACOS
|
|
10
9
|
} = require('./helper')
|
|
11
10
|
|
|
11
|
+
const SAFE_RM_PATH = path.join(__dirname, '..', 'bin', 'rm.sh')
|
|
12
|
+
|
|
13
|
+
const is_rm = type => type === 'rm'
|
|
14
|
+
// const is_safe_rm = type => type === 'safe-rm'
|
|
15
|
+
|
|
16
|
+
// Whether it is a test spec for AppleScript version of safe-rm
|
|
17
|
+
const is_as = type => type === 'safe-rm-as'
|
|
18
|
+
|
|
19
|
+
// We skip testing trash dir for vanilla rm
|
|
20
|
+
const should_skip_test_trash_dir = type => is_rm(type) || is_as(type)
|
|
12
21
|
|
|
13
22
|
module.exports = (
|
|
14
23
|
test,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
24
|
+
{
|
|
25
|
+
type,
|
|
26
|
+
// test_trash_dir,
|
|
27
|
+
command = SAFE_RM_PATH,
|
|
28
|
+
env = {}
|
|
29
|
+
} = {}
|
|
19
30
|
) => {
|
|
20
|
-
|
|
21
31
|
// Setup before each test
|
|
22
|
-
test.beforeEach(generateContextMethods(
|
|
32
|
+
test.beforeEach(generateContextMethods(command, env))
|
|
23
33
|
|
|
24
|
-
test(
|
|
34
|
+
test(`removes a single file`, async t => {
|
|
25
35
|
const {
|
|
26
36
|
createFile,
|
|
27
37
|
runRm,
|
|
@@ -35,7 +45,7 @@ module.exports = (
|
|
|
35
45
|
t.is(result.code, 0, 'exit code should be 0')
|
|
36
46
|
t.false(await pathExists(filepath), 'file should be removed')
|
|
37
47
|
|
|
38
|
-
if (
|
|
48
|
+
if (should_skip_test_trash_dir(type)) {
|
|
39
49
|
return
|
|
40
50
|
}
|
|
41
51
|
|
|
@@ -43,7 +53,8 @@ module.exports = (
|
|
|
43
53
|
|
|
44
54
|
t.is(files.length, 1, 'should be one in the trash')
|
|
45
55
|
t.is(
|
|
46
|
-
path.basename(files[0]),
|
|
56
|
+
path.basename(files[0]),
|
|
57
|
+
path.basename(filepath),
|
|
47
58
|
'file name should match'
|
|
48
59
|
)
|
|
49
60
|
})
|
|
@@ -58,7 +69,7 @@ module.exports = (
|
|
|
58
69
|
? ` with ext "${ext}"`
|
|
59
70
|
: ''
|
|
60
71
|
|
|
61
|
-
test(
|
|
72
|
+
test(`removes multiple files of the same name${extra}`, async t => {
|
|
62
73
|
const {
|
|
63
74
|
createFile,
|
|
64
75
|
runRm,
|
|
@@ -73,13 +84,13 @@ module.exports = (
|
|
|
73
84
|
const to_next_second = 1000 - now % 1000
|
|
74
85
|
await delay(to_next_second)
|
|
75
86
|
|
|
76
|
-
const filepath1 = await createFile(full_name, '1')
|
|
87
|
+
const filepath1 = await createFile({name: full_name, content: '1'})
|
|
77
88
|
const result1 = await runRm([filepath1])
|
|
78
89
|
|
|
79
|
-
const filepath2 = await createFile(full_name, '2')
|
|
90
|
+
const filepath2 = await createFile({name: full_name, content: '2'})
|
|
80
91
|
const result2 = await runRm([filepath2])
|
|
81
92
|
|
|
82
|
-
const filepath3 = await createFile(full_name, '3')
|
|
93
|
+
const filepath3 = await createFile({name: full_name, content: '3'})
|
|
83
94
|
const result3 = await runRm([filepath3])
|
|
84
95
|
|
|
85
96
|
assertEmptySuccess(t, result1)
|
|
@@ -91,11 +102,10 @@ module.exports = (
|
|
|
91
102
|
assertEmptySuccess(t, result3)
|
|
92
103
|
t.false(await pathExists(filepath3), 'file 3 should be removed')
|
|
93
104
|
|
|
94
|
-
if (
|
|
105
|
+
if (should_skip_test_trash_dir(type)) {
|
|
95
106
|
return
|
|
96
107
|
}
|
|
97
108
|
|
|
98
|
-
|
|
99
109
|
const files = (await lsFileInTrash(full_name))
|
|
100
110
|
.sort((a, b) => a.length - b.length)
|
|
101
111
|
|
|
@@ -120,6 +130,7 @@ module.exports = (
|
|
|
120
130
|
t.true(files.every(f => f.endsWith(ext)), 'should have the same ext')
|
|
121
131
|
|
|
122
132
|
t.is(fb1, filename)
|
|
133
|
+
t.is(fbs1[0], filename)
|
|
123
134
|
t.is(fbs2[0], filename)
|
|
124
135
|
t.is(fbs3[0], filename)
|
|
125
136
|
|
|
@@ -136,7 +147,7 @@ module.exports = (
|
|
|
136
147
|
for (const file of files) {
|
|
137
148
|
const match = file.match(re)
|
|
138
149
|
const n = match[2]
|
|
139
|
-
? parseInt(match[2])
|
|
150
|
+
? parseInt(match[2], 10)
|
|
140
151
|
: 0
|
|
141
152
|
|
|
142
153
|
const f = file.slice(0, match.index)
|
|
@@ -153,7 +164,7 @@ module.exports = (
|
|
|
153
164
|
})
|
|
154
165
|
})
|
|
155
166
|
|
|
156
|
-
test(
|
|
167
|
+
test(`removes a single file in trash permanently`, async t => {
|
|
157
168
|
const {
|
|
158
169
|
trash_path,
|
|
159
170
|
createFile,
|
|
@@ -162,7 +173,7 @@ module.exports = (
|
|
|
162
173
|
lsFileInTrash
|
|
163
174
|
} = t.context
|
|
164
175
|
|
|
165
|
-
const filepath = await createFile(path.join(trash_path, uuid()))
|
|
176
|
+
const filepath = await createFile({name: path.join(trash_path, uuid())})
|
|
166
177
|
const result = await runRm([filepath], {
|
|
167
178
|
env: {
|
|
168
179
|
SAFE_RM_PERM_DEL_FILES_IN_TRASH: 'yes'
|
|
@@ -172,7 +183,7 @@ module.exports = (
|
|
|
172
183
|
assertEmptySuccess(t, result)
|
|
173
184
|
t.false(await pathExists(filepath), 'file should be removed')
|
|
174
185
|
|
|
175
|
-
if (
|
|
186
|
+
if (should_skip_test_trash_dir(type)) {
|
|
176
187
|
return
|
|
177
188
|
}
|
|
178
189
|
|
|
@@ -181,13 +192,10 @@ module.exports = (
|
|
|
181
192
|
t.is(files.length, 0, 'should be already removed')
|
|
182
193
|
})
|
|
183
194
|
|
|
184
|
-
test(
|
|
195
|
+
test(`#22 exit code with -f option`, async t => {
|
|
185
196
|
const {
|
|
186
197
|
source_path,
|
|
187
|
-
|
|
188
|
-
runRm,
|
|
189
|
-
pathExists,
|
|
190
|
-
lsFileInTrash
|
|
198
|
+
runRm
|
|
191
199
|
} = t.context
|
|
192
200
|
|
|
193
201
|
const filepath = path.join(source_path, uuid())
|
|
@@ -210,7 +218,7 @@ module.exports = (
|
|
|
210
218
|
}`)
|
|
211
219
|
})
|
|
212
220
|
|
|
213
|
-
test(
|
|
221
|
+
test(`removes an empty directory: -d`, async t => {
|
|
214
222
|
const {
|
|
215
223
|
createDir,
|
|
216
224
|
runRm,
|
|
@@ -228,4 +236,40 @@ module.exports = (
|
|
|
228
236
|
assertEmptySuccess(t, result2)
|
|
229
237
|
t.false(await pathExists(dirpath), 'directory should be removed')
|
|
230
238
|
})
|
|
239
|
+
|
|
240
|
+
// Only test for safe-rm
|
|
241
|
+
!is_rm(type) && test(`protected rules`, async t => {
|
|
242
|
+
const {
|
|
243
|
+
createDir,
|
|
244
|
+
createFile,
|
|
245
|
+
runRm,
|
|
246
|
+
pathExists
|
|
247
|
+
} = t.context
|
|
248
|
+
|
|
249
|
+
const config_root = await createDir({
|
|
250
|
+
name: '.safe-rm'
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
const dir = await createDir()
|
|
254
|
+
const filepath = await createFile({
|
|
255
|
+
under: dir
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
await createFile({
|
|
259
|
+
name: '.gitignore',
|
|
260
|
+
under: config_root,
|
|
261
|
+
content: `${dir}
|
|
262
|
+
`
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
const result = await runRm([filepath], {
|
|
266
|
+
env: {
|
|
267
|
+
SAFE_RM_CONFIG_ROOT: config_root
|
|
268
|
+
}
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
t.is(result.code, 1, 'exit code should be 1')
|
|
272
|
+
t.true(result.stderr.includes('protected'), 'stderr should include "protected"')
|
|
273
|
+
t.true(await pathExists(filepath), 'file should not be removed')
|
|
274
|
+
})
|
|
231
275
|
}
|
package/test/helper.js
CHANGED
|
@@ -6,7 +6,6 @@ const fse = require('fs-extra')
|
|
|
6
6
|
const {v4: uuid} = require('uuid')
|
|
7
7
|
const log = require('util').debuglog('safe-rm')
|
|
8
8
|
|
|
9
|
-
const SAFE_RM_PATH = path.join(__dirname, '..', 'bin', 'rm.sh')
|
|
10
9
|
const TEST_DIR = path.join(tmp.dirSync().name, 'safe-rm-tests')
|
|
11
10
|
|
|
12
11
|
const IS_MACOS = process.platform === 'darwin'
|
|
@@ -14,8 +13,8 @@ const IS_MACOS = process.platform === 'darwin'
|
|
|
14
13
|
&& !process.env.SAFE_RM_DEBUG_LINUX
|
|
15
14
|
|
|
16
15
|
const generateContextMethods = (
|
|
17
|
-
rm_command
|
|
18
|
-
rm_command_env
|
|
16
|
+
rm_command,
|
|
17
|
+
rm_command_env
|
|
19
18
|
) => async t => {
|
|
20
19
|
const root_path = path.join(TEST_DIR, uuid())
|
|
21
20
|
t.context.root = await fse.ensureDir(root_path)
|
|
@@ -25,26 +24,33 @@ const generateContextMethods = (
|
|
|
25
24
|
? rm_command_env.SAFE_RM_TRASH
|
|
26
25
|
: path.join(root_path, 'trash')
|
|
27
26
|
|
|
28
|
-
|
|
27
|
+
t.context.trash_path = process.platform === 'darwin'
|
|
29
28
|
? trash
|
|
30
29
|
: path.join(trash, 'files')
|
|
31
30
|
|
|
32
31
|
await Promise.all([
|
|
33
32
|
fse.ensureDir(source_path),
|
|
34
|
-
fse.ensureDir(trash_path)
|
|
33
|
+
fse.ensureDir(t.context.trash_path)
|
|
35
34
|
])
|
|
36
35
|
|
|
37
36
|
// Helper function to create a temporary directory
|
|
38
|
-
async function createDir (
|
|
39
|
-
|
|
37
|
+
async function createDir ({
|
|
38
|
+
name = uuid(),
|
|
39
|
+
under
|
|
40
|
+
} = {}) {
|
|
41
|
+
const dirpath = path.resolve(under || t.context.source_path, name)
|
|
40
42
|
await fse.ensureDir(dirpath)
|
|
41
43
|
|
|
42
44
|
return dirpath
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
// Helper function to create a temporary file
|
|
46
|
-
async function createFile (
|
|
47
|
-
|
|
48
|
+
async function createFile ({
|
|
49
|
+
name = uuid(),
|
|
50
|
+
content = 'test content',
|
|
51
|
+
under
|
|
52
|
+
} = {}) {
|
|
53
|
+
const filepath = path.resolve(under || t.context.source_path, name)
|
|
48
54
|
await fs.writeFile(filepath, content)
|
|
49
55
|
|
|
50
56
|
return filepath
|
|
@@ -58,7 +64,7 @@ const generateContextMethods = (
|
|
|
58
64
|
} = {}) {
|
|
59
65
|
return new Promise(resolve => {
|
|
60
66
|
const env = {
|
|
61
|
-
...process.env,
|
|
67
|
+
// ...process.env,
|
|
62
68
|
...{
|
|
63
69
|
SAFE_RM_TRASH: t.context.trash_path
|
|
64
70
|
},
|
|
@@ -115,7 +121,6 @@ const generateContextMethods = (
|
|
|
115
121
|
}
|
|
116
122
|
}
|
|
117
123
|
|
|
118
|
-
|
|
119
124
|
async function lsFileInMacTrash (filepath) {
|
|
120
125
|
const {trash_path} = t.context
|
|
121
126
|
|
|
@@ -161,9 +166,9 @@ const generateContextMethods = (
|
|
|
161
166
|
}
|
|
162
167
|
|
|
163
168
|
const assertEmptySuccess = (t, result, a = '', b = '', c = '') => {
|
|
164
|
-
t.is(result.code, 0,
|
|
165
|
-
t.is(result.stdout, '',
|
|
166
|
-
t.is(result.stderr, '',
|
|
169
|
+
t.is(result.code, 0, `exit code should be 0${a}`)
|
|
170
|
+
t.is(result.stdout, '', `stdout should be empty${b}`)
|
|
171
|
+
t.is(result.stderr, '', `stderr should be empty${c}`)
|
|
167
172
|
}
|
|
168
173
|
|
|
169
174
|
module.exports = {
|
package/test/rm.test.js
CHANGED
package/test/safe-rm-as.test.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
const test = require('ava')
|
|
2
2
|
const home = require('home')
|
|
3
3
|
|
|
4
|
-
const
|
|
4
|
+
const run = require('./cases')
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
run(test, {
|
|
7
|
+
type: 'safe-rm-as',
|
|
8
|
+
env: {
|
|
9
|
+
SAFE_RM_TRASH: home.resolve('~/.Trash')
|
|
10
|
+
}
|
|
8
11
|
})
|
package/test/safe-rm.test.js
CHANGED
package/test/code.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
const test = require('ava')
|
|
2
|
-
const {v4: uuid} = require('uuid')
|
|
3
|
-
|
|
4
|
-
const {
|
|
5
|
-
generateContextMethods
|
|
6
|
-
} = require('./helper')
|
|
7
|
-
|
|
8
|
-
const RM_PATH = '/bin/rm'
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
// Setup before each test
|
|
12
|
-
test.beforeEach(generateContextMethods)
|
|
13
|
-
|
|
14
|
-
// Cleanup after each test
|
|
15
|
-
test.afterEach.always(async t => {
|
|
16
|
-
t.context.cleanup()
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const CASES = [
|
|
21
|
-
[
|
|
22
|
-
// Directories
|
|
23
|
-
[],
|
|
24
|
-
// Files
|
|
25
|
-
[uuid()],
|
|
26
|
-
// args
|
|
27
|
-
[]
|
|
28
|
-
]
|
|
29
|
-
]
|
|
30
|
-
|
|
31
|
-
CASES.forEach((c, i) => {
|
|
32
|
-
test(`${i}: rm`, async t => {
|
|
33
|
-
const [dirs, files, _args, files_to_del = files] = typeof c === 'function'
|
|
34
|
-
? c(t)
|
|
35
|
-
: c
|
|
36
|
-
|
|
37
|
-
const args = typeof _args === 'function'
|
|
38
|
-
? _args(files_to_del)
|
|
39
|
-
|
|
40
|
-
const dirpaths = await Promise.all(dirs.map(t.context.dir))
|
|
41
|
-
const filepaths = await Promise.all(files.map(t.context.file))
|
|
42
|
-
|
|
43
|
-
const result = await runRm([...args, ...filepaths, ...dirpaths], {
|
|
44
|
-
command: RM_PATH
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
t.is(result.code, 0)
|
|
48
|
-
|
|
49
|
-
for (const filepath of filepaths) {
|
|
50
|
-
t.false(await pathExists(filepath))
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
for (const dirpath of dirpaths) {
|
|
54
|
-
t.false(await pathExists
|
|
55
|
-
})
|
|
56
|
-
})
|