diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..07cc284 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +FROM openeuler/go:1.23.4-oe2403lts as BUILDER +RUN dnf -y install git gcc + +ARG USER +ARG PASS +RUN echo "machine github.com login $USER password $PASS" > ~/.netrc + +# build binary +WORKDIR /opt/source +COPY . . +RUN go env -w GO111MODULE=on && \ + go env -w CGO_ENABLED=1 && \ + go build -a -o robot-universal-review -buildmode=pie -ldflags "-s -linkmode 'external' -extldflags '-Wl,-z,now'" . + +# copy binary config and utils +FROM openeuler/openeuler:24.03-lts +RUN dnf -y upgrade && \ + dnf in -y shadow && \ + groupadd -g 1000 robot && \ + useradd -u 1000 -g robot -s /bin/bash -m robot + +USER robot + +COPY --chown=robot --from=BUILDER /opt/source/robot-universal-review /opt/app/robot-universal-review + +ENTRYPOINT ["/opt/app/robot-universal-review"] + diff --git a/README.md b/README.md index 7a23d1c..3e09998 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,61 @@ -# robot-universal-review -Open source community PR merge bots for different code hosting platforms +# robot-gitee-openeuler-review +[中文README](README_zh_CN.md) +### Overview + +The bot provides Code Review-related functionality for the community. Provides `lgtm`, `approved` labels, PR merge command and tracking PR source code changes to automatically remove obsolete `lgtm`, `approved` labels and automatically merge PR when PR merge conditions are met. + +### Features + +- **Command** + + The following command are provided: + + | command | example | description | who can use | + | ----------------- | ---------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | + | /lgtm [cancel] | /lgtm
/lgtm cancel | Add or remove the `lgtm` label for a Pull Request, this label will be used for Pull Request merge determination. | Collaborators of this repository.
Pull Request authors can use the `/lgtm cancel` command, but cannot use the `/lgtm` command. | + | /approve [cancel] | /approve
/approve cancel | Add or remove the `approved` label for a Pull Request, this label will be used for Pull Request merge determination. | Collaborators of this repository. | + | /check-pr | /check-pr | Check whether the current PR's tag meets the condition, if it does, it is merged into the PR. | Anyone can trigger such a command on a Pull Request. | + +- **Specify the number of lgtm labels** + + The [configuration item](#configuration) provides a setting for the number of PR `lgtm` tags. When this configuration item is greater than 1, the contents of the `lgtm` tags consist of `lgtm-user`. ps:the `user` is the login id of the user using /lgtm command in the gitee platform. + +- **Automatic cleaning of lgtm labels** + + We will remove the existing `lgtm` labels when a new commit is submitted for the PR. + +- **Merge PR** + + 1. Auto-merge: automatically detects the conditions for PR merge, and automatically merges in when the merge conditions are met. + 2. Manual check-trigger merge-in: Use the **/check-pr** command to trigger the robot to check the current merge-in condition of the PR, and give the corresponding prompt when the merge-in condition is not met, otherwise the PR is merged in. + +### Configuration + +example: + +```yaml +#no additional description of the configuration items are not required +config_items: + - repos: #list of warehouses to be managed by robot (required) + - owner/repo + - owner1 + excluded_repos: #robot manages the list of repositories to be excluded + - owner1/repo1 + lgtm_counts_required: 1 #lgtm label threshold + labels_for_merge: #labels required for PR merging + - ci-pipline-success + missing_labels_for_merge: #labels that cannot exist when PR is merged in + - ci-pipline-failed + # specify it should check the devepler's permission besed on the owners file in sig directory when the developer comment /lgtm or /approve command. + check_permission_based_on_sig_owners: true + # is the directory of Sig. It must be set when CheckPermissionBasedOnSigOwners is true. + sigs_dir: sig + # merge_method is the method to merge PR.The default method of merge. valid options are squash and merge. + merge_method: merge + unable_checking_reviewer_for_pr: true #Whether to check the reviewer +``` + + + + + diff --git a/README_zh_CN.md b/README_zh_CN.md new file mode 100644 index 0000000..4696df0 --- /dev/null +++ b/README_zh_CN.md @@ -0,0 +1,57 @@ +# robot-gitee-openeuler-review + +### 概述 + +该机器人为社区提供了Code Review 相关功能。提供了`lgtm`、`approved`标签、PR合入指令以及跟踪PR源代码改变自动清除过时的`lgtm`、`approved`标签,并在满足PR合入的条件自动合入PR。 + +### 功能 + +- **命令** + + 提供如下指令: + + | 命令 | 示例 | 描述 | 谁能使用 | + | ----------------- | ---------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | + | /lgtm [cancel] | /lgtm
/lgtm cancel | 为一个Pull Request添加或者删除`lgtm`标签,这个标签将用于Pull Request合入判断。 | 这个仓库的协作者。Pull Request作者能使用`/lgtm cancel`命令,但是不能使用`/lgtm`命令。 | + | /approve [cancel] | /approve
/approve cancel | 为一个Pull Request添加或者删除`approved`标签,这个标签将用于Pull Request合入判断。 | 这个仓库的协作者。 | + | /check-pr | /check-pr | 检测当前PR的标签是否满足条件,如果满足即合入PR。 | 任何人都能在一个Pull Request上触发这种命令。 | + +- **指定lgtm标签个数** + + [配置项](#configuration)提供了PR `lgtm`标签的个数设置,当该配置项大于1时,`lgtm`标签的内容以`lgtm-user`组成。ps: user为使用/lgtm命令的用户在码云平台的login id。 + +- **自动清理lgtm标签** + + 当PR有新的commit提交时我们将会移除已存在的`lgtm`标签。 + +- **PR合入** + + 1. 自动合入:自动检测PR合入的条件,满足合入条件即自动合入。 + 2. 手动检查触发合入:使用**/check-pr**指令可以触发机器人检查PR当前的合入条件,不满足合入条件时给与相应提示,否则PR合入。 + + +### 配置
+ +例子: + +```yaml +#无额外说明配置项为非必须项 +config_items: + - repos: #robot需管理的仓库列表(必需) + - owner/repo + - owner1 + excluded_repos: #robot 管理列表中需排除的仓库 + - owner1/repo1 + lgtm_counts_required: 1 #lgtm标签阈值 + labels_for_merge: #PR合入需要的标签 + - ci-pipline-success + missing_labels_for_merge: #PR合入时不能存在的标签 + - ci-pipline-failed + # 指定在开发者评论/lgtm 或/approve 命令时根据sig 目录下的owners 文件检查开发者的权限。 + check_permission_based_on_sig_owners: true + # Sig 的目录。当 CheckPermissionBasedOnSigOwners 为真时必须设置它。 + sigs_dir: sig + merge_method: merge #PR合入时使用的方式,可选项:merge、squash.默认merge. + unable_checking_reviewer_for_pr: true #是否检查审核人 +``` + diff --git a/actions.go b/actions.go new file mode 100644 index 0000000..d01cbe8 --- /dev/null +++ b/actions.go @@ -0,0 +1,126 @@ +package main + +import ( + "fmt" + "regexp" + "strings" + + "github.com/opensourceways/robot-framework-lib/client" + "k8s.io/apimachinery/pkg/util/sets" +) + +var regCheckPr = regexp.MustCompile(`(?mi)^/check-pr\s*$`) + +func (bot *robot) clearLabel(evt *client.GenericEvent, org, repo, number string) error { + + labels := bot.getPRLabelSet(org, repo, number) + v := getLGTMLabelsOnPR(labels) + + if labels.Has(approvedLabel) { + v = append(v, approvedLabel) + } + + if len(v) > 0 { + + if ok := bot.cli.RemovePRLabels(org, repo, number, v); !ok { + return nil + } + + var noteComment string + if bot.cli.CheckIfPRSourceCodeUpdateEvent(evt) { + noteComment = commentClearLabelCaseByReopenPR + } + + if bot.cli.CheckIfPRSourceCodeUpdateEvent(evt) { + noteComment = commentClearLabelCaseByPRUpdate + } + + bot.cli.CreatePRComment( + org, repo, number, + fmt.Sprintf(noteComment, strings.Join(v, ", ")), + ) + } + + return nil +} +func (bot *robot) checkCommenterPermission(org, repo, author, commenter string, fn func()) (pass bool) { + if author == commenter { + return true + } + pass, success := bot.cli.CheckPermission(org, repo, commenter) + bot.log.Infof("request success: %t, the %s has permission to the repo[%s/%s]: %t", success, commenter, org, repo, pass) + + if success && !pass { + fn() + } + return pass && success +} + +func (bot *robot) getPRLabelSet(org, repo, number string) sets.Set[string] { + res := sets.New[string]() + + labels, ok := bot.cli.GetPullRequestLabels(org, repo, number) + if !ok { + return res + } + + for _, v := range labels { + res.Insert(v) + } + + if res.Has("") { + res.Delete("") + } + + return res +} + +func (bot *robot) genMergeMethod(org, repo, number string) string { + mergeMethod := "merge" + + prLabels := bot.getPRLabelSet(org, repo, number) + + for p := range prLabels { + if strings.HasPrefix(p, "merge/") { + if strings.Split(p, "/")[1] == "squash" { + return "squash" + } + + return strings.Split(p, "/")[1] + } + } + + return mergeMethod +} + +func (bot *robot) handleCheckPR(configmap *repoConfig, comment, commenter, org, repo, number string) error { + if !regCheckPr.MatchString(comment) { + return nil + } + if err := bot.handleMerge(configmap, org, repo, number); err != nil { + claYesLabel := "" + for _, labelForMerge := range configmap.LabelsForMerge { + if strings.Contains(labelForMerge, "-cla/yes") { + claYesLabel = labelForMerge + break + } + } + comment := fmt.Sprintf("@%s, this pr is not mergeable and the reasons are below:\n%s\n\n***lgtm***: "+ + "A label mandatory for merging a pull request. The repository collaborators can comment '/lgtm' to "+ + "add the label. The creator of a pull request can comment '/lgtm cancel' to remove the label, but "+ + "cannot run the '/lgtm' command to add the label.\n***approved***: A label mandatory for merging a "+ + "pull request. The repository collaborators can comment '/approve' to add the label and comment "+ + "'/approve cancel' to remove the label.\n***%s***: A label mandatory for merging a pull request. "+ + "The author of each commit of a pull request must sign the Contributor License Agreement (CLA). "+ + "Otherwise, the pull request will fail to be merged. After signing the CLA, the author can comment "+ + "'/check-cla' to check the CLA status again.\n***wait_confirm***: A label for confirming pull request "+ + "merging. A pull request with this label cannot be automatically merged. This label is added because "+ + "members (including maintainers, committers, and repository administrators) are to be added to "+ + "sig-info.yaml in the pull request. To remove the label, all members to be added must comment "+ + "'/lgtm' in the pull request.", + commenter, err.Error(), claYesLabel) + bot.cli.CreatePRComment(org, repo, number, comment) + return err + } + return nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..bc3e8da --- /dev/null +++ b/go.mod @@ -0,0 +1,19 @@ +module github.com/opensourceways/robot-universal-review + +go 1.21 + +require ( + github.com/opensourceways/robot-framework-lib v0.2.1 + github.com/opensourceways/server-common-lib v1.0.0 + github.com/sirupsen/logrus v1.9.3 + k8s.io/apimachinery v0.29.4 +) + +require ( + github.com/go-resty/resty/v2 v2.11.0 // indirect + github.com/opensourceways/go-gitcode v0.2.0 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/sys v0.18.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f3717a5 --- /dev/null +++ b/go.sum @@ -0,0 +1,78 @@ +github.com/agiledragon/gomonkey/v2 v2.12.0 h1:ek0dYu9K1rSV+TgkW5LvNNPRWyDZVIxGMCFI6Pz9o38= +github.com/agiledragon/gomonkey/v2 v2.12.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8= +github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= +github.com/opensourceways/go-gitcode v0.2.0 h1:+JJTHp4fnuQj5zfL3Y5nIxixTMbB/eGe+2/o/Xdz1K8= +github.com/opensourceways/go-gitcode v0.2.0/go.mod h1:2BDl00PrpmMeVmD4NxO99DZiRcqx5jszNlGwPs1i9TQ= +github.com/opensourceways/robot-framework-lib v0.2.1 h1:2mtwMwqzzSYZb7kEEUEiMqNYIp89vW3ude+wB5Rdoo0= +github.com/opensourceways/robot-framework-lib v0.2.1/go.mod h1:LT6nNkE9Qd+3T/ILg3LbX9j/ip9YF1Jocia4czEJZ+Y= +github.com/opensourceways/server-common-lib v1.0.0 h1:uZikXrFsibI3fmSqVVWPYLBFNOM9IO8Hsux5b5neJLI= +github.com/opensourceways/server-common-lib v1.0.0/go.mod h1:AVDRCS30/uJXO7WONPa1U+AQePXr488+7qZFC7EjJzE= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/apimachinery v0.29.4 h1:RaFdJiDmuKs/8cm1M6Dh1Kvyh59YQFDcFuFTSmXes6Q= +k8s.io/apimachinery v0.29.4/go.mod h1:i3FJVwhvSp/6n8Fl4K97PJEP8C+MM+aoDq4+ZJBf70Y= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=