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
|
const api = require('./api')
const auth = require('./auth')
const zmk = require('../zmk')
const MODE_FILE = '100644'
class MissingRepoFile extends Error {
constructor(path) {
super()
this.name = 'MissingRepoFile'
this.path = path
this.errors = [`Missing file ${path}`]
}
}
async function fetchKeyboardFiles (installationId, repository, branch) {
const { data: { token: installationToken } } = await auth.createInstallationToken(installationId)
const { data: info } = await fetchFile(installationToken, repository, 'config/info.json', { raw: true, branch })
const { data: keymap } = await fetchFile(installationToken, repository, 'config/keymap.json', { raw: true, branch })
const originalCodeKeymap = await findCodeKeymap(installationToken, repository, branch)
return { info, keymap, originalCodeKeymap }
}
async function fetchFile (installationToken, repository, path, options = {}) {
const { raw = false, branch = null } = options
const url = `/repos/${repository}/contents/${path}`
const params = {}
if (branch) {
params.ref = branch
}
const headers = { Accept: raw ? 'application/vnd.github.v3.raw' : 'application/json' }
try {
return await api.request({ url, headers, params, token: installationToken })
} catch (err) {
if (err.response?.status === 404) {
throw new MissingRepoFile(path)
}
}
}
async function findCodeKeymap (installationToken, repository, branch) {
// Assume that the relevant files are under `config/` and not a complicated
// directory structure, and that there are fewer than 1000 files in this path
// (a limitation of GitHub's repo contents API).
const { data: directory } = await fetchFile(installationToken, repository, 'config', { branch })
const originalCodeKeymap = directory.find(file => file.name.toLowerCase().endsWith('.keymap'))
if (!originalCodeKeymap) {
throw new MissingRepoFile('config/*.keymap')
}
return originalCodeKeymap
}
async function commitChanges (installationId, repository, branch, layout, keymap) {
const { data: { token: installationToken } } = await auth.createInstallationToken(installationId)
const generatedKeymap = zmk.generateKeymap(layout, keymap)
const originalCodeKeymap = await findCodeKeymap(installationToken, repository, branch)
const { data: {sha, commit} } = await api.request({ url: `/repos/${repository}/commits/${branch}`, token: installationToken })
const { data: { sha: newTreeSha } } = await api.request({
url: `/repos/${repository}/git/trees`,
method: 'POST',
token: installationToken,
data: {
base_tree: commit.tree.sha,
tree: [
{
path: originalCodeKeymap.path,
mode: MODE_FILE,
type: 'blob',
content: generatedKeymap.code
},
{
path: 'config/keymap.json',
mode: MODE_FILE,
type: 'blob',
content: generatedKeymap.json
}
]
}
})
const { data: { sha: newSha } } = await api.request({
url: `/repos/${repository}/git/commits`,
method: 'POST',
token: installationToken,
data: {
tree: newTreeSha,
message: 'Updated keymap',
parents: [sha]
}
})
await api.request({
url: `/repos/${repository}/git/refs/heads/${branch}`,
method: 'PATCH',
token: installationToken,
data: {
sha: newSha
}
})
}
module.exports = {
MissingRepoFile,
fetchKeyboardFiles,
findCodeKeymap,
commitChanges
}
|