@typinghare/trick 1.0.4 → 1.0.7
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/README.md +86 -0
- package/dist/app.js +46 -10
- package/dist/config.js +1 -1
- package/dist/encrypt.js +2 -2
- package/package.json +12 -3
- package/src/app.ts +73 -13
- package/src/config.ts +6 -5
- package/src/encrypt.ts +2 -2
- package/trick.config.json +10 -3
- package/.prettierrc.yaml +0 -6
- package/.trick/encrypted/test/resources/really.json +0 -1
- package/.trick/encrypted/test/resources/task.yml +0 -1
package/README.md
CHANGED
|
@@ -3,5 +3,91 @@
|
|
|
3
3
|
# Install
|
|
4
4
|
|
|
5
5
|
```shell
|
|
6
|
+
# npm
|
|
7
|
+
npm install -g @typinghare/trick
|
|
8
|
+
|
|
9
|
+
# pnpm
|
|
6
10
|
pnpm add -g @typinghare/trick
|
|
11
|
+
|
|
12
|
+
# yarn
|
|
13
|
+
yarn add -g @typinghare/trick
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Philosophy
|
|
17
|
+
|
|
18
|
+
We often add sensitive files, such as `.env` and `api_key.conf`, to `.gitignore`, preventing them from being committed or even pushed to remote depots for safety reasons. Then, we have to manually copy the file to the server. It could be effortless if we have only one file, but imagine we have more in a bigger project. Even worse, some careless people (me) even lost their sensitive files after they changed their computers!
|
|
19
|
+
|
|
20
|
+
**Trick** helps you to encrypt sensitive files with a secret so that you can upload the file to Git platforms. Later on the server, just use the same secret to decrypt the files with ease.
|
|
21
|
+
|
|
22
|
+
## Quick Example
|
|
23
|
+
|
|
24
|
+
Set up the **secret name** and the files needed to be encrypted:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# This will create a trick.config.json at the first time
|
|
28
|
+
# trick add <secret-name> [files...]
|
|
29
|
+
$ trick add AN_EXAMPLE_SECRET .env api_key.conf
|
|
30
|
+
|
|
31
|
+
# Display the list of secret names and the files bound
|
|
32
|
+
$ trick list
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Set up the `AN_EXAMPLE_SECRET` using `export` or put it in the login file:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
$ export AN_EXAMPLE_SECRET="fQyX5O59MJuFD4l/tf2FYApqZY12N+UatEC6FC6mN7k="
|
|
39
|
+
# Alternatively, put the above command to login files like `.bashrc` and `.zshrc`
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Encrypt the files:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
$ trick encrypt AN_EXAMPLE_SECRET
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
You will see the following output:
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
[ENCRYPTED] .env -> .trick/encrypted/.env.enc
|
|
52
|
+
[ENCRYPTED] api_key.conf -> .trick/encrypted/api_key.conf.enc
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Encrypted files are all saved to `.trick`. On the server side, you can encrypt the files effortlessly:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
$ export AN_EXAMPLE_SECRET="fQyX5O59MJuFD4l/tf2FYApqZY12N+UatEC6FC6mN7k="
|
|
59
|
+
$ trick decrypt AN_EXAMPLE_SECRET
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
And you will see that the files are restored:
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
[DECRYPTED] .trick/encrypted/.env.enc -> .env
|
|
66
|
+
[DECRYPTED] .trick/encrypted/api_key.conf.enc -> api_key.conf
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
> [!IMPORTANT]
|
|
70
|
+
>
|
|
71
|
+
> You need to keep the secret to yourself! Make a copy on Cloud or write it down!
|
|
72
|
+
|
|
73
|
+
## More Features
|
|
74
|
+
|
|
75
|
+
### Default Secret Name
|
|
76
|
+
|
|
77
|
+
You can set the default secret name so that you don't need to input it every time:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# Set the default secret name
|
|
81
|
+
$ trick set-default AN_EXAMPLE_SECRET
|
|
82
|
+
|
|
83
|
+
# Display the default secret name
|
|
84
|
+
$ trick get-default
|
|
7
85
|
```
|
|
86
|
+
|
|
87
|
+
Now you can encrypt and decrypt more easily:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
$ trick encrypt
|
|
91
|
+
$ trick decrypt
|
|
92
|
+
```
|
|
93
|
+
|
package/dist/app.js
CHANGED
|
@@ -6,29 +6,65 @@ import { TRICK_ENCRYPTED_DIR } from './constant.js';
|
|
|
6
6
|
import fsExtra from 'fs-extra';
|
|
7
7
|
import chalk from 'chalk';
|
|
8
8
|
const program = new Command();
|
|
9
|
-
program.version('
|
|
9
|
+
program.version('1.0.7');
|
|
10
10
|
program.description('Save credential files to remote safely.');
|
|
11
11
|
program
|
|
12
12
|
.command('add')
|
|
13
|
-
.description('
|
|
13
|
+
.description('Add a target or add files to an existing target.')
|
|
14
14
|
.argument('<secret-name>', 'The name of secret in the environment')
|
|
15
15
|
.argument('[files...]', 'Files this target will encrypt')
|
|
16
16
|
.action(async (secretName, files) => {
|
|
17
17
|
await updateConfig((config) => {
|
|
18
18
|
try {
|
|
19
|
-
getTargetFromConfig(config, secretName);
|
|
19
|
+
const target = getTargetFromConfig(config, secretName);
|
|
20
|
+
target.files.push(...files);
|
|
21
|
+
return true;
|
|
20
22
|
}
|
|
21
23
|
catch (err) {
|
|
22
24
|
config.default_secret_name = secretName;
|
|
23
|
-
config.targets.push({
|
|
24
|
-
secret_name: secretName,
|
|
25
|
-
files,
|
|
26
|
-
});
|
|
25
|
+
config.targets.push({ secret_name: secretName, files });
|
|
27
26
|
return true;
|
|
28
27
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
program
|
|
31
|
+
.command('remove')
|
|
32
|
+
.description('Remove files from a specific target.')
|
|
33
|
+
.argument('<secret-name>', 'The name of secret in the environment.')
|
|
34
|
+
.argument('[files...]', 'Files to remove.')
|
|
35
|
+
.option('-t, --target', 'Remove the target instead.')
|
|
36
|
+
.action(async (secretName, files, options) => {
|
|
37
|
+
if (options.target) {
|
|
38
|
+
await updateConfig((config) => {
|
|
39
|
+
const index = config.targets.findIndex((target) => target.secret_name === secretName);
|
|
40
|
+
if (index == -1) {
|
|
41
|
+
console.log(chalk.yellow(`[WARNING] Target not found: ${secretName}`));
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
config.targets.splice(index, 1);
|
|
45
|
+
console.log(`[SUCCESS] Removed target: ${secretName}`);
|
|
46
|
+
return true;
|
|
47
|
+
});
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
await updateConfig((config) => {
|
|
51
|
+
try {
|
|
52
|
+
const target = getTargetFromConfig(config, secretName);
|
|
53
|
+
const target_files = target.files;
|
|
54
|
+
for (const file of files) {
|
|
55
|
+
const index = target_files.indexOf(file);
|
|
56
|
+
if (index != -1) {
|
|
57
|
+
target_files.splice(index, 1);
|
|
58
|
+
console.log(`[SUCCESS] Removed file: ${file}`);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
console.log(chalk.yellow(`[WARNING] File does not exist in the target: ${file}`));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
config.default_secret_name = secretName;
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
32
68
|
});
|
|
33
69
|
});
|
|
34
70
|
function checkSecretName(secretName, defaultSecretName) {
|
package/dist/config.js
CHANGED
|
@@ -10,7 +10,7 @@ export class ReadConfigError extends Error {
|
|
|
10
10
|
}
|
|
11
11
|
export async function writeConfig(config) {
|
|
12
12
|
try {
|
|
13
|
-
await fsExtra.
|
|
13
|
+
await fsExtra.writeFile(CONFIG_FILE_NAME, JSON.stringify(config, null, 2));
|
|
14
14
|
}
|
|
15
15
|
catch (err) {
|
|
16
16
|
throw new WriteConfigError();
|
package/dist/encrypt.js
CHANGED
|
@@ -36,7 +36,7 @@ export async function encryptFile(srcFilePath, destFilePath, secret, iteration_c
|
|
|
36
36
|
'-out',
|
|
37
37
|
destFilePath,
|
|
38
38
|
'-pass',
|
|
39
|
-
`pass:${secret}`,
|
|
39
|
+
`'pass:${secret}'`,
|
|
40
40
|
].join(' ');
|
|
41
41
|
await fsExtra.ensureDir(path.dirname(destFilePath));
|
|
42
42
|
try {
|
|
@@ -69,7 +69,7 @@ export async function decryptFile(srcFilePath, destFilePath, secret, iteration_c
|
|
|
69
69
|
'-out',
|
|
70
70
|
srcFilePath,
|
|
71
71
|
'-pass',
|
|
72
|
-
`pass:${secret}`,
|
|
72
|
+
`'pass:${secret}'`,
|
|
73
73
|
].join(' ');
|
|
74
74
|
await fsExtra.ensureDir(path.dirname(srcFilePath));
|
|
75
75
|
try {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typinghare/trick",
|
|
3
3
|
"description": "Save credential files to remote safely.",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.7",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"type": "module",
|
|
@@ -22,9 +22,18 @@
|
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/fs-extra": "^11.0.4",
|
|
25
|
-
"prettier": "^3.3.3"
|
|
25
|
+
"prettier": "^3.3.3",
|
|
26
|
+
"typescript": "^5.8.2"
|
|
27
|
+
},
|
|
28
|
+
"prettier": {
|
|
29
|
+
"trailingComma": "es5",
|
|
30
|
+
"tabWidth": 4,
|
|
31
|
+
"semi": false,
|
|
32
|
+
"singleQuote": true,
|
|
33
|
+
"endOfLine": "lf",
|
|
34
|
+
"printWidth": 80
|
|
26
35
|
},
|
|
27
36
|
"scripts": {
|
|
28
|
-
"build": "tsc"
|
|
37
|
+
"build": "node_modules/typescript/bin/tsc"
|
|
29
38
|
}
|
|
30
39
|
}
|
package/src/app.ts
CHANGED
|
@@ -20,35 +20,95 @@ import chalk from 'chalk'
|
|
|
20
20
|
|
|
21
21
|
const program = new Command()
|
|
22
22
|
|
|
23
|
-
program.version('
|
|
23
|
+
program.version('1.0.7')
|
|
24
24
|
program.description('Save credential files to remote safely.')
|
|
25
25
|
|
|
26
26
|
program
|
|
27
27
|
.command('add')
|
|
28
|
-
.description('
|
|
28
|
+
.description('Add a target or add files to an existing target.')
|
|
29
29
|
.argument('<secret-name>', 'The name of secret in the environment')
|
|
30
30
|
.argument('[files...]', 'Files this target will encrypt')
|
|
31
31
|
.action(async (secretName: string, files: string[]): Promise<void> => {
|
|
32
32
|
await updateConfig((config) => {
|
|
33
33
|
try {
|
|
34
|
-
getTargetFromConfig(config, secretName)
|
|
34
|
+
const target = getTargetFromConfig(config, secretName)
|
|
35
|
+
target.files.push(...files)
|
|
36
|
+
|
|
37
|
+
return true
|
|
35
38
|
} catch (err) {
|
|
36
39
|
config.default_secret_name = secretName
|
|
37
|
-
config.targets.push({
|
|
38
|
-
|
|
39
|
-
files,
|
|
40
|
-
})
|
|
40
|
+
config.targets.push({ secret_name: secretName, files })
|
|
41
|
+
|
|
41
42
|
return true
|
|
42
43
|
}
|
|
43
|
-
|
|
44
|
-
console.error(
|
|
45
|
-
`Target with the secret name already exists: ${secretName}`
|
|
46
|
-
)
|
|
47
|
-
console.error('Abort adding target')
|
|
48
|
-
process.exit(1)
|
|
49
44
|
})
|
|
50
45
|
})
|
|
51
46
|
|
|
47
|
+
program
|
|
48
|
+
.command('remove')
|
|
49
|
+
.description('Remove files from a specific target.')
|
|
50
|
+
.argument('<secret-name>', 'The name of secret in the environment.')
|
|
51
|
+
.argument('[files...]', 'Files to remove.')
|
|
52
|
+
.option('-t, --target', 'Remove the target instead.')
|
|
53
|
+
.action(
|
|
54
|
+
async (
|
|
55
|
+
secretName: string,
|
|
56
|
+
files: string[],
|
|
57
|
+
options: {
|
|
58
|
+
target: boolean
|
|
59
|
+
}
|
|
60
|
+
): Promise<void> => {
|
|
61
|
+
if (options.target) {
|
|
62
|
+
await updateConfig((config) => {
|
|
63
|
+
const index: number = config.targets.findIndex(
|
|
64
|
+
(target) => target.secret_name === secretName
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
if (index == -1) {
|
|
68
|
+
console.log(
|
|
69
|
+
chalk.yellow(
|
|
70
|
+
`[WARNING] Target not found: ${secretName}`
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
return false
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
config.targets.splice(index, 1)
|
|
78
|
+
console.log(`[SUCCESS] Removed target: ${secretName}`)
|
|
79
|
+
|
|
80
|
+
return true
|
|
81
|
+
})
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
await updateConfig((config) => {
|
|
86
|
+
try {
|
|
87
|
+
const target = getTargetFromConfig(config, secretName)
|
|
88
|
+
const target_files = target.files
|
|
89
|
+
for (const file of files) {
|
|
90
|
+
const index = target_files.indexOf(file)
|
|
91
|
+
if (index != -1) {
|
|
92
|
+
target_files.splice(index, 1)
|
|
93
|
+
console.log(`[SUCCESS] Removed file: ${file}`)
|
|
94
|
+
continue
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log(
|
|
98
|
+
chalk.yellow(
|
|
99
|
+
`[WARNING] File does not exist in the target: ${file}`
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
} catch (err: unknown) {
|
|
104
|
+
config.default_secret_name = secretName
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return true
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
|
|
52
112
|
function checkSecretName(
|
|
53
113
|
secretName?: string,
|
|
54
114
|
defaultSecretName?: string
|
package/src/config.ts
CHANGED
|
@@ -18,15 +18,16 @@ const defaultConfig: Config = {
|
|
|
18
18
|
targets: [],
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export class WriteConfigError extends Error {
|
|
22
|
-
}
|
|
21
|
+
export class WriteConfigError extends Error {}
|
|
23
22
|
|
|
24
|
-
export class ReadConfigError extends Error {
|
|
25
|
-
}
|
|
23
|
+
export class ReadConfigError extends Error {}
|
|
26
24
|
|
|
27
25
|
export async function writeConfig(config: Config): Promise<void> {
|
|
28
26
|
try {
|
|
29
|
-
await fsExtra.
|
|
27
|
+
await fsExtra.writeFile(
|
|
28
|
+
CONFIG_FILE_NAME,
|
|
29
|
+
JSON.stringify(config, null, 2)
|
|
30
|
+
)
|
|
30
31
|
} catch (err) {
|
|
31
32
|
throw new WriteConfigError()
|
|
32
33
|
}
|
package/src/encrypt.ts
CHANGED
|
@@ -43,7 +43,7 @@ export async function encryptFile(
|
|
|
43
43
|
'-out',
|
|
44
44
|
destFilePath,
|
|
45
45
|
'-pass',
|
|
46
|
-
`pass:${secret}`,
|
|
46
|
+
`'pass:${secret}'`,
|
|
47
47
|
].join(' ')
|
|
48
48
|
|
|
49
49
|
await fsExtra.ensureDir(path.dirname(destFilePath))
|
|
@@ -93,7 +93,7 @@ export async function decryptFile(
|
|
|
93
93
|
'-out',
|
|
94
94
|
srcFilePath,
|
|
95
95
|
'-pass',
|
|
96
|
-
`pass:${secret}`,
|
|
96
|
+
`'pass:${secret}'`,
|
|
97
97
|
].join(' ')
|
|
98
98
|
|
|
99
99
|
await fsExtra.ensureDir(path.dirname(srcFilePath))
|
package/trick.config.json
CHANGED
|
@@ -2,12 +2,19 @@
|
|
|
2
2
|
"iteration_count": 114514,
|
|
3
3
|
"targets": [
|
|
4
4
|
{
|
|
5
|
-
"secret_name": "
|
|
5
|
+
"secret_name": "TRICK_SECRET_NAME",
|
|
6
6
|
"files": [
|
|
7
7
|
"test/resources/really.json",
|
|
8
8
|
"test/resources/task.yml"
|
|
9
9
|
]
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"secret_name": "ANOTHER_TRICK_SECRET_NAME",
|
|
13
|
+
"files": [
|
|
14
|
+
"test/main/resources/database.properties",
|
|
15
|
+
"test/main/resources/password.properties"
|
|
16
|
+
]
|
|
10
17
|
}
|
|
11
18
|
],
|
|
12
|
-
"default_secret_name": "
|
|
13
|
-
}
|
|
19
|
+
"default_secret_name": "ANOTHER_TRICK_SECRET_NAME"
|
|
20
|
+
}
|
package/.prettierrc.yaml
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Salted__�;��a��S�K�9ℑ���2�����4�'Q���\0]�G~G01�߯G���
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Salted__�)�"�K7�bk��aQ��ܰ^�B9��������.3.�g�!g�$�XjM�q�~q�c76C�i����0�S
|