mirror of
https://github.com/silvanocerza/github-gitless-sync.git
synced 2026-07-22 12:10:28 +00:00
187 lines
5.6 KiB
TypeScript
187 lines
5.6 KiB
TypeScript
import { requestUrl } from "obsidian";
|
|
import Logger from "src/logger";
|
|
import { GitHubSyncSettings } from "src/settings/settings";
|
|
|
|
export type RepoContent = {
|
|
files: { [key: string]: GetTreeResponseItem };
|
|
sha: string;
|
|
};
|
|
|
|
/**
|
|
* Represents a single item in a tree response from the GitHub API.
|
|
*/
|
|
export type GetTreeResponseItem = {
|
|
path: string;
|
|
mode: string;
|
|
type: string;
|
|
sha: string;
|
|
size: number;
|
|
url: string;
|
|
};
|
|
|
|
export type NewTreeRequestItem = {
|
|
path: string;
|
|
mode: string;
|
|
type: string;
|
|
sha?: string | null;
|
|
content?: string;
|
|
};
|
|
|
|
/**
|
|
* Represents a git blob response from the GitHub API.
|
|
*/
|
|
export type BlobFile = {
|
|
sha: string;
|
|
node_id: string;
|
|
size: number;
|
|
url: string;
|
|
content: string;
|
|
encoding: string;
|
|
};
|
|
|
|
export default class GithubClient {
|
|
constructor(
|
|
private settings: GitHubSyncSettings,
|
|
private logger: Logger,
|
|
) {}
|
|
|
|
headers() {
|
|
return {
|
|
Accept: "application/vnd.github+json",
|
|
Authorization: `Bearer ${this.settings.githubToken}`,
|
|
"X-GitHub-Api-Version": "2022-11-28",
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Gets the content of the repo.
|
|
*
|
|
* @returns Array of files in the directory in the remote repo
|
|
*/
|
|
async getRepoContent(): Promise<RepoContent> {
|
|
const res = await requestUrl({
|
|
url: `https://api.github.com/repos/${this.settings.githubOwner}/${this.settings.githubRepo}/git/trees/${this.settings.githubBranch}?recursive=1`,
|
|
headers: this.headers(),
|
|
throw: false,
|
|
});
|
|
if (res.status < 200 || res.status >= 400) {
|
|
await this.logger.error("Failed to get repo content", res);
|
|
throw new Error(`Failed to get repo content, status ${res.status}`);
|
|
}
|
|
const files = res.json.tree
|
|
.filter((file: GetTreeResponseItem) => file.type === "blob")
|
|
.reduce(
|
|
(
|
|
acc: { [key: string]: GetTreeResponseItem },
|
|
file: GetTreeResponseItem,
|
|
) => ({ ...acc, [file.path]: file }),
|
|
{},
|
|
);
|
|
return { files, sha: res.json.sha };
|
|
}
|
|
|
|
async createTree(tree: { tree: NewTreeRequestItem[]; base_tree: string }) {
|
|
const res = await requestUrl({
|
|
url: `https://api.github.com/repos/${this.settings.githubOwner}/${this.settings.githubRepo}/git/trees`,
|
|
headers: this.headers(),
|
|
method: "POST",
|
|
body: JSON.stringify(tree),
|
|
throw: false,
|
|
});
|
|
if (res.status < 200 || res.status >= 400) {
|
|
await this.logger.error("Failed to create tree", res);
|
|
throw new Error(`Failed to create tree, status ${res.status}`);
|
|
}
|
|
return res.json.sha;
|
|
}
|
|
|
|
async createCommit(message: string, treeSha: string, parent: string) {
|
|
const res = await requestUrl({
|
|
url: `https://api.github.com/repos/${this.settings.githubOwner}/${this.settings.githubRepo}/git/commits`,
|
|
headers: this.headers(),
|
|
method: "POST",
|
|
body: JSON.stringify({
|
|
message: message,
|
|
tree: treeSha,
|
|
parents: [parent],
|
|
}),
|
|
throw: false,
|
|
});
|
|
if (res.status < 200 || res.status >= 400) {
|
|
await this.logger.error("Failed to create commit", res);
|
|
throw new Error(`Failed to create commit, status ${res.status}`);
|
|
}
|
|
return res.json.sha;
|
|
}
|
|
|
|
async getBranchHeadSha() {
|
|
const res = await requestUrl({
|
|
url: `https://api.github.com/repos/${this.settings.githubOwner}/${this.settings.githubRepo}/git/refs/heads/${this.settings.githubBranch}`,
|
|
headers: this.headers(),
|
|
throw: false,
|
|
});
|
|
if (res.status < 200 || res.status >= 400) {
|
|
await this.logger.error("Failed to get branch head sha", res);
|
|
throw new Error(`Failed to get branch head sha, status ${res.status}`);
|
|
}
|
|
return res.json.object.sha;
|
|
}
|
|
|
|
async updateBranchHead(sha: string) {
|
|
const res = await requestUrl({
|
|
url: `https://api.github.com/repos/${this.settings.githubOwner}/${this.settings.githubRepo}/git/refs/heads/${this.settings.githubBranch}`,
|
|
headers: this.headers(),
|
|
method: "PATCH",
|
|
body: JSON.stringify({
|
|
sha: sha,
|
|
}),
|
|
throw: false,
|
|
});
|
|
if (res.status < 200 || res.status >= 400) {
|
|
await this.logger.error("Failed to update branch head sha", res);
|
|
throw new Error(`Failed to update branch head sha, status ${res.status}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets a blob from its sha
|
|
* @param url blob sha
|
|
*/
|
|
async getBlob(sha: string): Promise<BlobFile> {
|
|
const res = await requestUrl({
|
|
url: `https://api.github.com/repos/${this.settings.githubOwner}/${this.settings.githubRepo}/git/blobs/${sha}`,
|
|
headers: this.headers(),
|
|
throw: false,
|
|
});
|
|
if (res.status < 200 || res.status >= 400) {
|
|
await this.logger.error("Failed to get blob", res);
|
|
throw new Error(`Failed to get blob, status ${res.status}`);
|
|
}
|
|
return res.json;
|
|
}
|
|
|
|
/**
|
|
* Create a new file in the repo, the content must be base64 encoded or the request will fail.
|
|
*
|
|
* @param message commit message
|
|
* @param path path to create in the repo
|
|
* @param content base64 encoded content of the file
|
|
*/
|
|
async createFile(path: string, content: string, message: string) {
|
|
const res = await requestUrl({
|
|
url: `https://api.github.com/repos/${this.settings.githubOwner}/${this.settings.githubRepo}/contents/${path}`,
|
|
headers: this.headers(),
|
|
method: "PUT",
|
|
body: JSON.stringify({
|
|
message: message,
|
|
content: content,
|
|
branch: this.settings.githubBranch,
|
|
}),
|
|
throw: false,
|
|
});
|
|
if (res.status < 200 || res.status >= 400) {
|
|
await this.logger.error("Failed to create file", res);
|
|
throw new Error(`Failed to create file, status ${res.status}`);
|
|
}
|
|
}
|
|
}
|