• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

suculent / thinx-device-api / #252646970

27 Oct 2017 03:17PM UTC coverage: 12.466% (+1.3%) from 11.197%
#252646970

push

suculent
added support for displaying/exporting extended SigFox attributes

37 of 1808 branches covered (2.05%)

Branch coverage included in aggregate %.

735 of 4385 relevant lines covered (16.76%)

0.17 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

12.88
/lib/thinx/buildlog.js
1
/** This THiNX-RTM API module is responsible for build logging. */
2

3
// #esversion: 6
4

5
var Buildlog = (function() {
1✔
6

7
        var app_config = require("../../conf/config.json");
1✔
8
        if (typeof(process.env.CIRCLE_USERNAME) !== "undefined") {
1!
9
                console.log("ยป Configuring for Circle CI...");
×
10
                app_config = require("../../conf/config-test.json");
×
11
        }
12
        var db = app_config.database_uri;
1✔
13
        var nano = require("nano")(db);
1✔
14

15
        var fs = require("fs");
1✔
16
        var readline = require("readline");
1✔
17
        var exec = require("child_process");
1✔
18
        var mkdirp = require("mkdirp");
1✔
19

20
        var tail = null;
1✔
21
        var err_callback = null;
1✔
22
        var parser = null;
1✔
23
        var websocket = null;
1✔
24

25
        Tail = require("tail").Tail;
1✔
26

27
        var prefix = "";
1✔
28
        try {
1✔
29
                var pfx_path = app_config.project_root + '/conf/.thx_prefix';
1✔
30
                if (fs.existsSync(pfx_path)) {
1!
31
                        prefix = fs.readFileSync(pfx_path) + "_";
×
32
                }
33
        } catch (e) {
34
                //console.log(e);
35
        }
36

37
        nano.db.create(prefix + "managed_builds", function(err, body, header) {
1✔
38
                if (err.statusCode != 412) {
1!
39
                        console.log("[buildlog] db error " + err);
1✔
40
                }
41
        });
42

43
        var buildlib = require("nano")(db).use(prefix + "managed_builds");
1✔
44

45
        function ab2str(buf) {
46
                return String.fromCharCode.apply(null, new Uint16Array(buf));
×
47
        }
48

49
        function str2ab(str) {
50
                var buf = new ArrayBuffer(str.length * 2); // 2 bytes for each char
×
51
                var bufView = new Uint16Array(buf);
×
52
                for (var i = 0, strLen = str.length; i < strLen; i++) {
×
53
                        bufView[i] = str.charCodeAt(i);
×
54
                }
55
                return buf;
×
56
        }
57

58
        var _private = {
1✔
59

60
                // real-life log path example:
61
                // /root/thinx-device-api/data/cedc16bb6bb06daaa3ff6d30666d91aacd6e3efbf9abbc151b4dcade59af7c12/f8e88e40-43c8-11e7-9ad3-b7281c2b9610/08880d80-5db4-11e7-bc78-f76a3906007e/08880d80-5db4-11e7-bc78-f76a3906007e.log
62

63
                pathForOwner: function(owner) {
64
                        var user_path = app_config.project_root + app_config.deploy_root + "/" +
×
65
                                owner;
66
                        return user_path;
×
67
                }
68
        };
69

70
        // public
71
        var _public = {
1✔
72

73
                /**
74
                 * Store new record in build log
75
                 * @param {string} build_id - UUID of the build
76
                 * @param {string} owner - 'owner' id of the build owner
77
                 * @param {string} udid - UDID of the target device
78
                 * @param {string} message - build log status message
79
                 */
80

81
                log: function(build_id, owner, udid, message, contents) {
82

83
                        if (typeof(owner) === "undefined") {
×
84
                                throw ("Invalid Log owner: " + owner);
×
85
                        }
86

87
                        var mtime = new Date();
×
88

89
                        // TODO: should be simplified, investigate impact.
90
                        var record = {
×
91
                                "message": message,
92
                                "udid": udid,
93
                                "timestamp": mtime,
94
                                "build": build_id
95
                        };
96

97
                        if (typeof(contents) !== "undefined") {
×
98
                                record.contents = contents;
×
99
                        }
100

101
                        buildlib.get(build_id, function(err, existing) {
×
102

103
                                // initial log record
104
                                if (err || (typeof(existing) === "undefined")) {
×
105
                                        var now = new Date();
×
106
                                        var initial_record = {
×
107
                                                timestamp: mtime,
108
                                                last_update: now,
109
                                                start_time: now,
110
                                                owner: owner,
111
                                                build_id: build_id,
112
                                                udid: udid,
113
                                                log: [record]
114
                                        };
115

116
                                        if (err.toString().indexOf("Document update conflict") !== -1) {
×
117
                                                // log/last_update from timestamp update
118
                                                buildlib.atomic("logs", "log", build_id, record, function(error,
×
119
                                                        body) {
120
                                                        if (err) {
×
121
                                                                console.log("Log update (existing) error: " + err, body);
×
122
                                                        } else {
123
                                                                console.log("BuildLog updated atomically.");
×
124
                                                        }
125
                                                });
126
                                        } else {
127
                                                buildlib.insert(initial_record, build_id, function(err,
×
128
                                                        body, header) {
129
                                                        if (err) {
×
130

131
                                                                // log/last_update from timestamp update
132
                                                                buildlib.atomic("logs", "log", build_id, record, function(error,
×
133
                                                                        body) {
134
                                                                        if (err) {
×
135
                                                                                console.log("Log update (new) error: " + err, body);
×
136
                                                                        }
137
                                                                });
138
                                                        }
139
                                                });
140
                                        }
141

142
                                } else {
143

144
                                        // log/last_update from timestamp update
145
                                        buildlib.atomic("logs", "log", build_id, record, function(error,
×
146
                                                body) {
147
                                                if (err) {
×
148
                                                        console.log("Log update (existing) error: " + err, body);
×
149
                                                }
150
                                        });
151
                                }
152
                        });
153
                },
154

155
                /**
156
                 * Fetch record from build log
157
                 * @param {string} build_id - UUID of the build
158
                 * @param {function} callback (err, body) - async return callback
159
                 */
160

161
                fetch: function(build_id, callback) {
162

163
                        buildlib.get(build_id, function(err, body) {
×
164

165
                                if (err) {
×
166
                                        console.log("[buildlog] Error fetching build log " + build_id);
×
167
                                        if (err.toString().indexOf("Error: missing") !== -1) {
×
168
                                                callback(false, "error_missing:" + build_id); // FIXME: this is not a standard response, change to JSON
×
169
                                        } else {
170
                                                callback(false, err);
×
171
                                        }
172
                                        return;
×
173
                                }
174

175
                                if ((typeof(body.log) === "undefined") || (body.log.count === 0)) {
×
176
                                        console.log("[buildlog] body has no log...");
×
177
                                        callback(false, {});
×
178
                                        return;
×
179
                                }
180

181
                                var bodykeys = Object.keys(body.log);
×
182
                                var blog = body.log[bodykeys[0]];
×
183
                                var path = _public.pathForDevice(blog.owner, blog.udid);
×
184
                                var build_log_path = path + "/" + build_id + "/build.log";
×
185

186
                                var log_info = {};
×
187
                                if (typeof(body.log) !== "undefined") {
×
188
                                        log_info = body.log;
×
189
                                }
190
                                if (fs.existsSync(build_log_path)) {
×
191
                                        var log_contents = fs.readFileSync(build_log_path);
×
192
                                        var response = {
×
193
                                                log: log_info,
194
                                                contents: log_contents
195
                                        };
196
                                        callback(false, response);
×
197
                                } else {
198
                                        var short_response = {
×
199
                                                log: log_info
200
                                        };
201
                                        callback(false, short_response);
×
202
                                }
203
                        });
204
                },
205

206
                /**
207
                 * List build logs
208
                 * @param {string} owner - 'owner' id
209
                 * @param {function} callback (err, body) - async return callback
210
                 */
211

212
                list: function(owner, callback) {
213
                        buildlib.view("builds", "latest_builds", {
×
214
                                "limit": 100,
215
                                "descending": true,
216
                                "key": owner
217
                        }, function(err, body) {
218
                                if (err) {
×
219
                                        console.log("[buildlog] Error listing builds for owner...");
×
220
                                        callback(err, {});
×
221
                                } else {
222
                                        callback(false, body);
×
223
                                }
224
                        });
225
                },
226

227
                /**
228
                 * Watch build log
229
                 * @param {string} build_id - UUID of the build
230
                 * @param {string} owner - owner of the request/socket
231
                 * @param {Websocket} socket - socket that will be used as output
232
                 * @param {function} err_callback (data) - async return callback for line events
233
                 */
234

235
                logtail: function(build_id, owner, socket, error_callback) {
236

237
                        websocket = socket;
×
238

239
                        if (typeof(error_callback) !== "undefined") {
×
240
                                err_callback = error_callback;
×
241
                        }
242

243
                        _public.fetch(build_id, function(err, body) {
×
244

245
                                        if (err.toString().indexOf("error") !== -1) {
×
246
                                                console.log("_public.fetch:build_id err: " + JSON.stringify(err));
×
247
                                                return;
×
248
                                        }
249

250
                                        //var message = body.message.toString('utf8');
251
                                        //console.log("Extracted message: " + message);
252

253
                                        if (typeof(body) === "undefined") {
×
254
                                                if (typeof(websocket) !== "undefined" && websocket !== null) {
×
255
                                                        try {
×
256
                                                                websocket.send(JSON.stringify({
×
257
                                                                        log: "Sorry, no log records fetched."
258
                                                                }));
259
                                                        } catch (e) { /* handle error */ }
260
                                                } else {
261
                                                        console.log("[logtail] no websocket.");
×
262
                                                        err_callback("[logtail] no websocket");
×
263
                                                }
264
                                                return;
×
265
                                        }
266

267
                                        if (body.length === 0) {
×
268
                                                err_callback("[logtail] body not found");
×
269
                                                console.log("[logtail] body not found");
×
270
                                                return;
×
271
                                        }
272

273

274
                                        if (typeof(body.log) === "undefined") {
×
275
                                                console.log("[logtail] log not found in " + JSON.stringify(body));
×
276
                                                //err_callback("[logtail] body not found");
277
                                                //return;
278
                                                body.log = [];
×
279
                                        }
280

281
                                        if (typeof(body.log) === "undefined") {
×
282
                                                body.log = [];
×
283
                                        }
284

285
                                        body.log.push({
×
286
                                                message: "Waiting for build log...",
287
                                                udid: body.udid,
288
                                                date: body.timestamp,
289
                                                build: body.build_id
290
                                        });
291

292
                                        var build = body.log[0];
×
293

294
                                        if (typeof(build.owner) === "undefined") {
×
295
                                                build.owner = owner;
×
296
                                                console.log(
×
297
                                                        "[logtail] build has no owner - FIXME: hacking, injecting body owner: " + build.owner
298
                                                );
299
                                        }
300

301
                                        if (err) {
×
302
                                                console.log("[logtail] error: " + err);
×
303
                                        } else {
304

305
                                                var message = ab2str(build.message);
×
306

307
                                                if (message === "") {
×
308
                                                        message = build.message;
×
309
                                                }
310

311
                                                console.log("[logtail] fetched build message: " + message);
×
312

313
                                                if (typeof(websocket) !== "undefined" && websocket !== null) {
×
314
                                                        try {
×
315
                                                                websocket.send(message);
×
316
                                                        } catch (e) { /* handle error */
317
                                                                console.log(e);
×
318
                                                        }
319
                                                } else {
320
                                                        console.log("[logtail][head] no websocket.");
×
321
                                                }
322

323
                                                var build_udid = build.udid;
×
324
                                                var path = _public.pathForDevice(build.owner, build.udid);
×
325
                                                var build_path = path + "/" + build_id;
×
326

327
                                                // Whole build path is created here, because build log is the first thing being written here if nothing else.
328
                                                if (!fs.existsSync(build_path)) {
×
329
                                                        mkdirp.sync(build_path);
×
330
                                                        console.log("Created build_path: " + build_path);
×
331
                                                } else {
332
                                                        console.log("build_path: " + build_path + " already exists.");
×
333
                                                }
334

335
                                                var build_log_path = build_path + "/build.log";
×
336

337
                                                console.log("Searching for build-log " + build_log_path);
×
338

339
                                                // Create file before trying to tail, do not wait for builder to do it...
340
                                                var PRE = "LOG_DIR=`dirname " + build_log_path +
×
341
                                                        "`; [ ! -d $LOG_DIR ] && mkdir -p $LOG_DIR; touch " +
342
                                                        build_log_path;
343
                                                console.log(PRE);
×
344
                                                var presult = exec.execSync(PRE);
×
345

346
                                                if (fs.existsSync(build_log_path)) {
×
347

348
                                                        console.log("File " + build_log_path + " found, starting tail...");
×
349

350
                                                        var options = {
×
351
                                                                fromBeginning: true,
352
                                                                fsWatchOptions: {},
353
                                                                follow: true
354
                                                        };
355

356
                                                        if (tail !== null) {
×
357
                                                                console.log("Unwatching existing tail...");
×
358
                                                                tail.unwatch();
×
359
                                                                tail = null;
×
360
                                                        }
361

362
                                                        console.log("Initializing new tail...");
×
363
                                                        tail = new Tail(build_log_path, options);
×
364

365
                                                        tail.on("line", function(data) {
×
366
                                                                var logline = data.toString();
×
367
                                                                if (logline.indexOf("[logtail]") !== -1) return;
×
368
                                                                if ((logline === "") || (logline === "\n")) return;
×
369
                                                                if (typeof(websocket) !== "undefined" && websocket !== null) {
×
370
                                                                        try {
×
371
                                                                                websocket.send(logline);
×
372
                                                                        } catch (e) {
373
                                                                                // usually returns 'Error: not opened' when the pipe gets broken
374
                                                                                // console.log(e);
375
                                                                        }
376
                                                                } else {
377
                                                                        console.log("[logtail][line] no websocket.");
×
378
                                                                }
379
                                                        });
380

381
                                                        tail.on("error", function(error) {
×
382
                                                                console.log("ERROR: ", error);
×
383
                                                                if (typeof(err_callback) !== "undefined") {
×
384
                                                                        err_callback("fake build log error");
×
385
                                                                }
386
                                                        });
387

388
                                                        // hack to start on non-changing files
389
                                                        tail.watchEvent.call(tail, "change");
×
390

391
                                                } else {
392

393
                                                        if (typeof(websocket) !== "undefined" && websocket !== null) {
×
394
                                                                try {
×
395
                                                                        var logline = "Log not found at: " + build_log_path;
×
396
                                                                        websocket.send(logline);
×
397
                                                                } catch (e) {
398
                                                                        /* handle error */
399
                                                                        console.log(e);
×
400
                                                                }
401
                                                        } else {
402
                                                                console.log("[logtail][line] no websocket.");
×
403
                                                        }
404
                                                }
405
                                        }
406

407
                                } // no error
408

409
                        ); // build fetch
410
                },
411

412
                pathForDevice: function(owner, udid) {
413
                        this.owner = owner;
×
414
                        this.udid = udid;
×
415
                        var user_path = _private.pathForOwner(owner);
×
416
                        var device_path = user_path + "/" + udid;
×
417
                        return device_path;
×
418
                }
419

420
        };
421

422
        return _public;
1✔
423

424
})();
425

426
exports.log = Buildlog.log;
1✔
427
exports.fetch = Buildlog.fetch;
1✔
428
exports.list =
1✔
429
        Buildlog.list;
430
exports.tail = Buildlog.tail;
1✔
431
exports.logtail = Buildlog.logtail;
1✔
432

433
exports.pathForDevice = Buildlog.pathForDevice;
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc