From 69f2f4dc6ab47f91965f3d93963f1bad965347d7 Mon Sep 17 00:00:00 2001 From: xiongxiao Date: Mon, 2 Feb 2026 04:46:21 +0800 Subject: [PATCH] test add git modules --- src/git/index.ts | 676 +++++++++++++++++++++++++++++++++++++++++++++++ test/git.ts | 32 +++ 2 files changed, 708 insertions(+) create mode 100644 src/git/index.ts create mode 100644 test/git.ts diff --git a/src/git/index.ts b/src/git/index.ts new file mode 100644 index 0000000..e50a62c --- /dev/null +++ b/src/git/index.ts @@ -0,0 +1,676 @@ +import { CNBCore, CNBCoreOptions, Result } from "../cnb-core.ts"; + +/** + * Git 提交信息 + */ +export type Commit = { + sha: string; + short_sha: string; + title: string; + message: string; + author: { + name: string; + email: string; + username: string; + avatar_url: string; + }; + committer: { + name: string; + email: string; + username: string; + avatar_url: string; + }; + parents: Array<{ sha: string }>; + created_at: string; +}; + +/** + * Git 分支信息 + */ +export type Branch = { + name: string; + commit: { + sha: string; + url: string; + }; +}; + +/** + * Git 分支详细信息 + */ +export type BranchDetail = Branch & { + protected: boolean; + default: boolean; +}; + +/** + * Git 标签信息 + */ +export type Tag = { + name: string; + commit: { + sha: string; + url: string; + }; + message: string; + annotation: Record; + created_at: string; +}; + +/** + * Git 文件内容信息 + */ +export type Content = { + name: string; + path: string; + sha: string; + size: number; + url: string; + html_url: string; + git_url: string; + download_url: string; + type: string; + content?: string; + encoding?: string; +}; + +/** + * Git Blob 对象 + */ +export type Blob = { + sha: string; + size: number; + url: string; + content?: string; +}; + +/** + * Git 比较结果 + */ +export type CompareResponse = { + status: string; + ahead_by: number; + behind_by: number; + total_commits: number; + commits: Commit[]; + files: Array<{ + sha: string; + filename: string; + status: string; + additions: number; + deletions: number; + changes: number; + patch?: string; + }>; +}; + +/** + * Git 提交状态 + */ +export type CommitStatus = { + id: string; + state: string; + description: string; + target_url: string; + context: string; + created_at: string; + updated_at: string; +}; + +/** + * Git 提交附件 + */ +export type CommitAsset = { + id: string; + name: string; + path: string; + sha1: string; + size: number; + created_at: string; + uploader: { + username: string; + nickname: string; + avatar_url: string; + }; +}; + +/** + * Git 提交附件上传 URL + */ +export type CommitAssetUploadURL = { + upload_url: string; + verify_url: string; + upload_token: string; +}; + +/** + * Git 标签注释 + */ +export type TagAnnotation = { + key: string; + value: string; + creator: string; + created_at: string; +}; + +/** + * Git 头引用 + */ +export type HeadRef = { + default_branch: string; +}; + +/** + * Git 操作类 + * 提供 Git 相关的 API 操作,包括提交、分支、标签、文件等 + */ +export class Git extends CNBCore { + constructor(options: CNBCoreOptions) { + super({ token: options.token, cookie: options.cookie }); + } + + /** + * 查询提交列表 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param params 查询参数 + * @returns 提交列表 + */ + async listCommits(repo: string, params?: ListCommitsParams): Promise> { + const url = `/${repo}/-/git/commits`; + return this.get({ url, params }); + } + + /** + * 查询指定提交 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param ref 提交的哈希值或分支名称 + * @returns 提交信息 + */ + async getCommit(repo: string, ref: string): Promise> { + const url = `/${repo}/-/git/commits/${ref}`; + return this.get({ url }); + } + + /** + * 查询指定提交的提交状态 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param commitish Git 引用标识符,分支名称、提交哈希值或标签名称 + * @returns 提交状态列表 + */ + async getCommitStatuses(repo: string, commitish: string): Promise> { + const url = `/${repo}/-/git/commit-statuses/${commitish}`; + return this.get({ url }); + } + + /** + * 查询分支列表 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param params 分页参数 + * @returns 分支列表 + */ + async listBranches(repo: string, params?: PaginationParams): Promise> { + const url = `/${repo}/-/git/branches`; + return this.get({ url, params }); + } + + /** + * 创建新分支 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param data 创建分支的数据 + * @returns 创建结果 + */ + async createBranch(repo: string, data: CreateBranchData): Promise> { + const url = `/${repo}/-/git/branches`; + return this.post({ url, data }); + } + + /** + * 查询指定分支 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param branch 分支名称 + * @returns 分支详细信息 + */ + async getBranch(repo: string, branch: string): Promise> { + const url = `/${repo}/-/git/branches/${branch}`; + return this.get({ url }); + } + + /** + * 删除指定分支 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param branch 分支名称 + * @returns 删除结果 + */ + async deleteBranch(repo: string, branch: string): Promise> { + const url = `/${repo}/-/git/branches/${branch}`; + return this.delete({ url }); + } + + /** + * 查询标签列表 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param params 分页参数 + * @returns 标签列表 + */ + async listTags(repo: string, params?: PaginationParams): Promise> { + const url = `/${repo}/-/git/tags`; + return this.get({ url, params }); + } + + /** + * 创建标签 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param data 创建标签的数据 + * @returns 创建的标签信息 + */ + async createTag(repo: string, data: CreateTagData): Promise> { + const url = `/${repo}/-/git/tags`; + return this.post({ url, data }); + } + + /** + * 查询指定标签 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param tag 标签名称 + * @returns 标签信息 + */ + async getTag(repo: string, tag: string): Promise> { + const url = `/${repo}/-/git/tags/${tag}`; + return this.get({ url }); + } + + /** + * 删除指定标签 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param tag 标签名称 + * @returns 删除结果 + */ + async deleteTag(repo: string, tag: string): Promise> { + const url = `/${repo}/-/git/tags/${tag}`; + return this.delete({ url }); + } + + /** + * 创建 Blob 对象 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param data 创建 Blob 的数据 + * @returns 创建的 Blob 信息 + */ + async createBlob(repo: string, data: CreateBlobData): Promise> { + const url = `/${repo}/-/git/blobs`; + return this.post({ url, data }); + } + + /** + * 查询仓库文件内容(根目录) + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param params 查询参数 + * @returns 文件或目录内容 + */ + async getContent(repo: string, params?: GetContentParams): Promise> { + const url = `/${repo}/-/git/contents`; + return this.get({ url, params }); + } + + /** + * 查询指定路径的文件内容 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param filePath 文件路径 + * @param params 查询参数 + * @returns 文件内容 + */ + async getContentWithPath(repo: string, filePath: string, params?: GetContentWithPathParams): Promise> { + const url = `/${repo}/-/git/contents/${filePath}`; + return this.get({ url, params }); + } + + /** + * 获取原始文件内容 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param refWithPath Git 引用(分支、标签、提交哈希)和文件路径 + * @param params 查询参数 + * @returns 原始文件内容 + */ + async getRaw(repo: string, refWithPath: string, params?: GetRawParams): Promise { + const url = `/${repo}/-/git/raw/${refWithPath}`; + const response = await this.get({ url, params, useOrigin: true }); + return response; + } + + /** + * 下载仓库内容归档 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param refWithPath Git 引用(分支名、标签名、提交哈希或分支名/文件路径) + * @param useOrigin 是否返回原始响应 + * @returns 归档文件 + */ + async getArchive(repo: string, refWithPath: string, useOrigin?: boolean): Promise { + const url = `/${repo}/-/git/archive/${refWithPath}`; + return this.get({ url, useOrigin }); + } + + /** + * 打包下载提交变更文件 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param sha1 提交的哈希值 + * @returns 变更文件归档 + */ + async getArchiveCommitChangedFiles(repo: string, sha1: string): Promise { + const url = `/${repo}/-/git/archive-commit-changed-files/${sha1}`; + return this.get({ url, useOrigin: true }); + } + + /** + * 打包下载两次引用之间的变更文件 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param baseHead 用于 Git 比较操作的基准和头部分支或提交的 SHA 值,格式:base...head + * @returns 变更文件归档 + */ + async getArchiveCompareChangedFiles(repo: string, baseHead: string): Promise { + const url = `/${repo}/-/git/archive-compare-changed-files/${baseHead}`; + return this.get({ url, useOrigin: true }); + } + + /** + * 查询指定提交的元数据 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param sha 提交的哈希值 + * @returns 提交元数据列表 + */ + async getCommitAnnotations(repo: string, sha: string): Promise> { + const url = `/${repo}/-/git/commit-annotations/${sha}`; + return this.get({ url }); + } + + /** + * 设定指定提交的元数据 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param sha 提交的哈希值 + * @param data 提交的元数据 + * @returns 设置结果 + */ + async putCommitAnnotations(repo: string, sha: string, data: PutCommitAnnotationsData): Promise> { + const url = `/${repo}/-/git/commit-annotations/${sha}`; + return this.put({ url, data }); + } + + /** + * 删除指定提交的元数据 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param sha 提交的哈希值 + * @param key 提交的元数据键名 + * @returns 删除结果 + */ + async deleteCommitAnnotation(repo: string, sha: string, key: string): Promise> { + const url = `/${repo}/-/git/commit-annotations/${sha}/${key}`; + return this.delete({ url }); + } + + /** + * 批量查询提交的元数据 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param data 包含多个提交哈希的请求数据 + * @returns 提交元数据列表 + */ + async getCommitAnnotationsInBatch(repo: string, data: GetCommitAnnotationsInBatchData): Promise> { + const url = `/${repo}/-/git/commit-annotations-in-batch`; + return this.post({ url, data }); + } + + /** + * 查询指定提交的附件列表 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param sha1 提交的哈希值 + * @returns 附件列表 + */ + async getCommitAssetsBySha(repo: string, sha1: string): Promise> { + const url = `/${repo}/-/git/commit-assets/${sha1}`; + return this.get({ url }); + } + + /** + * 新增提交附件(获取上传 URL) + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param sha1 提交的哈希值 + * @param data 创建附件上传 URL 的数据 + * @returns 上传 URL 信息 + */ + async postCommitAssetUploadURL(repo: string, sha1: string, data: PostCommitAssetUploadURLData): Promise> { + const url = `/${repo}/-/git/commit-assets/${sha1}/asset-upload-url`; + return this.post({ url, data }); + } + + /** + * 确认提交附件上传完成 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param sha1 提交的哈希值 + * @param uploadToken 上传令牌 + * @param assetPath 附件路径 + * @param params 确认参数 + * @returns 确认结果 + */ + async postCommitAssetUploadConfirmation(repo: string, sha1: string, uploadToken: string, assetPath: string, params?: PostCommitAssetUploadConfirmationParams): Promise> { + const url = `/${repo}/-/git/commit-assets/${sha1}/asset-upload-confirmation/${uploadToken}/${assetPath}`; + return this.post({ url, params }); + } + + /** + * 删除指定提交的附件 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param sha1 提交的哈希值 + * @param assetId 附件唯一标识符 + * @returns 删除结果 + */ + async deleteCommitAsset(repo: string, sha1: string, assetId: string): Promise> { + const url = `/${repo}/-/git/commit-assets/${sha1}/${assetId}`; + return this.delete({ url }); + } + + /** + * 获取提交附件下载链接 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param commitId 提交的哈希值 + * @param filename 文件名称 + * @param params 查询参数 + * @returns 302 重定向到下载地址 + */ + async getCommitAssets(repo: string, commitId: string, filename: string, params?: GetCommitAssetsParams): Promise { + const url = `/${repo}/-/commit-assets/download/${commitId}/${filename}`; + return this.get({ url, params, useOrigin: true }); + } + + /** + * 比较两个提交、分支或标签之间的差异 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param baseHead 用于 Git 比较操作的基准和头部分支或提交的 SHA 值,格式:base...head + * @returns 比较结果 + */ + async getCompareCommits(repo: string, baseHead: string): Promise> { + const url = `/${repo}/-/git/compare/${baseHead}`; + return this.get({ url }); + } + + /** + * 获取仓库默认分支 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @returns 默认分支信息 + */ + async getHead(repo: string): Promise> { + const url = `/${repo}/-/git/head`; + return this.get({ url }); + } + + /** + * 查询指定标签的元数据 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param tag 标签名称 + * @returns 标签元数据列表 + */ + async getTagAnnotations(repo: string, tag: string): Promise> { + const url = `/${repo}/-/git/tag-annotations/${tag}`; + return this.get({ url }); + } + + /** + * 设定指定标签的元数据 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param tag 标签名称 + * @param data 标签元数据 + * @returns 设置结果 + */ + async putTagAnnotations(repo: string, tag: string, data: PutTagAnnotationsData): Promise> { + const url = `/${repo}/-/git/tag-annotations/${tag}`; + return this.put({ url, data }); + } + + /** + * 删除指定标签的元数据 + * @param repo 仓库名称,格式:组织名称/仓库名称 + * @param tagWithKey 标签名称和键名组合 + * @returns 删除结果 + */ + async deleteTagAnnotation(repo: string, tagWithKey: string): Promise> { + const url = `/${repo}/-/git/tag-annotations/${tagWithKey}`; + return this.delete({ url }); + } +} + +/** + * 分页参数 + */ +type PaginationParams = { + /** 分页页码,默认: 1 */ + page?: number; + /** 分页页大小,默认: 30 */ + page_size?: number; +}; + +/** + * 查询提交列表参数 + */ +type ListCommitsParams = PaginationParams & { + /** 提交标识符,分支名称或提交哈希值 */ + sha?: string; + /** 作者匹配模式,支持 Git 原生正则表达式 */ + author?: string; + /** 提交者匹配模式,支持 Git 原生正则表达式 */ + committer?: string; + /** 提交时间起始范围,示例:2025-01-01T00:00:00Z */ + since?: string; + /** 提交时间结束范围,示例:2025-12-31T23:59:59Z */ + until?: string; +}; + +/** + * 创建分支参数 + */ +type CreateBranchData = { + /** 分支名称 */ + branch_name: string; + /** 起始点,默认使用仓库默认分支 */ + start_point?: string; +}; + +/** + * 创建标签参数 + */ +type CreateTagData = { + /** 标签名称 */ + tag_name: string; + /** 目标提交或分支,默认使用当前分支 */ + target?: string; + /** 标签消息 */ + message?: string; + /** 标签注释 */ + annotation?: Record; +}; + +/** + * 创建 Blob 参数 + */ +type CreateBlobData = { + /** 文件内容 */ + content: string; + /** 编码格式,默认: utf-8 */ + encoding?: string; +}; + +/** + * 查询内容参数 + */ +type GetContentParams = { + /** Git 引用,分支名、标签名或提交哈希 */ + ref?: string; +}; + +/** + * 查询指定路径内容参数 + */ +type GetContentWithPathParams = { + /** Git 引用,分支名、标签名或提交哈希 */ + ref?: string; +}; + +/** + * 获取原始文件参数 + */ +type GetRawParams = { + /** 最大 Blob 大小限制(字节) */ + max_in_byte?: number; +}; + +/** + * 设置提交注释参数 + */ +type PutCommitAnnotationsData = { + /** 注释键值对 */ + annotations: Record; +}; + +/** + * 批量查询提交注释参数 + */ +type GetCommitAnnotationsInBatchData = { + /** 提交哈希值列表 */ + shas: string[]; +}; + +/** + * 创建提交附件上传 URL 参数 + */ +type PostCommitAssetUploadURLData = { + /** 附件名称 */ + name: string; + /** 附件路径 */ + path: string; + /** 内容长度 */ + content_length: number; + /** 内容类型 */ + content_type: string; + /** 内容摘要 */ + digest: string; +}; + +/** + * 确认附件上传参数 + */ +type PostCommitAssetUploadConfirmationParams = { + /** 附件保持的天数,0 表示永久,最大 180 天 */ + ttl?: number; +}; + +/** + * 获取提交附件参数 + */ +type GetCommitAssetsParams = { + /** 是否可分享,true 表示下载地址有效期 12 小时,最多下载 10 次 */ + share?: boolean; +}; + +/** + * 设置标签注释参数 + */ +type PutTagAnnotationsData = { + /** 注释键值对 */ + annotations: Record; +}; diff --git a/test/git.ts b/test/git.ts new file mode 100644 index 0000000..334c52d --- /dev/null +++ b/test/git.ts @@ -0,0 +1,32 @@ +import { Git } from '../src/git/index.ts' +import { token, cookie, showMore } from './common.ts'; + +const git = new Git({ token, cookie }); + +const repo = 'kevisual/cnb'; + +const listFiles = async () => { + // const res = await git.getContentWithPath(repo, 'README.md'); + // console.log("res", showMore(res)); + const res2 = await git.getContentWithPath(repo, 'src/issue'); + console.log("res2", showMore(res2)); + const res3 = await git.getContentWithPath(repo, 'src/issue/index.ts'); + console.log("res3", showMore(res3)); +} + +// listFiles(); + +const createBlob = async () => { + const res = await git.createBlob(repo, { + content: 'Hello, CNB!', + encoding: 'utf-8', + }); + console.log("createBlob res", showMore(res)); +} + +createBlob(); + +const getRaw = async () => { + const res = await git.getRaw(repo, 'main:README.md'); + console.log("getRaw res", showMore(res)); +} \ No newline at end of file