git ssb

0+

ev / microbay



forked from Dominic / patchbay

Tree: f7ca54c0c9db01f4eb92a59c6b61eda900f89e9f

Files: f7ca54c0c9db01f4eb92a59c6b61eda900f89e9f / modules / git.js

7388 bytesRaw
1var h = require('hyperscript')
2var pull = require('pull-stream')
3var paramap = require('pull-paramap')
4var human = require('human-time')
5
6var plugs = require('../plugs')
7var message_link = plugs.first(exports.message_link = [])
8var sbot_links = plugs.first(exports.sbot_links = [])
9var sbot_links2 = plugs.first(exports.sbot_links2 = [])
10var sbot_get = plugs.first(exports.sbot_get = [])
11var getAvatar = require('ssb-avatar')
12var avatar_name = plugs.first(exports.avatar_name = [])
13var markdown = plugs.first(exports.markdown = [])
14
15var self_id = require('../keys').id
16
17function shortRefName(ref) {
18 return ref.replace(/^refs\/(heads|tags)\//, '')
19}
20
21function repoLink(id) {
22 var el = h('a', {href: '#'+id}, id.substr(0, 10) + '…')
23 getAvatar({links: sbot_links}, self_id, id, function (err, avatar) {
24 if(err) return console.error(err)
25 el.textContent = avatar.name
26 })
27 return el
28}
29
30function getIssueState(id, cb) {
31 pull(
32 sbot_links({dest: id, rel: 'issues', values: true}),
33 pull.map(function (msg) {
34 var issues = msg.value.content.issues
35 if (!Array.isArray(issues)) return
36 return issues.filter(function (issue) {
37 return issue.link === id
38 }).map(function (issue) {
39 return {
40 ts: msg.value.timestamp,
41 open: issue.open,
42 merged: issue.merged,
43 }
44 })
45 }),
46 pull.flatten(),
47 pull.collect(function (err, updates) {
48 if (err) return cb(err)
49 var open = true, merged = false
50 updates.sort(function (a, b) {
51 return b.ts - a.ts
52 }).forEach(function (update) {
53 if (update.open != null)
54 open = update.open
55 if (update.merged != null)
56 merged = update.merged
57 })
58 cb(null, open ? 'open' : merged ? 'merged' : 'closed')
59 })
60 )
61}
62
63//todo:
64function messageTimestampLink(msg) {
65 var m = human(msg.value.timestamp)
66 return h('a.timestamp', {
67 timestamp: msg.value.timestamp,
68 title: new Date(msg.value.timestamp),
69 href: '#'+msg.key
70 }, m.fromNow())
71}
72
73function tableRows(headerRow) {
74 var thead = h('thead'), tbody = h('tbody')
75 var first = true
76 var t = [thead, tbody]
77 t.append = function (row) {
78 if (first) {
79 first = false
80 thead.appendChild(headerRow)
81 }
82 tbody.appendChild(row)
83 }
84 return t
85}
86
87function repoName(id, link) {
88 var el = link
89 ? h('a', {href: '#'+id}, id.substr(0, 8) + '…')
90 : h('ins', id.substr(0, 8) + '…')
91 getAvatar({links: sbot_links}, self_id, id, function (err, avatar) {
92 if(err) return console.error(err)
93 el.textContent = avatar.name
94 })
95 return el
96}
97
98exports.message_content = function (msg, sbot) {
99 var c = msg.value.content
100
101 if(c.type === 'git-repo') {
102 var nameEl
103 var branchesT, tagsT, openIssuesT, closedIssuesT, openPRsT, closedPRsT
104 var div = h('div',
105 h('p', 'git repo ', repoName(msg.key)),
106 c.upstream ? h('p', 'fork of ', repoName(c.upstream, true)) : '',
107 h('p', h('code', 'ssb://' + msg.key)),
108 h('div.git-table-wrapper', {style: {'max-height': '12em'}},
109 h('table',
110 branchesT = tableRows(h('tr',
111 h('th', 'branch'),
112 h('th', 'commit'),
113 h('th', 'last update'))),
114 tagsT = tableRows(h('tr',
115 h('th', 'tag'),
116 h('th', 'commit'),
117 h('th', 'last update'))))),
118 h('div.git-table-wrapper', {style: {'max-height': '16em'}},
119 h('table',
120 openIssuesT = tableRows(h('tr',
121 h('th', 'open issues'))),
122 closedIssuesT = tableRows(h('tr',
123 h('th', 'closed issues'))))),
124 h('div.git-table-wrapper', {style: {'max-height': '16em'}},
125 h('table',
126 openPRsT = tableRows(h('tr',
127 h('th', 'open pull requests'))),
128 closedPRsT = tableRows(h('tr',
129 h('th', 'closed pull requests'))))))
130
131 // compute refs
132 var refs = {}
133 pull(
134 sbot_links({
135 reverse: true,
136 source: msg.value.author,
137 dest: msg.key,
138 rel: 'repo',
139 values: true
140 }),
141 pull.drain(function (link) {
142 var refUpdates = link.value.content.refs
143 for (var ref in refUpdates) {
144 if (refs[ref]) continue
145 refs[ref] = true
146 var rev = refUpdates[ref]
147 if (!rev) continue
148 var parts = /^refs\/(heads|tags)\/(.*)$/.exec(ref) || []
149 var t
150 if (parts[1] === 'heads') t = branchesT
151 else if (parts[1] === 'tags') t = tagsT
152 if (t) t.append(h('tr',
153 h('td', parts[2]),
154 h('td', h('code', rev)),
155 h('td', messageTimestampLink(link))))
156 }
157 }, function (err) {
158 if (err) console.error(err)
159 })
160 )
161
162 // list issues and pull requests
163 pull(
164 sbot_links({
165 reverse: true,
166 dest: msg.key,
167 rel: 'project',
168 values: true
169 }),
170 paramap(function (link, cb) {
171 getIssueState(link.key, function (err, state) {
172 if(err) return cb(err)
173 link.state = state
174 cb(null, link)
175 })
176 }),
177 pull.drain(function (link) {
178 var c = link.value.content
179 // TODO: support renamed issues
180 var title = c.title || (c.text ? c.text.length > 30
181 ? c.text.substr(0, 30) + '…'
182 : c.text : link.key)
183 var author = link.value.author
184 var t = c.type === 'pull-request'
185 ? link.state === 'open' ? openPRsT : closedPRsT
186 : link.state === 'open' ? openIssuesT : closedIssuesT
187 t.append(h('tr',
188 h('td',
189 h('a', {href: '#'+link.key}, title), h('br'),
190 h('small',
191 'opened ', messageTimestampLink(link),
192 ' by ', h('a', {href: '#'+author}, avatar_name(author))))))
193 }, function (err) {
194 if (err) console.error(err)
195 })
196 )
197
198 return div
199 }
200
201 if(c.type === 'git-update') {
202 return h('p',
203 'pushed to ',
204 repoLink(c.repo),
205 c.refs ? h('ul', Object.keys(c.refs).map(function (ref) {
206 var rev = c.refs[ref]
207 return h('li',
208 shortRefName(ref) + ': ',
209 rev ? h('code', rev) : h('em', 'deleted'))
210 })) : null,
211 Array.isArray(c.issues) ? c.issues.map(function (issue) {
212 if (issue.merged === true)
213 return ['Merged ', message_link(issue.link), ' in ',
214 h('code', issue.object), ' ', h('q', issue.label)]
215 if (issue.open === false)
216 return ['Closed ', message_link(issue.link), ' in ',
217 h('code', issue.object), ' ', h('q', issue.label)]
218 }) : null
219 )
220 }
221
222 if (c.type === 'issue') {
223 return h('div',
224 h('p', 'opened issue on ', repoLink(c.project)),
225 c.title ? h('h4', c.title) : '',
226 markdown(c)
227 )
228 }
229
230 if (c.type === 'pull-request') {
231 return h('div',
232 h('p', 'opened pull-request ',
233 'to ', repoLink(c.repo), ':', c.branch, ' ',
234 'from ', repoLink(c.head_repo), ':', c.head_branch),
235 c.title ? h('h4', c.title) : '',
236 markdown(c)
237 )
238 }
239}
240
241exports.message_meta = function (msg, sbot) {
242 var type = msg.value.content.type
243 if (type == 'issue' || type == 'pull-request') {
244 var el = h('em', '...')
245 getIssueState(msg.key, function (err, state) {
246 if (err) return console.error(err)
247 el.textContent = state
248 })
249 return el
250 }
251}
252
253

Built with git-ssb-web