UNPKG

isomorphic-git

Version:

Node library for interacting with git repositories, circa 2017

213 lines (196 loc) 5.54 kB
// We're implementing a non-standard clone based on the Github API first, because of CORS. // And because we already have the code. import axios from 'axios' import parseLinkHeader from 'parse-link-header' import GitObjectManager from '../managers/GitObjectManager' import GitCommit from '../models/GitCommit' import GitTree from '../models/GitTree' import write from '../utils/write' import resolveRef from '../utils/resolveRef' async function request ({ url, token, headers }) { let res = await axios.get(url, { headers: { Accept: 'application/vnd.github.v3+json', Authorization: 'token ' + token, ...headers } }) return res.data } async function fetchRemoteBranches ({ gitdir, remote, user, repo, token }) { return request({ token, url: `https://api.github.com/repos/${user}/${repo}/branches` }).then(json => Promise.all( json.map(branch => write( `${gitdir}/refs/remotes/${remote}/${branch.name}`, branch.commit.sha + '\n', { encoding: 'utf8' } ) ) ) ) } async function fetchTags ({ gitdir, user, repo, token }) { return request({ token, url: `https://api.github.com/repos/${user}/${repo}/tags` }).then(json => Promise.all( json.map(tag => // Curiously, tags are not separated between remotes like branches write(`${gitdir}/refs/tags/${tag.name}`, tag.commit.sha + '\n', { encoding: 'utf8' }) ) ) ) } async function fetchCommits ({ gitdir, url, user, repo, ref, since, token }) { if (!url) { url = `https://api.github.com/repos/${user}/${repo}/commits?` if (ref) url += `&sha=${ref}` if (since) { let date = new Date(since * 1000).toISOString() url += `&since=${date}` } } let res = await axios.get(url, { headers: { Accept: 'application/vnd.github.cryptographer-preview', Authorization: 'token ' + token } }) let json = res.data let link = parseLinkHeader(res.headers['link']) for (let commit of json) { if (!commit.commit.verification.payload) { console.log( `Commit ${commit.sha} skipped. Due to a technical limitations and my laziness, only signed commits can be cloned from Github over the API` ) continue } let comm = GitCommit.fromPayloadSignature({ payload: commit.commit.verification.payload, signature: commit.commit.verification.signature }) console.log('Created commit', comm) let oid = await GitObjectManager.write({ gitdir, type: 'commit', object: comm.toObject() }) if (commit.sha !== oid) { console.log("AHOY! MATEY! THAR BE TROUBLE WITH 'EM HASHES!") } console.log(`Stored commit ${commit.sha}`) } if (link && link.next) { return fetchCommits({ gitdir, user, repo, ref, since, token, url: link.next.url }) } } async function fetchTree ({ gitdir, url, user, repo, sha, since, token }) { let json = await request({ token, url: `https://api.github.com/repos/${user}/${repo}/git/trees/${sha}` }) let tree = new GitTree(json.tree) let oid = await GitObjectManager.write({ gitdir, type: 'tree', object: tree.toObject() }) if (sha !== oid) { console.log("AHOY! MATEY! THAR BE TROUBLE WITH 'EM HASHES!") } console.log(tree.render()) return Promise.all( json.tree.map(async entry => { if (entry.type === 'blob') { await fetchBlob({ gitdir, url, user, repo, sha: entry.sha, since, token }) } else if (entry.type === 'tree') { await fetchTree({ gitdir, url, user, repo, sha: entry.sha, since, token }) } }) ) } async function fetchBlob ({ gitdir, url, user, repo, sha, since, token }) { let res = await axios.get( `https://api.github.com/repos/${user}/${repo}/git/blobs/${sha}`, { headers: { Accept: 'application/vnd.github.raw', Authorization: 'token ' + token }, responseType: 'arraybuffer' } ) let oid = await GitObjectManager.write({ gitdir, type: 'blob', object: res.data }) if (sha !== oid) { console.log("AHOY! MATEY! THAR BE TROUBLE WITH 'EM HASHES!") } } export default async function fetch ({ gitdir, token, user, repo, ref, remote, since }) { let json if (!ref) { console.log('Determining the default branch') json = await request({ token, url: `https://api.github.com/repos/${user}/${repo}` }) ref = json.default_branch } console.log('Receiving branches list') let getBranches = fetchRemoteBranches({ gitdir, remote, user, repo, token }) console.log('Receiving tags list') let getTags = fetchTags({ gitdir, user, repo, token }) console.log('Receiving commits') let getCommits = fetchCommits({ gitdir, user, repo, token, ref }) await Promise.all([getBranches, getTags, getCommits]) // This is all crap to get a tree SHA from a commit SHA. Seriously. let oid = await resolveRef({ gitdir, ref: `${remote}/${ref}` }) let { type, object } = await GitObjectManager.read({ gitdir, oid }) if (type !== 'commit') throw new Error(`Unexpected type: ${type}`) let comm = GitCommit.from(object.toString('utf8')) let sha = comm.headers().tree console.log('tree: ', sha) await fetchTree({ gitdir, user, repo, token, sha }) }