@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 CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "tabSize": 4,
2
+ "tabWidth": 4,
3
3
  "singleQuote": true,
4
4
  "semi": true,
5
5
  "trailingComma": "es5",
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 --format='%Y %n' .backups/* | sort -r`;
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
- "name": "@frakjs/frak",
3
- "version": "0.3.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
- }
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
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import * as yaml from 'yaml';
3
+ import yaml from 'yaml';
4
4
 
5
5
  // Cached config object. The config is stored in here so it can
6
6
  // be loaded once and imported across all modules.
package/src/crypto.js CHANGED
@@ -1,5 +1,9 @@
1
1
  import crypto from 'node:crypto';
2
2
 
3
3
  export function sha256(text) {
4
+ if (typeof text === 'number') {
5
+ text = `${text}`;
6
+ }
7
+
4
8
  return crypto.createHash('sha256').update(text).digest('hex');
5
9
  }
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
- 'tarball=$(mktemp /tmp/frak-diff-tarball.XXXXXX);',
137
+ // Save the incoming tarball to /tmp
138
+ 'export tarball=$(mktemp /tmp/frak-diff-tarball.XXXXXX);',
118
139
  'cat > $tarball;',
119
- `cd ${config.root};`,
120
- diffs.join('; '),
121
- ';',
122
- 'rm $tarball',
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) {