git ssb

0+

cel-desktop / ssb-pkg



Tree: 1eb144e388da5f1a3466703f56ac4ab591d700fc

Files: 1eb144e388da5f1a3466703f56ac4ab591d700fc / prelude / install.js

9106 bytesRaw
1var os = require('os')
2var fs = require('fs')
3var zlib = require('zlib')
4var http = require('http')
5var stream = require('stream')
6var path = require('path')
7var STORE_CONTENT = 1
8
9var settings = JSON.parse(fs.readFileSync(3))
10
11function ReadBlobs(ids) {
12 if (!Array.isArray(ids)) throw new TypeError('blob ids should be array')
13 this.ids = ids
14 stream.Readable.call(this, ids)
15}
16ReadBlobs.prototype = new stream.Readable()
17ReadBlobs.prototype.constructor = ReadBlobs
18ReadBlobs.prototype.i = 0
19ReadBlobs.prototype.blobsBase = process.env.SSB_BLOBS_BASE
20 || 'http://localhost:8989/blobs/get/'
21ReadBlobs.prototype._read = function (size) {
22 if (!this.req) this._makeReq(this)
23 else if (this.res) this.res.resume()
24}
25ReadBlobs.prototype._destroy = function (err, cb) {
26 if (this.req) this.req.destroy(), this.req = null
27 if (this.res) this.res.destroy(), this.res = null
28 cb(err)
29}
30ReadBlobs.prototype._makeReq = function () {
31 if (this.i >= this.ids.length) return this.push(null)
32 var id = this.ids[this.i++]
33 var url = this.blobsBase + id
34 console.error(id)
35 var onResp = (res) => {
36 this.res = res
37 if (res.statusCode !== 200) {
38 var reqStatus = res.statusCode + ' ' + res.statusMessage
39 return this.destroy(new Error(reqStatus))
40 }
41 res.on('data', (data) => {
42 if (this.push(data) === false) res.pause()
43 })
44 res.on('end', () => {
45 this._makeReq()
46 })
47 }
48 this.req = http.get(url, onResp).on('error', (err) => {
49 // sometimes localhost resolves to 127.0.0.1 but ssb-ws listens on ::1
50 if (err.code === 'ECONNREFUSED' && err.address === '127.0.0.1') {
51 this.blobsBase = this.blobsBase.replace('localhost', '[::1]')
52 url = this.blobsBase + id
53 http.get(url, onResp).on('error', (err) => {
54 this.destroy(err)
55 })
56 } else {
57 this.destroy(err)
58 }
59 })
60}
61
62function Meter() {
63 stream.Transform.call(this)
64}
65Meter.prototype = new stream.Transform()
66Meter.prototype.constructor = Meter
67Meter.prototype.length = 0
68Meter.prototype._transform = function (buf, encoding, cb) {
69 this.length += buf.length
70 cb(null, buf)
71}
72
73function collect(cb) {
74 var bufs = []
75 return new stream.Writable({
76 write: function (chunk, encoding, cb) {
77 bufs.push(Buffer.from(chunk, encoding))
78 cb()
79 }
80 }).on('finish', function () {
81 cb(null, Buffer.concat(bufs))
82 }).on('error', cb)
83}
84
85function substitute(template, vars) {
86 return Object.keys(vars).reduce(function (str, key) {
87 return str.replace(key, vars[key])
88 }, template)
89}
90
91function discoverPlaceholder (binaryBuffer, searchString, padder) {
92 const placeholder = Buffer.from(searchString);
93 const position = binaryBuffer.indexOf(placeholder);
94 if (position === -1) return { notFound: true };
95 return { position, size: placeholder.length, padder };
96}
97
98function injectPlaceholder (fd, placeholder, value, cb) {
99 const { notFound, position, size, padder } = placeholder;
100 if (notFound) assert(false, 'Placeholder for not found');
101 if (typeof value === 'number') value = value.toString();
102 if (typeof value === 'string') value = Buffer.from(value);
103 const padding = Buffer.from(padder.repeat(size - value.length));
104 value = Buffer.concat([ value, padding ]);
105 fs.write(fd, value, 0, value.length, position, cb);
106}
107
108function discoverPlaceholders (binaryBuffer) {
109 return {
110 BAKERY: discoverPlaceholder(binaryBuffer, '\0' + '// BAKERY '.repeat(20), '\0'),
111 PAYLOAD_POSITION: discoverPlaceholder(binaryBuffer, '// PAYLOAD_POSITION //', ' '),
112 PAYLOAD_SIZE: discoverPlaceholder(binaryBuffer, '// PAYLOAD_SIZE //', ' '),
113 PRELUDE_POSITION: discoverPlaceholder(binaryBuffer, '// PRELUDE_POSITION //', ' '),
114 PRELUDE_SIZE: discoverPlaceholder(binaryBuffer, '// PRELUDE_SIZE //', ' ')
115 };
116}
117
118function injectPlaceholders (fd, placeholders, values, cb) {
119 injectPlaceholder(fd, placeholders.BAKERY, values.BAKERY, (error) => {
120 if (error) return cb(error);
121 injectPlaceholder(fd, placeholders.PAYLOAD_POSITION, values.PAYLOAD_POSITION, (error2) => {
122 if (error2) return cb(error2);
123 injectPlaceholder(fd, placeholders.PAYLOAD_SIZE, values.PAYLOAD_SIZE, (error3) => {
124 if (error3) return cb(error3);
125 injectPlaceholder(fd, placeholders.PRELUDE_POSITION, values.PRELUDE_POSITION, (error4) => {
126 if (error4) return cb(error4);
127 injectPlaceholder(fd, placeholders.PRELUDE_SIZE, values.PRELUDE_SIZE, cb);
128 });
129 });
130 });
131 });
132}
133
134function makeBakeryValueFromBakes (bakes) {
135 const parts = [];
136 if (bakes.length) {
137 for (let i = 0; i < bakes.length; i += 1) {
138 parts.push(Buffer.from(bakes[i]));
139 parts.push(Buffer.alloc(1));
140 }
141 parts.push(Buffer.alloc(1));
142 }
143 return Buffer.concat(parts);
144}
145
146function makePreludeBufferFromPrelude (prelude) {
147 return Buffer.from(
148 '(function(process, require, console, EXECPATH_FD, PAYLOAD_POSITION, PAYLOAD_SIZE) { ' +
149 prelude +
150 '\n})' // dont remove \n
151 );
152}
153
154function plusx(file) {
155 const s = fs.statSync(file)
156 const newMode = s.mode | 64 | 8 | 1
157 if (s.mode === newMode) return
158 const base8 = newMode.toString(8).slice(-3)
159 fs.chmodSync(file, base8)
160}
161
162var prefix = process.env.PREFIX || path.join(os.homedir(), '.local')
163var binDir = process.env.BIN_DIR || path.join(prefix, 'bin')
164var binPath = path.join(binDir, settings.binName)
165var binTmp = binPath + '~'
166
167// assume the node binary is the pkg one to use
168var nodeBuffer = fs.readFileSync(process.execPath)
169
170var placeholders = discoverPlaceholders(nodeBuffer)
171var payloadPosition = nodeBuffer.length, payloadSize
172var preludePosition, preludeSize
173var vfs
174var binFd, writeStream
175
176try {
177 fs.statSync(binDir)
178} catch(e) {
179 try { fs.mkdirSync(path.dirname(path.dirname(binDir))) } catch(e) {}
180 try { fs.mkdirSync(path.dirname(binDir)) } catch(e) {}
181 fs.mkdirSync(binDir)
182}
183
184function writeNodeBinary() {
185 fs.open(binTmp, 'w+', function (err, fd) {
186 if (err) throw err
187 binFd = fd
188 writeStream = fs.createWriteStream(null, {fd: fd})
189 writeStream.write(nodeBuffer, function (err) {
190 if (err) throw err
191 getVfs()
192 })
193 })
194}
195
196function getVfs() {
197 new ReadBlobs(settings.vfsBlobs)
198 .pipe(collect(function (err, vfsData) {
199 if (err) throw err
200 vfs = JSON.parse(vfsData)
201 getPayload()
202 }))
203}
204
205function getPayload() {
206 var payloadMeter = new Meter()
207 var readPayload = new ReadBlobs(settings.payloadBlobs)
208 .pipe(zlib.createGunzip())
209 .pipe(payloadMeter)
210 readPayload.pipe(writeStream, {end: false})
211 readPayload.on('end', function () {
212 payloadSize = payloadMeter.length
213 addFileBlobs()
214 })
215}
216
217function addFileBlobs() {
218 var offset = payloadSize
219 var fileBlobs = settings.fileBlobs
220 var queue = []
221 function enqueue(fileBlobsToAdd) {
222 for (var snap in fileBlobsToAdd) {
223 var ids = fileBlobsToAdd[snap]
224 queue.push({snap: snap, ids: ids})
225 }
226 }
227 if (Array.isArray(fileBlobs)) {
228 enqueue(fileBlobs)
229 } else [
230 os.platform() + '-' + os.arch(),
231 '*'
232 ].forEach(function (group) {
233 enqueue(fileBlobs[group])
234 })
235 ;(function next() {
236 if (!queue.length) return getPrelude()
237 var item = queue.shift()
238 var snap = item.snap, ids = item.ids
239
240 var fileMeter = new Meter()
241 var readFile = new ReadBlobs(ids)
242 .pipe(fileMeter)
243 readFile.pipe(writeStream, {end: false})
244 readFile.on('end', function () {
245 var fileSize = fileMeter.length
246 vfs[snap][STORE_CONTENT] = [offset, fileSize]
247 offset += fileSize
248 payloadSize += fileSize
249 next()
250 })
251 })()
252}
253
254function getPrelude() {
255 preludePosition = payloadPosition + payloadSize
256 new ReadBlobs(settings.preludeBlobs)
257 .pipe(collect(function (err, preludeData) {
258 if (err) throw err
259 preludeData = makePreludeBufferFromPrelude(
260 substitute(preludeData.toString('utf8'), {
261 '%VIRTUAL_FILESYSTEM%': JSON.stringify(vfs),
262 '%DEFAULT_ENTRYPOINT%': JSON.stringify(settings.entrypoint)
263 })
264 )
265 preludeSize = preludeData.length
266 writeStream.write(preludeData, function (err) {
267 if (err) throw err
268 inject()
269 })
270 }))
271}
272
273function inject() {
274 injectPlaceholders(binFd, placeholders, {
275 BAKERY: makeBakeryValueFromBakes(settings.bakes),
276 PAYLOAD_POSITION: payloadPosition,
277 PAYLOAD_SIZE: payloadSize,
278 PRELUDE_POSITION: preludePosition,
279 PRELUDE_SIZE: preludeSize
280 }, function (err) {
281 if (err) throw err
282 fs.close(binFd, function (err) {
283 if (err) throw err
284 putBins()
285 })
286 })
287}
288
289function addOtherBin(name, snap) {
290 var script = `#!/bin/sh
291PKG_ENTRYPOINT=${snap} exec ${settings.binName} "$@"
292`
293 var binPath = path.join(binDir, name)
294 var binTmp = binPath + '~'
295 fs.writeFileSync(binTmp, script)
296 plusx(binTmp)
297 fs.renameSync(binTmp, binPath)
298 console.log(binPath)
299}
300
301function putBins() {
302 plusx(binTmp)
303 fs.renameSync(binTmp, binPath)
304
305 var others = settings.otherEntrypoints
306 if (others) for (var name in others) {
307 var snap = others[name]
308 addOtherBin(name, snap)
309 }
310
311 console.log(binPath)
312 process.exit(0)
313}
314
315writeNodeBinary()
316

Built with git-ssb-web