import { Octokit } from '@octokit/rest' import { readRemoteFolder } from '../content' import { buildLfsPointer, splitLfsFiles, uploadToLfs } from '../lfs' import type { GitProvider, GitRemoteConfig, PushFilesParams } from '../types' export function createGitHubProvider(remote: GitRemoteConfig, branch: string): GitProvider { const octokit = new Octokit({ auth: remote.token, baseUrl: remote.apiBaseUrl, }) return { getRemoteFolder(folderPath) { return readRemoteFolder(remote, branch, folderPath) }, async pushFiles({ files, deletePaths, commitMessage }: PushFilesParams) { const { lfsFiles, regularFiles } = splitLfsFiles(files) await uploadToLfs( remote, lfsFiles.map((file) => ({ oid: file.oid, size: file.size, contentBase64: file.contentBase64 })), ) const { owner, repo } = remote const { data: ref } = await octokit.git.getRef({ owner, repo, ref: `heads/${branch}`, }) const latestCommitSha = ref.object.sha const { data: commit } = await octokit.git.getCommit({ owner, repo, commit_sha: latestCommitSha, }) const allFiles = [...regularFiles, ...lfsFiles] const blobResults = await Promise.all( allFiles.map((file) => { const lfs = lfsFiles.find((lfsFile) => lfsFile.path === file.path) if (lfs) { const pointer = buildLfsPointer(lfs.oid, lfs.size) return octokit.git.createBlob({ owner, repo, content: Buffer.from(pointer, 'utf-8').toString('base64'), encoding: 'base64', }) } return octokit.git.createBlob({ owner, repo, content: file.contentBase64, encoding: 'base64', }) }), ) const newFilePaths = new Set(files.map((file) => file.path)) const deleteEntries = deletePaths .filter((path) => !newFilePaths.has(path)) .map((path) => ({ path, mode: '100644' as const, type: 'blob' as const, sha: null, })) const { data: newTree } = await octokit.git.createTree({ owner, repo, base_tree: commit.tree.sha, tree: [ ...allFiles.map((file, index) => ({ path: file.path, mode: '100644' as const, type: 'blob' as const, sha: blobResults[index].data.sha, })), ...deleteEntries, ], }) const { data: newCommit } = await octokit.git.createCommit({ owner, repo, message: commitMessage, tree: newTree.sha, parents: [latestCommitSha], }) await octokit.git.updateRef({ owner, repo, ref: `heads/${branch}`, sha: newCommit.sha, }) return { commitUrl: newCommit.html_url || `${remote.webUrl}/commit/${newCommit.sha}` } }, } }