From 5bfc489b81e6041d09c714c02216dde0cbef6028 Mon Sep 17 00:00:00 2001 From: yjieliang Date: Fri, 15 Dec 2023 11:26:35 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=B5=81=E6=B0=B4=E7=BA=BF=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E4=BB=A3=E7=A0=81=E5=BA=93=E5=BC=80=E5=90=AF=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=88=86=E6=94=AF=E4=BF=9D=E6=8A=A4=20#9814?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/api/constant/CommonConstants.kt | 1 + .../tencent/devops/scm/code/git/api/GitApi.kt | 134 +++++++++++ .../repository/service/RepositoryService.kt | 93 +++++++- .../repository/service/scm/GitService.kt | 213 ++++++++++++++++++ .../repository/service/scm/IGitService.kt | 59 +++++ .../tencent/devops/store/dao/atom/AtomDao.kt | 16 ++ 6 files changed, 513 insertions(+), 3 deletions(-) diff --git a/src/backend/ci/core/common/common-api/src/main/kotlin/com/tencent/devops/common/api/constant/CommonConstants.kt b/src/backend/ci/core/common/common-api/src/main/kotlin/com/tencent/devops/common/api/constant/CommonConstants.kt index 1dd590018db..5ebfc706bd7 100644 --- a/src/backend/ci/core/common/common-api/src/main/kotlin/com/tencent/devops/common/api/constant/CommonConstants.kt +++ b/src/backend/ci/core/common/common-api/src/main/kotlin/com/tencent/devops/common/api/constant/CommonConstants.kt @@ -122,6 +122,7 @@ const val API_PERMISSION = "BK_CI_API_PERMISSION" // 请求API权限 const val REQUEST_IP = "X-Forwarded-For" // 请求IP const val BK_CREATE = "bkCreate" // 创建 const val BK_REVISE = "bkRevise" // 修改 +const val DEFAULT_PROTECTION_BRANCH_RULE_NAME = "master_branch_rule" const val KEY_START_TIME = "startTime" const val KEY_END_TIME = "endTime" diff --git a/src/backend/ci/core/common/common-scm/src/main/kotlin/com/tencent/devops/scm/code/git/api/GitApi.kt b/src/backend/ci/core/common/common-scm/src/main/kotlin/com/tencent/devops/scm/code/git/api/GitApi.kt index 3eade824306..358769d6ce1 100644 --- a/src/backend/ci/core/common/common-scm/src/main/kotlin/com/tencent/devops/scm/code/git/api/GitApi.kt +++ b/src/backend/ci/core/common/common-scm/src/main/kotlin/com/tencent/devops/scm/code/git/api/GitApi.kt @@ -684,4 +684,138 @@ open class GitApi { } return JsonUtil.getObjectMapper().readValue(responseBody) } + + fun listMergeRequest( + host: String, + token: String, + projectName: String, + sourceBranch: String?, + targetBranch: String?, + state: String?, + page: Int, + perPage: Int + ): List { + val queryParams = + "source_branch=$sourceBranch&target_branch=$targetBranch&state=$state&page=$page&per_page=$perPage" + val url = "projects/${urlEncode(projectName)}/merge_requests" + logger.info("list mr for project($projectName): url($url)") + val request = get(host, token, url, queryParams) + return JsonUtil.getObjectMapper().readValue>( + getBody(getMessageByLocale(CommonMessageCode.OPERATION_LIST_MR), request) + ) + } + + fun createMergeRequest( + host: String, + token: String, + projectName: String, + gitCreateMergeRequest: GitCreateMergeRequest + ): GitMrInfo { + val body = JsonUtil.getObjectMapper().writeValueAsString(gitCreateMergeRequest) + val url = "projects/${urlEncode(projectName)}/merge_requests/" + logger.info("create mr for project($projectName): url($url), $body") + val request = post(host, token, url, body) + try { + return callMethod( + operation = getMessageByLocale(CommonMessageCode.OPERATION_ADD_MR), + request = request, + classOfT = GitMrInfo::class.java + ) + } catch (t: GitApiException) { + if (t.code == 403) { + throw GitApiException(t.code, getMessageByLocale(CommonMessageCode.ADD_MR_FAIL)) + } + throw t + } + } + + /** + * 获取项目下保护分支所属规则组ID + */ + fun getProtectBranchRuleId( + host: String, + token: String, + projectName: String, + branch: String + ): Int? { + val url = "projects/${URLEncoder.encode(projectName, "UTF8")}/repository/branches/$branch/protect" + logger.info("getProtectDetail request url: $url") + val request = get(host, token, url, "") + val responseMap = callMethod(getMessageByLocale(CommonMessageCode.GET_PROTECT_BRANCH_RULE), request) + return responseMap["rule_id"] as? Int + } + + /** + * 创建项目下保护分支规则组 + */ + fun createProtectBranchRules( + host: String, + token: String, + projectName: String, + ruleMap: Map + ): Int { + if (ruleMap.isEmpty()) throw ErrorCodeException( + errorCode = CommonMessageCode.ERROR_INVALID_PARAM_, + params = arrayOf("ruleMap") + ) + val url = "projects/${URLEncoder.encode(projectName, "UTF8")}/protected_branch_rules" + val request = post(host, token, url, JsonUtil.toJson(ruleMap)) + val responseMap = callMethod(CommonMessageCode.UPDATE_PROTECT_BRANCH_RULE, request) + return responseMap["id"] as Int + } + + /** + * 将分支变为保护分支 + */ + fun setupProtectBranch( + host: String, + token: String, + projectName: String, + branch: String, + ruleId: Int + ): Boolean { + val url = "projects/${URLEncoder.encode(projectName, "UTF8")}/protected_branch_rules" + + "/$ruleId/branches?branch=$branch" + val request = post(host, token, url, "{}") + callMethod(CommonMessageCode.UPDATE_PROTECT_BRANCH_RULE, request) + return true + } + + /** + * 修改项目下保护分支规则组规则 + */ + fun updateProtectBranchRule( + host: String, + token: String, + projectName: String, + ruleId: Int, + ruleMap: Map + ): Boolean { + if (ruleMap.isEmpty()) throw ErrorCodeException( + errorCode = CommonMessageCode.ERROR_INVALID_PARAM_, + params = arrayOf("ruleMap") + ) + val url = "projects/${URLEncoder.encode(projectName, "UTF8")}/protected_branch_rules/$ruleId" + val request = put(host, token, url, JsonUtil.toJson(ruleMap)) + callMethod(CommonMessageCode.UPDATE_PROTECT_BRANCH_RULE, request) + return true + } + + + /** + * 修改项目组用户权限等级 + */ + fun updateProjectUserAccessLevel( + userId: Int, + host: String, + token: String, + projectName: String, + accessLevel: Int + ): Boolean { + val url = "projects/${URLEncoder.encode(projectName, "UTF8")}/members/$userId" + val handle = mapOf("access_level" to accessLevel) + val request = put(host, token, url, JsonUtil.toJson(handle)) + callMethod(CommonMessageCode.UPDATE_PROJECT_USER_ACCESS_LEVEL, request) + return true + } } diff --git a/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/RepositoryService.kt b/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/RepositoryService.kt index a08438d9342..ce89218b3a0 100644 --- a/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/RepositoryService.kt +++ b/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/RepositoryService.kt @@ -28,12 +28,15 @@ package com.tencent.devops.repository.service import com.tencent.devops.common.api.constant.CommonMessageCode +import com.tencent.devops.common.api.constant.DEFAULT_PROTECTION_BRANCH_RULE_NAME +import com.tencent.devops.common.api.constant.MASTER import com.tencent.devops.common.api.constant.coerceAtMaxLength import com.tencent.devops.common.api.enums.FrontendTypeEnum import com.tencent.devops.common.api.enums.RepositoryConfig import com.tencent.devops.common.api.enums.RepositoryType import com.tencent.devops.common.api.enums.ScmType import com.tencent.devops.common.api.exception.ErrorCodeException +import com.tencent.devops.common.api.exception.OauthForbiddenException import com.tencent.devops.common.api.exception.OperationException import com.tencent.devops.common.api.exception.ParamBlankException import com.tencent.devops.common.api.exception.PermissionForbiddenException @@ -43,12 +46,15 @@ import com.tencent.devops.common.api.util.DHUtil import com.tencent.devops.common.api.util.DateTimeUtil import com.tencent.devops.common.api.util.HashUtil import com.tencent.devops.common.api.util.MessageUtil +import com.tencent.devops.common.api.util.PageUtil import com.tencent.devops.common.api.util.timestamp import com.tencent.devops.common.api.util.timestampmilli import com.tencent.devops.common.auth.api.AuthPermission +import com.tencent.devops.common.pipeline.utils.RepositoryConfigUtils import com.tencent.devops.common.web.utils.I18nUtil import com.tencent.devops.model.repository.tables.records.TRepositoryRecord import com.tencent.devops.repository.constant.RepositoryMessageCode +import com.tencent.devops.repository.constant.RepositoryMessageCode.NOT_AUTHORIZED_BY_OAUTH import com.tencent.devops.repository.constant.RepositoryMessageCode.USER_CREATE_PEM_ERROR import com.tencent.devops.repository.dao.RepositoryCodeGitDao import com.tencent.devops.repository.dao.RepositoryDao @@ -72,15 +78,15 @@ import com.tencent.devops.scm.pojo.GitCommit import com.tencent.devops.scm.pojo.GitProjectInfo import com.tencent.devops.scm.pojo.GitRepositoryDirItem import com.tencent.devops.scm.pojo.GitRepositoryResp +import java.time.LocalDateTime +import java.util.Base64 +import javax.ws.rs.NotFoundException import org.jooq.DSLContext import org.jooq.impl.DSL import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Service -import java.time.LocalDateTime -import java.util.Base64 -import javax.ws.rs.NotFoundException @Service @Suppress("ALL") @@ -1142,6 +1148,87 @@ class RepositoryService @Autowired constructor( ) } + fun setProjectProtectionBranchDefaultRules( + userId: String, + repositoryHashId:String, + ruleMap: Map + ): Result { + try { + val accessToken = gitOauthService.getAccessToken(userId) ?: throw OauthForbiddenException( + message = MessageUtil.getMessageByLocale(NOT_AUTHORIZED_BY_OAUTH, I18nUtil.getLanguage(userId)) + ) + val repoId = serviceGet( + "", + RepositoryConfigUtils.buildConfig(repositoryHashId, RepositoryType.ID) + ).projectName + var ruleId = gitService.getProtectBranchRuleId( + token = accessToken.accessToken, + gitProjectId = repoId, + tokenType = TokenTypeEnum.OAUTH, + branch = MASTER + ).data + // 设置保护分支规则组默认规则 + if (ruleId == null) { + val newRuleMap = mutableMapOf() + newRuleMap.putAll(ruleMap) + newRuleMap["name"] = DEFAULT_PROTECTION_BRANCH_RULE_NAME + // 设置保护分支 + ruleId = gitService.createProtectBranchRules( + token = accessToken.accessToken, + gitProjectId = repoId, + tokenType = TokenTypeEnum.OAUTH, + ruleMap = newRuleMap + ).data!! + gitService.setupProtectBranch( + token = accessToken.accessToken, + gitProjectId = repoId, + tokenType = TokenTypeEnum.OAUTH, + ruleId = ruleId, + branch = MASTER + ) + } else { + gitService.updateProtectBranchRule( + token = accessToken.accessToken, + gitProjectId = repoId, + tokenType = TokenTypeEnum.OAUTH, + ruleId = ruleId, + ruleMap = ruleMap + ) + } + // 获取项目有权限的成员列表 + var page = PageUtil.DEFAULT_PAGE + do { + val repoAllMembers = gitService.getMembers( + token = accessToken.accessToken, + gitProjectId = repoId, + page = page, + pageSize = PageUtil.DEFAULT_PAGE_SIZE, + tokenType = TokenTypeEnum.OAUTH, + search = null + ).data + if (!repoAllMembers.isNullOrEmpty()) { + // 设置项目有MASTER权限的成员默认权限级别为DEVELOP + repoAllMembers.forEach { + if (it.accessLevel == GitAccessLevelEnum.MASTER.level) { + gitService.updateProjectUserAccessLevel( + userId = it.id, + gitProjectId = repoId, + tokenType = TokenTypeEnum.OAUTH, + token = accessToken.accessToken, + accessLevel = GitAccessLevelEnum.DEVELOPER.level + ) + } + } + } + page++ + } while (repoAllMembers?.size == PageUtil.DEFAULT_PAGE_SIZE) + + } catch (ignore: Throwable) { + logger.error("update atom[$repositoryHashId] project protection branch default rules error", ignore) + } + return Result(true) + } + companion object { private val logger = LoggerFactory.getLogger(RepositoryService::class.java) const val MAX_ALIAS_LENGTH = 255 diff --git a/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/scm/GitService.kt b/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/scm/GitService.kt index f8687325968..bdfb538628d 100644 --- a/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/scm/GitService.kt +++ b/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/scm/GitService.kt @@ -1967,4 +1967,217 @@ class GitService @Autowired constructor( ) ) } + + override fun listMergeRequest( + token: String, + tokenType: TokenTypeEnum, + gitProjectId: String, + gitListMergeRequest: GitListMergeRequest + ): Result> { + val mrInfoList = with(gitListMergeRequest) { + if (tokenType == TokenTypeEnum.OAUTH) { + GitOauthApi().listMergeRequest( + host = gitConfig.gitApiUrl, + token = token, + projectName = gitProjectId, + sourceBranch = sourceBranch, + targetBranch = targetBranch, + state = state, + page = page, + perPage = perPage + ) + } else { + GitApi().listMergeRequest( + host = gitConfig.gitApiUrl, + token = token, + projectName = gitProjectId, + sourceBranch = sourceBranch, + targetBranch = targetBranch, + state = state, + page = page, + perPage = perPage + ) + } + } + return Result(mrInfoList) + } + + override fun createBranch( + token: String, + tokenType: TokenTypeEnum, + gitProjectId: String, + gitCreateBranch: GitCreateBranch + ): Result { + if (tokenType == TokenTypeEnum.OAUTH) { + GitOauthApi().createBranch( + host = gitConfig.gitApiUrl, + token = token, + projectName = gitProjectId, + branch = gitCreateBranch.branchName, + ref = gitCreateBranch.ref + ) + } else { + GitApi().createBranch( + host = gitConfig.gitApiUrl, + token = token, + projectName = gitProjectId, + branch = gitCreateBranch.branchName, + ref = gitCreateBranch.ref + ) + } + return Result(true) + } + + override fun createMergeRequest( + token: String, + tokenType: TokenTypeEnum, + gitProjectId: String, + gitCreateMergeRequest: GitCreateMergeRequest + ): Result { + val mrInfo = if (tokenType == TokenTypeEnum.OAUTH) { + GitOauthApi().createMergeRequest( + host = gitConfig.gitApiUrl, + token = token, + projectName = gitProjectId, + gitCreateMergeRequest = gitCreateMergeRequest + ) + } else { + GitApi().createMergeRequest( + host = gitConfig.gitApiUrl, + token = token, + projectName = gitProjectId, + gitCreateMergeRequest = gitCreateMergeRequest + ) + } + return Result(mrInfo) + } + + override fun getProtectBranchRuleId( + token: String, + tokenType: TokenTypeEnum, + gitProjectId: String, + branch: String + ): Result { + val result = if (tokenType == TokenTypeEnum.OAUTH) { + GitOauthApi().getProtectBranchRuleId( + host = gitConfig.gitApiUrl, + token = token, + projectName = gitProjectId, + branch = branch + ) + } else { + GitApi().getProtectBranchRuleId( + host = gitConfig.gitApiUrl, + token = token, + projectName = gitProjectId, + branch = branch + ) + } + return Result(result) + } + + override fun createProtectBranchRules( + token: String, + tokenType: TokenTypeEnum, + gitProjectId: String, + ruleMap: Map + ): Result { + val result = if (tokenType == TokenTypeEnum.OAUTH) { + GitOauthApi().createProtectBranchRules( + host = gitConfig.gitApiUrl, + token = token, + projectName = gitProjectId, + ruleMap = ruleMap + ) + } else { + GitApi().createProtectBranchRules( + host = gitConfig.gitApiUrl, + token = token, + projectName = gitProjectId, + ruleMap = ruleMap + ) + } + return Result(result) + } + + override fun setupProtectBranch( + token: String, + tokenType: TokenTypeEnum, + gitProjectId: String, + branch: String, + ruleId: Int + ): Result { + val result = if (tokenType == TokenTypeEnum.OAUTH) { + GitOauthApi().setupProtectBranch( + host = gitConfig.gitApiUrl, + token = token, + projectName = gitProjectId, + branch = branch, + ruleId = ruleId + ) + } else { + GitApi().setupProtectBranch( + host = gitConfig.gitApiUrl, + token = token, + projectName = gitProjectId, + branch = branch, + ruleId = ruleId + ) + } + return Result(result) + } + + override fun updateProtectBranchRule( + token: String, + tokenType: TokenTypeEnum, + gitProjectId: String, + ruleId: Int, + ruleMap: Map + ): Result { + val result = if (tokenType == TokenTypeEnum.OAUTH) { + GitOauthApi().updateProtectBranchRule( + host = gitConfig.gitApiUrl, + token = token, + projectName = gitProjectId, + ruleId = ruleId, + ruleMap = ruleMap + ) + } else { + GitApi().updateProtectBranchRule( + host = gitConfig.gitApiUrl, + token = token, + projectName = gitProjectId, + ruleId = ruleId, + ruleMap = ruleMap + ) + } + return Result(result) + } + + override fun updateProjectUserAccessLevel( + userId: Int, + token: String, + tokenType: TokenTypeEnum, + gitProjectId: String, + accessLevel: Int + ): Result { + val result = if (tokenType == TokenTypeEnum.OAUTH) { + GitOauthApi().updateProjectUserAccessLevel( + host = gitConfig.gitApiUrl, + token = token, + userId = userId, + projectName = gitProjectId, + accessLevel = accessLevel + ) + } else { + GitApi().updateProjectUserAccessLevel( + host = gitConfig.gitApiUrl, + token = token, + userId = userId, + projectName = gitProjectId, + accessLevel = accessLevel + ) + } + return Result(result) + } } diff --git a/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/scm/IGitService.kt b/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/scm/IGitService.kt index 3cd040b1b8a..d406b0e31d7 100644 --- a/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/scm/IGitService.kt +++ b/src/backend/ci/core/repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/scm/IGitService.kt @@ -370,4 +370,63 @@ interface IGitService { path: String?, ignoreWhiteSpace: Boolean? ): Result> + + fun createBranch( + token: String, + tokenType: TokenTypeEnum, + gitProjectId: String, + gitCreateBranch: GitCreateBranch + ): Result + + fun listMergeRequest( + token: String, + tokenType: TokenTypeEnum, + gitProjectId: String, + gitListMergeRequest: GitListMergeRequest + ): Result> + + fun createMergeRequest( + token: String, + tokenType: TokenTypeEnum, + gitProjectId: String, + gitCreateMergeRequest: GitCreateMergeRequest + ): Result + + fun getProtectBranchRuleId( + token: String, + tokenType: TokenTypeEnum, + gitProjectId: String, + branch: String + ): Result + + fun createProtectBranchRules( + token: String, + tokenType: TokenTypeEnum, + gitProjectId: String, + ruleMap: Map + ): Result + + fun setupProtectBranch( + token: String, + tokenType: TokenTypeEnum, + gitProjectId: String, + branch: String, + ruleId: Int + ): Result + + fun updateProtectBranchRule( + token: String, + tokenType: TokenTypeEnum, + gitProjectId: String, + ruleId: Int, + ruleMap: Map + ): Result + + fun updateProjectUserAccessLevel( + userId: Int, + token: String, + tokenType: TokenTypeEnum, + gitProjectId: String, + accessLevel: Int + ): Result } diff --git a/src/backend/ci/core/store/biz-store/src/main/kotlin/com/tencent/devops/store/dao/atom/AtomDao.kt b/src/backend/ci/core/store/biz-store/src/main/kotlin/com/tencent/devops/store/dao/atom/AtomDao.kt index 7b70f8d0e80..1fd33612e9c 100644 --- a/src/backend/ci/core/store/biz-store/src/main/kotlin/com/tencent/devops/store/dao/atom/AtomDao.kt +++ b/src/backend/ci/core/store/biz-store/src/main/kotlin/com/tencent/devops/store/dao/atom/AtomDao.kt @@ -1367,4 +1367,20 @@ class AtomDao : AtomBaseDao() { .orderBy(tAtom.CREATE_TIME.desc()).limit(1).fetchOne(0, String::class.java) } } + + fun listAtomRepoId( + dslContext: DSLContext, + offset: Int, + limit: Int + ): Result> { + with(TAtom.T_ATOM) { + return dslContext.select(ATOM_CODE, REPOSITORY_HASH_ID, DSL.min(CREATE_TIME)) + .from(this) + .where(DELETE_FLAG.eq(false).and(REPOSITORY_HASH_ID.isNotNull)) + .groupBy(ATOM_CODE) + .orderBy(CREATE_TIME.asc(), ID.asc()) + .limit(limit).offset(offset) + .fetch() + } + } }