@frakjs/frak 0.3.0 → 0.5.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/.prettierrc +1 -1
- package/bin/frak.js +24 -2
- package/package.json +38 -38
- package/src/config.js +1 -1
- package/src/crypto.js +4 -0
- package/src/diff.js +45 -9
- package/src/humanize.js +2 -0
- package/src/rsync.js +1 -10
package/.prettierrc
CHANGED
package/bin/frak.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { execSync, spawn } from 'node:child_process';
|
|
4
|
-
import { writeFileSync, existsSync } from 'node:fs';
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
5
5
|
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
6
7
|
import * as ansi from '../src/ansi.js';
|
|
7
8
|
import * as cli from '../src/cli.js';
|
|
8
9
|
import config, { load as loadConfig } from '../src/config.js';
|
|
@@ -14,6 +15,9 @@ import * as rsync from '../src/rsync.js';
|
|
|
14
15
|
import * as ssh from '../src/ssh.js';
|
|
15
16
|
import * as date from '../src/date.js';
|
|
16
17
|
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = path.dirname(__filename);
|
|
20
|
+
|
|
17
21
|
/**
|
|
18
22
|
* Try to load config. If it fails, print error message in red.
|
|
19
23
|
*
|
|
@@ -39,6 +43,19 @@ function usage() {
|
|
|
39
43
|
console.log(' pull Download the remote file(s)');
|
|
40
44
|
console.log(' push Deploy website via rsync');
|
|
41
45
|
console.log(' backups:list List backups on the server');
|
|
46
|
+
console.log('');
|
|
47
|
+
console.log('Options:');
|
|
48
|
+
console.log(' --help Display the command usage');
|
|
49
|
+
console.log(' --version Show the current version');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Print the current version
|
|
54
|
+
*/
|
|
55
|
+
function version() {
|
|
56
|
+
const { version } = JSON.parse(readFileSync(path.join(__dirname, '../package.json')));
|
|
57
|
+
|
|
58
|
+
console.log('Version:', version);
|
|
42
59
|
}
|
|
43
60
|
|
|
44
61
|
/**
|
|
@@ -82,6 +99,8 @@ async function main(args) {
|
|
|
82
99
|
});
|
|
83
100
|
} catch (e) {
|
|
84
101
|
debug(e);
|
|
102
|
+
|
|
103
|
+
process.exit(e.status || 1);
|
|
85
104
|
}
|
|
86
105
|
} else if (command === 'diff') {
|
|
87
106
|
// Show the differences between local files and remote server
|
|
@@ -183,7 +202,7 @@ async function main(args) {
|
|
|
183
202
|
additions: `grep -e '^+' "$NAME"/*.patch | grep -v '^+++' | wc -l`,
|
|
184
203
|
deletions: `grep -e '^-' "$NAME"/*.patch | grep -v '^---' | wc -l`,
|
|
185
204
|
};
|
|
186
|
-
const ls = `stat
|
|
205
|
+
const ls = `stat -c '%Y %n' .backups/* | sort -r`;
|
|
187
206
|
|
|
188
207
|
const list = ssh.exec(
|
|
189
208
|
`${ls} | xargs -I {} sh -c 'TIMESTAMP=$(echo {} | cut -d" " -f1); NAME=$(echo {} | cut -d" " -f2-); echo $TIMESTAMP${delimeter}$NAME${delimeter}$(${grep.additions})${delimeter}$(${grep.deletions})'`,
|
|
@@ -215,6 +234,7 @@ async function main(args) {
|
|
|
215
234
|
.split(/\n/)
|
|
216
235
|
.forEach((backup) => {
|
|
217
236
|
const [timestamp, name, additions, deletions] = backup.split(delimeter);
|
|
237
|
+
debug(backup);
|
|
218
238
|
table.row([
|
|
219
239
|
name.replace(/^.backups\//, ''),
|
|
220
240
|
date.relative(timestamp * 1000),
|
|
@@ -225,6 +245,8 @@ async function main(args) {
|
|
|
225
245
|
});
|
|
226
246
|
|
|
227
247
|
table.print();
|
|
248
|
+
} else if (command === '--version' || command === 'version') {
|
|
249
|
+
version();
|
|
228
250
|
} else {
|
|
229
251
|
usage();
|
|
230
252
|
|
package/package.json
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
2
|
+
"name": "@frakjs/frak",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "A commandline deployment tool for syncing files to remote servers.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=12"
|
|
8
|
+
},
|
|
9
|
+
"bin": {
|
|
10
|
+
"frak": "bin/frak.js"
|
|
11
|
+
},
|
|
12
|
+
"type": "module",
|
|
13
|
+
"scripts": {
|
|
14
|
+
"eslint": "eslint",
|
|
15
|
+
"test": "node --experimental-test-coverage tests/index.test.js"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/frakjs/frak.git"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"rsync",
|
|
23
|
+
"deployment",
|
|
24
|
+
"ssh"
|
|
25
|
+
],
|
|
26
|
+
"author": "Frank Strube <contact@frakjs.com>",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/frakjs/frak/issues"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/frakjs/frak#readme",
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@eslint/js": "^9.33.0",
|
|
34
|
+
"eslint": "^9.33.0",
|
|
35
|
+
"globals": "^16.3.0"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"yaml": "^1.10.0"
|
|
39
|
+
}
|
|
40
40
|
}
|
package/src/config.js
CHANGED
package/src/crypto.js
CHANGED
package/src/diff.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import { statSync } from 'node:fs';
|
|
2
|
+
import { existsSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import * as ansi from './ansi.js';
|
|
4
4
|
import * as cli from './cli.js';
|
|
5
|
+
import * as crypto from './crypto.js';
|
|
5
6
|
import config from './config.js';
|
|
6
7
|
import debug from './debug.js';
|
|
7
8
|
|
|
@@ -16,6 +17,8 @@ export default async function diff(dryRunOutput, options) {
|
|
|
16
17
|
const changes = dryRunOutput
|
|
17
18
|
.split(/\r?\n/)
|
|
18
19
|
.filter(Boolean)
|
|
20
|
+
.filter((line) => !/^created directory .*/.test(line))
|
|
21
|
+
.filter((line) => !/^cannot delete non-empty directory:.*/.test(line))
|
|
19
22
|
.map((line) => {
|
|
20
23
|
// Split line into action and file, preserving whitespace for filenames
|
|
21
24
|
const [action, ...parts] = line.split(/\s+/);
|
|
@@ -52,7 +55,7 @@ export default async function diff(dryRunOutput, options) {
|
|
|
52
55
|
return Promise.reject('Process canceled by user');
|
|
53
56
|
}
|
|
54
57
|
|
|
55
|
-
const files = changes.map(([, file]) => file);
|
|
58
|
+
const files = changes.map(([, file]) => file).filter((file) => existsSync(file));
|
|
56
59
|
|
|
57
60
|
const diffs = changes
|
|
58
61
|
.sort((a, b) => a[1].localeCompare(b[1]))
|
|
@@ -109,18 +112,47 @@ export default async function diff(dryRunOutput, options) {
|
|
|
109
112
|
if (diffs.length > 0) {
|
|
110
113
|
debug({ diffs, files });
|
|
111
114
|
|
|
115
|
+
const diffsScriptFilename = `.frak-diffs-${crypto.sha256(Date.now())}.sh`;
|
|
116
|
+
|
|
117
|
+
writeFileSync(`/tmp/${diffsScriptFilename}`, diffs.join(';'));
|
|
118
|
+
|
|
112
119
|
// Tarball the local files up to the server
|
|
113
|
-
const tar = spawn('tar', ['cz', ...files]);
|
|
120
|
+
const tar = spawn('tar', ['cz', ...files, `/tmp/${diffsScriptFilename}`]);
|
|
121
|
+
|
|
122
|
+
// Handle tar errors
|
|
123
|
+
tar.on('error', (error) => {
|
|
124
|
+
throw error;
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Cleanup the diffs script
|
|
128
|
+
tar.on('exit', () => {
|
|
129
|
+
unlinkSync(`/tmp/${diffsScriptFilename}`);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Run ssh, it takes the tarball through stdin, and expands it inline to generate diffs.
|
|
133
|
+
// Don't forget, each command after the -- needs to end in a semicolon (;).
|
|
114
134
|
const ssh = spawn('ssh', [
|
|
115
135
|
config.server,
|
|
116
136
|
'--',
|
|
117
|
-
|
|
137
|
+
// Save the incoming tarball to /tmp
|
|
138
|
+
'export tarball=$(mktemp /tmp/frak-diff-tarball.XXXXXX);',
|
|
118
139
|
'cat > $tarball;',
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
';',
|
|
122
|
-
|
|
140
|
+
// Save the diffs script to the /tmp folder
|
|
141
|
+
`tar xzOf $tarball "tmp/${diffsScriptFilename}" > /tmp/${diffsScriptFilename};`,
|
|
142
|
+
process.env.DEBUG ? 'tar tf $tarball;' : '',
|
|
143
|
+
// Fixes missing root directory by jumping to an empty /tmp folder
|
|
144
|
+
`if [ -d "${config.root}" ] ; then cd "${config.root}"; else fakeroot=$(mktemp -d /tmp/frak-fake-root.XXXXXX); cd $fakeroot; fi ;`,
|
|
145
|
+
// Run the diffs script
|
|
146
|
+
`sh /tmp/${diffsScriptFilename};`,
|
|
147
|
+
// Clean up diffs script
|
|
148
|
+
`rm /tmp/${diffsScriptFilename};`,
|
|
149
|
+
// Clean up the tarball
|
|
150
|
+
'rm $tarball;',
|
|
151
|
+
// Clean up the fake root fix
|
|
152
|
+
'if [ -d "$fakeroot" ] ; then rmdir "$fakeroot" ; fi;',
|
|
123
153
|
]);
|
|
154
|
+
|
|
155
|
+
// Pipe the tar output to SSH
|
|
124
156
|
tar.stdout.pipe(ssh.stdin);
|
|
125
157
|
|
|
126
158
|
let less;
|
|
@@ -182,7 +214,7 @@ export default async function diff(dryRunOutput, options) {
|
|
|
182
214
|
});
|
|
183
215
|
} else {
|
|
184
216
|
/* node:coverage disable */
|
|
185
|
-
return await new Promise((resolve) => {
|
|
217
|
+
return await new Promise((resolve, reject) => {
|
|
186
218
|
let diff = '';
|
|
187
219
|
|
|
188
220
|
ssh.stdout.on('data', (data) => (diff += data));
|
|
@@ -192,6 +224,10 @@ export default async function diff(dryRunOutput, options) {
|
|
|
192
224
|
ssh.on('exit', () => {
|
|
193
225
|
resolve(diff);
|
|
194
226
|
});
|
|
227
|
+
|
|
228
|
+
ssh.on('error', (error) => {
|
|
229
|
+
reject(error);
|
|
230
|
+
});
|
|
195
231
|
});
|
|
196
232
|
/* node:coverage enable */
|
|
197
233
|
}
|
package/src/humanize.js
CHANGED
|
@@ -2,6 +2,8 @@ import * as ansi from './ansi.js';
|
|
|
2
2
|
|
|
3
3
|
export default function humanize(changes) {
|
|
4
4
|
return changes
|
|
5
|
+
.replace(/(^|\n)created directory .*\n/g, '$1')
|
|
6
|
+
.replace(/(^|\n)cannot delete non-empty directory: .*\n/g, '$1')
|
|
5
7
|
.replace(/cL\+\+\+\+\+\+\+\+\+/g, ansi.blue('New Link '))
|
|
6
8
|
.replace(/<f\+\+\+\+\+\+\+\+\+/g, ansi.green('New File '))
|
|
7
9
|
.replace(/>f\+\+\+\+\+\+\+\+\+/g, ansi.green('New File '))
|
package/src/rsync.js
CHANGED
|
@@ -14,16 +14,7 @@ export function filters() {
|
|
|
14
14
|
const { options } = parse(process.argv.slice(2));
|
|
15
15
|
const filters = [];
|
|
16
16
|
const includes = [];
|
|
17
|
-
const excludes = [
|
|
18
|
-
'.frak*',
|
|
19
|
-
'.git*',
|
|
20
|
-
'.svn*',
|
|
21
|
-
'.tags*',
|
|
22
|
-
'frak.config.js',
|
|
23
|
-
'Capfile',
|
|
24
|
-
'Gemfile*',
|
|
25
|
-
'Vagrantfile*',
|
|
26
|
-
];
|
|
17
|
+
const excludes = ['/.frak', '/.frakignore', '/.git/', '/.svn/', '/frak.config.js'];
|
|
27
18
|
|
|
28
19
|
// merge excludes via commandline
|
|
29
20
|
if (options.ignore) {
|