forked from balena-os/balena-supervisor
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sync-debug.js
executable file
·130 lines (115 loc) · 3.42 KB
/
sync-debug.js
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#!/usr/bin/env node
if (!process.argv[2] || ['help', '-h', '--help'].includes(process.argv[2])) {
console.log(`
Sync changes in the javascript code to a running local mode supervisor on a device on the local network
Usage:
./sync-debug.js <device IP>
Note that the device should be running a debug image.
`);
process.exit(1);
}
const ip = process.argv[2];
const { Livepush } = require('livepush');
const { fs } = require('mz');
const dockerode = require('dockerode');
const chokidar = require('chokidar');
const _ = require('lodash');
let lastReadTimestamp = null;
const setupLogs = async (containerId, docker) => {
const container = docker.getContainer(containerId);
const stream = await container.logs({
stdout: true,
stderr: true,
follow: true,
timestamps: true,
// We start from 0, as we risk not getting any logs to
// properly seed the value if the host and remote times differ
since: lastReadTimestamp != null ? lastReadTimestamp : 0,
});
stream.on('data', chunk => {
const { message, timestamp } = extractMessage(chunk);
lastReadTimestamp = Math.floor(timestamp.getTime() / 1000);
process.stdout.write(message);
});
stream.on('end', () => {
setupLogs(containerId, docker);
});
};
function extractMessage(msgBuf) {
// Non-tty message format from:
// https://docs.docker.com/engine/api/v1.30/#operation/ContainerAttach
if (_.includes([0, 1, 2], msgBuf[0])) {
// Take the header from this message, and parse it as normal
msgBuf = msgBuf.slice(8);
}
const str = msgBuf.toString();
const space = str.indexOf(' ');
return {
timestamp: new Date(str.slice(0, space)),
message: str.slice(space + 1),
};
}
const docker = new dockerode({
host: ip,
port: 2375,
});
let changedFiles = [];
let deletedFiles = [];
const performLivepush = _.debounce(async livepush => {
await livepush.performLivepush(changedFiles, deletedFiles);
changedFiles = [];
deletedFiles = [];
}, 1000);
(async () => {
console.log('Starting up...');
// Get the supervisor container id
const container = await docker.getContainer('resin_supervisor').inspect();
console.log('Supervisor container id: ', container.Id);
const containerId = container.Id;
const image = container.Image;
setupLogs(containerId, docker);
const livepush = await Livepush.init(
await fs.readFile('Dockerfile.debug'),
'.',
containerId,
// a bit of a hack, as the multistage images aren't
// present, but it shouldn't make a difference as these
// will never change
_.times(6, () => image),
docker,
);
chokidar
.watch('.', {
ignored: /((^|[\/\\])\..|node_modules.*)/,
ignoreInitial: false,
})
.on('add', path => {
changedFiles.push(path);
performLivepush(livepush, containerId, docker);
})
.on('change', path => {
changedFiles.push(path);
performLivepush(livepush, containerId, docker);
})
.on('unlink', path => {
deletedFiles.push(path);
performLivepush(livepush, containerId, docker);
});
livepush.on('commandExecute', ({ command }) => {
console.log('SYNC: executing:', command);
});
livepush.on('commandOutput', ({ output }) => {
const message = output.data.toString();
if (message.trim().length !== 0) {
process.stdout.write(`\t${message}`);
}
});
livepush.on('commandReturn', ({ returnCode }) => {
if (returnCode !== 0) {
console.log(`\tSYNC: Command return non zero exit status: ${returnCode}`);
}
});
livepush.on('containerRestart', () => {
console.log('SYNC: Restarting container...');
});
})();