aboutsummaryrefslogtreecommitdiffhomepage
path: root/api/services/github/files.js
blob: 40bcb4114cfcf76e04410f2a7bf37b75ff5d3aa4 (plain)
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
const api = require('./api')
const auth = require('./auth')
const zmk = require('../zmk')

const MODE_FILE = '100644'

class InvalidRepoError extends Error {}

async function fetchKeyboardFiles (installationId, repository) {
  const { data: { token: installationToken } } = await auth.createInstallationToken(installationId)
  try {
    const { data: info } = await fetchFile(installationToken, repository, 'config/info.json', true)
    const { data: keymap } = await fetchFile(installationToken, repository, 'config/keymap.json', true)

    return { info, keymap }
  } catch (err) {
    if (err.response && err.response.status === 404) {
      throw new InvalidRepoError()
    }

    throw err
  }
}

function fetchFile (installationToken, repository, path, raw = false) {
  const url = `/repos/${repository}/contents/${path}`
  const headers = { Accept: raw ? 'application/vnd.github.v3.raw' : 'application/json' }
  return api.request({ url, headers, token: installationToken })
}

async function commitChanges (installationId, repository, layout, keymap) {
  const { data: { token: installationToken } } = await auth.createInstallationToken(installationId)
  const generatedKeymap = zmk.generateKeymap(layout, keymap)

  // 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/')
  const originalCodeKeymap = directory.find(file => file.name.toLowerCase().endsWith('.keymap'))

  const { data: repo } = await api.request({ url: `/repos/${repository}`, token: installationToken })
  const { data: [{sha, commit}] } = await api.request({ url: `/repos/${repository}/commits?per_page=1`, 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/${repo.default_branch}`,
    method: 'PATCH',
    token: installationToken,
    data: {
      sha: newSha
    }
  })
}

module.exports = {
  InvalidRepoError,
  fetchKeyboardFiles,
  commitChanges
}