最近、同社の生産と研究の調整により、コードリポジトリをローカルの Gitlab から Yunxiao の Codeup に移行することが決定されました。 Gitlab が十分ではないというわけではありませんが、測定とセキュリティの点では Codeup がネイティブの Gitlab よりも優れています。さらに、同社の生産・研究管理もYunxiaoに移行し、統一的な管理が図られています。 Cloud Effect を使用しているのだから、Zadig ではなくその AppStack を使用すればいいのでは、と疑問に思う学生もいるかもしれません。 AppStack はまだ開発段階にあり、現在のニーズに適さない次のような問題があります。 - AppStack は、プライベート クラウド Kubernetes クラスター (パブリック ネットワーク アクセスなし) の管理をサポートしていません。
- AppStack は Helm タイプのアプリケーションをサポートしておらず、変換作業が比較的大きくなります。
また、私はZadigオープンソース製品の大ファンでもあります~~! ただし、Zadig では次のような非標準コード リポジトリのサポートが制限されています。 - 非標準コード リポジトリはリポジトリのリストをサポートしていないため、手動で入力する必要があります。
- 非標準コードリポジトリで作成されたパイプラインは、Webhook トリガーをネイティブにサポートしていません。
総合的に検討した結果、コードリポジトリ情報を手動で入力し、Webhook をサポートしないことは、全体的な使用には影響しませんが、一部のプロジェクトの作業効率にのみ影響します。 ただし、本来の制作や研究のリズムへの影響を最小限に抑えるために、サードパーティのウェアハウスの Webhook を実装して、Zadig パイプラインを自分でトリガーする予定です。複雑ではないからです。 全体的なアイデア実装は複雑ではなく、Webhook トリガー アクションを受信し、コンテンツを解析し、必要に応じて対応するパイプライン インターフェイスをトリガーするだけです。現在 (v1.17.0)、Zadig のトリガー パイプライン インターフェイスは正常に使用できます。 レンガを動かし始めるZadig API のラッピングまず、Zadig の API をカプセル化します。使いやすさを考慮して、go-zadig プロジェクト (https://github.com/joker-bai/go-zadig) を作成し、過去 2 日間で更新して、API の最新バージョン 1.17.0 をサポートしました。 主な追加内容は次のとおりです。 // 执行工作流type ExecWorkflowTaskOptions struct { WorkflowName string `json:"workflow_name"` ProjectName string `json:"project_name"` Input WorkflowInput `json:"input"` } type WorkflowInput struct { TargetEnv string `json:"target_env,omitempty"` Build ExecBuildArgs `json:"build"` Deploy ExecDeployArgs `json:"deploy"` } type ExecBuildArgs struct { Enabled bool `json:"enabled"` ServiceList []BuildServiceInfo `json:"service_list"` } type BuildServiceInfo struct { ServiceModule string `json:"service_module"` ServiceName string `json:"service_name"` RepoInfo []RepositoryInfo `json:"repo_info"` Inputs []UserInput `json:"inputs"` } type RepositoryInfo struct { CodehostName string `json:"codehost_name"` RepoNamespace string `json:"repo_namespace"` RepoName string `json:"repo_name"` Branch string `json:"branch"` PR int `json:"pr"` } type UserInput struct { Key string `json:"key"` Value string `json:"value"` } type ExecDeployArgs struct { Enabled bool `json:"enabled"` Source string `json:"source"` ServiceList []DeployServiceInfo `json:"service_list"` } type DeployServiceInfo struct { ServiceModule string `json:"service_module"` ServiceName string `json:"service_name"` Image string `json:"image"` } type ExecWorkflowTaskResponse struct { ProjectName string `json:"project_name,omitempty"` WorkflowName string `json:"workflow_name,omitempty"` TaskID int64 `json:"task_id,omitempty"` } func (w *WorkflowService) ExecWorkflowTask(opt *ExecWorkflowTaskOptions, options ...RequestOptionFunc) (*ExecWorkflowTaskResponse, *Response, error) { path := "openapi/workflows/product/task" req, err := w.client.NewRequest(http.MethodPost, path, opt, options) if err != nil { return nil, nil, err } task := new(ExecWorkflowTaskResponse) resp, err := w.client.Do(req, &task) if err != nil { return nil, resp, err } return task, resp, err } この部分は、標準ワークフローを実行するためのインターフェースです。カスタムワークフローは以前にも実装されており、変更はありません。 HTTP サーバーの開発Zadig はサードパーティ リポジトリの Webhook をネイティブにサポートしていないため、実装するには 2 つの方法があります。 - この機能を実装するには、Zadig ソース コードを自分で変更します。
- 調整する仲介者を見つけます。
ソースコードを変更する利点は、データに対して個別に多くの処理を実行する必要がなく、既製のものをそのまま使用できることです。しかし、私は後者を選択しました。主な理由は、私にとって初心者だったことと、ソース コードを変更するのが難しかったためです。 私はUberのfxフレームワークを使用しています。実際のところ、どのフレームワークを使用するかは重要ではありません。ロジック自体は非常に単純です。よりシンプルで使いやすいものを選択しました。
(1)データ構造を定義するpackage entity type ZadigWorkflowTask struct { ID int `json:"id"` ProjectName string `json:"project_name"` // 项目名ServiceModule string `json:"service_module"` // 服务组件名称ServiceName string `json:"service_name"` // 服务名CodehostName string `json:"codehost_name"` // 代码源别名RepoNamespace string `json:"repo_namespace"` // 仓库组名RepoName string `json:"repo_name"` // 仓库名WorkflowType string `json:"workflow_type"` // 工作流类型: product/custom JobName string `json:"job_name"` // 任务名workflow_type为custom生效JobType string `json:"job_type"` // 任务类型workflow_type为custom生效Registry string `json:"registry"` // 镜像仓库workflow_type为custom生效} func (z *ZadigWorkflowTask) Table() string { return "zadig_workflow_task" } type ZadigWorkflowName struct { ID int `json:"id"` WorkflowName string `json:"workflow_name"` // workflow名Branch string `json:"branch"` // 分支ProjectName string `json:"project_name"` // 项目名TargetEnv string `json:"target_env"` // 目标环境} func (z *ZadigWorkflowName) Table() string { return "zadig_workflow_name" }
私の定義は比較的単純で、命名は比較的ランダムです。主なフィールドは Zadig API に必要なフィールドであり、その他の不要なフィールドは書き込まれません。 (2)Zadigトリガー標準および非標準パイプラインを実装するpackage zadig import ( "github.com/joker-bai/go-zadig" "joker-bai/go-webhook/config" ) type Zadig struct { client *zadig.Client } func NewZadig(cfg *config.Config) *Zadig { client, err := zadig.NewClient(cfg.ZadigConfig.Token, zadig.WithBaseURL(cfg.ZadigConfig.URL)) if err != nil { panic(err) } return &Zadig{client: client} } // ExecProductWorkflowTask 执行标准工作流func (z *Zadig) ExecProductWorkflowTask(workflowName, projectName, targetEnv, serviceModule, serviceName, codehostName, repoNamespace, repoName, branch string) error { _, _, err := z.client.Workflow.ExecWorkflowTask(&zadig.ExecWorkflowTaskOptions{ WorkflowName: workflowName, ProjectName: projectName, Input: zadig.WorkflowInput{ TargetEnv: targetEnv, Build: zadig.ExecBuildArgs{ Enabled: true, ServiceList: []zadig.BuildServiceInfo{ { ServiceModule: serviceModule, ServiceName: serviceName, RepoInfo: []zadig.RepositoryInfo{ { CodehostName: codehostName, RepoNamespace: repoNamespace, RepoName: repoName, Branch: branch, }, }, }, }, }, Deploy: zadig.ExecDeployArgs{ Enabled: true, Source: "zadig", }, }, }) return err } // ExecCustomWorkflowTask 执行自定义工作流func (z *Zadig) ExecCustomWorkflowTask(projectName, workflowName, jobName, jobType, registry, serviceModule, serviceName, codehostName, repoNamespace, repoName, branch string) error { _, _, err := z.client.CustomWorkflow.CreateCustomWorkflowTask(&zadig.CreateCustomWorkflowTask{ ProjectName: projectName, WorkflowName: workflowName, Inputs: []zadig.CreateCustomWorkflowTaskInput{ { JobName: jobName, JobType: jobType, Parameters: zadig.CreateCustomWorkflowTaskParameters{ Register: registry, ServiceList: []zadig.ServiceList{ { ServiceModule: serviceModule, ServiceName: serviceName, RepoInfo: []zadig.RepoInfo{ { CodehostName: codehostName, RepoNamespace: repoNamespace, RepoName: repoName, Branch: branch, }, }, }, }, }, }, }, }) return err }
ここでは、実際の状況に応じてワークフロー API を選択し、必要なフィールドを選択します。 (3)サービス方式の実装package service import ( "context" "fmt" "github.com/sirupsen/logrus" "go.uber.org/fx" "joker-bai/go-webhook/config" "joker-bai/go-webhook/infrastructure/db" "joker-bai/go-webhook/internal/domain/entity" "joker-bai/go-webhook/pkg/zadig" ) // 获取并处理Codeup的Webhook type CodeupWebhookService struct { logger *logrus.Logger cfg *config.Config db *db.DataBase } var RegCodeupWebhookService = fx.Provide(func(logger *logrus.Logger, cfg *config.Config, db *db.DataBase) *CodeupWebhookService { return &CodeupWebhookService{ logger: logger, cfg: cfg, db: db, } }) // ExecZadigWorkflowTask 触发执行zadig的工作流func (c *CodeupWebhookService) ExecZadigWorkflowTask(ctx context.Context, repoName, branch string) error { client := zadig.NewZadig(c.cfg) // 从数据库中获取数据dbClient := c.db.Master.WithContext(ctx) var workflowTask entity.ZadigWorkflowTask workflowTaskRes := dbClient.Model(&workflowTask).Where("repo_name = ?", repoName).First(&workflowTask) if workflowTaskRes.Error != nil { return workflowTaskRes.Error } // 从数据库获取项目和工作流信息var flowName entity.ZadigWorkflowName workflowNameRes := dbClient.Model(&flowName).Where("branch = ? and project_name = ?", repoName, workflowTask.ProjectName).First(&flowName) if workflowNameRes.Error != nil { return workflowNameRes.Error } // 判断workflow的类别if workflowTask.WorkflowType == "product" { return client.ExecProductWorkflowTask( flowName.WorkflowName, workflowTask.ProjectName, flowName.TargetEnv, workflowTask.ServiceModule, workflowTask.ServiceName, workflowTask.CodehostName, workflowTask.RepoNamespace, workflowTask.RepoName, branch, ) } else if workflowTask.WorkflowType == "custom" { return client.ExecCustomWorkflowTask( workflowTask.ProjectName, flowName.WorkflowName, workflowTask.JobName, workflowTask.JobType, workflowTask.Registry, workflowTask.ServiceModule, workflowTask.ServiceName, workflowTask.CodehostName, workflowTask.RepoNamespace, workflowTask.RepoName, branch, ) } else { return fmt.Errorf("未匹配workflow类型") } }
ここでは、必要な情報がデータベースから取得され、さまざまなワークフロー タイプに応じてさまざまなインターフェイスが実行されます。 (4)コントローラメソッドの実装package application import ( "fmt" "github.com/gin-gonic/gin" "github.com/sirupsen/logrus" "go.uber.org/fx" "joker-bai/go-webhook/internal/core/base" "joker-bai/go-webhook/domain/service" "strings" "time" ) var regCodeupWebhookApplication = fx.Provide(NewCodeupWebhookController) type CodeupWebhookController struct { logger *logrus.Logger service *service.CodeupWebhookService } type CodeupWebhook struct { After string `json:"after"` AliyunPk string `json:"aliyun_pk"` Before string `json:"before"` CheckoutSha string `json:"checkout_sha"` Commits []Commits `json:"commits"` ObjectKind string `json:"object_kind"` ProjectID int `json:"project_id"` Ref string `json:"ref"` Repository Repository `json:"repository"` TotalCommitsCount int `json:"total_commits_count"` UserEmail string `json:"user_email"` UserExternUID string `json:"user_extern_uid"` UserID int `json:"user_id"` UserName string `json:"user_name"` } type Author struct { Email string `json:"email"` Name string `json:"name"` } type Commits struct { Author Author `json:"author"` ID string `json:"id"` Message string `json:"message"` Timestamp time.Time `json:"timestamp"` URL string `json:"url"` } type Repository struct { Description string `json:"description"` GitHTTPURL string `json:"git_http_url"` GitSecondaryHTTPURL string `json:"git_secondary_http_url"` GitSecondarySSHURL string `json:"git_secondary_ssh_url"` GitSSHURL string `json:"git_ssh_url"` Homepage string `json:"homepage"` Name string `json:"name"` URL string `json:"url"` VisibilityLevel int `json:"visibility_level"` } func NewCodeupWebhookController(logger *logrus.Logger, service *service.CodeupWebhookService) *CodeupWebhookController { return &CodeupWebhookController{ logger: logger, service: service, } } // DoCodeupWebhook 获取Codeup 代码webhook的Body func (c *CodeupWebhookController) DoCodeupWebhook(ctx *gin.Context) { output := base.NewResponse(ctx) var param CodeupWebhook err := ctx.ShouldBindJSON(¶m) if err != nil { output.Error(10000, err.Error()) return } // 获取repo_name,repo_namespace,branch repoName := param.Repository.Name branch := strings.Split(param.Ref, "/")[2] if err := c.service.ExecZadigWorkflowTask(ctx, repoName, branch); err != nil { c.logger.Error(err.Error()) output.Error(502, "执行workflow失败") } output.Success(gin.H{ "data": "ok", }) }
この部分はさらに簡単です。 Webhook からデータを取得し、サービスを呼び出して実行するだけです。 最後のステップはルーティングを追加することですが、ここでは説明しません。 レンガ造りは終わったレンガの移動が完了したら、開発した HTTP サーバーを検証します。 全体的なアイデアや開発作業量はそれほど大きくありません。上記のコードにはまだ調整が必要な箇所が多数あります。同じ要件がある場合は、自分で実装できます。デモを作りました。 ニーズを満たすために仲介業者を導入する利点は、それが比較的簡単なことです。他のコードを参照したり互換性を持たせる必要はありません。独自のロジックを実装するだけで済みます。デメリットとしては、データを個別に扱う必要があり、面倒なことです。 |