| @@ -0,0 +1,147 @@ | |||
| class Api::V1::Projects::SyncRepositoriesController < Api::V1::BaseController | |||
| before_action :require_public_and_member_above | |||
| def index | |||
| @sync_repositories = @project.sync_repositories | |||
| @group_sync_repository = @project.sync_repositories.group(:type, :external_repo_address, :sync_granularity, :external_token).count | |||
| end | |||
| def create | |||
| @sync_repository1, @sync_repository2, @sync_repository_branch1, @sync_repository_branch2 = Api::V1::Projects::SyncRepositories::CreateService.call(@project, sync_repository_params) | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| def update_info | |||
| return render_error("请输入正确的同步仓库ID") unless params[:sync_repository_ids].present? | |||
| Api::V1::Projects::SyncRepositories::UpdateService.call(@project, params[:sync_repository_ids], sync_repository_update_params) | |||
| render_ok | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| def sync | |||
| return render_error("请输入正确的同步方向!") if params[:sync_direction].blank? | |||
| if params[:repo_type].present? | |||
| @sync_repositories = SyncRepository.where(project: @project, type: params[:repo_type], sync_direction: params[:sync_direction]) | |||
| else | |||
| @sync_repositories = SyncRepository.where(project: @project, sync_direction: params[:sync_direction]) | |||
| end | |||
| branch = params[:ref].split("refs/heads/")[-1] | |||
| if params[:sync_direction].to_i == 1 | |||
| @sync_repository_branches = SyncRepositoryBranch.where(sync_repository_id: @sync_repositories, gitlink_branch_name: branch, enable: true) | |||
| else | |||
| @sync_repository_branches = SyncRepositoryBranch.where(sync_repository_id: @sync_repositories, external_branch_name: branch, enable: true) | |||
| end | |||
| # 全部分支同步暂时不做 | |||
| # @sync_repositories.each do |item| | |||
| # TouchSyncJob.perform_later(item) | |||
| # end | |||
| @sync_repository_branches.each do |item| | |||
| TouchSyncJob.set(wait: 5.seconds).perform_later(item) | |||
| end | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| def unbind | |||
| return render_error("请输入正确的同步仓库ID") unless params[:sync_repository_ids].present? | |||
| @sync_repositories = SyncRepository.where(id: params[:sync_repository_ids].split(",")) | |||
| @sync_repositories.each do |repo| | |||
| # Reposync::DeleteRepoService.call(repo.repo_name) # 解绑操作放在回调里 | |||
| Api::V1::Projects::Webhooks::DeleteService.call(@project, repo.webhook_gid) | |||
| repo.destroy | |||
| end | |||
| render_ok | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| def change_enable | |||
| return render_error("请输入正确的仓库类型") if params[:repo_type].blank? | |||
| return render_error("请输入正确的分支名称") if params[:gitlink_branch_name].blank? || params[:external_branch_name].blank? | |||
| # return render_error("请输入正确的状态") if params[:enable].blank? | |||
| @sync_repository_branches = SyncRepositoryBranch.joins(:sync_repository).where(sync_repositories: {project_id: @project.id, type: params[:repo_type]}, gitlink_branch_name: params[:gitlink_branch_name], external_branch_name: params[:external_branch_name]) | |||
| if @sync_repository_branches.update_all({enable: params[:enable]}) | |||
| @sync_repository_branches.each do |branch| | |||
| branch_sync_direction = branch&.sync_repository&.sync_direction.to_i | |||
| if branch_sync_direction == 1 | |||
| Reposync::UpdateBranchStatusService.call(branch&.sync_repository&.repo_name, branch.gitlink_branch_name, params[:enable]) | |||
| else | |||
| Reposync::UpdateBranchStatusService.call(branch&.sync_repository&.repo_name, branch.external_branch_name, params[:enable]) | |||
| end | |||
| TouchSyncJob.perform_later(branch) if params[:enable] && branch_sync_direction == params[:first_sync_direction].to_i | |||
| end | |||
| render_ok | |||
| else | |||
| render_error("更新失败!") | |||
| end | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| def create_branch | |||
| return render_error("请输入正确的同步仓库ID") unless params[:sync_repository_ids].present? | |||
| return render_error("请输入正确的Gitlink分支名称") unless params[:gitlink_branch_name].present? | |||
| return render_error("请输入正确的外部仓库分支名称") unless params[:external_branch_name].present? | |||
| return render_error("请输入正确的首次同步方向") unless params[:first_sync_direction].present? | |||
| params[:sync_repository_ids].split(",").each do |id| | |||
| repo = SyncRepository.find_by_id id | |||
| branch = Reposync::CreateSyncBranchService.call(repo.repo_name, params[:gitlink_branch_name], params[:external_branch_name]) | |||
| return render_error(branch[2]) if branch[0].to_i !=0 | |||
| sync_branch = SyncRepositoryBranch.create!(sync_repository_id: id, gitlink_branch_name: params[:gitlink_branch_name], external_branch_name: params[:external_branch_name], reposync_branch_id: branch[1]['id']) | |||
| TouchSyncJob.perform_later(sync_branch) if params[:first_sync_direction].to_i == repo.sync_direction | |||
| end | |||
| render_ok | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| def branches | |||
| return render_error("请输入正确的同步仓库ID") unless params[:sync_repository_ids].present? | |||
| @sync_repository_branches = SyncRepositoryBranch.where(sync_repository_id: params[:sync_repository_ids].split(",")) | |||
| @sync_repository_branches = @sync_repository_branches.ransack(gitlink_branch_name_or_external_branch_name_cont: params[:branch_name]).result if params[:branch_name].present? | |||
| @group_sync_repository_branch = @sync_repository_branches.joins(:sync_repository).group("sync_repositories.type, sync_repository_branches.gitlink_branch_name, sync_repository_branches.external_branch_name").select("sync_repositories.type as type,max(sync_repository_branches.updated_at) as updated_at, sync_repository_branches.gitlink_branch_name, sync_repository_branches.external_branch_name").sort_by{|i|i.updated_at} | |||
| @each_json = [] | |||
| @group_sync_repository_branch.each do |item| | |||
| branches = @sync_repository_branches.joins(:sync_repository).where(sync_repositories: {type: item.type}, gitlink_branch_name: item.gitlink_branch_name, external_branch_name: item.external_branch_name).order(updated_at: :desc) | |||
| branch = branches.first | |||
| @each_json << { | |||
| gitlink_branch_name: item.gitlink_branch_name, | |||
| external_branch_name: item.external_branch_name, | |||
| type: branch&.sync_repository&.type, | |||
| sync_time: branch.sync_time.present? ? branch.sync_time.strftime("%Y-%m-%d %H:%M:%S") : nil, | |||
| sync_status: branch.sync_status, | |||
| enable: branch.enable, | |||
| enable_num: branch.enable ? 1 : 0, | |||
| created_at: branch.created_at.to_i, | |||
| reposync_branch_ids: branches.pluck(:reposync_branch_id) | |||
| } | |||
| end | |||
| @each_json = @each_json.sort_by{|h| [-h[:enable_num], h[:created_at]]} | |||
| render :json => {total_count: @group_sync_repository_branch.count, sync_repository_branches: @each_json} | |||
| end | |||
| def history | |||
| return render_error("请输入正确的同步分支ID") unless params[:reposync_branch_ids] | |||
| @branch = SyncRepositoryBranch.find_by(reposync_branch_id: params[:reposync_branch_ids].split(",")[0]) | |||
| _, @reposync_branch_logs, @total_count, _ = Reposync::GetLogsService.call(nil, params[:reposync_branch_ids], page, limit) | |||
| end | |||
| private | |||
| def sync_repository_params | |||
| params.permit(:type, :external_token, :external_repo_address, :sync_granularity, :external_branch_name, :gitlink_branch_name, :first_sync_direction) | |||
| end | |||
| def sync_repository_update_params | |||
| params.permit(:external_token, :external_repo_address) | |||
| end | |||
| end | |||
| @@ -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) && result[0].to_i == 0 | |||
| touchable.update_attributes!({sync_status: 1, sync_time: Time.now}) | |||
| else | |||
| touchable.update_attributes!({sync_status: 2, sync_time: Time.now}) | |||
| end | |||
| end | |||
| end | |||
| end | |||
| @@ -55,14 +55,13 @@ | |||
| # default_branch :string(255) default("master") | |||
| # website :string(255) | |||
| # lesson_url :string(255) | |||
| # use_blockchain :boolean default("0") | |||
| # is_pinned :boolean default("0") | |||
| # recommend_index :integer default("0") | |||
| # use_blockchain :boolean default("0") | |||
| # pr_view_admin :boolean default("0") | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_projects_on_forked_count (forked_count) | |||
| # index_projects_on_forked_from_project_id (forked_from_project_id) | |||
| # index_projects_on_identifier (identifier) | |||
| # index_projects_on_invite_code (invite_code) | |||
| @@ -72,7 +71,6 @@ | |||
| # index_projects_on_license_id (license_id) | |||
| # index_projects_on_name (name) | |||
| # index_projects_on_platform (platform) | |||
| # index_projects_on_praises_count (praises_count) | |||
| # index_projects_on_project_category_id (project_category_id) | |||
| # index_projects_on_project_language_id (project_language_id) | |||
| # index_projects_on_project_type (project_type) | |||
| @@ -80,7 +78,6 @@ | |||
| # index_projects_on_rgt (rgt) | |||
| # index_projects_on_status (status) | |||
| # index_projects_on_updated_on (updated_on) | |||
| # index_projects_on_user_id (user_id) | |||
| # | |||
| class Project < ApplicationRecord | |||
| @@ -140,6 +137,7 @@ class Project < ApplicationRecord | |||
| has_many :commit_logs, dependent: :destroy | |||
| has_many :daily_project_statistics, dependent: :destroy | |||
| has_one :project_dataset, dependent: :destroy | |||
| has_many :sync_repositories, dependent: :destroy | |||
| after_create :incre_user_statistic, :incre_platform_statistic | |||
| after_save :check_project_members | |||
| before_save :set_invite_code, :reset_unmember_followed, :set_recommend_and_is_pinned, :reset_cache_data | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -0,0 +1,35 @@ | |||
| # == 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 | |||
| # external_token :string(255) | |||
| # webhook_gid :integer | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_sync_repositories_on_project_id (project_id) | |||
| # | |||
| class SyncRepository < ApplicationRecord | |||
| belongs_to :project | |||
| has_many :sync_repository_branches, dependent: :destroy | |||
| before_destroy :unbind_reposyncer | |||
| validates :repo_name, uniqueness: { message: "已存在" } | |||
| def unbind_reposyncer | |||
| Reposync::DeleteRepoService.call(self.repo_name) rescue nil | |||
| end | |||
| end | |||
| @@ -0,0 +1,38 @@ | |||
| # == 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 | |||
| # enable :boolean default("1") | |||
| # | |||
| # Indexes | |||
| # | |||
| # index_sync_repository_branches_on_sync_repository_id (sync_repository_id) | |||
| # | |||
| class SyncRepositoryBranch < ApplicationRecord | |||
| belongs_to :sync_repository | |||
| before_destroy :unbind_reposyncer | |||
| enum sync_status: {success: 1, failure: 2} | |||
| def unbind_reposyncer | |||
| if self.sync_repository.sync_direction.to_i == 1 | |||
| Reposync::DeleteBranchService.call(self.sync_repository&.repo_name, self.gitlink_branch_name) rescue nil | |||
| else | |||
| Reposync::DeleteBranchService.call(self.sync_repository&.repo_name, self.external_branch_name) rescue nil | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,109 @@ | |||
| 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, :sync_repository_branch1, :sync_repository_branch2, :gitea_webhook | |||
| 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? | |||
| create_webhook | |||
| if sync_granularity == 2 | |||
| # 创建两个不同方向的同步仓库 | |||
| create_sync_repository | |||
| # 创建两个不同方向的同步分支 | |||
| create_sync_repository_branch | |||
| # 第一次同步 | |||
| touch_first_sync_branch | |||
| else | |||
| create_sync_repository | |||
| touch_first_sync | |||
| end | |||
| [@sync_repository1, @sync_repository2, @sync_repository_branch1, @sync_repository_branch2] | |||
| 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, gitlink_token, external_repo_address, external_token, sync_granularity, 1) | |||
| repository2 = Reposync::CreateSyncRepoService.call(repo_name(2), gitlink_repo_address, gitlink_token, external_repo_address, external_token, sync_granularity, 2) | |||
| raise Error, '创建同步仓库失败' if repository1[0].to_i > 0 || repository2[0].to_i > 0 | |||
| @sync_repository1 = SyncRepository.create!(project: project, type: type, repo_name: repo_name(1), external_repo_address: external_repo_address, external_token: external_token, sync_granularity: sync_granularity, sync_direction: 1, webhook_gid: @gitea_webhook["id"]) | |||
| @sync_repository2 = SyncRepository.create!(project: project, type: type, repo_name: repo_name(2), external_repo_address: external_repo_address, external_token: external_token, sync_granularity: sync_granularity, sync_direction: 2, webhook_gid: @gitea_webhook["id"]) | |||
| 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) | |||
| raise Error, '创建同步仓库分支失败' if branch1[0].to_i > 0 || branch2[0].to_i > 0 | |||
| @sync_repository_branch1 = SyncRepositoryBranch.create!(sync_repository: @sync_repository1, gitlink_branch_name: gitlink_branch_name, external_branch_name: external_branch_name, reposync_branch_id: branch1[1]["id"]) | |||
| @sync_repository_branch2 = SyncRepositoryBranch.create!(sync_repository: @sync_repository2, gitlink_branch_name: gitlink_branch_name, external_branch_name: external_branch_name, reposync_branch_id: branch2[1]["id"]) | |||
| 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 | |||
| url = "" | |||
| if type == "SyncRepositories::Gitee" | |||
| url = "#{Rails.application.config_for(:configuration)['platform_url']}/api/v1/#{project&.owner&.login}/#{project&.identifier}/sync_repositories/sync?sync_direction=1&repo_type=SyncRepositories::Gitee" | |||
| else | |||
| url = "#{Rails.application.config_for(:configuration)['platform_url']}/api/v1/#{project&.owner&.login}/#{project&.identifier}/sync_repositories/sync?sync_direction=1&repo_type=SyncRepositories::Github" | |||
| end | |||
| webhook_params = { | |||
| active: true, | |||
| branch_filter: '*', | |||
| http_method: 'POST', | |||
| url: url, | |||
| content_type: 'json', | |||
| type: 'reposync', | |||
| events: ["push"] | |||
| } | |||
| @gitea_webhook = Api::V1::Projects::Webhooks::CreateService.call(project, webhook_params) | |||
| end | |||
| def repo_name(sync_direction) | |||
| if type == "SyncRepositories::Gitee" | |||
| return "gitee:#{project.id}:#{sync_granularity}:#{sync_direction}" | |||
| else | |||
| return "github:#{project.id}:#{sync_granularity}:#{sync_direction}" | |||
| end | |||
| end | |||
| def gitlink_repo_address | |||
| "#{EduSetting.get("gitlink_repo_domain")}/#{project.owner&.login}/#{project.identifier}.git" | |||
| end | |||
| def gitlink_token | |||
| EduSetting.get("gitlink_admin_token") | |||
| end | |||
| end | |||
| @@ -0,0 +1,40 @@ | |||
| class Api::V1::Projects::SyncRepositories::UpdateService < ApplicationService | |||
| include ActiveModel::Model | |||
| attr_reader :project, :external_token, :external_repo_address, :sync_repositories | |||
| attr_accessor :sync_repository1, :sync_repository2 | |||
| validates :external_repo_address, format: { with: CustomRegexp::URL_REGEX, multiline: true, message: "地址格式不正确" } | |||
| validates :external_token, presence: true | |||
| #Api::V1::Projects::SyncRepositories::UpdateService.call(Project.last, "21,22", {external_repo_address: "https://github.com/viletyy/testdevops.git", external_token:"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"}) | |||
| def initialize(project, sync_repository_ids, params) | |||
| @project = project | |||
| @sync_repositories = SyncRepository.where(project_id: project.id, id: sync_repository_ids.split(",")) | |||
| @external_token = params[:external_token] | |||
| @external_repo_address = params[:external_repo_address] | |||
| end | |||
| def call | |||
| raise Error, errors.full_messages.join(",") unless valid? | |||
| update_sync_repository | |||
| end | |||
| private | |||
| def update_sync_repository | |||
| @sync_repositories.each do |repo| | |||
| Reposync::UpdateRepoAddrService.call(repo&.repo_name, internal_repo_address, internal_token, external_repo_address, external_token) | |||
| repo.update_attributes!({external_repo_address: external_repo_address, external_token: external_token}) | |||
| end | |||
| end | |||
| def internal_repo_address | |||
| "#{EduSetting.get("gitlink_repo_domain")}/#{project.owner&.login}/#{project.identifier}.git" | |||
| end | |||
| def internal_token | |||
| EduSetting.get("gitlink_admin_token") | |||
| end | |||
| end | |||
| @@ -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 | |||
| @@ -0,0 +1,115 @@ | |||
| class Reposync::ClientService < ApplicationService | |||
| attr_reader :url, :params | |||
| def initialize(options={}) | |||
| @url = options[:url] | |||
| @params = options[:params] | |||
| end | |||
| def post(url, params={}) | |||
| puts "[reposync][POST] request params: #{params}" | |||
| conn.post do |req| | |||
| req.url full_url(url) | |||
| req.body = params[:data].to_json | |||
| end | |||
| end | |||
| def get(url, params={}) | |||
| puts "[reposync][GET] request params: #{params}" | |||
| conn.get do |req| | |||
| req.url full_url(url, 'get') | |||
| params.each_pair do |key, value| | |||
| req.params["#{key}"] = value | |||
| end | |||
| end | |||
| end | |||
| def delete(url, params={}) | |||
| puts "[reposync][DELETE] request params: #{params}" | |||
| conn.delete do |req| | |||
| req.url full_url(url) | |||
| req.body = params[:data].to_json | |||
| end | |||
| end | |||
| def patch(url, params={}) | |||
| puts "[reposync][PATCH] request params: #{params}" | |||
| conn.patch do |req| | |||
| req.url full_url(url) | |||
| req.body = params[:data].to_json | |||
| end | |||
| end | |||
| def put(url, params={}) | |||
| puts "[reposync][PUT] request params: #{params}" | |||
| conn.put do |req| | |||
| req.url full_url(url) | |||
| req.body = params[:data].to_json | |||
| end | |||
| end | |||
| def conn | |||
| @client ||= begin | |||
| Faraday.new(url: domain) do |req| | |||
| req.request :url_encoded | |||
| req.headers['Content-Type'] = 'application/json' | |||
| req.adapter Faraday.default_adapter | |||
| req.options.timeout = 100 # open/read timeout in seconds | |||
| req.options.open_timeout = 10 # connection open timeout in seconds | |||
| end | |||
| end | |||
| @client | |||
| end | |||
| def domain | |||
| EduSetting.get("reposync_api_domain") || "http://106.75.110.152:50087" | |||
| end | |||
| def full_url(api_rest, action='post') | |||
| url = [domain, api_rest].join('').freeze | |||
| url = action === 'get' ? url : URI.escape(url) | |||
| url = URI.escape(url) unless url.ascii_only? | |||
| puts "[reposync] request url: #{url}" | |||
| return url | |||
| end | |||
| def log_error(status, body) | |||
| puts "[reposync] status: #{status}" | |||
| puts "[reposync] body: #{body}" | |||
| end | |||
| def render_response(response) | |||
| status = response.status | |||
| body = JSON.parse(response&.body) | |||
| log_error(status, body) | |||
| if status == 200 | |||
| if body["code_status"].to_i == 0 | |||
| return [body["code_status"], body["data"], body["msg"]] | |||
| else | |||
| puts "[reposync][ERROR] code: #{body["code_status"]}" | |||
| puts "[reposync][ERROR] message: #{body["msg"]}" | |||
| return [body["code_status"], body["data"], body["msg"]] | |||
| end | |||
| end | |||
| end | |||
| def render_list_response(response) | |||
| status = response.status | |||
| body = JSON.parse(response&.body) | |||
| log_error(status, body) | |||
| if status == 200 | |||
| if body["code_status"].to_i == 0 | |||
| return [body["code_status"], body["data"], body["total"], body["msg"]] | |||
| else | |||
| puts "[reposync][ERROR] code: #{body["code_status"]}" | |||
| puts "[reposync][ERROR] message: #{body["msg"]}" | |||
| return [body["code_status"], body["data"], body["total"], body["msg"]] | |||
| end | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,30 @@ | |||
| class Reposync::CreateSyncBranchService < Reposync::ClientService | |||
| attr_accessor :repo_name, :internal_branch_name, :external_branch_name, :enable | |||
| def initialize(repo_name, internal_branch_name, external_branch_name, enable=true) | |||
| @repo_name = repo_name | |||
| @internal_branch_name = internal_branch_name | |||
| @external_branch_name = external_branch_name | |||
| @enable = enable | |||
| end | |||
| def call | |||
| result = post(url, request_params) | |||
| response = render_response(result) | |||
| end | |||
| private | |||
| def request_params | |||
| Hash.new.merge(data: { | |||
| internal_branch_name: internal_branch_name, | |||
| external_branch_name: external_branch_name, | |||
| enable: enable | |||
| }.stringify_keys) | |||
| end | |||
| def url | |||
| "/cerobot/sync/#{repo_name}/branch".freeze | |||
| end | |||
| end | |||
| @@ -0,0 +1,38 @@ | |||
| class Reposync::CreateSyncRepoService < Reposync::ClientService | |||
| attr_accessor :repo_name, :internal_repo_address, :inter_token, :external_repo_address, :exter_token, :sync_granularity, :sync_direction, :enable | |||
| def initialize(repo_name, internal_repo_address, inter_token, external_repo_address, exter_token, sync_granularity, sync_direction, enable=true) | |||
| @repo_name = repo_name | |||
| @internal_repo_address = internal_repo_address | |||
| @inter_token = inter_token | |||
| @external_repo_address = external_repo_address | |||
| @exter_token = exter_token | |||
| @sync_granularity = sync_granularity | |||
| @sync_direction = sync_direction | |||
| @enable = enable | |||
| end | |||
| def call | |||
| result = post(url, request_params) | |||
| response = render_response(result) | |||
| end | |||
| private | |||
| def request_params | |||
| Hash.new.merge(data: { | |||
| repo_name: repo_name, | |||
| enable: enable, | |||
| internal_repo_address: internal_repo_address, | |||
| inter_token: inter_token, | |||
| external_repo_address: external_repo_address, | |||
| exter_token: exter_token, | |||
| sync_granularity: sync_granularity, | |||
| sync_direction: sync_direction | |||
| }.stringify_keys) | |||
| end | |||
| def url | |||
| "/cerobot/sync/repo".freeze | |||
| end | |||
| end | |||
| @@ -0,0 +1,19 @@ | |||
| class Reposync::DeleteBranchService < Reposync::ClientService | |||
| attr_accessor :repo_name, :branch_name | |||
| def initialize(repo_name, branch_name) | |||
| @repo_name = repo_name | |||
| @branch_name = branch_name | |||
| end | |||
| def call | |||
| result = delete(url) | |||
| response = render_response(result) | |||
| end | |||
| private | |||
| def url | |||
| "/cerobot/sync/#{repo_name}/branch/#{branch_name}" | |||
| end | |||
| end | |||
| @@ -0,0 +1,18 @@ | |||
| class Reposync::DeleteRepoService < Reposync::ClientService | |||
| attr_accessor :repo_name | |||
| def initialize(repo_name) | |||
| @repo_name = repo_name | |||
| end | |||
| def call | |||
| result = delete(url) | |||
| response = render_response(result) | |||
| end | |||
| private | |||
| def url | |||
| "/cerobot/sync/repo/#{repo_name}" | |||
| end | |||
| end | |||
| @@ -0,0 +1,33 @@ | |||
| class Reposync::GetLogsService < Reposync::ClientService | |||
| attr_accessor :repo_name, :branch_id, :page_num, :page_size | |||
| def initialize(repo_name=nil, branch_id=nil, page_num=1, page_size=10) | |||
| @repo_name = repo_name | |||
| @branch_id = branch_id | |||
| @page_num = page_num | |||
| @page_size = page_size | |||
| end | |||
| def call | |||
| result = get(url, request_params) | |||
| response = render_list_response(result) | |||
| end | |||
| private | |||
| def request_params | |||
| params = { | |||
| page_num: page_num, | |||
| page_size: page_size, | |||
| create_sort: true | |||
| } | |||
| params.merge!(repo_name: repo_name) if repo_name.present? | |||
| params.merge!(branch_id: branch_id) if branch_id.present? | |||
| return params.stringify_keys | |||
| end | |||
| def url | |||
| "/cerobot/sync/repo/logs" | |||
| end | |||
| end | |||
| @@ -0,0 +1,30 @@ | |||
| class Reposync::GetSyncBranchesService < Reposync::ClientService | |||
| attr_accessor :repo_name, :page, :limit, :create_sort | |||
| def initialize(repo_name, page=1, limit=10, create_sort=false) | |||
| @repo_name = repo_name | |||
| @page = page | |||
| @limit = limit | |||
| @create_sort = create_sort | |||
| end | |||
| def call | |||
| result = get(url, request_params) | |||
| response = render_response(result) | |||
| end | |||
| private | |||
| def request_params | |||
| { | |||
| page: page, | |||
| limit: limit, | |||
| create_sort: create_sort | |||
| }.stringify_keys | |||
| end | |||
| def url | |||
| "/cerobot/sync/#{repo_name}/branch".freeze | |||
| end | |||
| end | |||
| @@ -0,0 +1,28 @@ | |||
| class Reposync::GetSyncReposService < Reposync::ClientService | |||
| attr_accessor :page, :limit, :create_sort | |||
| def initialize(page=1, limit=10, create_sort=false) | |||
| @page = page | |||
| @limit = limit | |||
| @create_sort = create_sort | |||
| end | |||
| def call | |||
| result = get(url, request_params) | |||
| response = render_response(result) | |||
| end | |||
| private | |||
| def request_params | |||
| { | |||
| page: page, | |||
| limit: limit, | |||
| create_sort: create_sort | |||
| }.stringify_keys | |||
| end | |||
| def url | |||
| "/cerobot/sync/repo".freeze | |||
| end | |||
| end | |||
| @@ -0,0 +1,22 @@ | |||
| class Reposync::SyncBranchService < Reposync::ClientService | |||
| attr_accessor :repo_name, :branch_name, :sync_direct | |||
| def initialize(repo_name, branch_name, sync_direct) | |||
| @repo_name = repo_name | |||
| @branch_name = branch_name | |||
| @sync_direct = sync_direct | |||
| end | |||
| def call | |||
| result = post(url) | |||
| response = render_response(result) | |||
| end | |||
| private | |||
| def url | |||
| "/cerobot/sync/#{repo_name}/branch/#{branch_name}?sync_direct=#{sync_direct}" | |||
| end | |||
| end | |||
| @@ -0,0 +1,18 @@ | |||
| class Reposync::SyncRepoService < Reposync::ClientService | |||
| attr_accessor :repo_name | |||
| def initialize(repo_name) | |||
| @repo_name = repo_name | |||
| end | |||
| def call | |||
| result = post(url) | |||
| response = render_response(result) | |||
| end | |||
| private | |||
| def url | |||
| "/cerobot/sync/repo/#{repo_name}" | |||
| end | |||
| end | |||
| @@ -0,0 +1,21 @@ | |||
| class Reposync::UpdateBranchStatusService < Reposync::ClientService | |||
| attr_accessor :repo_name, :branch_name, :enable | |||
| def initialize(repo_name, branch_name, enable) | |||
| @repo_name = repo_name | |||
| @branch_name = branch_name | |||
| @enable = enable | |||
| end | |||
| def call | |||
| result = put(url) | |||
| response = render_response(result) | |||
| end | |||
| private | |||
| def url | |||
| "/cerobot/sync/#{repo_name}/branch/#{branch_name}?enable=#{enable}" | |||
| end | |||
| end | |||
| @@ -0,0 +1,31 @@ | |||
| class Reposync::UpdateRepoAddrService < Reposync::ClientService | |||
| attr_accessor :repo_name, :internal_repo_address, :inter_token, :external_repo_address, :exter_token | |||
| def initialize(repo_name, internal_repo_address, inter_token, external_repo_address, exter_token) | |||
| @repo_name = repo_name | |||
| @internal_repo_address = internal_repo_address | |||
| @inter_token = inter_token | |||
| @external_repo_address = external_repo_address | |||
| @exter_token = exter_token | |||
| end | |||
| def call | |||
| result = put(url, request_params) | |||
| response = render_response(result) | |||
| end | |||
| private | |||
| def request_params | |||
| Hash.new.merge(data: { | |||
| internal_repo_address: internal_repo_address, | |||
| inter_token: inter_token, | |||
| external_repo_address: external_repo_address, | |||
| exter_token: exter_token | |||
| }.stringify_keys) | |||
| end | |||
| def url | |||
| "/cerobot/sync/repo/#{repo_name}/repo_addr".freeze | |||
| end | |||
| end | |||
| @@ -0,0 +1,19 @@ | |||
| class Reposync::UpdateRepoStatusService < Reposync::ClientService | |||
| attr_accessor :repo_name, :enable | |||
| def initialize(repo_name, enable) | |||
| @repo_name = repo_name | |||
| @enable = enable | |||
| end | |||
| def call | |||
| result = put(url) | |||
| response = render_response(result) | |||
| end | |||
| private | |||
| def url | |||
| "/cerobot/sync/repo/#{repo_name}?enable=#{enable}".freeze | |||
| end | |||
| end | |||
| @@ -0,0 +1,12 @@ | |||
| json.total_count @group_sync_repository_branch.count | |||
| json.sync_repository_branches @group_sync_repository_branch.each do |item| | |||
| json.gitlink_branch_name item.gitlink_branch_name | |||
| json.external_branch_name item.external_branch_name | |||
| branches = @sync_repository_branches.joins(:sync_repository).where(sync_repositories: {type: item.type}, gitlink_branch_name: item.gitlink_branch_name, external_branch_name: item.external_branch_name).order(updated_at: :desc) | |||
| branch = branches.first | |||
| json.type branch&.sync_repository&.type | |||
| json.sync_time branch.sync_time.present? ? branch.sync_time.strftime("%Y-%m-%d %H:%M:%S") : nil | |||
| json.sync_status branch.sync_status | |||
| json.enable branch.enable | |||
| json.reposync_branch_ids branches.pluck(:reposync_branch_id) | |||
| end | |||
| @@ -0,0 +1,5 @@ | |||
| json.gitlink_repo_address "#{EduSetting.get("gitlink_repo_domain")}/#{@project.owner&.login}/#{@project.identifier}.git" | |||
| json.external_repo_address @sync_repository1.external_repo_address | |||
| json.sync_granularity @sync_repository1.sync_granularity | |||
| json.gitlink_branch_name @sync_repository_branch1&.gitlink_branch_name | |||
| json.external_branch_name @sync_repository_branch1&.external_branch_name | |||
| @@ -0,0 +1,12 @@ | |||
| json.total_count @total_count | |||
| json.gitlink_branch_name @branch&.gitlink_branch_name | |||
| json.external_type @branch&.sync_repository&.type | |||
| json.external_branch_name @branch&.external_branch_name | |||
| json.logs @reposync_branch_logs.each do |log| | |||
| type = log['repo_name'].start_with?('gitee') ? 'gitee' : 'github' | |||
| json.change_from log['sync_direct'] == "to_inter" ? type : 'gitlink' | |||
| json.commit_id log['commit_id'] | |||
| json.sync_time log['update_at'] | |||
| json.log log['log'] | |||
| json.sync_status log['log'].include?("************ 分支同步完成 ************") ? 'success' : "failure" | |||
| end | |||
| @@ -0,0 +1,8 @@ | |||
| json.total_count @group_sync_repository.keys.count | |||
| json.sync_repositories @group_sync_repository.each do |key| | |||
| json.type key[0][0] | |||
| json.external_repo_address key[0][1] | |||
| json.sync_granularity key[0][2] | |||
| json.external_token key[0][3] | |||
| json.sync_repository_ids @sync_repositories.where(type: key[0][0], external_repo_address: key[0][1], sync_granularity: key[0][2]).pluck(:id) | |||
| end | |||
| @@ -78,6 +78,17 @@ defaults format: :json do | |||
| # projects文件夹下的 | |||
| scope module: :projects do | |||
| resources :sync_repositories, only: [:create, :index] do | |||
| collection do | |||
| post :update_info | |||
| post :sync | |||
| get :branches | |||
| post :change_enable | |||
| post :unbind | |||
| get :history | |||
| post :create_branch | |||
| end | |||
| end | |||
| resource :dataset, only: [:create, :update, :show] | |||
| resources :actions, module: 'actions' do | |||
| collection do | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -0,0 +1,5 @@ | |||
| class AddEnableToSyncRepositoryBranch < ActiveRecord::Migration[5.2] | |||
| def change | |||
| add_column :sync_repository_branches, :enable, :boolean, default: true | |||
| end | |||
| end | |||
| @@ -0,0 +1,5 @@ | |||
| class AddExternalTokenToSyncRepository < ActiveRecord::Migration[5.2] | |||
| def change | |||
| add_column :sync_repositories, :external_token, :string | |||
| end | |||
| end | |||
| @@ -0,0 +1,5 @@ | |||
| class AddWebhookGidToSyncRepository < ActiveRecord::Migration[5.2] | |||
| def change | |||
| add_column :sync_repositories, :webhook_gid, :integer | |||
| end | |||
| end | |||