From e358e3b6f68532ccfd1479af4ba57754b5ff7b2e Mon Sep 17 00:00:00 2001 From: yystopf Date: Tue, 16 Apr 2024 17:15:22 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E4=BB=93=E5=BA=93=E5=92=8Cwebhook=E8=A7=A6=E5=8F=91=E5=9C=B0?= =?UTF-8?q?=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../projects/sync_repositories_controller.rb | 24 +++++ app/jobs/touch_sync_job.rb | 24 +++++ app/models/sync_repositories/gitee.rb | 22 +++++ app/models/sync_repositories/github.rb | 21 ++++ app/models/sync_repository.rb | 25 +++++ app/models/sync_repository_branch.rb | 23 +++++ .../sync_repositories/create_service.rb | 95 +++++++++++++++++++ .../v1/projects/webhooks/create_service.rb | 2 +- config/routes/api.rb | 5 + ...20240415014011_create_sync_repositories.rb | 14 +++ ...5015216_create_sync_repository_branches.rb | 14 +++ 11 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 app/controllers/api/v1/projects/sync_repositories_controller.rb create mode 100644 app/jobs/touch_sync_job.rb create mode 100644 app/models/sync_repositories/gitee.rb create mode 100644 app/models/sync_repositories/github.rb create mode 100644 app/models/sync_repository.rb create mode 100644 app/models/sync_repository_branch.rb create mode 100644 app/services/api/v1/projects/sync_repositories/create_service.rb create mode 100644 db/migrate/20240415014011_create_sync_repositories.rb create mode 100644 db/migrate/20240415015216_create_sync_repository_branches.rb diff --git a/app/controllers/api/v1/projects/sync_repositories_controller.rb b/app/controllers/api/v1/projects/sync_repositories_controller.rb new file mode 100644 index 000000000..41ea1393f --- /dev/null +++ b/app/controllers/api/v1/projects/sync_repositories_controller.rb @@ -0,0 +1,24 @@ +class Api::V1::Projects::SyncRepositoriesController < Api::V1::BaseController + before_action :require_public_and_member_above + + def create + @sync_repositories = Api::V1::Projects::SyncRepositories::CreateService.call(@project, sync_repository_params) + end + + def sync + @sync_repositories = SyncRepository.where(project: @project) + @sync_repository_branches = SyncRepositoryBranch.where(sync_repository_id: @sync_repositories) + @sync_repositories.each do |item| + TouchSyncJob.perform_later(item) + end + @sync_repository_branches.each do |item| + TouchSyncJob.perform_later(item) + end + end + + private + def sync_repository_params + param.permit(:type, :external_token, :external_repo_address, :sync_granularity, :external_branch_name, :gitlink_branch_name, :first_sync_direction) + end + +end \ No newline at end of file diff --git a/app/jobs/touch_sync_job.rb b/app/jobs/touch_sync_job.rb new file mode 100644 index 000000000..e30b1cd31 --- /dev/null +++ b/app/jobs/touch_sync_job.rb @@ -0,0 +1,24 @@ +class TouchSyncJob < ApplicationJob + queue_as :default + + def perform(touchable) + puts "aaaa" + case touchable.class.to_s + when 'SyncRepositories::Github' || 'SyncRepositories::Gitee' + Reposync::SyncRepoService.call(touchable.repo_name) + when 'SyncRepositoryBranch' + sync_repository = touchable.sync_repository + result = [] + if sync_repository.sync_direction == 1 + result = Reposync::SyncBranchService.call(sync_repository.repo_name, touchable.gitlink_branch_name, sync_repository.sync_direction) + else + result = Reposync::SyncBranchService.call(sync_repository.repo_name, touchable.external_branch_name, sync_repository.sync_direction) + end + if result.is_a?(Array) + touchable.update_column(:sync_status, 1) + else + touchable.update_column(:sync_status, 2) + end + end + end +end \ No newline at end of file diff --git a/app/models/sync_repositories/gitee.rb b/app/models/sync_repositories/gitee.rb new file mode 100644 index 000000000..0a51b21c8 --- /dev/null +++ b/app/models/sync_repositories/gitee.rb @@ -0,0 +1,22 @@ +# == Schema Information +# +# Table name: sync_repositories +# +# id :integer not null, primary key +# project_id :integer +# type :string(255) +# repo_name :string(255) +# external_repo_address :string(255) +# sync_granularity :integer +# sync_direction :integer +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_sync_repositories_on_project_id (project_id) +# + +class SyncRepositories::Gitee < SyncRepository + +end diff --git a/app/models/sync_repositories/github.rb b/app/models/sync_repositories/github.rb new file mode 100644 index 000000000..1ef413a54 --- /dev/null +++ b/app/models/sync_repositories/github.rb @@ -0,0 +1,21 @@ +# == Schema Information +# +# Table name: sync_repositories +# +# id :integer not null, primary key +# project_id :integer +# type :string(255) +# repo_name :string(255) +# external_repo_address :string(255) +# sync_granularity :integer +# sync_direction :integer +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_sync_repositories_on_project_id (project_id) +# + +class SyncRepositories::Github < SyncRepository +end diff --git a/app/models/sync_repository.rb b/app/models/sync_repository.rb new file mode 100644 index 000000000..5ffdd5425 --- /dev/null +++ b/app/models/sync_repository.rb @@ -0,0 +1,25 @@ +# == Schema Information +# +# Table name: sync_repositories +# +# id :integer not null, primary key +# project_id :integer +# type :string(255) +# repo_name :string(255) +# external_repo_address :string(255) +# sync_granularity :integer +# sync_direction :integer +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_sync_repositories_on_project_id (project_id) +# + +class SyncRepository < ApplicationRecord + + belongs_to :project + + validates :repo_name, uniqueness: { message: "已存在" } +end diff --git a/app/models/sync_repository_branch.rb b/app/models/sync_repository_branch.rb new file mode 100644 index 000000000..2f2478231 --- /dev/null +++ b/app/models/sync_repository_branch.rb @@ -0,0 +1,23 @@ +# == Schema Information +# +# Table name: sync_repository_branches +# +# id :integer not null, primary key +# sync_repository_id :integer +# gitlink_branch_name :string(255) +# external_branch_name :string(255) +# sync_time :datetime +# sync_status :integer default("0") +# reposync_branch_id :integer +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_sync_repository_branches_on_sync_repository_id (sync_repository_id) +# + +class SyncRepositoryBranch < ApplicationRecord + + belongs_to :sync_repository +end diff --git a/app/services/api/v1/projects/sync_repositories/create_service.rb b/app/services/api/v1/projects/sync_repositories/create_service.rb new file mode 100644 index 000000000..088bd7b3b --- /dev/null +++ b/app/services/api/v1/projects/sync_repositories/create_service.rb @@ -0,0 +1,95 @@ +class Api::V1::Projects::SyncRepositories::CreateService < ApplicationService + + include ActiveModel::Model + + attr_reader :project, :type, :external_token, :external_repo_address, :sync_granularity, :external_branch_name, :gitlink_branch_name, :first_sync_direction + attr_accessor :sync_repository1, :sync_repository2 + + validates :type, inclusion: {in: %w(SyncRepositories::Gitee SyncRepositories::Github)} + validates :external_repo_address, format: { with: CustomRegexp::URL_REGEX, multiline: true, message: "地址格式不正确" } + validates :sync_granularity, :first_sync_direction, inclusion: {in: [1,2]} + validate :check_gitlink_branch_name + + def initialize(project, params) + @project = project + @type = params[:type] + @external_token = params[:external_token] + @external_repo_address = params[:external_repo_address] + @sync_granularity = params[:sync_granularity].to_i + @external_branch_name = params[:external_branch_name] + @gitlink_branch_name = params[:gitlink_branch_name] + @first_sync_direction = params[:first_sync_direction].to_i + end + + def call + raise Error, errors.full_messages.join(",") unless valid? + + if sync_granularity == 2 + # 创建两个不同方向的同步仓库 + create_sync_repository + # 创建两个不同方向的同步分支 + create_sync_repository_branch + # 第一次同步 + touch_first_sync_branch + else + create_sync_repository + touch_first_sync + end + create_webhook + end + + def check_gitlink_branch_name + if sync_granularity == 2 + result = $gitea_hat_client.get_repos_branch_name_set_by_owner_repo(project&.owner&.login, project&.identifier) rescue nil + raise Error, '分支不存在' if !result.include?(gitlink_branch_name) + end + end + + private + def create_sync_repository + repository1 = Reposync::CreateSyncRepoService.call(repo_name(1), gitlink_repo_address, external_repo_address, sync_granularity, 1) + repository2 = Reposync::CreateSyncRepoService.call(repo_name(2), gitlink_repo_address, external_repo_address, sync_granularity, 2) + @sync_repository1 = SyncRepository.create!(project: project, type: type, repo_name: repo_name(1), external_repo_address: external_repo_address, sync_granularity: sync_granularity, sync_direction: 1) + @sync_repository2 = SyncRepository.create!(project: project, type: type, repo_name: repo_name(2), external_repo_address: external_repo_address, sync_granularity: sync_granularity, sync_direction: 2) + end + + def create_sync_repository_branch + branch1 = Reposync::CreateSyncBranchService.call(repo_name(1),gitlink_branch_name, external_branch_name) + branch2 = Reposync::CreateSyncBranchService.call(repo_name(2),gitlink_branch_name, external_branch_name) + @sync_repository_branch1 = SyncRepositoryBranch.create!(sync_repository: @sync_repository1, gitlink_branch_name: gitlink_branch_name, external_branch_name: external_branch_name ) + @sync_repository_branch2 = SyncRepositoryBranch.create!(sync_repository: @sync_repository2, gitlink_branch_name: gitlink_branch_name, external_branch_name: external_branch_name) + end + + def touch_first_sync + first_sync_direction == 1 ? TouchSyncJob.perform_later(@sync_repository1) : TouchSyncJob.perform_later(@sync_repository2) + end + + def touch_first_sync_branch + first_sync_direction == 1 ? TouchSyncJob.perform_later(@sync_repository_branch1) : TouchSyncJob.perform_later(@sync_repository_branch2) + end + + def create_webhook + webhook_params = { + active: true, + branch_filter: '*', + http_method: 'POST', + url: "#{Rails.application.config_for(:configuration)['platform_url']}/api/v1/#{project&.owner&.login}/#{project&.identifier}/sync_repositories/sync", + content_type: 'json', + type: 'reposync', + events: ["push"] + } + Api::V1::Projects::Webhooks::CreateService.call(project, webhook_params) + end + + def repo_name(sync_direction) + if type == "SyncRepositories::Gitee" + return "gitee:#{project.owner&.login}:#{project.identifier}:#{sync_granularity}:#{sync_direction}" + else + return "github:#{project.owner&.login}:#{project.identifier}:#{sync_granularity}:#{sync_direction}" + end + end + + def gitlink_repo_address + "#{EduSetting.get("gitlink_repo_domain")}/#{project.owner&.login}/#{project.identifier}.git" + end +end \ No newline at end of file diff --git a/app/services/api/v1/projects/webhooks/create_service.rb b/app/services/api/v1/projects/webhooks/create_service.rb index 303f3b39f..829710cbe 100644 --- a/app/services/api/v1/projects/webhooks/create_service.rb +++ b/app/services/api/v1/projects/webhooks/create_service.rb @@ -8,7 +8,7 @@ class Api::V1::Projects::Webhooks::CreateService < ApplicationService validates :active, inclusion: {in: [true, false]} validates :http_method, inclusion: { in: %w(POST GET), message: "请输入正确的请求方式"} validates :content_type, inclusion: { in: %w(json form), message: "请输入正确的Content Type"} - validates :type, inclusion: {in: %w(gitea slack discord dingtalk telegram msteams feishu matrix jianmu softbot), message: "请输入正确的Webhook Type"} + validates :type, inclusion: {in: %w(gitea slack discord dingtalk telegram msteams feishu matrix jianmu softbot reposync), message: "请输入正确的Webhook Type"} def initialize(project, params, token=nil) @project = project @owner = project&.owner.login diff --git a/config/routes/api.rb b/config/routes/api.rb index 3f18235cd..6c0aa48cf 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -78,6 +78,11 @@ defaults format: :json do # projects文件夹下的 scope module: :projects do + resources :sync_repositories, only: [:create] do + collection do + post :sync + end + end resource :dataset, only: [:create, :update, :show] resources :actions, module: 'actions' do collection do diff --git a/db/migrate/20240415014011_create_sync_repositories.rb b/db/migrate/20240415014011_create_sync_repositories.rb new file mode 100644 index 000000000..d2908bfe9 --- /dev/null +++ b/db/migrate/20240415014011_create_sync_repositories.rb @@ -0,0 +1,14 @@ +class CreateSyncRepositories < ActiveRecord::Migration[5.2] + def change + create_table :sync_repositories do |t| + t.references :project + t.string :type + t.string :repo_name + t.string :external_repo_address + t.integer :sync_granularity + t.integer :sync_direction, comment: "1表示从gitlink到外部2表示从外部到gitlink" + + t.timestamps + end + end +end diff --git a/db/migrate/20240415015216_create_sync_repository_branches.rb b/db/migrate/20240415015216_create_sync_repository_branches.rb new file mode 100644 index 000000000..b44a6f396 --- /dev/null +++ b/db/migrate/20240415015216_create_sync_repository_branches.rb @@ -0,0 +1,14 @@ +class CreateSyncRepositoryBranches < ActiveRecord::Migration[5.2] + def change + create_table :sync_repository_branches do |t| + t.references :sync_repository + t.string :gitlink_branch_name, comment: 'gitlink分支' + t.string :external_branch_name, comment: '外部仓库分支' + t.datetime :sync_time + t.integer :sync_status, default: 0 + t.integer :reposync_branch_id + + t.timestamps + end + end +end