349 lines
8.4 KiB
Go
349 lines
8.4 KiB
Go
package push
|
||
|
||
import (
|
||
"encoding/base64"
|
||
"fmt"
|
||
"sync"
|
||
"time"
|
||
|
||
"code.gitea.io/sdk/gitea"
|
||
)
|
||
|
||
var (
|
||
// 全局客户端实例(单例)
|
||
defaultClient *GiteaClient
|
||
once sync.Once
|
||
initErr error
|
||
)
|
||
|
||
// GiteaClient Gitea 客户端封装
|
||
type GiteaClient struct {
|
||
client *gitea.Client
|
||
}
|
||
|
||
// RepoConfig 仓库配置
|
||
type RepoConfig struct {
|
||
Name string // 仓库名称
|
||
Description string // 仓库描述
|
||
Private bool // 是否私有,false 为公共仓库
|
||
AutoInit bool // 是否自动初始化
|
||
Gitignores string // .gitignore 模板,如 "Go", "Python"
|
||
License string // 许可证,如 "MIT", "Apache-2.0"
|
||
Readme string // README 模板,如 "Default"
|
||
}
|
||
|
||
// FileConfig 文件配置
|
||
type FileConfig struct {
|
||
Path string // 文件路径,如 "main.go"
|
||
Content string // 文件内容(原始字符串)
|
||
Message string // 提交信息
|
||
Branch string // 分支名,为空则使用默认分支
|
||
}
|
||
|
||
// TagConfig Tag 配置
|
||
type TagConfig struct {
|
||
TagName string // tag 名称,如 "v1.0.0"
|
||
Message string // tag 说明
|
||
}
|
||
|
||
// GetClient 获取全局客户端实例(单例模式)
|
||
func GetClient() (*GiteaClient, error) {
|
||
//if defaultClient == nil {
|
||
// once.Do(func() {
|
||
// defaultClient, initErr = NewGiteaClient(
|
||
// "https://gitea.cdlsxd.cn/api/v1",
|
||
// "rzy_lsxd_2026",
|
||
// )
|
||
// })
|
||
// if initErr != nil {
|
||
// return nil, initErr
|
||
// }
|
||
//}
|
||
return defaultClient, nil
|
||
}
|
||
|
||
// NewGiteaClient 创建 Gitea 客户端实例
|
||
func NewGiteaClient(url, token string) (*GiteaClient, error) {
|
||
client, err := gitea.NewClient(url, gitea.SetToken(token))
|
||
if err != nil {
|
||
return nil, fmt.Errorf("创建 Gitea 客户端失败: %v", err)
|
||
}
|
||
defaultClient = &GiteaClient{
|
||
client: client,
|
||
}
|
||
return defaultClient, nil
|
||
}
|
||
|
||
// CreateRepo 创建仓库(支持个人和组织)
|
||
func (g *GiteaClient) CreateRepo(cfg RepoConfig, orgName ...string) (*gitea.Repository, error) {
|
||
if g.client == nil {
|
||
return nil, fmt.Errorf("客户端未初始化")
|
||
}
|
||
|
||
opts := gitea.CreateRepoOption{
|
||
Name: cfg.Name,
|
||
Description: cfg.Description,
|
||
Private: cfg.Private,
|
||
AutoInit: cfg.AutoInit,
|
||
Gitignores: cfg.Gitignores,
|
||
License: cfg.License,
|
||
//Readme: cfg.Readme,
|
||
}
|
||
|
||
var repo *gitea.Repository
|
||
var err error
|
||
|
||
// 如果传入了组织名,则为组织创建仓库
|
||
if len(orgName) > 0 && orgName[0] != "" {
|
||
repo, _, err = g.client.CreateOrgRepo(orgName[0], opts)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("为组织 %s 创建仓库失败: %v", orgName[0], err)
|
||
}
|
||
} else {
|
||
repo, _, err = g.client.CreateRepo(opts)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("创建仓库失败: %v", err)
|
||
}
|
||
}
|
||
|
||
return repo, nil
|
||
}
|
||
|
||
// PushFile 推送单个文件
|
||
func (g *GiteaClient) PushFile(owner, repoName string, fileCfg FileConfig) error {
|
||
if g.client == nil {
|
||
return fmt.Errorf("客户端未初始化")
|
||
}
|
||
// Base64 编码文件内容
|
||
contentBase64 := base64.StdEncoding.EncodeToString([]byte(fileCfg.Content))
|
||
|
||
opts := gitea.CreateFileOptions{
|
||
FileOptions: gitea.FileOptions{
|
||
Message: fileCfg.Message,
|
||
BranchName: fileCfg.Branch,
|
||
},
|
||
Content: contentBase64,
|
||
}
|
||
|
||
_, _, err := g.client.CreateFile(owner, repoName, fileCfg.Path, opts)
|
||
if err != nil {
|
||
return fmt.Errorf("推送文件 %s 失败: %v", fileCfg.Path, err)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// PushFiles 批量推送多个文件
|
||
func (g *GiteaClient) PushFiles(owner, repoName string, files []FileConfig) error {
|
||
if g.client == nil {
|
||
return fmt.Errorf("客户端未初始化")
|
||
}
|
||
|
||
for _, file := range files {
|
||
if err := g.PushFile(owner, repoName, file); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// CreateTag 创建标签
|
||
func (g *GiteaClient) CreateTag(owner, repoName string, tagCfg TagConfig) (*gitea.Tag, error) {
|
||
if g.client == nil {
|
||
return nil, fmt.Errorf("客户端未初始化")
|
||
}
|
||
|
||
opts := gitea.CreateTagOption{
|
||
TagName: tagCfg.TagName,
|
||
Message: tagCfg.Message,
|
||
}
|
||
|
||
tag, _, err := g.client.CreateTag(owner, repoName, opts)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("创建标签 %s 失败: %v", tagCfg.TagName, err)
|
||
}
|
||
|
||
return tag, nil
|
||
}
|
||
|
||
// CreateRepoAndPush 创建仓库并推送代码(完整流程)
|
||
func (g *GiteaClient) CreateRepoAndPush(cfg RepoConfig, files []FileConfig, tagCfg *TagConfig, orgName ...string) error {
|
||
if g.client == nil {
|
||
return fmt.Errorf("客户端未初始化")
|
||
}
|
||
|
||
// 1. 创建仓库
|
||
repo, err := g.CreateRepo(cfg, orgName...)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 等待仓库初始化完成
|
||
if cfg.AutoInit {
|
||
fmt.Println("⏳ 等待仓库初始化...")
|
||
time.Sleep(2 * time.Second)
|
||
}
|
||
|
||
// 2. 推送代码
|
||
if len(files) > 0 {
|
||
fmt.Println("📤 开始推送文件...")
|
||
if err := g.PushFiles(repo.Owner.UserName, repo.Name, files); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
// 3. 创建标签(如果需要)
|
||
if tagCfg != nil && tagCfg.TagName != "" {
|
||
fmt.Println("🏷️ 创建标签...")
|
||
if _, err := g.CreateTag(repo.Owner.UserName, repo.Name, *tagCfg); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
fmt.Println("🎉 全部操作完成!")
|
||
return nil
|
||
}
|
||
|
||
// ==================== 便捷函数(使用全局单例) ====================
|
||
|
||
// CreateRepoQuick 使用全局客户端快速创建仓库
|
||
func CreateRepoQuick(cfg RepoConfig, orgName ...string) (*gitea.Repository, error) {
|
||
client, err := GetClient()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return client.CreateRepo(cfg, orgName...)
|
||
}
|
||
|
||
// PushFileQuick 使用全局客户端快速推送文件
|
||
func PushFileQuick(owner, repoName string, fileCfg FileConfig) error {
|
||
client, err := GetClient()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return client.PushFile(owner, repoName, fileCfg)
|
||
}
|
||
|
||
// PushFilesQuick 使用全局客户端快速批量推送文件
|
||
func PushFilesQuick(owner, repoName string, files []FileConfig) error {
|
||
client, err := GetClient()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return client.PushFiles(owner, repoName, files)
|
||
}
|
||
|
||
// CreateTagQuick 使用全局客户端快速创建标签
|
||
func CreateTagQuick(owner, repoName string, tagCfg TagConfig) (*gitea.Tag, error) {
|
||
client, err := GetClient()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return client.CreateTag(owner, repoName, tagCfg)
|
||
}
|
||
|
||
// PushQuick 使用全局客户端快速完整推送
|
||
func PushQuick(repoName, description string, files []FileConfig, tagCfg *TagConfig, orgName ...string) error {
|
||
client, err := GetClient()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
cfg := RepoConfig{
|
||
Name: repoName,
|
||
Description: description,
|
||
Private: false,
|
||
AutoInit: true,
|
||
Gitignores: "Go",
|
||
License: "MIT",
|
||
Readme: "Default",
|
||
}
|
||
|
||
return client.CreateRepoAndPush(cfg, files, tagCfg, orgName...)
|
||
}
|
||
|
||
// ==================== 示例 ====================
|
||
|
||
// ExampleUsage 使用示例
|
||
func ExampleUsage() {
|
||
// 准备文件
|
||
files := []FileConfig{
|
||
{
|
||
Path: "main.go",
|
||
Content: `package main\n\nimport "fmt"\n\nfunc main() {\n\tfmt.Println("Hello, Gitea!")\n}\n`,
|
||
Message: "添加主程序",
|
||
Branch: "main",
|
||
},
|
||
{
|
||
Path: "README.md",
|
||
Content: `# Auto Created Repo\n\nThis repo is created by Gitea API.`,
|
||
Message: "添加 README",
|
||
Branch: "main",
|
||
},
|
||
}
|
||
|
||
tagCfg := &TagConfig{
|
||
TagName: "v1.0.0",
|
||
Message: "首次发布",
|
||
}
|
||
|
||
// 方式1:使用快捷函数(自动使用全局单例)
|
||
err := PushQuick("my-auto-repo", "通过 API 自动创建的公共仓库", files, tagCfg)
|
||
if err != nil {
|
||
fmt.Printf("❌ 操作失败: %v\n", err)
|
||
}
|
||
|
||
// 方式2:手动获取客户端实例(更灵活)
|
||
client, err := GetClient()
|
||
if err != nil {
|
||
fmt.Printf("获取客户端失败: %v\n", err)
|
||
return
|
||
}
|
||
|
||
// 创建私有仓库
|
||
cfg := RepoConfig{
|
||
Name: "private-repo",
|
||
Description: "私有仓库",
|
||
Private: true,
|
||
AutoInit: true,
|
||
}
|
||
|
||
// 为组织创建仓库
|
||
err = client.CreateRepoAndPush(cfg, files, nil, "my-org")
|
||
if err != nil {
|
||
fmt.Printf("❌ 操作失败: %v\n", err)
|
||
}
|
||
|
||
// 方式3:单独使用各个功能
|
||
// 只创建仓库
|
||
repoCfg := RepoConfig{
|
||
Name: "test-repo",
|
||
Private: false,
|
||
AutoInit: true,
|
||
}
|
||
repo, err := CreateRepoQuick(repoCfg)
|
||
if err != nil {
|
||
fmt.Printf("创建仓库失败: %v\n", err)
|
||
}
|
||
|
||
// 只推送文件
|
||
err = PushFileQuick(repo.Owner.UserName, repo.Name, FileConfig{
|
||
Path: "test.txt",
|
||
Content: "Hello World",
|
||
Message: "添加测试文件",
|
||
Branch: "main",
|
||
})
|
||
if err != nil {
|
||
fmt.Printf("推送文件失败: %v\n", err)
|
||
}
|
||
|
||
// 只创建标签
|
||
_, err = CreateTagQuick(repo.Owner.UserName, repo.Name, TagConfig{
|
||
TagName: "v0.1.0",
|
||
Message: "测试版本",
|
||
})
|
||
if err != nil {
|
||
fmt.Printf("创建标签失败: %v\n", err)
|
||
}
|
||
}
|