gitsheets 0.21.1 → 0.21.5
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/bin/cli.js +36 -14
- package/commands/edit.js +16 -10
- package/commands/normalize.js +17 -3
- package/commands/read.js +3 -8
- package/commands/singer-target.js +11 -9
- package/lib/Sheet.js +17 -6
- package/lib/path/Template.js +8 -7
- package/package.json +4 -3
package/bin/cli.js
CHANGED
|
@@ -2,18 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
// setup logger
|
|
5
|
-
const logger = require('winston')
|
|
6
|
-
|
|
5
|
+
const logger = require('winston');
|
|
6
|
+
const loggerConsole = new logger.transports.Console({
|
|
7
|
+
level: process.env.DEBUG ? 'debug' : 'info',
|
|
8
|
+
format: logger.format.combine(
|
|
9
|
+
logger.format.colorize(),
|
|
10
|
+
logger.format.prettyPrint(),
|
|
11
|
+
logger.format.splat(),
|
|
12
|
+
logger.format.simple(),
|
|
13
|
+
),
|
|
7
14
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
15
|
+
// all logger output to STDERR
|
|
16
|
+
stderrLevels: Object.keys(require('winston/lib/winston/config').cli.levels),
|
|
17
|
+
});
|
|
18
|
+
logger.add(loggerConsole);
|
|
11
19
|
|
|
12
|
-
|
|
13
|
-
// all logger output to STDERR
|
|
14
|
-
for (const level in logger.levels) {
|
|
15
|
-
logger.default.transports.console.stderrLevels[level] = true;
|
|
16
|
-
}
|
|
20
|
+
module.exports = { logger };
|
|
17
21
|
|
|
18
22
|
|
|
19
23
|
// route command line
|
|
@@ -25,15 +29,33 @@ require('yargs')
|
|
|
25
29
|
default: false,
|
|
26
30
|
global: true,
|
|
27
31
|
})
|
|
32
|
+
.option('q', {
|
|
33
|
+
alias: 'quiet',
|
|
34
|
+
type: 'boolean',
|
|
35
|
+
default: false,
|
|
36
|
+
global: true,
|
|
37
|
+
})
|
|
28
38
|
.check(function (argv) {
|
|
29
39
|
if (argv.debug) {
|
|
30
|
-
|
|
40
|
+
loggerConsole.level = 'debug';
|
|
31
41
|
} else if (argv.quiet) {
|
|
32
|
-
|
|
42
|
+
loggerConsole.level = 'error';
|
|
33
43
|
}
|
|
34
44
|
|
|
35
45
|
return true;
|
|
36
46
|
})
|
|
37
|
-
.commandDir('../commands')
|
|
47
|
+
.commandDir('../commands', { exclude: /\.test\.js$/ })
|
|
38
48
|
.demandCommand()
|
|
39
|
-
.
|
|
49
|
+
.showHelpOnFail(false, 'Specify --help for available options')
|
|
50
|
+
.fail((msg, err) => {
|
|
51
|
+
logger.error(msg || err.message);
|
|
52
|
+
|
|
53
|
+
if (err) {
|
|
54
|
+
logger.debug(err.stack);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
process.exit(1);
|
|
58
|
+
})
|
|
59
|
+
.strict()
|
|
60
|
+
.help()
|
|
61
|
+
.argv;
|
package/commands/edit.js
CHANGED
|
@@ -19,6 +19,8 @@ exports.builder = {
|
|
|
19
19
|
exports.handler = async function edit({ recordPath, resumePath, encoding }) {
|
|
20
20
|
const fs = require('fs');
|
|
21
21
|
const { spawn } = require('child_process');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
const tmp = require('tmp');
|
|
22
24
|
const TOML = require('@iarna/toml');
|
|
23
25
|
const Repository = require('../lib/Repository.js');
|
|
24
26
|
const Sheet = require('../lib/Sheet.js')
|
|
@@ -26,17 +28,21 @@ exports.handler = async function edit({ recordPath, resumePath, encoding }) {
|
|
|
26
28
|
const git = await repo.getGit();
|
|
27
29
|
|
|
28
30
|
// open record
|
|
29
|
-
|
|
31
|
+
let recordToml = fs.readFileSync(resumePath || recordPath, encoding);
|
|
30
32
|
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
// try to parse and format
|
|
34
|
+
try {
|
|
35
|
+
const record = TOML.parse(recordToml);
|
|
36
|
+
recordToml = Sheet.stringifyRecord(record);
|
|
37
|
+
} catch (err) {
|
|
38
|
+
console.warn(`Failed to parse opened record:\n${err}`);
|
|
39
|
+
}
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
// get temp path
|
|
42
|
+
const { name: tempFilePath } = tmp.fileSync({
|
|
43
|
+
prefix: path.basename(recordPath, '.toml'),
|
|
44
|
+
postfix: '.toml',
|
|
45
|
+
discardDescriptor: true,
|
|
40
46
|
});
|
|
41
47
|
|
|
42
48
|
// populate temp path
|
|
@@ -67,7 +73,7 @@ exports.handler = async function edit({ recordPath, resumePath, encoding }) {
|
|
|
67
73
|
try {
|
|
68
74
|
editedRecord = TOML.parse(editedToml);
|
|
69
75
|
} catch (err) {
|
|
70
|
-
console.error(`Failed to parse record:\n${err}`);
|
|
76
|
+
console.error(`Failed to parse edited record:\n${err}`);
|
|
71
77
|
console.error(`To resume editing, run: git sheet edit ${recordPath} ${tempFilePath}`);
|
|
72
78
|
process.exit(1);
|
|
73
79
|
}
|
package/commands/normalize.js
CHANGED
|
@@ -54,9 +54,23 @@ exports.handler = async function query({
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
// loop through all records and re-upsert
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
try {
|
|
58
|
+
for await (const record of sheet.query()) {
|
|
59
|
+
const originalPath = record[Symbol.for('gitsheets-path')];
|
|
60
|
+
logger.info(`rewriting ${path.join(root, prefix, sheetName, originalPath)}.toml`);
|
|
61
|
+
const { path: normalizedPath } = await sheet.upsert(record);
|
|
62
|
+
|
|
63
|
+
if (normalizedPath !== originalPath) {
|
|
64
|
+
logger.warn(`^- moved to ${path.join(root, prefix, sheetName, normalizedPath)}.toml`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
} catch (err) {
|
|
68
|
+
if (err.constructor.name == 'TomlError') {
|
|
69
|
+
logger.error(`failed to parse ${path.join(root, prefix, err.file)}\n${err.message}`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
throw err;
|
|
60
74
|
}
|
|
61
75
|
}
|
|
62
76
|
|
package/commands/read.js
CHANGED
|
@@ -3,7 +3,7 @@ exports.desc = 'Read a record, converting to desired format';
|
|
|
3
3
|
exports.builder = {
|
|
4
4
|
'record-path': {
|
|
5
5
|
type: 'string',
|
|
6
|
-
describe: 'The path to a record file to
|
|
6
|
+
describe: 'The path to a record file to read',
|
|
7
7
|
demandOption: true,
|
|
8
8
|
},
|
|
9
9
|
encoding: {
|
|
@@ -24,12 +24,7 @@ exports.builder = {
|
|
|
24
24
|
|
|
25
25
|
exports.handler = async function edit({ recordPath, encoding, format, headers }) {
|
|
26
26
|
const fs = require('fs');
|
|
27
|
-
const { spawn } = require('child_process');
|
|
28
27
|
const TOML = require('@iarna/toml');
|
|
29
|
-
const Repository = require('../lib/Repository.js');
|
|
30
|
-
const Sheet = require('../lib/Sheet.js')
|
|
31
|
-
const repo = await Repository.getFromEnvironment({ working: true });
|
|
32
|
-
const git = await repo.getGit();
|
|
33
28
|
|
|
34
29
|
// open record
|
|
35
30
|
const recordToml = fs.readFileSync(recordPath, encoding);
|
|
@@ -64,6 +59,6 @@ async function outputCsv(record, { headers = true, delimiter = ',' } = {}) {
|
|
|
64
59
|
}
|
|
65
60
|
|
|
66
61
|
async function outputToml(record) {
|
|
67
|
-
const
|
|
68
|
-
console.log(`${
|
|
62
|
+
const Sheet = require('../lib/Sheet.js')
|
|
63
|
+
console.log(`${Sheet.stringifyRecord(record)}`);
|
|
69
64
|
}
|
|
@@ -101,11 +101,20 @@ exports.handler = async function singerTarget({
|
|
|
101
101
|
const clearedSheets = new Set();
|
|
102
102
|
const writtenStreams = new Set();
|
|
103
103
|
for await (const { type, stream, ...message} of readMessages({ jsonlFile })) {
|
|
104
|
-
const sheet = sheets[stream];
|
|
105
|
-
|
|
106
104
|
console.log(`${type}\t${stream}`, message);
|
|
107
105
|
|
|
108
106
|
|
|
107
|
+
// ignore unhandled message types for now
|
|
108
|
+
if (type == 'STATE' || type == 'ACTIVATE_VERSION') {
|
|
109
|
+
console.warn(`ignoring ${type} message`);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
// get sheet
|
|
115
|
+
const sheet = sheets[stream];
|
|
116
|
+
|
|
117
|
+
|
|
109
118
|
// create schema if needed
|
|
110
119
|
if (!sheet) {
|
|
111
120
|
if (type == 'SCHEMA') {
|
|
@@ -129,13 +138,6 @@ exports.handler = async function singerTarget({
|
|
|
129
138
|
}
|
|
130
139
|
|
|
131
140
|
|
|
132
|
-
// ignore state for now
|
|
133
|
-
if (type == 'STATE') {
|
|
134
|
-
console.warn('ignoring STATE message');
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
|
|
139
141
|
// handle record message
|
|
140
142
|
if (type == 'RECORD') {
|
|
141
143
|
if (deleteMissing && !clearedSheets.has(sheet)) {
|
package/lib/Sheet.js
CHANGED
|
@@ -118,9 +118,20 @@ class Sheet extends Configurable
|
|
|
118
118
|
async readRecord (blob, path = null) {
|
|
119
119
|
const cache = this.#recordCache.get(blob.hash);
|
|
120
120
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
let record;
|
|
122
|
+
|
|
123
|
+
if (cache) {
|
|
124
|
+
record = v8.deserialize(cache);
|
|
125
|
+
} else {
|
|
126
|
+
const toml = await blob.read();
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
record = TOML.parse(toml);
|
|
130
|
+
} catch (err) {
|
|
131
|
+
err.file = path;
|
|
132
|
+
throw err;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
124
135
|
|
|
125
136
|
// annotate with gitsheets keys
|
|
126
137
|
record[RECORD_SHEET_KEY] = this.name;
|
|
@@ -153,14 +164,14 @@ class Sheet extends Configurable
|
|
|
153
164
|
const pathTemplate = PathTemplate.fromString(pathTemplateString);
|
|
154
165
|
const sheetDataTree = await this.dataTree.getSubtree(sheetRoot);
|
|
155
166
|
|
|
156
|
-
BLOBS: for await (const blob of pathTemplate.queryTree(sheetDataTree, query)) {
|
|
157
|
-
const record = await this.readRecord(blob);
|
|
167
|
+
BLOBS: for await (const { blob, path: blobPath } of pathTemplate.queryTree(sheetDataTree, query)) {
|
|
168
|
+
const record = await this.readRecord(blob, blobPath);
|
|
158
169
|
|
|
159
170
|
if (!queryMatches(query, record)) {
|
|
160
171
|
continue BLOBS;
|
|
161
172
|
}
|
|
162
173
|
|
|
163
|
-
record[RECORD_PATH_KEY] = pathTemplate.render(record);
|
|
174
|
+
record[RECORD_PATH_KEY] = blobPath || pathTemplate.render(record);
|
|
164
175
|
|
|
165
176
|
yield record;
|
|
166
177
|
}
|
package/lib/path/Template.js
CHANGED
|
@@ -50,7 +50,7 @@ class Template
|
|
|
50
50
|
return recordPath.join('/');
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
async* queryTree (tree, query, depth = 0) {
|
|
53
|
+
async* queryTree (tree, query, pathPrefix = '', depth = 0) {
|
|
54
54
|
const numComponents = this.#components.length;
|
|
55
55
|
|
|
56
56
|
if (!tree) {
|
|
@@ -68,7 +68,7 @@ class Template
|
|
|
68
68
|
const child = await currentTree.getChild(`${nextName}.toml`);
|
|
69
69
|
|
|
70
70
|
if (child) {
|
|
71
|
-
yield child;
|
|
71
|
+
yield { path: path.join(pathPrefix, nextName), blob: child };
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
// absolute match on a leaf, we're done with this query
|
|
@@ -97,8 +97,9 @@ class Template
|
|
|
97
97
|
continue;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
const childName = childPath.substr(0, childPath.length - 5);
|
|
101
|
+
attachmentsPrefix = `${childName}/`;
|
|
102
|
+
yield { path: path.join(pathPrefix, childName), blob: child };
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
return;
|
|
@@ -117,14 +118,14 @@ class Template
|
|
|
117
118
|
} else {
|
|
118
119
|
// each tree in current tree could contain matching records
|
|
119
120
|
const children = await currentTree.getChildren();
|
|
120
|
-
for (const
|
|
121
|
-
const child = children[
|
|
121
|
+
for (const childPath in children) {
|
|
122
|
+
const child = children[childPath];
|
|
122
123
|
|
|
123
124
|
if (!child.isTree) {
|
|
124
125
|
continue;
|
|
125
126
|
}
|
|
126
127
|
|
|
127
|
-
yield* this.queryTree(child, query, i+1);
|
|
128
|
+
yield* this.queryTree(child, query, path.join(pathPrefix, childPath), i+1);
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gitsheets",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.5",
|
|
4
4
|
"description": "A toolkit for using a git repository to store low-volume, high-touch, human-scale data",
|
|
5
5
|
"main": "lib/GitSheets.js",
|
|
6
6
|
"scripts": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"fast-csv": "^4.3.6",
|
|
27
27
|
"fast-json-patch": "^2.2.1",
|
|
28
28
|
"get-stream": "^5.2.0",
|
|
29
|
-
"hologit": "^0.40.
|
|
29
|
+
"hologit": "^0.40.5",
|
|
30
30
|
"http-assert": "^1.4.1",
|
|
31
31
|
"koa": "^2.13.1",
|
|
32
32
|
"koa-bodyparser": "^4.3.0",
|
|
@@ -36,8 +36,9 @@
|
|
|
36
36
|
"rfc6902": "^4.0.1",
|
|
37
37
|
"sort-keys": "^4.2.0",
|
|
38
38
|
"streaming-json-stringify": "^3.1.0",
|
|
39
|
+
"tmp": "^0.2.1",
|
|
39
40
|
"to-readable-stream": "^2.1.0",
|
|
40
|
-
"winston": "^
|
|
41
|
+
"winston": "^3.3.3",
|
|
41
42
|
"yargs": "^13.3.2"
|
|
42
43
|
},
|
|
43
44
|
"devDependencies": {
|