From 42de922959855c2d6b7e2e7fbc1574b56a0f8b77 Mon Sep 17 00:00:00 2001 From: Rafaela Soares Date: Thu, 27 Feb 2025 17:15:28 +0000 Subject: [PATCH 01/11] add pairing script --- .github/workflows/test.yml | 51 +++++++ scripts/ci/pairing/.golangci.yml | 35 +++++ scripts/ci/pairing/Makefile | 5 + scripts/ci/pairing/README.adoc | 22 +++ scripts/ci/pairing/cmd/main.go | 19 +++ scripts/ci/pairing/go.mod | 44 ++++++ scripts/ci/pairing/go.sum | 124 +++++++++++++++ scripts/ci/pairing/make/build.mk | 16 ++ scripts/ci/pairing/make/pairing.mk | 8 + scripts/ci/pairing/make/test.mk | 5 + scripts/ci/pairing/make/verbose.mk | 24 +++ scripts/ci/pairing/pkg/cmd/flags/flags.go | 11 ++ scripts/ci/pairing/pkg/cmd/pair.go | 176 ++++++++++++++++++++++ scripts/ci/pairing/pkg/cmd/pair_test.go | 82 ++++++++++ scripts/ci/pairing/pkg/cmd/root.go | 32 ++++ scripts/ci/pairing/pkg/cmd/types.go | 25 +++ 16 files changed, 679 insertions(+) create mode 100644 .github/workflows/test.yml create mode 100644 scripts/ci/pairing/.golangci.yml create mode 100644 scripts/ci/pairing/Makefile create mode 100644 scripts/ci/pairing/README.adoc create mode 100644 scripts/ci/pairing/cmd/main.go create mode 100644 scripts/ci/pairing/go.mod create mode 100644 scripts/ci/pairing/go.sum create mode 100644 scripts/ci/pairing/make/build.mk create mode 100644 scripts/ci/pairing/make/pairing.mk create mode 100644 scripts/ci/pairing/make/test.mk create mode 100644 scripts/ci/pairing/make/verbose.mk create mode 100644 scripts/ci/pairing/pkg/cmd/flags/flags.go create mode 100644 scripts/ci/pairing/pkg/cmd/pair.go create mode 100644 scripts/ci/pairing/pkg/cmd/pair_test.go create mode 100644 scripts/ci/pairing/pkg/cmd/root.go create mode 100644 scripts/ci/pairing/pkg/cmd/types.go diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..f773e56 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,51 @@ +name: Build & Test +on: + push: + branches: + - master + tags-ignore: + - '*.*' + pull_request: + branches: + - master + +jobs: + test: + runs-on: ${{ matrix.os }} + defaults: + run: + working-directory: ./scripts/ci/pairing + strategy: + matrix: + os: [ ubuntu-latest ] + name: Test + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version-file: ./scripts/ci/pairing/go.mod + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles ('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Lint + uses: golangci/golangci-lint-action@v6 + with: + version: v1.63.1 + skip-pkg-cache: true + skip-build-cache: true + working-directory: ./scripts/ci/pairing + args: --config=.golangci.yml --verbose + + - name: Test + run: | + make test \ No newline at end of file diff --git a/scripts/ci/pairing/.golangci.yml b/scripts/ci/pairing/.golangci.yml new file mode 100644 index 0000000..cb39ae5 --- /dev/null +++ b/scripts/ci/pairing/.golangci.yml @@ -0,0 +1,35 @@ +run: + # timeout for analysis, e.g. 30s, 5m, default is 1m + timeout: 10m + +linters: + enable: + - megacheck + - gocyclo + - gofmt + - revive + - misspell + presets: # groups of linters. See https://golangci-lint.run/usage/linters/ + - bugs + - unused + disable: + - golint # deprecated, use 'revive' + - scopelint # deprecated, use 'exportloopref' + - contextcheck # too many false-positives + - noctx # not needed + +# all available settings of specific linters +linters-settings: + unparam: + # Inspect exported functions, default is false. Set to true if no external program/library imports your code. + # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: + # if it's called for subdir of a project it can't find external interfaces. All text editor integrations + # with golangci-lint call it on a directory with the changed file. + check-exported: true + testifylint: + disable: + - suite-dont-use-pkg + revive: + rules: + - name: dot-imports + disabled: true # we allow for dot-import \ No newline at end of file diff --git a/scripts/ci/pairing/Makefile b/scripts/ci/pairing/Makefile new file mode 100644 index 0000000..4d53244 --- /dev/null +++ b/scripts/ci/pairing/Makefile @@ -0,0 +1,5 @@ +# It's necessary to set this because some environments don't link sh -> bash. +SHELL := /bin/bash + +include ./make/*.mk +.DEFAULT_GOAL := help \ No newline at end of file diff --git a/scripts/ci/pairing/README.adoc b/scripts/ci/pairing/README.adoc new file mode 100644 index 0000000..4d26804 --- /dev/null +++ b/scripts/ci/pairing/README.adoc @@ -0,0 +1,22 @@ += Pairing Mechanism + +This pairing mechanism, implemented in Go, simplifies the process by offering a more maintainable approach. +It reduces complexity and eliminates redundancy, providing a more efficient and reusable solution for pairing. + + +== Build + +Requires Go version 1.22.x (1.22.12 or higher) - download for your development environment https://golang.org/dl/[here]. + +To build the binary, clone the latest version of toolchain-cicd repository: + +``` +git clone https://github.com/codeready-toolchain/toolchain-cicd.git +cd scripts/ci/pairing +``` + +and build the binary + +``` +make build-pairing PAIRING_DIR= +``` \ No newline at end of file diff --git a/scripts/ci/pairing/cmd/main.go b/scripts/ci/pairing/cmd/main.go new file mode 100644 index 0000000..905754a --- /dev/null +++ b/scripts/ci/pairing/cmd/main.go @@ -0,0 +1,19 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import "github.com/codeready-toolchain/pairing/pkg/cmd" + +func main() { + cmd.Execute() +} diff --git a/scripts/ci/pairing/go.mod b/scripts/ci/pairing/go.mod new file mode 100644 index 0000000..ea59519 --- /dev/null +++ b/scripts/ci/pairing/go.mod @@ -0,0 +1,44 @@ +module github.com/codeready-toolchain/pairing + +go 1.22.0 + +toolchain go1.22.12 + +require ( + github.com/go-git/go-git/v5 v5.13.2 + github.com/google/go-github/v44 v44.1.0 + github.com/spf13/cobra v1.9.1 + github.com/stretchr/testify v1.10.0 +) + +require ( + dario.cat/mergo v1.0.0 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/ProtonMail/go-crypto v1.1.5 // indirect + github.com/cloudflare/circl v1.3.7 // indirect + github.com/cyphar/filepath-securejoin v0.3.6 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-git/go-billy/v5 v5.6.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/google/go-querystring v1.1.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/pjbgf/sha1cd v0.3.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect + github.com/skeema/knownhosts v1.3.0 // indirect + github.com/spf13/pflag v1.0.6 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect + golang.org/x/crypto v0.32.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.34.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/scripts/ci/pairing/go.sum b/scripts/ci/pairing/go.sum new file mode 100644 index 0000000..b41fb06 --- /dev/null +++ b/scripts/ci/pairing/go.sum @@ -0,0 +1,124 @@ +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4= +github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= +github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM= +github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +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/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM= +github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= +github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= +github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0= +github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-github/v44 v44.1.0 h1:shWPaufgdhr+Ad4eo/pZv9ORTxFpsxPEPEuuXAKIQGA= +github.com/google/go-github/v44 v44.1.0/go.mod h1:iWn00mWcP6PRWHhXm0zuFJ8wbEjE5AGO5D5HXYM4zgw= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= +github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= +github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.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.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/scripts/ci/pairing/make/build.mk b/scripts/ci/pairing/make/build.mk new file mode 100644 index 0000000..9695d86 --- /dev/null +++ b/scripts/ci/pairing/make/build.mk @@ -0,0 +1,16 @@ +# By default the project should be build under GOPATH/src/github.com// +GO_PACKAGE_ORG_NAME ?= $(shell basename $$(dirname $$PWD)) +GO_PACKAGE_REPO_NAME ?= $(shell basename $$PWD) +GO_PACKAGE_PATH ?= github.com/${GO_PACKAGE_ORG_NAME}/${GO_PACKAGE_REPO_NAME} + +GO111MODULE?=on +export GO111MODULE +goarch?=$(shell go env GOARCH) + +.PHONY: build +## Build e2e test files +build: + @go version + mkdir -p $(OUT_DIR)/bin || true + $(Q)CGO_ENABLED=0 GOARCH=${goarch} GOOS=linux \ + go build ${V_FLAG} ./... \ No newline at end of file diff --git a/scripts/ci/pairing/make/pairing.mk b/scripts/ci/pairing/make/pairing.mk new file mode 100644 index 0000000..14f1464 --- /dev/null +++ b/scripts/ci/pairing/make/pairing.mk @@ -0,0 +1,8 @@ +.PHONY: build-pairing +build-pairing: + # Clean the directory if it exists + if [ -d "${PAIRING_DIR}" ]; then rm -rf ${PAIRING_DIR}; fi + # Create the directory + mkdir -p ${PAIRING_DIR} + # Build the Go binary into the specified directory + go build -o ${PAIRING_DIR}/pairing ./cmd \ No newline at end of file diff --git a/scripts/ci/pairing/make/test.mk b/scripts/ci/pairing/make/test.mk new file mode 100644 index 0000000..14ffe10 --- /dev/null +++ b/scripts/ci/pairing/make/test.mk @@ -0,0 +1,5 @@ +.PHONY: test +## runs the tests (use `test-in-container` on macOS) +test: build + @echo "running the pairing unit tests..." + go test -v -failfast ./... \ No newline at end of file diff --git a/scripts/ci/pairing/make/verbose.mk b/scripts/ci/pairing/make/verbose.mk new file mode 100644 index 0000000..f156737 --- /dev/null +++ b/scripts/ci/pairing/make/verbose.mk @@ -0,0 +1,24 @@ +# When you run make VERBOSE=1 (the default), executed commands will be printed +# before executed. If you run make VERBOSE=2 verbose flags are turned on and +# quiet flags are turned off for various commands. Use V_FLAG in places where +# you can toggle on/off verbosity using -v. Use Q_FLAG in places where you can +# toggle on/off quiet mode using -q. Use S_FLAG where you want to toggle on/off +# silence mode using -s... +VERBOSE ?= 1 +Q = @ +Q_FLAG = -q +QUIET_FLAG = --quiet +V_FLAG = +S_FLAG = -s +X_FLAG = +ifeq ($(VERBOSE),1) + Q = +endif +ifeq ($(VERBOSE),2) + Q = + Q_FLAG = + QUIET_FLAG = + S_FLAG = + V_FLAG = -v + X_FLAG = -x +endif \ No newline at end of file diff --git a/scripts/ci/pairing/pkg/cmd/flags/flags.go b/scripts/ci/pairing/pkg/cmd/flags/flags.go new file mode 100644 index 0000000..bb4501a --- /dev/null +++ b/scripts/ci/pairing/pkg/cmd/flags/flags.go @@ -0,0 +1,11 @@ +package flags + +import ( + "github.com/spf13/cobra" +) + +func MustMarkRequired(cmd *cobra.Command, name string) { + if err := cmd.MarkFlagRequired(name); err != nil { + panic(err) + } +} diff --git a/scripts/ci/pairing/pkg/cmd/pair.go b/scripts/ci/pairing/pkg/cmd/pair.go new file mode 100644 index 0000000..96a235d --- /dev/null +++ b/scripts/ci/pairing/pkg/cmd/pair.go @@ -0,0 +1,176 @@ +package cmd + +import ( + "context" + "encoding/json" + "fmt" + "log" + "os" + "strconv" + + "github.com/codeready-toolchain/pairing/pkg/cmd/flags" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" + "github.com/google/go-github/v44/github" + "github.com/spf13/cobra" +) + +type PairingServiceInterface interface { + shouldPair(orgForPairing, repoForPairing, currentRemoteName, currentBranchName string) (bool, error) +} + +type PairingService struct{} + +func (s *PairingService) shouldPair(orgForPairing, repoForPairing, currentRemoteName, currentBranchName string) (bool, error) { + return shouldPair(orgForPairing, repoForPairing, currentRemoteName, currentBranchName) +} + +func NewPairCmd() *cobra.Command { + var cloneDir, organization, repository string + + command := &cobra.Command{ + Use: "pair --clone-dir= --organization= --repository=", + Short: "Pair PRs in CI between the given organization and repository into the given clone directory", + Long: `Automatically tries to pair a PR opened on a specific repository with a branch of the same name that potentially could exist in the given organization and repository.`, + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + return Pair(cloneDir, organization, repository, &PairingService{}) + }, + } + + command.Flags().StringVarP(&cloneDir, "clone-dir", "c", "", "Directory to clone into") + flags.MustMarkRequired(command, "clone-dir") + + command.Flags().StringVarP(&organization, "organization", "o", "", "Organization to pair") + flags.MustMarkRequired(command, "organization") + + command.Flags().StringVarP(&repository, "repository", "r", "", "Repository to pair") + flags.MustMarkRequired(command, "repository") + + return command +} + +// listOpenPRs lists open pull requests for the given repository +func listOpenPRs(owner, repo string) ([]*github.PullRequest, error) { + client := github.NewClient(nil) // no authentication needed + + // list the open pull requests + opt := &github.PullRequestListOptions{ + State: "open", + } + + prs, _, err := client.PullRequests.List(context.Background(), owner, repo, opt) + if err != nil { + return nil, fmt.Errorf("could not list pull requests: %w", err) + } + + return prs, nil +} + +// shouldPair determines whether the given remote and branch name should be paired +// based on existing open pull requests in the given organization and repository. +func shouldPair(orgForPairing, repoForPairing, currentRemoteName, currentBranchName string) (bool, error) { + pullRequests, err := listOpenPRs(orgForPairing, repoForPairing) + if err != nil { + return false, err + } + + for _, pull := range pullRequests { + if pull.GetHead().GetRef() == currentBranchName && pull.GetUser().GetLogin() == currentRemoteName { + return true, nil + } + } + + return false, nil +} + +// getCurrentPRInfo gets the current info of the PR that triggered the pairing +// the pairing can be triggered by CI job or GH action +func getCurrentPRInfo() (*PullRequestMetadata, error) { + pr := &PullRequestMetadata{} + jobSpecEnvVarData := os.Getenv("JOB_SPEC") + + // running CI job + if jobSpecEnvVarData != "" { + log.Println("running in CI job") + jobSpec := &JobSpec{} + if err := json.Unmarshal([]byte(jobSpecEnvVarData), jobSpec); err != nil { + return pr, fmt.Errorf("error when parsing openshift job spec data: %w", err) + } + if len(jobSpec.Refs.Pulls) == 1 { + pull := jobSpec.Refs.Pulls[0] + pr = &PullRequestMetadata{ + RemoteName: pull.Author, + Number: strconv.Itoa(pull.Number), + BranchName: pull.HeadRef, + } + } else { + fmt.Println("No pull request data found.") + } + // running GH action + } else if os.Getenv("GITHUB_ACTIONS") != "" { + log.Println("running in GH action") + pr = &PullRequestMetadata{ + RemoteName: os.Getenv("AUTHOR"), + BranchName: os.Getenv("GITHUB_HEAD_REF"), + } + } + + return pr, nil +} + +func clone(cloneDir, org, repo, prRemoteName, prBranchName string, p PairingServiceInterface) error { + branch := "master" + + cloneDirInfo, err := os.Stat(cloneDir) + if !os.IsNotExist(err) && cloneDirInfo.IsDir() { + log.Printf("folder %s already exists... removing", cloneDir) + + err := os.RemoveAll(cloneDir) + if err != nil { + return fmt.Errorf("error removing %s folder: %w", cloneDir, err) + } + } + + // if CI + if prRemoteName != "" && prBranchName != "" { + shouldPair, err := p.shouldPair(org, repo, prRemoteName, prBranchName) + if err != nil { + return err + } + // check if pairing is required + if shouldPair { + org = prRemoteName + branch = prBranchName + } + } + + url := fmt.Sprintf("https://github.com/%s/%s", org, repo) + refName := fmt.Sprintf("refs/heads/%s", branch) + + log.Printf("cloning '%s' with git ref '%s'", url, refName) + + _, err = git.PlainClone(cloneDir, false, &git.CloneOptions{ + URL: url, + ReferenceName: plumbing.ReferenceName(refName), + Progress: os.Stdout, + }) + if err != nil { + return fmt.Errorf("failed to clone repository: %w", err) + } + + return nil +} + +func Pair(cloneDir, org, repo string, p PairingServiceInterface) error { + prBranchName, prRemoteName := "", "" + if os.Getenv("CI") == "true" { + pr, err := getCurrentPRInfo() + if err != nil { + return err + } + prBranchName, prRemoteName = pr.BranchName, pr.RemoteName + } + + return clone(cloneDir, org, repo, prRemoteName, prBranchName, p) +} diff --git a/scripts/ci/pairing/pkg/cmd/pair_test.go b/scripts/ci/pairing/pkg/cmd/pair_test.go new file mode 100644 index 0000000..7b74d6d --- /dev/null +++ b/scripts/ci/pairing/pkg/cmd/pair_test.go @@ -0,0 +1,82 @@ +package cmd + +import ( + "fmt" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +// Mock the shouldPair function +type MockPairingService struct { + mock.Mock +} + +func (m *MockPairingService) shouldPair(orgForPairing, repoForPairing, currentRemoteName, currentBranchName string) (bool, error) { + return true, nil +} + +func TestPair(t *testing.T) { + t.Run("error during clone: repository not found", func(t *testing.T) { + expectedError := fmt.Errorf("failed to clone repository: authentication required: Repository not found.") + pair(t, "/tmp/repository-not-found", "codeready-toolchain", "host-operato", expectedError, &PairingService{}) + }) + + t.Run("not running in ci", func(t *testing.T) { + pair(t, "/tmp/not-running-in-ci", "codeready-toolchain", "host-operator", nil, &PairingService{}) + }) + + t.Run("running in ci - gh action", func(t *testing.T) { + t.Setenv("CI", "true") + t.Setenv("GITHUB_ACTIONS", "true") + t.Setenv("AUTHOR", "rsoaresd") + t.Setenv("GITHUB_HEAD_REF", "clean_only_when_test_passed") + + pair(t, "/tmp/running-in-gh-action", "kubesaw", "ksctl", nil, &PairingService{}) + }) + + t.Run("running in ci - prow job", func(t *testing.T) { + t.Setenv("CI", "true") + t.Setenv("JOB_SPEC", `{"type":"presubmit","job":"pull-ci-codeready-toolchain-toolchain-e2e-master-e2e","buildid":"1889023022812106752","prowjobid":"9ccb229f-aebf-45d6-90e2-2388663e8b9a","refs":{"org":"codeready-toolchain","repo":"toolchain-e2e","repo_link":"https://github.com/codeready-toolchain/toolchain-e2e","base_ref":"master","base_sha":"47ac08434063871caf78c8f3d6dbab6df61ecb63","base_link":"https://github.com/codeready-toolchain/toolchain-e2e/commit/47ac08434063871caf78c8f3d6dbab6df61ecb63","pulls":[{"number":1113,"author":"rsoaresd","sha":"67ece9d9716bcc8556f91c0c909ecea4b7c17bff","head_ref":"test-pairing","link":"https://github.com/codeready-toolchain/toolchain-e2e/pull/1113","commit_link":"https://github.com/codeready-toolchain/toolchain-e2e/pull/1113/commits/67ece9d9716bcc8556f91c0c909ecea4b7c17bff","author_link":"https://github.com/rsoaresd"}]},"decoration_config":{"timeout":"2h0m0s","grace_period":"15s","utility_images":{"clonerefs":"us-docker.pkg.dev/k8s-infra-prow/images/clonerefs:v20250205-e871edfd1","initupload":"us-docker.pkg.dev/k8s-infra-prow/images/initupload:v20250205-e871edfd1","entrypoint":"us-docker.pkg.dev/k8s-infra-prow/images/entrypoint:v20250205-e871edfd1","sidecar":"us-docker.pkg.dev/k8s-infra-prow/images/sidecar:v20250205-e871edfd1"},"resources":{"clonerefs":{"limits":{"memory":"3Gi"},"requests":{"cpu":"100m","memory":"500Mi"}},"initupload":{"limits":{"memory":"200Mi"},"requests":{"cpu":"100m","memory":"50Mi"}},"place_entrypoint":{"limits":{"memory":"100Mi"},"requests":{"cpu":"100m","memory":"25Mi"}},"sidecar":{"limits":{"memory":"2Gi"},"requests":{"cpu":"100m","memory":"250Mi"}}},"gcs_configuration":{"bucket":"test-platform-results","path_strategy":"single","default_org":"openshift","default_repo":"origin","mediaTypes":{"log":"text/plain"},"compress_file_types":["txt","log","json","tar","html","yaml"]},"gcs_credentials_secret":"gce-sa-credentials-gcs-publisher","skip_cloning":true,"censor_secrets":true}}`) + + pair(t, "/tmp/running-in-ci-prow-job", "codeready-toolchain", "host-operator", nil, &PairingService{}) + + }) + + t.Run("error parsing openshift job spec data", func(t *testing.T) { + t.Setenv("CI", "true") + t.Setenv("JOB_SPEC", `"type"`) + expectedError := fmt.Errorf("error when parsing openshift job spec data: json: cannot unmarshal string into Go value of type cmd.JobSpec") + + pair(t, "/tmp/running-in-ci-prow-job", "codeready-toolchain", "host-operator", expectedError, &PairingService{}) + }) + + t.Run("should pair", func(t *testing.T) { + t.Setenv("CI", "true") + t.Setenv("GITHUB_ACTIONS", "true") + t.Setenv("AUTHOR", "rsoaresd") + t.Setenv("GITHUB_HEAD_REF", "master") + + pairingServiceMock := new(MockPairingService) + pair(t, "/tmp/should-pair", "codeready-toolchain", "host-operator", nil, pairingServiceMock) + + }) +} + +func pair(t *testing.T, cloneDir, org, repo string, expectedError error, p PairingServiceInterface) { + err := Pair(cloneDir, org, repo, p) + + defer func() { + if err := os.RemoveAll(cloneDir); err != nil { + t.Fatalf("failed to remove test directory: %v", err) + } + }() + + if expectedError == nil { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, expectedError.Error()) + } +} diff --git a/scripts/ci/pairing/pkg/cmd/root.go b/scripts/ci/pairing/pkg/cmd/root.go new file mode 100644 index 0000000..b0f31c9 --- /dev/null +++ b/scripts/ci/pairing/pkg/cmd/root.go @@ -0,0 +1,32 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" +) + +// rootCmd represents the base command when called without any subcommands +var rootCmd = NewRootCmd() + +func NewRootCmd() *cobra.Command { + return &cobra.Command{ + Use: "test-e2e", + Short: "test-e2e command-line", + Long: `test-e2e command-line that helps you pair PRs`, + } +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +func init() { + rootCmd.AddCommand(NewPairCmd()) +} diff --git a/scripts/ci/pairing/pkg/cmd/types.go b/scripts/ci/pairing/pkg/cmd/types.go new file mode 100644 index 0000000..4c0d5d6 --- /dev/null +++ b/scripts/ci/pairing/pkg/cmd/types.go @@ -0,0 +1,25 @@ +package cmd + +type Pull struct { + Author string `json:"author"` + Number int `json:"number"` + HeadRef string `json:"head_ref"` +} + +type Refs struct { + Pulls []Pull `json:"pulls"` +} + +type JobSpec struct { + Refs Refs `json:"refs"` +} + +type PullRequestMetadata struct { + Author string + Organization string + RepoName string + BranchName string + CommitSHA string + Number string + RemoteName string +} From 56c78e0dee0d709fb13a617c9baf0a62345095a7 Mon Sep 17 00:00:00 2001 From: Rafaela Soares Date: Thu, 27 Feb 2025 17:17:08 +0000 Subject: [PATCH 02/11] improve --- scripts/ci/pairing/README.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/pairing/README.adoc b/scripts/ci/pairing/README.adoc index 4d26804..bed5b54 100644 --- a/scripts/ci/pairing/README.adoc +++ b/scripts/ci/pairing/README.adoc @@ -18,5 +18,5 @@ cd scripts/ci/pairing and build the binary ``` -make build-pairing PAIRING_DIR= +make build ``` \ No newline at end of file From f7f9ea52f811653bf2323b8aebf06850da9dd1f7 Mon Sep 17 00:00:00 2001 From: Rafaela Soares Date: Thu, 27 Feb 2025 17:42:56 +0000 Subject: [PATCH 03/11] improve --- scripts/ci/pairing/README.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/pairing/README.adoc b/scripts/ci/pairing/README.adoc index bed5b54..a7778cd 100644 --- a/scripts/ci/pairing/README.adoc +++ b/scripts/ci/pairing/README.adoc @@ -18,5 +18,5 @@ cd scripts/ci/pairing and build the binary ``` -make build +make build-pairing PAIRING_DIR= ``` \ No newline at end of file From b8323b9b161f8deb7cda252861ed9ff88bf097ab Mon Sep 17 00:00:00 2001 From: Rafaela Soares Date: Thu, 6 Mar 2025 12:00:57 +0000 Subject: [PATCH 04/11] requested changes improve improvements improve improve clean improve clean improve improve improve improve --- .github/workflows/test.yml | 6 +- {scripts/ci/pairing => pairing}/.golangci.yml | 0 {scripts/ci/pairing => pairing}/Makefile | 0 {scripts/ci/pairing => pairing}/README.adoc | 4 +- {scripts/ci/pairing => pairing}/cmd/main.go | 0 {scripts/ci/pairing => pairing}/go.mod | 33 ++- {scripts/ci/pairing => pairing}/go.sum | 129 +++++++---- pairing/make/build.mk | 8 + .../ci/pairing => pairing}/make/pairing.mk | 0 {scripts/ci/pairing => pairing}/make/test.mk | 0 .../ci/pairing => pairing}/make/verbose.mk | 0 .../pkg/cmd/flags/flags.go | 0 pairing/pkg/cmd/pair.go | 219 ++++++++++++++++++ .../pairing => pairing}/pkg/cmd/pair_test.go | 2 +- .../ci/pairing => pairing}/pkg/cmd/root.go | 3 - pairing/pkg/cmd/types.go | 14 ++ scripts/ci/pairing/make/build.mk | 16 -- scripts/ci/pairing/pkg/cmd/pair.go | 176 -------------- scripts/ci/pairing/pkg/cmd/types.go | 25 -- 19 files changed, 341 insertions(+), 294 deletions(-) rename {scripts/ci/pairing => pairing}/.golangci.yml (100%) rename {scripts/ci/pairing => pairing}/Makefile (100%) rename {scripts/ci/pairing => pairing}/README.adoc (86%) rename {scripts/ci/pairing => pairing}/cmd/main.go (100%) rename {scripts/ci/pairing => pairing}/go.mod (56%) rename {scripts/ci/pairing => pairing}/go.sum (50%) create mode 100644 pairing/make/build.mk rename {scripts/ci/pairing => pairing}/make/pairing.mk (100%) rename {scripts/ci/pairing => pairing}/make/test.mk (100%) rename {scripts/ci/pairing => pairing}/make/verbose.mk (100%) rename {scripts/ci/pairing => pairing}/pkg/cmd/flags/flags.go (100%) create mode 100644 pairing/pkg/cmd/pair.go rename {scripts/ci/pairing => pairing}/pkg/cmd/pair_test.go (97%) rename {scripts/ci/pairing => pairing}/pkg/cmd/root.go (63%) create mode 100644 pairing/pkg/cmd/types.go delete mode 100644 scripts/ci/pairing/make/build.mk delete mode 100644 scripts/ci/pairing/pkg/cmd/pair.go delete mode 100644 scripts/ci/pairing/pkg/cmd/types.go diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f773e56..34595ee 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: runs-on: ${{ matrix.os }} defaults: run: - working-directory: ./scripts/ci/pairing + working-directory: ./pairing strategy: matrix: os: [ ubuntu-latest ] @@ -27,7 +27,7 @@ jobs: - name: Install Go uses: actions/setup-go@v5 with: - go-version-file: ./scripts/ci/pairing/go.mod + go-version-file: ./pairing/go.mod - name: Cache dependencies uses: actions/cache@v4 @@ -43,7 +43,7 @@ jobs: version: v1.63.1 skip-pkg-cache: true skip-build-cache: true - working-directory: ./scripts/ci/pairing + working-directory: ./pairing args: --config=.golangci.yml --verbose - name: Test diff --git a/scripts/ci/pairing/.golangci.yml b/pairing/.golangci.yml similarity index 100% rename from scripts/ci/pairing/.golangci.yml rename to pairing/.golangci.yml diff --git a/scripts/ci/pairing/Makefile b/pairing/Makefile similarity index 100% rename from scripts/ci/pairing/Makefile rename to pairing/Makefile diff --git a/scripts/ci/pairing/README.adoc b/pairing/README.adoc similarity index 86% rename from scripts/ci/pairing/README.adoc rename to pairing/README.adoc index a7778cd..60d3177 100644 --- a/scripts/ci/pairing/README.adoc +++ b/pairing/README.adoc @@ -6,13 +6,13 @@ It reduces complexity and eliminates redundancy, providing a more efficient and == Build -Requires Go version 1.22.x (1.22.12 or higher) - download for your development environment https://golang.org/dl/[here]. +Requires Go version 1.21.x (1.21.13 or higher) - download for your development environment https://golang.org/dl/[here]. To build the binary, clone the latest version of toolchain-cicd repository: ``` git clone https://github.com/codeready-toolchain/toolchain-cicd.git -cd scripts/ci/pairing +cd pairing ``` and build the binary diff --git a/scripts/ci/pairing/cmd/main.go b/pairing/cmd/main.go similarity index 100% rename from scripts/ci/pairing/cmd/main.go rename to pairing/cmd/main.go diff --git a/scripts/ci/pairing/go.mod b/pairing/go.mod similarity index 56% rename from scripts/ci/pairing/go.mod rename to pairing/go.mod index ea59519..7ec2d5d 100644 --- a/scripts/ci/pairing/go.mod +++ b/pairing/go.mod @@ -1,44 +1,39 @@ module github.com/codeready-toolchain/pairing -go 1.22.0 - -toolchain go1.22.12 +go 1.21 require ( - github.com/go-git/go-git/v5 v5.13.2 - github.com/google/go-github/v44 v44.1.0 - github.com/spf13/cobra v1.9.1 + github.com/go-git/go-git/v5 v5.12.0 + github.com/spf13/cobra v1.8.0 github.com/stretchr/testify v1.10.0 ) require ( dario.cat/mergo v1.0.0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/ProtonMail/go-crypto v1.1.5 // indirect + github.com/ProtonMail/go-crypto v1.0.0 // indirect github.com/cloudflare/circl v1.3.7 // indirect - github.com/cyphar/filepath-securejoin v0.3.6 // indirect + github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.6.2 // indirect + github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/google/go-querystring v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/pjbgf/sha1cd v0.3.2 // indirect + github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect - github.com/skeema/knownhosts v1.3.0 // indirect - github.com/spf13/pflag v1.0.6 // indirect + github.com/skeema/knownhosts v1.2.2 // indirect + github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect - golang.org/x/crypto v0.32.0 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.34.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/crypto v0.21.0 // indirect + golang.org/x/mod v0.12.0 // indirect + golang.org/x/net v0.22.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/tools v0.13.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/scripts/ci/pairing/go.sum b/pairing/go.sum similarity index 50% rename from scripts/ci/pairing/go.sum rename to pairing/go.sum index b41fb06..6bf140b 100644 --- a/scripts/ci/pairing/go.sum +++ b/pairing/go.sum @@ -3,43 +3,40 @@ dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4= -github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= +github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= -github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM= -github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= 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/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM= -github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= -github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= +github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= +github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= -github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= +github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0= -github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A= +github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= +github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-github/v44 v44.1.0 h1:shWPaufgdhr+Ad4eo/pZv9ORTxFpsxPEPEuuXAKIQGA= -github.com/google/go-github/v44 v44.1.0/go.mod h1:iWn00mWcP6PRWHhXm0zuFJ8wbEjE5AGO5D5HXYM4zgw= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= @@ -53,26 +50,26 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= -github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= -github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= -github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= +github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= -github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= +github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= @@ -82,36 +79,70 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +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.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +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/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.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-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.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/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.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.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= -golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/pairing/make/build.mk b/pairing/make/build.mk new file mode 100644 index 0000000..7fafb1c --- /dev/null +++ b/pairing/make/build.mk @@ -0,0 +1,8 @@ +goarch?=$(shell go env GOARCH) + +.PHONY: build +build: + @go version + mkdir -p $(OUT_DIR)/bin || true + $(Q)CGO_ENABLED=0 GOARCH=${goarch} GOOS=linux \ + go build ${V_FLAG} ./... \ No newline at end of file diff --git a/scripts/ci/pairing/make/pairing.mk b/pairing/make/pairing.mk similarity index 100% rename from scripts/ci/pairing/make/pairing.mk rename to pairing/make/pairing.mk diff --git a/scripts/ci/pairing/make/test.mk b/pairing/make/test.mk similarity index 100% rename from scripts/ci/pairing/make/test.mk rename to pairing/make/test.mk diff --git a/scripts/ci/pairing/make/verbose.mk b/pairing/make/verbose.mk similarity index 100% rename from scripts/ci/pairing/make/verbose.mk rename to pairing/make/verbose.mk diff --git a/scripts/ci/pairing/pkg/cmd/flags/flags.go b/pairing/pkg/cmd/flags/flags.go similarity index 100% rename from scripts/ci/pairing/pkg/cmd/flags/flags.go rename to pairing/pkg/cmd/flags/flags.go diff --git a/pairing/pkg/cmd/pair.go b/pairing/pkg/cmd/pair.go new file mode 100644 index 0000000..6283876 --- /dev/null +++ b/pairing/pkg/cmd/pair.go @@ -0,0 +1,219 @@ +package cmd + +import ( + "encoding/json" + "fmt" + "io" + "log" + "net/http" + "os" + "strings" + + "github.com/codeready-toolchain/pairing/pkg/cmd/flags" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing" + "github.com/spf13/cobra" +) + +type PairingServiceInterface interface { + shouldPair(forkRepoURL, branchForParing string) (bool, error) +} + +type PairingService struct{} + +func (s *PairingService) shouldPair(forkRepoURL, branchForParing string) (bool, error) { + return shouldPair(forkRepoURL, branchForParing) +} + +func NewPairCmd() *cobra.Command { + var cloneDir, organization, repository string + + command := &cobra.Command{ + Use: "pair --clone-dir= --organization= --repository=", + Short: "Pair PRs in CI between the given organization and repository into the given clone directory", + Long: `Automatically tries to pair a PR opened on a specific repository with a branch of the same name that potentially could exist in the given organization and repository.`, + Args: cobra.ExactArgs(0), + RunE: func(cmd *cobra.Command, args []string) error { + return Pair(cloneDir, organization, repository, &PairingService{}) + }, + } + + command.Flags().StringVarP(&cloneDir, "clone-dir", "c", "", "Directory to clone into") + flags.MustMarkRequired(command, "clone-dir") + + command.Flags().StringVarP(&organization, "organization", "o", "", "Organization to pair") + flags.MustMarkRequired(command, "organization") + + command.Flags().StringVarP(&repository, "repository", "r", "", "Repository to pair") + flags.MustMarkRequired(command, "repository") + + return command +} + +// shouldPair checks if a branch with the same ref exists in the user's fork of the given repository +func shouldPair(forkRepoURL, branchForParing string) (bool, error) { + url := fmt.Sprintf("%s/info/refs?service=git-upload-pack", forkRepoURL) + + resp, err := http.Get(url) // #nosec G107 + if err != nil { + return false, err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return false, err + } + + lines := strings.Split(string(body), "\n") + var branchHash string + for _, line := range lines { + fields := strings.Fields(line) + if len(fields) == 2 && strings.Contains(fields[1], fmt.Sprintf("refs/heads/%s", branchForParing)) { + branchHash = fields[0] + break + } + } + + if branchHash == "" { + return false, fmt.Errorf("branch not found") + } + + return true, nil +} + +// getCurrentPRInfo gets the current info of the PR that triggered the pairing +// the pairing can be triggered by OpenShift-CI job or GH action +func getCurrentPRInfo() (string, string, error) { + // JOB_SPEC contains all the info about the running OpenShift-CI job, + // including the refs (org, repo, pulls, etc.) + jobSpecEnvVarData := os.Getenv("JOB_SPEC") + + // running OpenShift-CI job + if jobSpecEnvVarData != "" { + log.Println("running in OpenShift-CI job") + jobSpec := &JobSpec{} + if err := json.Unmarshal([]byte(jobSpecEnvVarData), jobSpec); err != nil { + return "", "", fmt.Errorf("error when parsing openshift job spec data: %w", err) + } + if len(jobSpec.Refs.Pulls) == 1 { + pull := jobSpec.Refs.Pulls[0] + return pull.Author, pull.HeadRef, nil + } else { + return "", "", fmt.Errorf("no pull request data found or more than one pull found") + } + // running GH action + } else if os.Getenv("GITHUB_ACTIONS") != "" { + log.Println("running in GH action") + return os.Getenv("AUTHOR"), os.Getenv("GITHUB_HEAD_REF"), nil + } + + return "", "", fmt.Errorf("not running in OpenShift-CI job either GH action") +} + +func clone(cloneDir, url string) (*git.Repository, error) { + repo, err := git.PlainClone(cloneDir, false, &git.CloneOptions{ + URL: url, + ReferenceName: plumbing.ReferenceName("refs/heads/master"), + // Depth: 1, + Progress: os.Stdout, + }) + if err != nil { + return nil, fmt.Errorf("failed to clone repository: %w", err) + } + + return repo, nil +} + +func cloneAndPair(cloneDir, parentRepoURL, forkRepoURL, remoteBranch string) error { + log.Printf("branch ref of the user's fork (%s) to be used for pairing: %s\n", forkRepoURL, remoteBranch) + cloneDirInfo, err := os.Stat(cloneDir) + + if !os.IsNotExist(err) { + if cloneDirInfo.IsDir() { + log.Printf("folder %s already exists... removing", cloneDir) + + err := os.RemoveAll(cloneDir) + if err != nil { + return fmt.Errorf("error removing %s folder: %w", cloneDir, err) + } + } else { + return fmt.Errorf("cloneDir %s provided is not a directory", cloneDir) + } + } + + // clone parent repo + // git clone parentRepoURL cloneDir + log.Printf("cloning parent repository %s\n", parentRepoURL) + repo, err := clone(cloneDir, parentRepoURL) + if err != nil { + return fmt.Errorf("failed to clone repository: %w", err) + } + + // add the user's fork as remote + // git remote add external forkRepoURL + remote, err := repo.CreateRemote(&config.RemoteConfig{ + Name: "external", + URLs: []string{forkRepoURL}, + }) + if err != nil { + return fmt.Errorf("failed to add remote: %w", err) + } + + // fetch the remote branch + // git fetch external remoteBranch + err = repo.Fetch(&git.FetchOptions{ + RemoteName: remote.Config().Name, + RefSpecs: []config.RefSpec{ + config.RefSpec(fmt.Sprintf("+refs/heads/%s:refs/remotes/%s/%s", remoteBranch, remote.Config().Name, remoteBranch)), + }, + }) + if err != nil { + return fmt.Errorf("failed to fetch: %w", err) + } + + remoteRefName := plumbing.NewRemoteReferenceName(remote.Config().Name, remoteBranch) + reference, err := repo.Reference(remoteRefName, true) + if err != nil { + return fmt.Errorf("fetched branch does not exist: %w", err) + } + + // merge the remote branch with master + // git merge remoteBranch + err = repo.Merge(*reference, git.MergeOptions{}) + if err != nil { + return fmt.Errorf("failed to merge: %w", err) + } + + return err +} + +func Pair(cloneDir, organization, repository string, p PairingServiceInterface) error { + if os.Getenv("CI") == "true" { + authorName, remoteBranch, err := getCurrentPRInfo() + if err != nil { + return err + } + + forkRepoURL := fmt.Sprintf("https://github.com/%s/%s.git", authorName, repository) + + shouldPair, err := p.shouldPair(forkRepoURL, remoteBranch) + if err != nil { + log.Printf("should not pair: %s", err.Error()) + } + + parentRepoURL := fmt.Sprintf("https://github.com/%s/%s.git", organization, repository) + + if shouldPair { + fmt.Println("should pair") + return cloneAndPair(cloneDir, parentRepoURL, forkRepoURL, remoteBranch) + } + + log.Printf("no pairing needed. cloning parent repo %s\n", parentRepoURL) + _, err = clone(cloneDir, parentRepoURL) + return err + } + + return fmt.Errorf("not running in CI") +} diff --git a/scripts/ci/pairing/pkg/cmd/pair_test.go b/pairing/pkg/cmd/pair_test.go similarity index 97% rename from scripts/ci/pairing/pkg/cmd/pair_test.go rename to pairing/pkg/cmd/pair_test.go index 7b74d6d..189bf94 100644 --- a/scripts/ci/pairing/pkg/cmd/pair_test.go +++ b/pairing/pkg/cmd/pair_test.go @@ -14,7 +14,7 @@ type MockPairingService struct { mock.Mock } -func (m *MockPairingService) shouldPair(orgForPairing, repoForPairing, currentRemoteName, currentBranchName string) (bool, error) { +func (m *MockPairingService) shouldPair(forkRepoURL, branchForParing string) (bool, error) { return true, nil } diff --git a/scripts/ci/pairing/pkg/cmd/root.go b/pairing/pkg/cmd/root.go similarity index 63% rename from scripts/ci/pairing/pkg/cmd/root.go rename to pairing/pkg/cmd/root.go index b0f31c9..11e79ec 100644 --- a/scripts/ci/pairing/pkg/cmd/root.go +++ b/pairing/pkg/cmd/root.go @@ -7,7 +7,6 @@ import ( "github.com/spf13/cobra" ) -// rootCmd represents the base command when called without any subcommands var rootCmd = NewRootCmd() func NewRootCmd() *cobra.Command { @@ -18,8 +17,6 @@ func NewRootCmd() *cobra.Command { } } -// Execute adds all child commands to the root command and sets flags appropriately. -// This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { if err := rootCmd.Execute(); err != nil { fmt.Println(err) diff --git a/pairing/pkg/cmd/types.go b/pairing/pkg/cmd/types.go new file mode 100644 index 0000000..b692b91 --- /dev/null +++ b/pairing/pkg/cmd/types.go @@ -0,0 +1,14 @@ +package cmd + +type Pull struct { + Author string `json:"author"` + HeadRef string `json:"head_ref"` +} + +type Refs struct { + Pulls []Pull `json:"pulls"` +} + +type JobSpec struct { + Refs Refs `json:"refs"` +} diff --git a/scripts/ci/pairing/make/build.mk b/scripts/ci/pairing/make/build.mk deleted file mode 100644 index 9695d86..0000000 --- a/scripts/ci/pairing/make/build.mk +++ /dev/null @@ -1,16 +0,0 @@ -# By default the project should be build under GOPATH/src/github.com// -GO_PACKAGE_ORG_NAME ?= $(shell basename $$(dirname $$PWD)) -GO_PACKAGE_REPO_NAME ?= $(shell basename $$PWD) -GO_PACKAGE_PATH ?= github.com/${GO_PACKAGE_ORG_NAME}/${GO_PACKAGE_REPO_NAME} - -GO111MODULE?=on -export GO111MODULE -goarch?=$(shell go env GOARCH) - -.PHONY: build -## Build e2e test files -build: - @go version - mkdir -p $(OUT_DIR)/bin || true - $(Q)CGO_ENABLED=0 GOARCH=${goarch} GOOS=linux \ - go build ${V_FLAG} ./... \ No newline at end of file diff --git a/scripts/ci/pairing/pkg/cmd/pair.go b/scripts/ci/pairing/pkg/cmd/pair.go deleted file mode 100644 index 96a235d..0000000 --- a/scripts/ci/pairing/pkg/cmd/pair.go +++ /dev/null @@ -1,176 +0,0 @@ -package cmd - -import ( - "context" - "encoding/json" - "fmt" - "log" - "os" - "strconv" - - "github.com/codeready-toolchain/pairing/pkg/cmd/flags" - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/plumbing" - "github.com/google/go-github/v44/github" - "github.com/spf13/cobra" -) - -type PairingServiceInterface interface { - shouldPair(orgForPairing, repoForPairing, currentRemoteName, currentBranchName string) (bool, error) -} - -type PairingService struct{} - -func (s *PairingService) shouldPair(orgForPairing, repoForPairing, currentRemoteName, currentBranchName string) (bool, error) { - return shouldPair(orgForPairing, repoForPairing, currentRemoteName, currentBranchName) -} - -func NewPairCmd() *cobra.Command { - var cloneDir, organization, repository string - - command := &cobra.Command{ - Use: "pair --clone-dir= --organization= --repository=", - Short: "Pair PRs in CI between the given organization and repository into the given clone directory", - Long: `Automatically tries to pair a PR opened on a specific repository with a branch of the same name that potentially could exist in the given organization and repository.`, - Args: cobra.ExactArgs(0), - RunE: func(cmd *cobra.Command, args []string) error { - return Pair(cloneDir, organization, repository, &PairingService{}) - }, - } - - command.Flags().StringVarP(&cloneDir, "clone-dir", "c", "", "Directory to clone into") - flags.MustMarkRequired(command, "clone-dir") - - command.Flags().StringVarP(&organization, "organization", "o", "", "Organization to pair") - flags.MustMarkRequired(command, "organization") - - command.Flags().StringVarP(&repository, "repository", "r", "", "Repository to pair") - flags.MustMarkRequired(command, "repository") - - return command -} - -// listOpenPRs lists open pull requests for the given repository -func listOpenPRs(owner, repo string) ([]*github.PullRequest, error) { - client := github.NewClient(nil) // no authentication needed - - // list the open pull requests - opt := &github.PullRequestListOptions{ - State: "open", - } - - prs, _, err := client.PullRequests.List(context.Background(), owner, repo, opt) - if err != nil { - return nil, fmt.Errorf("could not list pull requests: %w", err) - } - - return prs, nil -} - -// shouldPair determines whether the given remote and branch name should be paired -// based on existing open pull requests in the given organization and repository. -func shouldPair(orgForPairing, repoForPairing, currentRemoteName, currentBranchName string) (bool, error) { - pullRequests, err := listOpenPRs(orgForPairing, repoForPairing) - if err != nil { - return false, err - } - - for _, pull := range pullRequests { - if pull.GetHead().GetRef() == currentBranchName && pull.GetUser().GetLogin() == currentRemoteName { - return true, nil - } - } - - return false, nil -} - -// getCurrentPRInfo gets the current info of the PR that triggered the pairing -// the pairing can be triggered by CI job or GH action -func getCurrentPRInfo() (*PullRequestMetadata, error) { - pr := &PullRequestMetadata{} - jobSpecEnvVarData := os.Getenv("JOB_SPEC") - - // running CI job - if jobSpecEnvVarData != "" { - log.Println("running in CI job") - jobSpec := &JobSpec{} - if err := json.Unmarshal([]byte(jobSpecEnvVarData), jobSpec); err != nil { - return pr, fmt.Errorf("error when parsing openshift job spec data: %w", err) - } - if len(jobSpec.Refs.Pulls) == 1 { - pull := jobSpec.Refs.Pulls[0] - pr = &PullRequestMetadata{ - RemoteName: pull.Author, - Number: strconv.Itoa(pull.Number), - BranchName: pull.HeadRef, - } - } else { - fmt.Println("No pull request data found.") - } - // running GH action - } else if os.Getenv("GITHUB_ACTIONS") != "" { - log.Println("running in GH action") - pr = &PullRequestMetadata{ - RemoteName: os.Getenv("AUTHOR"), - BranchName: os.Getenv("GITHUB_HEAD_REF"), - } - } - - return pr, nil -} - -func clone(cloneDir, org, repo, prRemoteName, prBranchName string, p PairingServiceInterface) error { - branch := "master" - - cloneDirInfo, err := os.Stat(cloneDir) - if !os.IsNotExist(err) && cloneDirInfo.IsDir() { - log.Printf("folder %s already exists... removing", cloneDir) - - err := os.RemoveAll(cloneDir) - if err != nil { - return fmt.Errorf("error removing %s folder: %w", cloneDir, err) - } - } - - // if CI - if prRemoteName != "" && prBranchName != "" { - shouldPair, err := p.shouldPair(org, repo, prRemoteName, prBranchName) - if err != nil { - return err - } - // check if pairing is required - if shouldPair { - org = prRemoteName - branch = prBranchName - } - } - - url := fmt.Sprintf("https://github.com/%s/%s", org, repo) - refName := fmt.Sprintf("refs/heads/%s", branch) - - log.Printf("cloning '%s' with git ref '%s'", url, refName) - - _, err = git.PlainClone(cloneDir, false, &git.CloneOptions{ - URL: url, - ReferenceName: plumbing.ReferenceName(refName), - Progress: os.Stdout, - }) - if err != nil { - return fmt.Errorf("failed to clone repository: %w", err) - } - - return nil -} - -func Pair(cloneDir, org, repo string, p PairingServiceInterface) error { - prBranchName, prRemoteName := "", "" - if os.Getenv("CI") == "true" { - pr, err := getCurrentPRInfo() - if err != nil { - return err - } - prBranchName, prRemoteName = pr.BranchName, pr.RemoteName - } - - return clone(cloneDir, org, repo, prRemoteName, prBranchName, p) -} diff --git a/scripts/ci/pairing/pkg/cmd/types.go b/scripts/ci/pairing/pkg/cmd/types.go deleted file mode 100644 index 4c0d5d6..0000000 --- a/scripts/ci/pairing/pkg/cmd/types.go +++ /dev/null @@ -1,25 +0,0 @@ -package cmd - -type Pull struct { - Author string `json:"author"` - Number int `json:"number"` - HeadRef string `json:"head_ref"` -} - -type Refs struct { - Pulls []Pull `json:"pulls"` -} - -type JobSpec struct { - Refs Refs `json:"refs"` -} - -type PullRequestMetadata struct { - Author string - Organization string - RepoName string - BranchName string - CommitSHA string - Number string - RemoteName string -} From 58862df6b94994e192fca6a56835ab5f81598cc9 Mon Sep 17 00:00:00 2001 From: Rafaela Soares Date: Thu, 6 Mar 2025 15:28:19 +0000 Subject: [PATCH 05/11] clean --- pairing/make/test.mk | 1 - 1 file changed, 1 deletion(-) diff --git a/pairing/make/test.mk b/pairing/make/test.mk index 14ffe10..d8ece47 100644 --- a/pairing/make/test.mk +++ b/pairing/make/test.mk @@ -1,5 +1,4 @@ .PHONY: test -## runs the tests (use `test-in-container` on macOS) test: build @echo "running the pairing unit tests..." go test -v -failfast ./... \ No newline at end of file From 28150d76d4b22a94d599c67d0d5ef1fab1d29113 Mon Sep 17 00:00:00 2001 From: Rafaela Soares Date: Mon, 10 Mar 2025 11:22:16 +0000 Subject: [PATCH 06/11] improve --- pairing/pkg/cmd/pair.go | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/pairing/pkg/cmd/pair.go b/pairing/pkg/cmd/pair.go index 6283876..b00f02f 100644 --- a/pairing/pkg/cmd/pair.go +++ b/pairing/pkg/cmd/pair.go @@ -113,6 +113,21 @@ func getCurrentPRInfo() (string, string, error) { } func clone(cloneDir, url string) (*git.Repository, error) { + cloneDirInfo, err := os.Stat(cloneDir) + + if !os.IsNotExist(err) { + if cloneDirInfo.IsDir() { + log.Printf("folder %s already exists... removing", cloneDir) + + err := os.RemoveAll(cloneDir) + if err != nil { + return nil, fmt.Errorf("error removing %s folder: %w", cloneDir, err) + } + } else { + return nil, fmt.Errorf("cloneDir %s provided is not a directory", cloneDir) + } + } + repo, err := git.PlainClone(cloneDir, false, &git.CloneOptions{ URL: url, ReferenceName: plumbing.ReferenceName("refs/heads/master"), @@ -128,20 +143,6 @@ func clone(cloneDir, url string) (*git.Repository, error) { func cloneAndPair(cloneDir, parentRepoURL, forkRepoURL, remoteBranch string) error { log.Printf("branch ref of the user's fork (%s) to be used for pairing: %s\n", forkRepoURL, remoteBranch) - cloneDirInfo, err := os.Stat(cloneDir) - - if !os.IsNotExist(err) { - if cloneDirInfo.IsDir() { - log.Printf("folder %s already exists... removing", cloneDir) - - err := os.RemoveAll(cloneDir) - if err != nil { - return fmt.Errorf("error removing %s folder: %w", cloneDir, err) - } - } else { - return fmt.Errorf("cloneDir %s provided is not a directory", cloneDir) - } - } // clone parent repo // git clone parentRepoURL cloneDir From 6ca2a13df93bbd79f13b1c88ed4df5fc2e462bd5 Mon Sep 17 00:00:00 2001 From: Rafaela Soares Date: Tue, 11 Mar 2025 10:18:34 +0000 Subject: [PATCH 07/11] improve --- pairing/pkg/cmd/pair.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pairing/pkg/cmd/pair.go b/pairing/pkg/cmd/pair.go index b00f02f..72b72ce 100644 --- a/pairing/pkg/cmd/pair.go +++ b/pairing/pkg/cmd/pair.go @@ -191,6 +191,9 @@ func cloneAndPair(cloneDir, parentRepoURL, forkRepoURL, remoteBranch string) err } func Pair(cloneDir, organization, repository string, p PairingServiceInterface) error { + parentRepoURL := fmt.Sprintf("https://github.com/%s/%s.git", organization, repository) + + // running in CI if os.Getenv("CI") == "true" { authorName, remoteBranch, err := getCurrentPRInfo() if err != nil { @@ -204,17 +207,17 @@ func Pair(cloneDir, organization, repository string, p PairingServiceInterface) log.Printf("should not pair: %s", err.Error()) } - parentRepoURL := fmt.Sprintf("https://github.com/%s/%s.git", organization, repository) - if shouldPair { - fmt.Println("should pair") return cloneAndPair(cloneDir, parentRepoURL, forkRepoURL, remoteBranch) } - log.Printf("no pairing needed. cloning parent repo %s\n", parentRepoURL) + log.Printf("running in CI but no pairing needed. cloning parent repo %s\n", parentRepoURL) _, err = clone(cloneDir, parentRepoURL) return err } - return fmt.Errorf("not running in CI") + // not running in CI + log.Printf("not running in CI, so pairing is not needed. cloning parent repo %s", parentRepoURL) + _, err := clone(cloneDir, parentRepoURL) + return err } From 2e6e279dcde4ba115d74dc16f90cf9cdf871a7bd Mon Sep 17 00:00:00 2001 From: Rafaela Soares Date: Tue, 11 Mar 2025 10:42:30 +0000 Subject: [PATCH 08/11] improve --- pairing/pkg/cmd/pair.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pairing/pkg/cmd/pair.go b/pairing/pkg/cmd/pair.go index 72b72ce..91cb26a 100644 --- a/pairing/pkg/cmd/pair.go +++ b/pairing/pkg/cmd/pair.go @@ -76,8 +76,9 @@ func shouldPair(forkRepoURL, branchForParing string) (bool, error) { } } + // branch not found if branchHash == "" { - return false, fmt.Errorf("branch not found") + return false, nil } return true, nil @@ -204,7 +205,7 @@ func Pair(cloneDir, organization, repository string, p PairingServiceInterface) shouldPair, err := p.shouldPair(forkRepoURL, remoteBranch) if err != nil { - log.Printf("should not pair: %s", err.Error()) + return err } if shouldPair { From eab421855a1d68b3cdeca0e315212c1aa49dd775 Mon Sep 17 00:00:00 2001 From: Rafaela Soares Date: Tue, 11 Mar 2025 12:29:11 +0000 Subject: [PATCH 09/11] improve unit tests --- pairing/pkg/cmd/pair.go | 4 +++ pairing/pkg/cmd/pair_test.go | 47 +++++++++++++++++++++++++++++++----- pairing/workload-analyzer | 1 + 3 files changed, 46 insertions(+), 6 deletions(-) create mode 160000 pairing/workload-analyzer diff --git a/pairing/pkg/cmd/pair.go b/pairing/pkg/cmd/pair.go index 91cb26a..477900a 100644 --- a/pairing/pkg/cmd/pair.go +++ b/pairing/pkg/cmd/pair.go @@ -66,6 +66,10 @@ func shouldPair(forkRepoURL, branchForParing string) (bool, error) { return false, err } + if resp.StatusCode != 200 { + return false, fmt.Errorf("failed to get repo: %s", string(body)) + } + lines := strings.Split(string(body), "\n") var branchHash string for _, line := range lines { diff --git a/pairing/pkg/cmd/pair_test.go b/pairing/pkg/cmd/pair_test.go index 189bf94..ad47cb7 100644 --- a/pairing/pkg/cmd/pair_test.go +++ b/pairing/pkg/cmd/pair_test.go @@ -5,8 +5,8 @@ import ( "os" "testing" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" ) // Mock the shouldPair function @@ -20,10 +20,29 @@ func (m *MockPairingService) shouldPair(forkRepoURL, branchForParing string) (bo func TestPair(t *testing.T) { t.Run("error during clone: repository not found", func(t *testing.T) { - expectedError := fmt.Errorf("failed to clone repository: authentication required: Repository not found.") + expectedError := fmt.Errorf("failed to clone repository: authentication required") pair(t, "/tmp/repository-not-found", "codeready-toolchain", "host-operato", expectedError, &PairingService{}) }) + t.Run("error during clone: cloneDir provided is not a directory", func(t *testing.T) { + filePath := "/tmp/file.txt" + file, err := os.Create(filePath) + require.NoError(t, err) + defer file.Close() + defer os.Remove(filePath) + + expectedError := fmt.Errorf("cloneDir /tmp/file.txt provided is not a directory") + pair(t, filePath, "codeready-toolchain", "host-operator", expectedError, &PairingService{}) + }) + + t.Run("cloneDir already exists (should clean it and return no error)", func(t *testing.T) { + // folder already exists + cloneDir := "/tmp/host-operator" + require.NoError(t, os.Mkdir(cloneDir, os.ModePerm)) + + pair(t, cloneDir, "codeready-toolchain", "host-operator", nil, &PairingService{}) + }) + t.Run("not running in ci", func(t *testing.T) { pair(t, "/tmp/not-running-in-ci", "codeready-toolchain", "host-operator", nil, &PairingService{}) }) @@ -45,7 +64,7 @@ func TestPair(t *testing.T) { }) - t.Run("error parsing openshift job spec data", func(t *testing.T) { + t.Run("running in ci - prow job - error parsing openshift job spec data", func(t *testing.T) { t.Setenv("CI", "true") t.Setenv("JOB_SPEC", `"type"`) expectedError := fmt.Errorf("error when parsing openshift job spec data: json: cannot unmarshal string into Go value of type cmd.JobSpec") @@ -53,7 +72,7 @@ func TestPair(t *testing.T) { pair(t, "/tmp/running-in-ci-prow-job", "codeready-toolchain", "host-operator", expectedError, &PairingService{}) }) - t.Run("should pair", func(t *testing.T) { + t.Run("running in ci - should pair", func(t *testing.T) { t.Setenv("CI", "true") t.Setenv("GITHUB_ACTIONS", "true") t.Setenv("AUTHOR", "rsoaresd") @@ -61,7 +80,23 @@ func TestPair(t *testing.T) { pairingServiceMock := new(MockPairingService) pair(t, "/tmp/should-pair", "codeready-toolchain", "host-operator", nil, pairingServiceMock) + }) + + t.Run("running in ci - failed to pair", func(t *testing.T) { + t.Setenv("CI", "true") + t.Setenv("GITHUB_ACTIONS", "true") + t.Setenv("AUTHOR", "rsoaesd") + t.Setenv("GITHUB_HEAD_REF", "clean_only_when_test_passed") + + expectedError := fmt.Errorf("failed to get repo: Repository not found.") + pair(t, "/tmp/running-in-gh-action", "kubesaw", "ksctl", expectedError, &PairingService{}) + }) + + t.Run("running in ci - but not running in OpenShift-CI job either GH action", func(t *testing.T) { + t.Setenv("CI", "true") + expectedError := fmt.Errorf("not running in OpenShift-CI job either GH action") + pair(t, "/tmp/running-in-gh-action", "kubesaw", "ksctl", expectedError, &PairingService{}) }) } @@ -75,8 +110,8 @@ func pair(t *testing.T, cloneDir, org, repo string, expectedError error, p Pairi }() if expectedError == nil { - assert.NoError(t, err) + require.NoError(t, err) } else { - assert.EqualError(t, err, expectedError.Error()) + require.EqualError(t, err, expectedError.Error()) } } diff --git a/pairing/workload-analyzer b/pairing/workload-analyzer new file mode 160000 index 0000000..9699bcb --- /dev/null +++ b/pairing/workload-analyzer @@ -0,0 +1 @@ +Subproject commit 9699bcb9f5634b740a7cda1616823b1e31a2260b From 4cb2f8843cdff8e9dc1eb6f8a9cc80df917a601d Mon Sep 17 00:00:00 2001 From: Rafaela Soares Date: Thu, 13 Mar 2025 12:51:53 +0000 Subject: [PATCH 10/11] improve --- pairing/pkg/cmd/pair.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pairing/pkg/cmd/pair.go b/pairing/pkg/cmd/pair.go index 477900a..58ffc06 100644 --- a/pairing/pkg/cmd/pair.go +++ b/pairing/pkg/cmd/pair.go @@ -74,7 +74,7 @@ func shouldPair(forkRepoURL, branchForParing string) (bool, error) { var branchHash string for _, line := range lines { fields := strings.Fields(line) - if len(fields) == 2 && strings.Contains(fields[1], fmt.Sprintf("refs/heads/%s", branchForParing)) { + if len(fields) == 2 && fields[1] == fmt.Sprintf("refs/heads/%s", branchForParing) { branchHash = fields[0] break } From d27187d66bfd24e71bf2fa17366c55f5bf010dd4 Mon Sep 17 00:00:00 2001 From: Rafaela Soares Date: Tue, 27 May 2025 16:57:03 +0100 Subject: [PATCH 11/11] requested changes --- .github/workflows/test.yml | 7 +- pairing/go.mod | 3 +- pairing/go.sum | 8 ++- pairing/make/build.mk | 9 ++- pairing/pkg/cmd/pair.go | 71 +++++++----------- pairing/pkg/cmd/pair_test.go | 99 +++++++++++++++++--------- pairing/pkg/cmd/testdata/prow_job.json | 93 ++++++++++++++++++++++++ pairing/workload-analyzer | 1 - 8 files changed, 203 insertions(+), 88 deletions(-) create mode 100644 pairing/pkg/cmd/testdata/prow_job.json delete mode 160000 pairing/workload-analyzer diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 34595ee..0503644 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -38,13 +38,12 @@ jobs: ${{ runner.os }}-go- - name: Lint - uses: golangci/golangci-lint-action@v6 + uses: golangci/golangci-lint-action@v8 with: - version: v1.63.1 + version: v2.1.6 skip-pkg-cache: true skip-build-cache: true - working-directory: ./pairing - args: --config=.golangci.yml --verbose + args: --config=./.golangci.yml --verbose - name: Test run: | diff --git a/pairing/go.mod b/pairing/go.mod index 7ec2d5d..7d56677 100644 --- a/pairing/go.mod +++ b/pairing/go.mod @@ -6,6 +6,7 @@ require ( github.com/go-git/go-git/v5 v5.12.0 github.com/spf13/cobra v1.8.0 github.com/stretchr/testify v1.10.0 + gopkg.in/h2non/gock.v1 v1.1.2 ) require ( @@ -19,6 +20,7 @@ require ( github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect @@ -27,7 +29,6 @@ require ( github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect github.com/skeema/knownhosts v1.2.2 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/objx v0.5.2 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect golang.org/x/crypto v0.21.0 // indirect golang.org/x/mod v0.12.0 // indirect diff --git a/pairing/go.sum b/pairing/go.sum index 6bf140b..2f32b07 100644 --- a/pairing/go.sum +++ b/pairing/go.sum @@ -37,6 +37,8 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= @@ -50,6 +52,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= @@ -71,8 +75,6 @@ github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyh github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= @@ -147,6 +149,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= +gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/pairing/make/build.mk b/pairing/make/build.mk index 7fafb1c..32ac19f 100644 --- a/pairing/make/build.mk +++ b/pairing/make/build.mk @@ -5,4 +5,11 @@ build: @go version mkdir -p $(OUT_DIR)/bin || true $(Q)CGO_ENABLED=0 GOARCH=${goarch} GOOS=linux \ - go build ${V_FLAG} ./... \ No newline at end of file + go build ${V_FLAG} ./... + +.PHONY: lint +lint: +ifeq (, $(shell which golangci-lint 2>/dev/null)) + $(error "golangci-lint not found in PATH. Please install it using instructions on https://golangci-lint.run/usage/install/#local-installation") +endif + golangci-lint ${V_FLAG} run --config=./.golangci.yml --verbose ./... \ No newline at end of file diff --git a/pairing/pkg/cmd/pair.go b/pairing/pkg/cmd/pair.go index 58ffc06..7e15976 100644 --- a/pairing/pkg/cmd/pair.go +++ b/pairing/pkg/cmd/pair.go @@ -1,6 +1,7 @@ package cmd import ( + "bufio" "encoding/json" "fmt" "io" @@ -16,15 +17,7 @@ import ( "github.com/spf13/cobra" ) -type PairingServiceInterface interface { - shouldPair(forkRepoURL, branchForParing string) (bool, error) -} - -type PairingService struct{} - -func (s *PairingService) shouldPair(forkRepoURL, branchForParing string) (bool, error) { - return shouldPair(forkRepoURL, branchForParing) -} +type ApplyPairFunc func(repo *git.Repository, forkRepoURL, remoteBranch string) error func NewPairCmd() *cobra.Command { var cloneDir, organization, repository string @@ -35,7 +28,7 @@ func NewPairCmd() *cobra.Command { Long: `Automatically tries to pair a PR opened on a specific repository with a branch of the same name that potentially could exist in the given organization and repository.`, Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { - return Pair(cloneDir, organization, repository, &PairingService{}) + return Pair(cloneDir, organization, repository, applyPair) }, } @@ -70,22 +63,15 @@ func shouldPair(forkRepoURL, branchForParing string) (bool, error) { return false, fmt.Errorf("failed to get repo: %s", string(body)) } - lines := strings.Split(string(body), "\n") - var branchHash string - for _, line := range lines { - fields := strings.Fields(line) + scanner := bufio.NewScanner(strings.NewReader(string(body))) + for scanner.Scan() { + fields := strings.Fields(scanner.Text()) if len(fields) == 2 && fields[1] == fmt.Sprintf("refs/heads/%s", branchForParing) { - branchHash = fields[0] - break + return true, nil } } - // branch not found - if branchHash == "" { - return false, nil - } - - return true, nil + return false, nil } // getCurrentPRInfo gets the current info of the PR that triggered the pairing @@ -120,7 +106,7 @@ func getCurrentPRInfo() (string, string, error) { func clone(cloneDir, url string) (*git.Repository, error) { cloneDirInfo, err := os.Stat(cloneDir) - if !os.IsNotExist(err) { + if err != nil && !os.IsNotExist(err) { if cloneDirInfo.IsDir() { log.Printf("folder %s already exists... removing", cloneDir) @@ -135,7 +121,7 @@ func clone(cloneDir, url string) (*git.Repository, error) { repo, err := git.PlainClone(cloneDir, false, &git.CloneOptions{ URL: url, - ReferenceName: plumbing.ReferenceName("refs/heads/master"), + ReferenceName: "refs/heads/master", // Depth: 1, Progress: os.Stdout, }) @@ -146,17 +132,9 @@ func clone(cloneDir, url string) (*git.Repository, error) { return repo, nil } -func cloneAndPair(cloneDir, parentRepoURL, forkRepoURL, remoteBranch string) error { +func applyPair(repo *git.Repository, forkRepoURL, remoteBranch string) error { log.Printf("branch ref of the user's fork (%s) to be used for pairing: %s\n", forkRepoURL, remoteBranch) - // clone parent repo - // git clone parentRepoURL cloneDir - log.Printf("cloning parent repository %s\n", parentRepoURL) - repo, err := clone(cloneDir, parentRepoURL) - if err != nil { - return fmt.Errorf("failed to clone repository: %w", err) - } - // add the user's fork as remote // git remote add external forkRepoURL remote, err := repo.CreateRemote(&config.RemoteConfig{ @@ -169,17 +147,17 @@ func cloneAndPair(cloneDir, parentRepoURL, forkRepoURL, remoteBranch string) err // fetch the remote branch // git fetch external remoteBranch + remoteRefName := plumbing.NewRemoteReferenceName(remote.Config().Name, remoteBranch) err = repo.Fetch(&git.FetchOptions{ RemoteName: remote.Config().Name, RefSpecs: []config.RefSpec{ - config.RefSpec(fmt.Sprintf("+refs/heads/%s:refs/remotes/%s/%s", remoteBranch, remote.Config().Name, remoteBranch)), + config.RefSpec(fmt.Sprintf("+refs/heads/%s:%s", remoteBranch, remoteRefName)), }, }) if err != nil { return fmt.Errorf("failed to fetch: %w", err) } - remoteRefName := plumbing.NewRemoteReferenceName(remote.Config().Name, remoteBranch) reference, err := repo.Reference(remoteRefName, true) if err != nil { return fmt.Errorf("fetched branch does not exist: %w", err) @@ -187,7 +165,7 @@ func cloneAndPair(cloneDir, parentRepoURL, forkRepoURL, remoteBranch string) err // merge the remote branch with master // git merge remoteBranch - err = repo.Merge(*reference, git.MergeOptions{}) + err = repo.Merge(*reference, git.MergeOptions{Strategy: git.FastForwardMerge}) if err != nil { return fmt.Errorf("failed to merge: %w", err) } @@ -195,8 +173,13 @@ func cloneAndPair(cloneDir, parentRepoURL, forkRepoURL, remoteBranch string) err return err } -func Pair(cloneDir, organization, repository string, p PairingServiceInterface) error { +func Pair(cloneDir, organization, repository string, applyPair ApplyPairFunc) error { parentRepoURL := fmt.Sprintf("https://github.com/%s/%s.git", organization, repository) + log.Printf("cloning parent repo %s", parentRepoURL) + repo, err := clone(cloneDir, parentRepoURL) + if err != nil { + return err + } // running in CI if os.Getenv("CI") == "true" { @@ -207,22 +190,20 @@ func Pair(cloneDir, organization, repository string, p PairingServiceInterface) forkRepoURL := fmt.Sprintf("https://github.com/%s/%s.git", authorName, repository) - shouldPair, err := p.shouldPair(forkRepoURL, remoteBranch) + shouldPair, err := shouldPair(forkRepoURL, remoteBranch) if err != nil { return err } if shouldPair { - return cloneAndPair(cloneDir, parentRepoURL, forkRepoURL, remoteBranch) + return applyPair(repo, forkRepoURL, remoteBranch) } - log.Printf("running in CI but no pairing needed. cloning parent repo %s\n", parentRepoURL) - _, err = clone(cloneDir, parentRepoURL) - return err + log.Println("running in CI but no pairing needed") + return nil } // not running in CI - log.Printf("not running in CI, so pairing is not needed. cloning parent repo %s", parentRepoURL) - _, err := clone(cloneDir, parentRepoURL) - return err + log.Println("not running in CI, so pairing is not needed") + return nil } diff --git a/pairing/pkg/cmd/pair_test.go b/pairing/pkg/cmd/pair_test.go index ad47cb7..25f8b20 100644 --- a/pairing/pkg/cmd/pair_test.go +++ b/pairing/pkg/cmd/pair_test.go @@ -2,65 +2,70 @@ package cmd import ( "fmt" + "net/http" "os" "testing" - "github.com/stretchr/testify/mock" + "github.com/go-git/go-git/v5" "github.com/stretchr/testify/require" + "gopkg.in/h2non/gock.v1" ) -// Mock the shouldPair function -type MockPairingService struct { - mock.Mock -} - -func (m *MockPairingService) shouldPair(forkRepoURL, branchForParing string) (bool, error) { - return true, nil -} - func TestPair(t *testing.T) { t.Run("error during clone: repository not found", func(t *testing.T) { expectedError := fmt.Errorf("failed to clone repository: authentication required") - pair(t, "/tmp/repository-not-found", "codeready-toolchain", "host-operato", expectedError, &PairingService{}) + temp := mkdirTemp(t, "repository-not-found-") + pair(t, temp, "codeready-toolchain", "host-operato", expectedError, applyPair) }) t.Run("error during clone: cloneDir provided is not a directory", func(t *testing.T) { - filePath := "/tmp/file.txt" - file, err := os.Create(filePath) + file, err := os.CreateTemp("", "file-") require.NoError(t, err) defer file.Close() - defer os.Remove(filePath) + defer os.Remove(file.Name()) - expectedError := fmt.Errorf("cloneDir /tmp/file.txt provided is not a directory") - pair(t, filePath, "codeready-toolchain", "host-operator", expectedError, &PairingService{}) + expectedError := fmt.Errorf("failed to clone repository: path is not a directory: %s", file.Name()) + pair(t, file.Name(), "codeready-toolchain", "host-operator", expectedError, applyPair) }) - t.Run("cloneDir already exists (should clean it and return no error)", func(t *testing.T) { + t.Run("not running in ci, cloneDir already exists (should clean it and return no error)", func(t *testing.T) { // folder already exists cloneDir := "/tmp/host-operator" require.NoError(t, os.Mkdir(cloneDir, os.ModePerm)) - pair(t, cloneDir, "codeready-toolchain", "host-operator", nil, &PairingService{}) + pair(t, cloneDir, "codeready-toolchain", "host-operator", nil, applyPair) }) t.Run("not running in ci", func(t *testing.T) { - pair(t, "/tmp/not-running-in-ci", "codeready-toolchain", "host-operator", nil, &PairingService{}) + temp := mkdirTemp(t, "not-running-in-ci-") + pair(t, temp, "codeready-toolchain", "host-operator", nil, applyPair) }) - t.Run("running in ci - gh action", func(t *testing.T) { + t.Run("running in ci - gh action - no pairing", func(t *testing.T) { t.Setenv("CI", "true") t.Setenv("GITHUB_ACTIONS", "true") - t.Setenv("AUTHOR", "rsoaresd") - t.Setenv("GITHUB_HEAD_REF", "clean_only_when_test_passed") + t.Setenv("AUTHOR", "cosmic") + t.Setenv("GITHUB_HEAD_REF", "branch-test") + + // pair not needed since it did not found 'branch-test' branch + SetupGockWithCleanup(t, "/cosmic/ksctl.git/info/refs", "00484f0b3f2ae6b774416cc91e779cca4a8bb71af054 refs/heads/branch", http.StatusOK, "service", "git-upload-pack") - pair(t, "/tmp/running-in-gh-action", "kubesaw", "ksctl", nil, &PairingService{}) + temp := mkdirTemp(t, "running-in-ci-gh-action-") + pair(t, temp, "kubesaw", "ksctl", nil, applyPair) }) - t.Run("running in ci - prow job", func(t *testing.T) { + t.Run("running in ci - prow job - no pairing", func(t *testing.T) { t.Setenv("CI", "true") - t.Setenv("JOB_SPEC", `{"type":"presubmit","job":"pull-ci-codeready-toolchain-toolchain-e2e-master-e2e","buildid":"1889023022812106752","prowjobid":"9ccb229f-aebf-45d6-90e2-2388663e8b9a","refs":{"org":"codeready-toolchain","repo":"toolchain-e2e","repo_link":"https://github.com/codeready-toolchain/toolchain-e2e","base_ref":"master","base_sha":"47ac08434063871caf78c8f3d6dbab6df61ecb63","base_link":"https://github.com/codeready-toolchain/toolchain-e2e/commit/47ac08434063871caf78c8f3d6dbab6df61ecb63","pulls":[{"number":1113,"author":"rsoaresd","sha":"67ece9d9716bcc8556f91c0c909ecea4b7c17bff","head_ref":"test-pairing","link":"https://github.com/codeready-toolchain/toolchain-e2e/pull/1113","commit_link":"https://github.com/codeready-toolchain/toolchain-e2e/pull/1113/commits/67ece9d9716bcc8556f91c0c909ecea4b7c17bff","author_link":"https://github.com/rsoaresd"}]},"decoration_config":{"timeout":"2h0m0s","grace_period":"15s","utility_images":{"clonerefs":"us-docker.pkg.dev/k8s-infra-prow/images/clonerefs:v20250205-e871edfd1","initupload":"us-docker.pkg.dev/k8s-infra-prow/images/initupload:v20250205-e871edfd1","entrypoint":"us-docker.pkg.dev/k8s-infra-prow/images/entrypoint:v20250205-e871edfd1","sidecar":"us-docker.pkg.dev/k8s-infra-prow/images/sidecar:v20250205-e871edfd1"},"resources":{"clonerefs":{"limits":{"memory":"3Gi"},"requests":{"cpu":"100m","memory":"500Mi"}},"initupload":{"limits":{"memory":"200Mi"},"requests":{"cpu":"100m","memory":"50Mi"}},"place_entrypoint":{"limits":{"memory":"100Mi"},"requests":{"cpu":"100m","memory":"25Mi"}},"sidecar":{"limits":{"memory":"2Gi"},"requests":{"cpu":"100m","memory":"250Mi"}}},"gcs_configuration":{"bucket":"test-platform-results","path_strategy":"single","default_org":"openshift","default_repo":"origin","mediaTypes":{"log":"text/plain"},"compress_file_types":["txt","log","json","tar","html","yaml"]},"gcs_credentials_secret":"gce-sa-credentials-gcs-publisher","skip_cloning":true,"censor_secrets":true}}`) - pair(t, "/tmp/running-in-ci-prow-job", "codeready-toolchain", "host-operator", nil, &PairingService{}) + prowJob, err := os.ReadFile("testdata/prow_job.json") + require.NoError(t, err) + t.Setenv("JOB_SPEC", string(prowJob)) + + // pair not needed since it did not found 'branch-test' branch + SetupGockWithCleanup(t, "/cosmic/host-operator.git/info/refs", "00484f0b3f2ae6b774416cc91e779cca4a8bb71af054 refs/heads/branch", http.StatusOK, "service", "git-upload-pack") + + temp := mkdirTemp(t, "running-in-ci-prow-job-") + pair(t, temp, "codeready-toolchain", "host-operator", nil, applyPair) }) @@ -69,17 +74,25 @@ func TestPair(t *testing.T) { t.Setenv("JOB_SPEC", `"type"`) expectedError := fmt.Errorf("error when parsing openshift job spec data: json: cannot unmarshal string into Go value of type cmd.JobSpec") - pair(t, "/tmp/running-in-ci-prow-job", "codeready-toolchain", "host-operator", expectedError, &PairingService{}) + temp := mkdirTemp(t, "running-in-ci-prow-job-") + pair(t, temp, "codeready-toolchain", "host-operator", expectedError, applyPair) }) t.Run("running in ci - should pair", func(t *testing.T) { t.Setenv("CI", "true") t.Setenv("GITHUB_ACTIONS", "true") - t.Setenv("AUTHOR", "rsoaresd") + t.Setenv("AUTHOR", "cosmic") t.Setenv("GITHUB_HEAD_REF", "master") - pairingServiceMock := new(MockPairingService) - pair(t, "/tmp/should-pair", "codeready-toolchain", "host-operator", nil, pairingServiceMock) + // pair needed since it found 'master' branch + SetupGockWithCleanup(t, "/cosmic/host-operator.git/info/refs", "00484f0b3f2ae6b774416cc91e779cca4a8bb71af054 refs/heads/master", http.StatusOK, "service", "git-upload-pack") + + fakeApplyPair := func(repo *git.Repository, forkRepoURL, remoteBranch string) error { + return nil + } + + temp := mkdirTemp(t, "should-pair-") + pair(t, temp, "codeready-toolchain", "host-operator", nil, fakeApplyPair) }) t.Run("running in ci - failed to pair", func(t *testing.T) { @@ -89,19 +102,21 @@ func TestPair(t *testing.T) { t.Setenv("GITHUB_HEAD_REF", "clean_only_when_test_passed") expectedError := fmt.Errorf("failed to get repo: Repository not found.") - pair(t, "/tmp/running-in-gh-action", "kubesaw", "ksctl", expectedError, &PairingService{}) + temp := mkdirTemp(t, "running-in-gh-action-") + pair(t, temp, "kubesaw", "ksctl", expectedError, applyPair) }) t.Run("running in ci - but not running in OpenShift-CI job either GH action", func(t *testing.T) { t.Setenv("CI", "true") expectedError := fmt.Errorf("not running in OpenShift-CI job either GH action") - pair(t, "/tmp/running-in-gh-action", "kubesaw", "ksctl", expectedError, &PairingService{}) + temp := mkdirTemp(t, "running-in-gh-action-") + pair(t, temp, "kubesaw", "ksctl", expectedError, applyPair) }) } -func pair(t *testing.T, cloneDir, org, repo string, expectedError error, p PairingServiceInterface) { - err := Pair(cloneDir, org, repo, p) +func pair(t *testing.T, cloneDir, org, repo string, expectedError error, applyPair ApplyPairFunc) { + err := Pair(cloneDir, org, repo, applyPair) defer func() { if err := os.RemoveAll(cloneDir); err != nil { @@ -115,3 +130,19 @@ func pair(t *testing.T, cloneDir, org, repo string, expectedError error, p Pairi require.EqualError(t, err, expectedError.Error()) } } + +func SetupGockWithCleanup(t *testing.T, path string, body string, statusCode int, matchKey, matchValue string) { + gock.New("https://github.com"). + Get(path). + MatchParam(matchKey, matchValue). + Persist(). + Reply(statusCode). + BodyString(body) + t.Cleanup(gock.OffAll) +} + +func mkdirTemp(t *testing.T, dirName string) string { + temp, err := os.MkdirTemp("", dirName) + require.NoError(t, err) + return temp +} diff --git a/pairing/pkg/cmd/testdata/prow_job.json b/pairing/pkg/cmd/testdata/prow_job.json new file mode 100644 index 0000000..cb669ac --- /dev/null +++ b/pairing/pkg/cmd/testdata/prow_job.json @@ -0,0 +1,93 @@ +{ + "type": "presubmit", + "job": "pull-ci-codeready-toolchain-toolchain-e2e-master-e2e", + "buildid": "1889023022812106752", + "prowjobid": "9ccb229f-aebf-45d6-90e2-2388663e8b9a", + "refs": { + "org": "codeready-toolchain", + "repo": "toolchain-e2e", + "repo_link": "https://github.com/codeready-toolchain/toolchain-e2e", + "base_ref": "master", + "base_sha": "47ac08434063871caf78c8f3d6dbab6df61ecb63", + "base_link": "https://github.com/codeready-toolchain/toolchain-e2e/commit/47ac08434063871caf78c8f3d6dbab6df61ecb63", + "pulls": [ + { + "number": 1113, + "author": "cosmic", + "sha": "67ece9d9716bcc8556f91c0c909ecea4b7c17bff", + "head_ref": "branch-test", + "link": "https://github.com/codeready-toolchain/toolchain-e2e/pull/1113", + "commit_link": "https://github.com/codeready-toolchain/toolchain-e2e/pull/1113/commits/67ece9d9716bcc8556f91c0c909ecea4b7c17bff", + "author_link": "https://github.com/cosmic" + } + ] + }, + "decoration_config": { + "timeout": "2h0m0s", + "grace_period": "15s", + "utility_images": { + "clonerefs": "us-docker.pkg.dev/k8s-infra-prow/images/clonerefs:v20250205-e871edfd1", + "initupload": "us-docker.pkg.dev/k8s-infra-prow/images/initupload:v20250205-e871edfd1", + "entrypoint": "us-docker.pkg.dev/k8s-infra-prow/images/entrypoint:v20250205-e871edfd1", + "sidecar": "us-docker.pkg.dev/k8s-infra-prow/images/sidecar:v20250205-e871edfd1" + }, + "resources": { + "clonerefs": { + "limits": { + "memory": "3Gi" + }, + "requests": { + "cpu": "100m", + "memory": "500Mi" + } + }, + "initupload": { + "limits": { + "memory": "200Mi" + }, + "requests": { + "cpu": "100m", + "memory": "50Mi" + } + }, + "place_entrypoint": { + "limits": { + "memory": "100Mi" + }, + "requests": { + "cpu": "100m", + "memory": "25Mi" + } + }, + "sidecar": { + "limits": { + "memory": "2Gi" + }, + "requests": { + "cpu": "100m", + "memory": "250Mi" + } + } + }, + "gcs_configuration": { + "bucket": "test-platform-results", + "path_strategy": "single", + "default_org": "openshift", + "default_repo": "origin", + "mediaTypes": { + "log": "text/plain" + }, + "compress_file_types": [ + "txt", + "log", + "json", + "tar", + "html", + "yaml" + ] + }, + "gcs_credentials_secret": "gce-sa-credentials-gcs-publisher", + "skip_cloning": true, + "censor_secrets": true + } +} \ No newline at end of file diff --git a/pairing/workload-analyzer b/pairing/workload-analyzer deleted file mode 160000 index 9699bcb..0000000 --- a/pairing/workload-analyzer +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9699bcb9f5634b740a7cda1616823b1e31a2260b