Files: 28bf61d928af584e5e33b424fc41deafa9dddb77 / lib / util.js
6998 bytesRaw
1 | var path = require('path') |
2 | var ssbRef = require('ssb-ref') |
3 | var proc = require('child_process') |
4 | var u = exports |
5 | |
6 | u.help = function (cmd) { |
7 | require('./help').fn({_: [cmd]}) |
8 | } |
9 | |
10 | u.getAppName = function () { |
11 | return 'ssb_appname' in process.env ? process.env.ssb_appname : |
12 | u.gitSync('config', 'ssb.appname') |
13 | } |
14 | |
15 | u.gitSync = function () { |
16 | var args = [].concat.apply([], arguments) |
17 | return proc.spawnSync('git', args, {encoding: 'utf8'}).stdout.trim() |
18 | } |
19 | |
20 | u.getCurrentBranch = function () { |
21 | return u.gitSync('symbolic-ref', '--short', 'HEAD') |
22 | } |
23 | |
24 | function getRemoteUrl(name) { |
25 | if (!name) return null |
26 | return u.gitSync('ls-remote', '--get-url', name) |
27 | } |
28 | |
29 | function repoId(id) { |
30 | if (!id) return null |
31 | id = String(id).replace(/^ssb:\/*/, '') |
32 | return ssbRef.isMsg(id) || /^#[^\s#]{1,29}$/.test(id) ? id : null |
33 | } |
34 | |
35 | u.getRemote = function (name) { |
36 | return name |
37 | ? repoId(name) || repoId(getRemoteUrl(name)) |
38 | : repoId(getRemoteUrl('origin')) || repoId(getRemoteUrl('ssb')) |
39 | } |
40 | |
41 | function fork(before, after) { |
42 | return function () { |
43 | before.apply(this, arguments) |
44 | after.apply(this, arguments) |
45 | } |
46 | } |
47 | |
48 | u.getSbot = function (config, cb) { |
49 | var keys = require('ssb-keys') |
50 | .loadOrCreateSync(path.join(config.path, 'secret')) |
51 | require('ssb-client')(keys, config, function (err, sbot) { |
52 | if (err) return cb(err) |
53 | var keepalive = setInterval(sbot.whoami, 15e3) |
54 | sbot.close = fork(sbot.close, function () { |
55 | clearInterval(keepalive) |
56 | }) |
57 | cb(null, sbot) |
58 | }) |
59 | } |
60 | |
61 | u.gitDir = function () { |
62 | return u.gitSync('rev-parse', '-q', '--git-dir') |
63 | } |
64 | |
65 | u.gitError = function (stderr) { |
66 | var err = stderr.toString().trim() |
67 | if (!/^fatal/.test(err)) err = new Error(err) |
68 | return err |
69 | } |
70 | |
71 | u.editor = function (name, defaultText, useText, cb) { |
72 | var fs = require('fs') |
73 | var filename = path.resolve(u.gitDir(), name + '_EDITMSG') |
74 | |
75 | // prepare the file for editing |
76 | var separator = '=== This line and anything below will be removed ===' |
77 | var text = fs.existsSync(filename) ? |
78 | fs.readFileSync(filename, 'utf8').split(separator)[0].trim() : '' |
79 | text += '\n\n' + separator + '\n' + defaultText |
80 | fs.writeFileSync(filename, text) |
81 | |
82 | // edit the file |
83 | var editor = u.gitSync('var', 'GIT_EDITOR') |
84 | var args = [filename] |
85 | // give vim special formats, like hub(1) does |
86 | if (/[ng]?vim?$/.test(editor)) |
87 | args.unshift('-c', '"set ft=markdown tw=0 wrap lbr"') |
88 | var child = proc.spawn(editor, args, {shell: true, stdio: 'inherit'}) |
89 | child.on('exit', function (code) { |
90 | if (code) return cb('error using text editor') |
91 | fs.readFile(filename, 'utf8', function (err, text) { |
92 | if (err) return cb(err) |
93 | // use the data |
94 | useText(text.split(separator)[0].trim(), function (err) { |
95 | if (err) return cb(err) |
96 | // delete the file after succesful read |
97 | fs.unlink(filename, cb) |
98 | }) |
99 | }) |
100 | }) |
101 | } |
102 | |
103 | // Get a name for a thing from multiple fallback sources |
104 | u.getName = function (sbot, sources, dest, cb) { |
105 | var pull = require('pull-stream') |
106 | var cat = require('pull-cat') |
107 | var name |
108 | pull( |
109 | cat(sources.map(function (source) { |
110 | return sbot.links({ |
111 | source: source, dest: dest, rel: 'about', |
112 | values: true, keys: false, meta: false, |
113 | reverse: true |
114 | }) |
115 | })), |
116 | pull.drain(function (value) { |
117 | name = value && value.content && value.content.name |
118 | if (name) return false |
119 | }, function (err) { |
120 | cb(err === true ? null : err, name) |
121 | }) |
122 | ) |
123 | } |
124 | |
125 | u.formatTitle = function (str, len) { |
126 | str = String(str).replace(/[\n\r\t ]+/g, ' ') |
127 | if (str.length > len) str = str.substr(0, len) + '…' |
128 | return str |
129 | } |
130 | |
131 | u.issueStateBool = function (argv) { |
132 | return argv.all || (argv.open && argv.closed) ? null : |
133 | argv.open ? true : |
134 | argv.closed ? false : |
135 | true |
136 | } |
137 | |
138 | u.getForks = function(sbot, baseMsg, eachFork) { |
139 | var cat = require('pull-cat') |
140 | var pull = require('pull-stream') |
141 | if (baseMsg.key[0] === '#') return pull.once(baseMsg) |
142 | return cat([ |
143 | pull.once(baseMsg), |
144 | pull( |
145 | sbot.links({ |
146 | dest: baseMsg.key, |
147 | rel: 'upstream', |
148 | values: true |
149 | }), |
150 | pull.map(function (msg) { |
151 | if (eachFork) eachFork(msg, baseMsg) |
152 | return u.getForks(sbot, msg, eachFork) |
153 | }), |
154 | pull.flatten() |
155 | ) |
156 | ]) |
157 | } |
158 | |
159 | function formatRecps(r) { |
160 | switch (typeof r) { |
161 | case 'object': return r |
162 | case 'string': return r.split(',') |
163 | default: return [] |
164 | } |
165 | } |
166 | |
167 | u.getRecps = function (sbot, argv, cb) { |
168 | if (!argv.recps && !argv.private) return cb() |
169 | sbot.whoami(function (err, feed) { |
170 | if (err) return cb(err) |
171 | var recps = [feed.id] |
172 | var recpsArr = formatRecps(argv.recps) |
173 | for (var i = 0; i in recpsArr; i++) { |
174 | var recp = recpsArr[i] |
175 | if (!recp || ~recps.indexOf(recp)) continue |
176 | if (!ssbRef.isFeed(recp)) return cb(new Error('Invalid feed: ' + recp)) |
177 | recps.push(recp) |
178 | } |
179 | cb(null, recps) |
180 | }) |
181 | } |
182 | |
183 | u.getMsg = function (sbot, id, cb) { |
184 | sbot.get(id, function (err, value) { |
185 | if (err) return cb(new Error(err.stack || err)) |
186 | cb(null, {key: id, value: value}) |
187 | }) |
188 | } |
189 | |
190 | u.publishToSameRecps = function (sbot, rootId, value, cb) { |
191 | sbot.get(rootId, function (err, rootValue) { |
192 | if (err) return cb(err) |
193 | if (typeof rootValue.content === 'string') { |
194 | sbot.private.unbox(rootValue.content, function (err, rootContent) { |
195 | if (err) return cb(err) |
196 | if (!rootContent) return cb(new Error('unable to decrypt')) |
197 | var recps = rootContent.recps || [rootValue.author] |
198 | value.recps = recps |
199 | if (process.env.DRY_RUN) throw JSON.stringify(value, 0, 2) |
200 | sbot.private.publish(value, recps, cb) |
201 | }) |
202 | } else { |
203 | if (process.env.DRY_RUN) throw JSON.stringify(value, 0, 2) |
204 | sbot.publish(value, cb) |
205 | } |
206 | }) |
207 | } |
208 | |
209 | u.unbox = function (sbot, msg, cb) { |
210 | if (msg && msg.value && typeof msg.value.content === 'string') { |
211 | sbot.private.unbox(msg.value.content, function (err, content) { |
212 | if (content) msg.value.content = content |
213 | msg.value.private = true |
214 | cb(null, msg) |
215 | }) |
216 | } else { |
217 | cb(null, msg) |
218 | } |
219 | } |
220 | |
221 | u.unboxMessages = function (sbot) { |
222 | return u.unbox.bind(this, sbot) |
223 | } |
224 | |
225 | u.gitUpdates = function (sbot, repoId) { |
226 | var pull = require('pull-stream') |
227 | var paramap = require('pull-paramap') |
228 | |
229 | if (!repoId) return pull.empty() |
230 | |
231 | if (sbot.backlinks) return pull( |
232 | sbot.backlinks.read({ |
233 | reverse: true, |
234 | query: [{$filter: { |
235 | dest: repoId, |
236 | value: { |
237 | content: { |
238 | type: 'git-update', |
239 | repo: repoId |
240 | } |
241 | } |
242 | }}] |
243 | }), |
244 | paramap(u.unboxMessages(sbot), 4) |
245 | ) |
246 | |
247 | if (repoId[0] === '#') return pull.error(new Error( |
248 | 'The ssb-backlinks plugin is required to use channel repos')) |
249 | |
250 | return pull( |
251 | sbot.links({ |
252 | dest: repoId, |
253 | rel: 'repo', |
254 | values: true, |
255 | meta: false, |
256 | reverse: true, |
257 | }), |
258 | paramap(u.unboxMessages(sbot), 4), |
259 | pull.filter(function (msg) { |
260 | var c = msg.value.content |
261 | return c.type === 'git-update' |
262 | }) |
263 | ) |
264 | } |
265 |
Built with git-ssb-web