diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 850b861..5c5fffd 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -12,7 +12,7 @@ jobs: - name: set up golang uses: actions/setup-go@v5 with: - go-version: 1.22 + go-version: 1.25 - name: unit test run: go test -parallel 4 -covermode=atomic -coverprofile=profile.cov -v ./... - name: upload coverage to codecov diff --git a/.github/workflows/create-pull-request.yml b/.github/workflows/create-pull-request.yml index a72d0d7..37e68ed 100644 --- a/.github/workflows/create-pull-request.yml +++ b/.github/workflows/create-pull-request.yml @@ -13,7 +13,7 @@ jobs: uses: actions/checkout@v4 - name: Create Pull Request env: - GH_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }} run: | # Get current branch name branch_name=$(git symbolic-ref --short HEAD) diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 4bf830b..ccfe0be 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -1,16 +1,12 @@ name: Lint Golang code -on: [pull_request] +on: + pull_request: + branches-ignore: + - master jobs: - lint: - runs-on: ubuntu-latest - steps: - - name: check out code - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: 1.22 - cache: false - - name: run linter - uses: golangci/golangci-lint-action@v6 - with: - version: v1.58.2 + call-lint: + uses: dictyBase/workflows/.github/workflows/golang-lint.yaml@develop + with: + repository: ${{ github.repository }} + ref: ${{ github.event.pull_request.head.sha }} + version: v2.8.0-alpine diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 77bc98e..183be6d 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -12,7 +12,7 @@ jobs: - name: setup golang uses: actions/setup-go@v5 with: - go-version: 1.22 + go-version: 1.25 - name: generate and publish binary if: startsWith(github.ref, 'refs/tags/') uses: goreleaser/goreleaser-action@v6 diff --git a/.github/workflows/testcov.yaml b/.github/workflows/testcov.yaml index 625170a..4d942e2 100644 --- a/.github/workflows/testcov.yaml +++ b/.github/workflows/testcov.yaml @@ -7,7 +7,7 @@ jobs: - name: set up golang uses: actions/setup-go@v5 with: - go-version: 1.22 + go-version: 1.25 - name: check out code uses: actions/checkout@v4 - name: unit test diff --git a/.golangci.yml b/.golangci.yml index aa482e6..a42b0d5 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,61 +1,74 @@ -linters-settings: - lll: - line-length: 2380 - funlen: - lines: 75 - errcheck: - ignore : "" +version: "2" +run: + allow-parallel-runners: true linters: - # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint - disable-all: true + default: none enable: - asciicheck + - bidichk - bodyclose - - cyclop + - copyloopvar + - cyclop - decorder - dogsled - - dupl - - errcheck - - errname - funlen - - gochecknoinits + - funcorder - goconst - - gocritic - gocyclo - - godot - - gofmt - - goimports - gosec - - gosimple - govet - gocognit + - godoclint + - intrange - ineffassign + - importas + - inamedparam + - interfacebloat - lll + - loggercheck - maintidx - misspell + - mnd + - modernize - nakedret - nestif - - nilerr - - nolintlint - - prealloc - - paralleltest + - protogetter - revive - - rowserrcheck + - recvcheck - staticcheck - - stylecheck - - typecheck + - testifylint + - modernize - unconvert - - thelper - - tparallel + - usestdlibvars - unparam - unused - - unconvert - - unparam - - varnamelen - - wastedassign - whitespace - - wrapcheck - - # don't enable: - # - godox - maligned,prealloc - # - gochecknoglobals + - wsl_v5 + settings: + funlen: + lines: 120 + statements: 50 + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - third_party$ + - builtin$ + - examples$ + - internal/baserow/ + - internal/k8s/ +formatters: + enable: + - gofumpt + - goimports + - golines + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..3beb554 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,22 @@ +# AGENTS.md + + +## Essential Commands + +```bash +# Test +gotestsum --format pkgname-and-test-fails --format-hide-empty-pkg -- ./... + +# Test (verbose) +gotestsum --format testdox --format-hide-empty-pkg -- ./... + +# Watch mode +gotestsum --watch --format pkgname-and-test-fails --format-hide-empty-pkg -- ./... + +# Lint +golangci-lint run ./... + +# Format +golangci-lint fmt + + diff --git a/build/package/Dockerfile b/build/package/Dockerfile index 44f7113..805d375 100644 --- a/build/package/Dockerfile +++ b/build/package/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.22-bullseye AS builder +FROM golang:1.25-trixie AS builder LABEL maintainer="Siddhartha Basu " ENV GOPROXY https://proxy.golang.org ENV GO111MODULE=on \ diff --git a/cmd/github-actions/main.go b/cmd/github-actions/main.go index 307cc04..e9b6d6e 100644 --- a/cmd/github-actions/main.go +++ b/cmd/github-actions/main.go @@ -39,6 +39,7 @@ func main() { Value: "dictyBase", }, } + app.Commands = []cli.Command{ cmd.IssueCommentCmds(), cmd.CommentsCountByDateCmds(), diff --git a/go.mod b/go.mod index 304a978..c24de0f 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/dictyBase-docker/github-actions -go 1.22 +go 1.25.0 require ( github.com/Jeffail/gabs/v2 v2.7.0 @@ -10,45 +10,44 @@ require ( github.com/repeale/fp-go v0.11.1 github.com/sethvargo/go-githubactions v1.3.0 github.com/sirupsen/logrus v1.9.3 - github.com/stretchr/testify v1.10.0 + github.com/stretchr/testify v1.11.1 github.com/urfave/cli v1.22.16 - golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d - golang.org/x/oauth2 v0.26.0 - golang.org/x/sync v0.11.0 - golang.org/x/text v0.22.0 - google.golang.org/api v0.219.0 + golang.org/x/exp v0.0.0-20260508232706-74f9aab9d74a + golang.org/x/oauth2 v0.36.0 + golang.org/x/sync v0.20.0 + golang.org/x/text v0.37.0 + google.golang.org/api v0.279.0 ) require ( - cloud.google.com/go/auth v0.14.0 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect - cloud.google.com/go/compute/metadata v0.6.0 // indirect + cloud.google.com/go/auth v0.20.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-ini/ini v1.66.6 // indirect - github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect - github.com/googleapis/gax-go/v2 v2.14.1 // indirect - github.com/kr/text v0.2.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.15 // indirect + github.com/googleapis/gax-go/v2 v2.22.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect - go.opentelemetry.io/otel v1.32.0 // indirect - go.opentelemetry.io/otel/metric v1.32.0 // indirect - go.opentelemetry.io/otel/trace v1.32.0 // indirect - golang.org/x/crypto v0.32.0 // indirect - golang.org/x/net v0.34.0 // indirect - golang.org/x/sys v0.29.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 // indirect - google.golang.org/grpc v1.70.0 // indirect - google.golang.org/protobuf v1.36.4 // indirect - gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect + go.opentelemetry.io/otel v1.43.0 // indirect + go.opentelemetry.io/otel/metric v1.43.0 // indirect + go.opentelemetry.io/otel/trace v1.43.0 // indirect + golang.org/x/crypto v0.51.0 // indirect + golang.org/x/net v0.54.0 // indirect + golang.org/x/sys v0.44.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60 // indirect + google.golang.org/grpc v1.81.1 // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 41202d3..0aa9f1f 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,16 @@ -cloud.google.com/go/auth v0.14.0 h1:A5C4dKV/Spdvxcl0ggWwWEzzP7AZMJSEIgrkngwhGYM= -cloud.google.com/go/auth v0.14.0/go.mod h1:CYsoRL1PdiDuqeQpZE0bP2pnPrGqFcOkI0nldEQis+A= -cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= -cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= -cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= -cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= +cloud.google.com/go/auth v0.20.0 h1:kXTssoVb4azsVDoUiF8KvxAqrsQcQtB53DcSgta74CA= +cloud.google.com/go/auth v0.20.0/go.mod h1:942/yi/itH1SsmpyrbnTMDgGfdy2BUqIKyd0cyYLc5Q= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/Jeffail/gabs/v2 v2.7.0 h1:Y2edYaTcE8ZpRsR2AtmPu5xQdFDIthFG0jYhu5PY8kg= github.com/Jeffail/gabs/v2 v2.7.0/go.mod h1:dp5ocw1FvBBQYssgHsG7I1WYsiLRtkUaB1FEtSwvNUw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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= @@ -18,16 +19,16 @@ github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/go-ini/ini v1.66.6 h1:h6k2Bb0HWS/BXXHCXj4QHjxPmlIU4NK+7MuLp9SD+4k= github.com/go-ini/ini v1.66.6/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 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-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II= github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= github.com/google/go-github/v62 v62.0.0 h1:/6mGCaRywZz9MuHyw9gD1CwsbmBX8GWsbFkwMmHdhl4= @@ -39,24 +40,24 @@ github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= -github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= -github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= -github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= -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/googleapis/enterprise-certificate-proxy v0.3.15 h1:xolVQTEXusUcAA5UgtyRLjelpFFHWlPQ4XfWGc7MBas= +github.com/googleapis/enterprise-certificate-proxy v0.3.15/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= +github.com/googleapis/gax-go/v2 v2.22.0 h1:PjIWBpgGIVKGoCXuiCoP64altEJCj3/Ei+kSU5vlZD4= +github.com/googleapis/gax-go/v2 v2.22.0/go.mod h1:irWBbALSr0Sk3qlqb9SyJ1h68WjgeFuiOzI4Rqw5+aY= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/minio/minio-go v6.0.14+incompatible h1:fnV+GD28LeqdN6vT2XdGKW8Qe/IfjJDswNVuni6km9o= github.com/minio/minio-go v6.0.14+incompatible/go.mod h1:7guKYtitv8dktvNUGrhzmNlA5wrAABTQXCoesZdFQO8= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 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/repeale/fp-go v0.11.1 h1:Q/e+gNyyHaxKAyfdbBqvip3DxhVWH453R+kthvSr9Mk= github.com/repeale/fp-go v0.11.1/go.mod h1:4KrwQJB1VRY+06CA+jTc4baZetr6o2PeuqnKr5ybQUc= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sethvargo/go-githubactions v1.3.0 h1:Kg633LIUV2IrJsqy2MfveiED/Ouo+H2P0itWS0eLh8A= @@ -72,57 +73,63 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -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/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ= github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= -go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= -go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= -go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= -go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= -go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= -go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= -go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= -go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= -go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= -go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -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-20220613132600-b0d781184e0d h1:vtUKgx8dahOomfFzLREU8nSv25YHnTgLBn4rDnWZdU0= -golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= +golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= +golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= +golang.org/x/exp v0.0.0-20260508232706-74f9aab9d74a h1:+3jdDGGB8NGb1Zktc737jlt3/A5f6UlwSzmvqUuufxw= +golang.org/x/exp v0.0.0-20260508232706-74f9aab9d74a/go.mod h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -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/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= +golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= -golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= +golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.219.0 h1:nnKIvxKs/06jWawp2liznTBnMRQBEPpGo7I+oEypTX0= -google.golang.org/api v0.219.0/go.mod h1:K6OmjGm+NtLrIkHxv1U3a0qIf/0JOvAHd5O/6AoyKYE= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/api v0.279.0 h1:hsx2M2OaRcaKtVYK6vXEUnQvdjnend7ZYES+lYaot74= +google.golang.org/api v0.279.0/go.mod h1:B9TqLBwJqVjp1mtt7WeoQwWRwvu/400y5lETOql+giQ= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= -google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 h1:91mG8dNTpkC0uChJUQ9zCiRqx3GEEFOWaRZ0mI6Oj2I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= -google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= -google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= -google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= -google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0= +google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I= +google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 h1:41r6JMbpzBMen0R/4TZeeAmGXSJC7DftGINUodzTkPI= +google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:EIQZ5bFCfRQDV4MhRle7+OgjNtZ6P1PiZBgAKuxXu/Y= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60 h1:seT2EwLWM78plQ7wcDfuWBc/4FAEAXDDiaSol4ku4qo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= +google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/app/analytics/analytics.go b/internal/app/analytics/analytics.go index c1810c5..76a86d6 100644 --- a/internal/app/analytics/analytics.go +++ b/internal/app/analytics/analytics.go @@ -40,11 +40,16 @@ func generateReportRequest(clt *cli.Context) *ga.ReportRequest { } func Report(clt *cli.Context) error { - srv, err := ga.NewService(context.Background(), option.WithCredentialsFile(clt.String("credential-file"))) + srv, err := ga.NewService( + context.Background(), + option.WithAuthCredentialsFile(option.ServiceAccount, clt.String("credential-file")), + ) if err != nil { return fmt.Errorf("error in creating service client %s", err) } + rq := &ga.GetReportsRequest{ReportRequests: []*ga.ReportRequest{generateReportRequest(clt)}} + res, err := ga.NewReportsService(srv).BatchGet(rq).Do() if err != nil { return fmt.Errorf("error in running the query %s", err) @@ -55,19 +60,25 @@ func Report(clt *cli.Context) error { func writeOutput(clt *cli.Context, res *ga.GetReportsResponse) error { fhr := os.Stdout + if len(clt.String("output")) > 1 { ch, err := os.Create(clt.String("output")) if err != nil { return fmt.Errorf("error in creating file %s %s", clt.String("output"), err) } + fhr = ch } + defer fhr.Close() + wrt := csv.NewWriter(fhr) + err := wrt.Write(processReportHeader(res)) if err != nil { return fmt.Errorf("error in writing header %s", err) } + for _, row := range res.Reports[0].Data.Rows { for _, metric := range row.Metrics { dataRow := slices.Insert(metric.Values, 0, fmtDate(row.Dimensions[0])) @@ -76,7 +87,9 @@ func writeOutput(clt *cli.Context, res *ga.GetReportsResponse) error { } } } + wrt.Flush() + if err := wrt.Error(); err != nil { return fmt.Errorf("error in finishing csv output %s", err) } diff --git a/internal/app/chart/chart.go b/internal/app/chart/chart.go index 55e4d7c..f6edc0c 100644 --- a/internal/app/chart/chart.go +++ b/internal/app/chart/chart.go @@ -5,13 +5,16 @@ import ( "github.com/urfave/cli" ) +const exitFailure = 2 + func DeployChart(clt *cli.Context) error { helm, err := runner.NewHelm() if err != nil { - return cli.NewExitError(err.Error(), 2) + return cli.NewExitError(err.Error(), exitFailure) } + if err := helm.IsConnected(); err != nil { - return cli.NewExitError(err.Error(), 2) + return cli.NewExitError(err.Error(), exitFailure) } return installOrUpgrade(clt, helm) @@ -20,8 +23,9 @@ func DeployChart(clt *cli.Context) error { func installOrUpgrade(clt *cli.Context, helm *runner.Helm) error { isok, err := helm.IsChartDeployed(clt.String("name")) if err != nil { - return cli.NewExitError(err.Error(), 2) + return cli.NewExitError(err.Error(), exitFailure) } + prc := &runner.ChartParams{ Name: clt.String("name"), Namespace: clt.String("namespace"), @@ -30,11 +34,11 @@ func installOrUpgrade(clt *cli.Context, helm *runner.Helm) error { } if isok { if err := helm.UpgradeChart(prc); err != nil { - return cli.NewExitError(err.Error(), 2) + return cli.NewExitError(err.Error(), exitFailure) } } else { if err := helm.InstallChart(prc); err != nil { - return cli.NewExitError(err.Error(), 2) + return cli.NewExitError(err.Error(), exitFailure) } } diff --git a/internal/app/chatops/deploy.go b/internal/app/chatops/deploy.go index e0981eb..a6b6f5b 100644 --- a/internal/app/chatops/deploy.go +++ b/internal/app/chatops/deploy.go @@ -56,7 +56,8 @@ type Inputs struct { // WorkflowDispatchEvent is triggered when someone triggers a workflow run on GitHub or // sends a POST request to the create a workflow dispatch event endpoint. // -// GitHub API docs: https://docs.github.com/en/developers/webhooks-and-events/webhook-events-and-payloads#workflow_dispatch +// GitHub API docs: +// https://docs.github.com/en/developers/webhooks-and-events/webhook-events-and-payloads#workflow_dispatch type WorkflowDispatchEvent struct { Inputs json.RawMessage `json:"inputs,omitempty"` Ref *string `json:"ref,omitempty"` @@ -75,10 +76,12 @@ type Output struct { func getWorkflowInputsFromJSON(r io.Reader) (*Inputs, error) { inp := &Inputs{} + w := &WorkflowDispatchEvent{} if err := json.NewDecoder(r).Decode(w); err != nil { return inp, fmt.Errorf("error in decoding json %s", err) } + if err := json.Unmarshal(w.Inputs, &inp); err != nil { return inp, fmt.Errorf("error in decoding json data to struct %s", err) } @@ -92,24 +95,30 @@ func ParseDeployCommand(clt *cli.Context) error { return fmt.Errorf("error in reading content from file %s", err) } defer r.Close() + pjson, err := getWorkflowInputsFromJSON(r) if err != nil { return err } + act := githubactions.New() log := logger.GetLogger(clt) + oinput, err := parseWorkflowInputs(pjson) if err != nil { return fmt.Errorf("error in parsing workflow inputs %s", err) } + imageTag := oinput.ImageTag // add image tag prefixes for developers if clt.Bool("frontend") && pjson.Cluster == "erickube" { imageTag = fmt.Sprintf("ericdev-%s", oinput.ImageTag) } + if clt.Bool("frontend") && pjson.Cluster == "siddkube" { imageTag = fmt.Sprintf("devsidd-%s", oinput.ImageTag) } + act.SetOutput("image_tag", imageTag) act.SetOutput("ref", oinput.Ref) log.Info("added all keys to the output") @@ -128,6 +137,7 @@ func parseWorkflowInputs(param *Inputs) (*Output, error) { ctx: context.Background(), branchClient: client.Repositories, } + if strings.Contains(param.URL, "pull") { o, err := parsePR(prc, param) if err != nil { @@ -136,6 +146,7 @@ func parseWorkflowInputs(param *Inputs) (*Output, error) { return o, nil } + o, err := parseIssue(bclient, param) if err != nil { return out, err @@ -146,6 +157,7 @@ func parseWorkflowInputs(param *Inputs) (*Output, error) { func parsePR(prc *pullRequestClient, param *Inputs) (*Output, error) { out := &Output{} + if param.Commit == "" { ref, err := prc.getHeadCommitFromPR( param.RepositoryName, @@ -155,11 +167,13 @@ func parsePR(prc *pullRequestClient, param *Inputs) (*Output, error) { if err != nil { return out, err } + out.ImageTag = fmt.Sprintf("pr-%s-%s", param.IssueNumber, ref[0:7]) out.Ref = ref return out, nil } + out.ImageTag = fmt.Sprintf("pr-%s-%s", param.IssueNumber, param.Commit[0:7]) out.Ref = param.Commit @@ -173,6 +187,7 @@ func (prc *pullRequestClient) getHeadCommitFromPR( if err != nil { return "", fmt.Errorf("error converting string to int %s", err) } + pgr, _, err := prc.pullRequestClient.Get( context.Background(), owner, @@ -188,6 +203,7 @@ func (prc *pullRequestClient) getHeadCommitFromPR( func parseIssue(bc *branchClient, param *Inputs) (*Output, error) { out := &Output{} + if param.Branch != "" { ref, err := bc.getHeadCommitFromBranch( param.RepositoryName, @@ -197,12 +213,14 @@ func parseIssue(bc *branchClient, param *Inputs) (*Output, error) { if err != nil { return out, err } + cb := strings.ReplaceAll(param.Branch, "/", "-") out.ImageTag = fmt.Sprintf("%s-%s", cb, ref[0:7]) out.Ref = ref return out, nil } + if param.Commit != "" { out.ImageTag = param.Commit[0:7] out.Ref = param.Commit diff --git a/internal/app/chatops/deploy_test.go b/internal/app/chatops/deploy_test.go index 5b24630..5d0bd54 100644 --- a/internal/app/chatops/deploy_test.go +++ b/internal/app/chatops/deploy_test.go @@ -39,13 +39,16 @@ func (m *mockBranchClient) GetBranch( func openTestJSON(filename string) (*os.File, error) { file := &os.File{} + dir, err := os.Getwd() if err != nil { return file, fmt.Errorf("unable to get current dir %s", err) } + path := filepath.Join( filepath.Dir(dir), "../../testdata", filename, ) + r, err := os.Open(path) if err != nil { return file, fmt.Errorf("error in reading content from file %s", err) @@ -65,21 +68,19 @@ func TestGetWorkflowInputsFromJSON(t *testing.T) { err, "should not receive error from extracting workflow inputs", ) - assert.Equal(input.Cluster, "erickube", "should match cluster") + assert.Equal("erickube", input.Cluster, "should match cluster") assert.Equal( - input.URL, "https://github.com/dictybase-playground/github-actions-experiments/pull/18#issuecomment-690700284", + input.URL, "should match html-url", ) - assert.Equal(input.IssueNumber, "18", "should match issue number") + assert.Equal("18", input.IssueNumber, "should match issue number") assert.Equal( - input.RepositoryName, - "github-actions-experiments", + "github-actions-experiments", input.RepositoryName, "should match repository name", ) assert.Equal( - input.RepositoryOwner, - "dictybase-playground", + "dictybase-playground", input.RepositoryOwner, "should match repository owner", ) assert.Empty(input.Commit, "should have empty commit value") @@ -94,8 +95,7 @@ func TestGetWorkflowInputsFromJSON(t *testing.T) { ) assert.Empty(ijson.Commit, "should have empty commit value") assert.Equal( - ijson.Branch, - "feature/new-command", + "feature/new-command", ijson.Branch, "should have empty branch value", ) // check json payload for commits @@ -108,8 +108,7 @@ func TestGetWorkflowInputsFromJSON(t *testing.T) { ) assert.Empty(ijson3.Branch, "should have empty branch value") assert.Equal( - ijson3.Commit, - "f85f132b3a986c12eb0c2a61d60a5c3dd8347bf3", + "f85f132b3a986c12eb0c2a61d60a5c3dd8347bf3", ijson3.Commit, "should match commit value", ) } @@ -136,7 +135,7 @@ func TestParsePR(t *testing.T) { } o, err := parsePR(prc, inp) assert.NoError(err, "should not have error from parsing pr") - assert.Equal(o.ImageTag, "pr-9-f85f132", "should match pr image tag") + assert.Equal("pr-9-f85f132", o.ImageTag, "should match pr image tag") assert.Equal(o.Ref, inp.Commit, "should match ref value") // test output when not given a commit @@ -145,7 +144,7 @@ func TestParsePR(t *testing.T) { } iss2, err := parsePR(prc, i2) assert.NoError(err, "should not have error from parsing pr") - assert.Equal(iss2.ImageTag, "pr-9-17f9184", "should match pr image tag") + assert.Equal("pr-9-17f9184", iss2.ImageTag, "should match pr image tag") assert.Equal(iss2.Ref, mockSHA, "should match ref value") } @@ -171,7 +170,7 @@ func TestParseIssue(t *testing.T) { } o, err := parseIssue(bcl, inp) assert.NoError(err, "should not have error from parsing issue") - assert.Equal(o.ImageTag, "f85f132", "should match commit image tag") + assert.Equal("f85f132", o.ImageTag, "should match commit image tag") assert.Equal(o.Ref, inp.Commit, "should match ref value") // test when given a branch i2 := &Inputs{ @@ -181,8 +180,7 @@ func TestParseIssue(t *testing.T) { iss2, err := parseIssue(bcl, i2) assert.NoError(err, "should not have error from parsing issue") assert.Equal( - iss2.ImageTag, - "feature-new-command-17f9184", + "feature-new-command-17f9184", iss2.ImageTag, "should match branch image tag", ) assert.Equal(iss2.Ref, mockSHA, "should match ref value") diff --git a/internal/app/comment/ontology.go b/internal/app/comment/ontology.go index 986b556..7eb8cbc 100644 --- a/internal/app/comment/ontology.go +++ b/internal/app/comment/ontology.go @@ -63,15 +63,19 @@ type reportContent struct { Violations []string } +const exitFailure = 2 + func OntoReportOnPullComment(clt *cli.Context) error { cf, err := listCommittedFiles(clt.String("commit-list-file")) if err != nil { - return cli.NewExitError(err.Error(), 2) + return cli.NewExitError(err.Error(), exitFailure) } + rps, err := ontoReport(clt, cf) if err != nil { - return cli.NewExitError(err.Error(), 2) + return cli.NewExitError(err.Error(), exitFailure) } + err = createCommentFromReport(&reportParams{ prid: clt.Int("pull-request-id"), repository: clt.GlobalString("repository"), @@ -81,7 +85,7 @@ func OntoReportOnPullComment(clt *cli.Context) error { data: rps, }) if err != nil { - return cli.NewExitError(err.Error(), 2) + return cli.NewExitError(err.Error(), exitFailure) } return reportStatusError(rps) @@ -92,6 +96,7 @@ func ontoReport( cf []string, ) (map[string][]*reportContent, error) { rcs := make(map[string][]*reportContent) + for _, folder := range cf { html, err := readHTMLContent( fmt.Sprintf( @@ -102,6 +107,7 @@ func ontoReport( if err != nil { return rcs, err } + viol, err := ontology.ParseViolations( fmt.Sprintf("%s/%s.json", clt.String("report-dir"), folder), "ERROR", @@ -110,6 +116,7 @@ func ontoReport( if !ontology.IsViolationNotFound(err) { return rcs, fmt.Errorf("ontology not found %s", err) } + if _, ok := rcs["pass"]; ok { rcs["pass"] = append(rcs["pass"], &reportContent{ Name: fmt.Sprintf("%s.obo", folder), @@ -121,6 +128,7 @@ func ontoReport( continue } + if _, ok := rcs["fail"]; ok { rcs["fail"] = append(rcs["fail"], &reportContent{ Name: fmt.Sprintf("%s.obo", folder), @@ -130,6 +138,7 @@ func ontoReport( continue } + rcs["fail"] = []*reportContent{{ Name: fmt.Sprintf("%s.obo", folder), Violations: viol, @@ -144,6 +153,7 @@ func readHTMLContent(file string) (string, error) { if _, err := os.Stat(file); os.IsNotExist(err) { return "", nil } + ct, err := os.ReadFile(file) if err != nil { return "", fmt.Errorf("error in reading file %s", err) @@ -156,7 +166,7 @@ func reportStatusError(rs map[string][]*reportContent) error { if _, ok := rs["fail"]; ok { return cli.NewExitError( fmt.Sprintf("failed report count %d", len(rs["fail"])), - 2, + exitFailure, ) } @@ -168,10 +178,12 @@ func createCommentFromReport(args *reportParams) error { if err != nil { return fmt.Errorf("error in getting github client %s", err) } + mkd, err := mkdownOutput(args.data) if err != nil { return err } + _, _, err = gclient.Issues.CreateComment( context.Background(), args.owner, @@ -187,12 +199,14 @@ func createCommentFromReport(args *reportParams) error { return nil } -func mkdownOutput(data interface{}) (*bytes.Buffer, error) { +func mkdownOutput(data any) (*bytes.Buffer, error) { out := bytes.NewBufferString("") + t, err := template.New("onto-report").Parse(tmpl) if err != nil { return out, fmt.Errorf("error in parsing template %s", err) } + if err := t.Execute(out, data); err != nil { return out, fmt.Errorf("error in executing template %s", err) } @@ -202,15 +216,18 @@ func mkdownOutput(data interface{}) (*bytes.Buffer, error) { func listCommittedFiles(path string) ([]string, error) { var afiles []string + r, err := os.Open(path) if err != nil { return afiles, fmt.Errorf("unable to open file %s", err) } defer r.Close() + scanner := bufio.NewScanner(r) for scanner.Scan() { afiles = append(afiles, baseNoSuffix(scanner.Text())) } + if err := scanner.Err(); err != nil { return afiles, fmt.Errorf("error from scanning %s", err) } diff --git a/internal/app/comment/ontology_test.go b/internal/app/comment/ontology_test.go index f8b09bc..45628f3 100644 --- a/internal/app/comment/ontology_test.go +++ b/internal/app/comment/ontology_test.go @@ -3,7 +3,6 @@ package comment import ( "fmt" "os" - "strings" "testing" "github.com/stretchr/testify/require" @@ -116,6 +115,7 @@ func failAndPassData() map[string][]*reportContent { func TestMkdownOutput(t *testing.T) { t.Parallel() + htmlStr := []string{ "Full report", "bootstrap", @@ -127,6 +127,7 @@ func TestMkdownOutput(t *testing.T) { assert := require.New(t) bout, err := mkdownOutput(failAndPassData()) assert.NoError(err, "should not produce any error from template execution") + subslice := []string{ "dicty_env", "dicty_pheno", @@ -135,13 +136,16 @@ func TestMkdownOutput(t *testing.T) { "green is good", } for _, n := range subslice { - assert.True(strings.Contains(bout.String(), n)) + assert.Contains(bout.String(), n) } + for _, s := range htmlStr { assert.Containsf(bout.String(), s, "should have the string %s", s) } + bout, err = mkdownOutput(failData()) assert.NoError(err, "should not produce any error from template execution") + subslice = []string{ "dicty_env", "dicty_pheno", @@ -149,23 +153,29 @@ func TestMkdownOutput(t *testing.T) { "green is good", } for _, n := range subslice { - assert.True(strings.Contains(bout.String(), n)) + assert.Contains(bout.String(), n) } - assert.False(strings.Contains(bout.String(), "dicty_assay")) + + assert.NotContains(bout.String(), "dicty_assay") + for _, s := range htmlStr { assert.Containsf(bout.String(), s, "should have the string %s", s) } + bout, err = mkdownOutput(passData()) assert.NoError(err, "should not produce any error from template execution") + subslice = []string{ "dicty_assay", "dicty_flower", } for _, n := range subslice { - assert.True(strings.Contains(bout.String(), n)) + assert.Contains(bout.String(), n) } - assert.False(strings.Contains(bout.String(), "dicty_pheno")) - assert.False(strings.Contains(bout.String(), "best of the best")) + + assert.NotContains(bout.String(), "dicty_pheno") + assert.NotContains(bout.String(), "best of the best") + for _, s := range htmlStr { assert.Containsf(bout.String(), s, "should have the string %s", s) } @@ -179,7 +189,9 @@ func TestListCommittedFiles(t *testing.T) { err, "should not throw error from creating a temp file", ) + defer os.Remove(tmpf.Name()) + content := []string{"/onto/dicty_assay.obo", "/pronto/dicty_flower.obo"} for _, line := range content { if _, err := fmt.Fprintf(tmpf, "%s\n", line); err != nil { @@ -189,6 +201,7 @@ func TestListCommittedFiles(t *testing.T) { ) } } + files, err := listCommittedFiles(tmpf.Name()) assert.NoError(err, "should not throw error from getting the list") assert.ElementsMatch( diff --git a/internal/app/dagger/dagger.go b/internal/app/dagger/dagger.go index 1c09849..7985b47 100644 --- a/internal/app/dagger/dagger.go +++ b/internal/app/dagger/dagger.go @@ -22,25 +22,38 @@ import ( const ( owner = "dagger" repo = "dagger" + + // chunkSize represents the chunk size (1KB) for copying the dagger binary. + chunkSize = 1024 + + // execFileMode represents the file mode for the dagger binary. + execFileMode = 0o755 + + exitFailure = 2 ) // SetupDaggerCheckSum sets up the Dagger checksum and outputs it to GitHub Actions. func SetupDaggerCheckSum(clt *cli.Context) error { var dver string + if len(clt.String("version")) == 0 { ver, err := fetchDaggerVersion() if err != nil { - return cli.NewExitError(err.Error(), 2) + return cli.NewExitError(err.Error(), exitFailure) } + dver = ver } else { dver = fmt.Sprintf("v%s", clt.String("version")) } + gclient := github.NewClient(nil) + rel, err := fetchDaggerRelease(gclient, dver) if err != nil { - return cli.NewExitError(err.Error(), 2) + return cli.NewExitError(err.Error(), exitFailure) } + checksum, err := fetchDaggerCheckSum( clt.String("checksum-file"), clt.String("dagger-file"), @@ -48,11 +61,13 @@ func SetupDaggerCheckSum(clt *cli.Context) error { rel, ) if err != nil { - return cli.NewExitError(err.Error(), 2) + return cli.NewExitError(err.Error(), exitFailure) } + gha := githubactions.New() gha.SetOutput("dagger_version", dver) gha.SetOutput("dagger_bin_checksum", checksum) + return nil } @@ -61,10 +76,12 @@ func SetupDaggerBin(clt *cli.Context) error { dver := clt.String("dagger-version") binDir := clt.String("dagger-bin-dir") gclient := github.NewClient(nil) + rel, err := fetchDaggerRelease(gclient, dver) if err != nil { - return cli.NewExitError(err.Error(), 2) + return cli.NewExitError(err.Error(), exitFailure) } + err = fetchDaggerBinary( clt.String("dagger-file"), dver, @@ -73,8 +90,9 @@ func SetupDaggerBin(clt *cli.Context) error { rel, ) if err != nil { - return cli.NewExitError(err.Error(), 2) + return cli.NewExitError(err.Error(), exitFailure) } + return nil } @@ -89,6 +107,7 @@ func fetchDaggerRelease( if err != nil { return nil, handleError("error in fetching release %s", err) } + return rel, nil } @@ -98,15 +117,18 @@ func fetchDaggerBinary( rel *github.RepositoryRelease, ) error { tarballName := fmt.Sprintf("dagger_%s_%s", ver, fileSuffix) + idx, err := findTarballIndex(rel, tarballName) if err != nil { return err } + reader, err := downloadReleaseAsset(gclient, rel.Assets[idx].GetID()) if err != nil { return err } defer reader.Close() + return extractTarball(reader, filepath.Join(binDir, "dagger")) } @@ -123,6 +145,7 @@ func findTarballIndex( errors.New("could not find dagger tarball file"), ) } + return idx, nil } @@ -139,6 +162,7 @@ func downloadReleaseAsset( if err != nil { return nil, handleError("error in downloading asset %s", err) } + return reader, nil } @@ -148,6 +172,7 @@ func extractTarball(reader io.ReadCloser, binFileName string) error { return handleError("extractTarGz: NewReader failed: %w", err) } defer uncompressedStream.Close() + tarReader := tar.NewReader(uncompressedStream) for { header, err := tarReader.Next() @@ -155,15 +180,18 @@ func extractTarball(reader io.ReadCloser, binFileName string) error { if err == io.EOF { break } + return handleError("extractTarGz: Next() failed: %w", err) } + if header.Name != "dagger" { continue } + writer, err := os.OpenFile( binFileName, os.O_CREATE|os.O_RDWR, - os.FileMode(0755), + os.FileMode(execFileMode), ) if err != nil { return handleError( @@ -171,20 +199,24 @@ func extractTarball(reader io.ReadCloser, binFileName string) error { err, ) } + for { - _, err := io.CopyN(writer, tarReader, 1024) + _, err := io.CopyN(writer, tarReader, chunkSize) if err != nil { if err == io.EOF { break } + return handleError( "error in writing dagger bin file in temp dir %s", err, ) } } + defer writer.Close() } + return nil } @@ -194,6 +226,7 @@ func fetchDaggerCheckSum( rel *github.RepositoryRelease, ) (string, error) { var empty string + idx := slices.IndexFunc(rel.Assets, func(ast *github.ReleaseAsset) bool { return ast.GetName() == checksumFileName }) @@ -203,6 +236,7 @@ func fetchDaggerCheckSum( errors.New("could not find checksum file"), ) } + reader, _, err := gclient.Repositories.DownloadReleaseAsset( context.Background(), owner, repo, @@ -212,7 +246,9 @@ func fetchDaggerCheckSum( if err != nil { return empty, handleError("error in downloading asset %s", err) } + var line string + scanner := bufio.NewScanner(reader) for scanner.Scan() { if strings.Contains(scanner.Text(), daggerFileName) { @@ -220,6 +256,7 @@ func fetchDaggerCheckSum( break } } + if err := scanner.Err(); err != nil { return empty, handleError("error in reading checksum file %s", err) } @@ -229,11 +266,13 @@ func fetchDaggerCheckSum( func fetchDaggerVersion() (string, error) { var empty string + resp, err := http.Get("https://dl.dagger.io/dagger/latest_version") if err != nil { return empty, handleError("error in fetching dagger version %s", err) } defer resp.Body.Close() + bcont, err := io.ReadAll(resp.Body) if err != nil { return empty, handleError("error in reading response body", err) @@ -253,6 +292,7 @@ func handleError(msg string, err error) error { func RemoveInvalidControlChars(strc string) string { var builder strings.Builder + for _, rtc := range strc { if rtc >= 32 && rtc != 127 { builder.WriteRune(rtc) diff --git a/internal/app/deploy/deploy.go b/internal/app/deploy/deploy.go index 48204b9..e80c315 100644 --- a/internal/app/deploy/deploy.go +++ b/internal/app/deploy/deploy.go @@ -10,18 +10,23 @@ import ( "github.com/urfave/cli" ) +const exitFailure = 2 + func Status(clt *cli.Context) error { logger := logger.GetLogger(clt) + gclient, err := client.GetGithubClient(clt.GlobalString("token")) if err != nil { return cli.NewExitError( fmt.Sprintf("error in getting github client %s", err), - 2, + exitFailure, ) } + state := clt.String("state") url := clt.String("url") desc := fmt.Sprintf("setting deployment status %s", clt.String("state")) + dsp, _, err := gclient.Repositories.CreateDeploymentStatus( context.Background(), clt.GlobalString("owner"), @@ -39,9 +44,10 @@ func Status(clt *cli.Context) error { state, err, ), - 2, + exitFailure, ) } + logger.Infof( "created deployment status %s with id %d", dsp.GetState(), diff --git a/internal/app/deploy/share.go b/internal/app/deploy/share.go index 7dff7be..3147312 100644 --- a/internal/app/deploy/share.go +++ b/internal/app/deploy/share.go @@ -23,10 +23,12 @@ type Payload struct { func GetPayload(data []byte) (*Payload, error) { var s string + pld := new(Payload) if err := json.Unmarshal(data, &s); err != nil { return pld, fmt.Errorf("error in decoding json data to string %s", err) } + if err := json.Unmarshal([]byte(s), pld); err != nil { return pld, fmt.Errorf("error in decoding string to structure %s", err) } @@ -40,16 +42,20 @@ func ShareDeployPayload(clt *cli.Context) error { return fmt.Errorf("error in reading content from file %s", err) } defer pld.Close() + gdl := &github.Deployment{} if err := json.NewDecoder(pld).Decode(gdl); err != nil { return fmt.Errorf("error in decoding json %s", err) } + pgdl, err := GetPayload(gdl.Payload) if err != nil { return err } + act := githubactions.New() log := logger.GetLogger(clt) + act.SetOutput("id", strconv.Itoa(int(gdl.GetID()))) act.SetOutput("url", gdl.GetURL()) act.SetOutput("cluster", pgdl.Cluster) diff --git a/internal/app/gcloud/gcloud.go b/internal/app/gcloud/gcloud.go index 01be960..9709fc6 100644 --- a/internal/app/gcloud/gcloud.go +++ b/internal/app/gcloud/gcloud.go @@ -5,18 +5,21 @@ import ( "github.com/urfave/cli" ) +const exitFailure = 2 + func K8sClusterCredentials(clt *cli.Context) error { gcloud, err := runner.NewGcloud() if err != nil { - return cli.NewExitError(err.Error(), 2) + return cli.NewExitError(err.Error(), exitFailure) } + err = gcloud.GetClusterCredentials( clt.String("project"), clt.String("zone"), clt.String("cluster"), ) if err != nil { - return cli.NewExitError(err.Error(), 2) + return cli.NewExitError(err.Error(), exitFailure) } return nil diff --git a/internal/app/issue/issue.go b/internal/app/issue/issue.go index 1ddd577..4848c91 100644 --- a/internal/app/issue/issue.go +++ b/internal/app/issue/issue.go @@ -15,6 +15,12 @@ import ( "github.com/urfave/cli" ) +const ( + issuesPerPage = 15 + commentsPerPage = 30 + exitFailure = 2 +) + const ( layout = "01/02/2006" dateFilterlayout = "2006-01-02" @@ -26,13 +32,16 @@ func CommentsCountByDate(clt *cli.Context) error { if err != nil { return cli.NewExitError( fmt.Sprintf("error in getting github client %s", err), - 2, + exitFailure, ) } + opt := &github.SearchOptions{ - ListOptions: github.ListOptions{PerPage: 15}, + ListOptions: github.ListOptions{PerPage: issuesPerPage}, } + var totalComments, totalIssues int + query := fmt.Sprintf( "repo:%s/%s created:>=%s", clt.GlobalString("owner"), @@ -48,20 +57,25 @@ func CommentsCountByDate(clt *cli.Context) error { if err != nil { return cli.NewExitError( fmt.Sprintf("error in fetching issues %s", err), - 2, + exitFailure, ) } + totalIssues += len(result.Issues) for _, iss := range result.Issues { totalComments += *iss.Comments } + if resp.NextPage == 0 { break } + opt.Page = resp.NextPage } + fmt.Printf("total no of issues %d\n", totalIssues) fmt.Printf("total no of comments %d\n", totalComments) + return nil } @@ -72,6 +86,7 @@ func CommentsReport(clt *cli.Context) error { } else { fname = fmt.Sprintf("%s-%s.csv", clt.String("output"), time.Now().Format(fileLayout)) } + output, err := os.Create(fname) if err != nil { return cli.NewExitError( @@ -80,22 +95,26 @@ func CommentsReport(clt *cli.Context) error { clt.String("output"), err, ), - 2, + exitFailure, ) } defer output.Close() + writer := csv.NewWriter(output) + gclient, err := client.GetGithubClient(clt.GlobalString("token")) if err != nil { return cli.NewExitError( fmt.Sprintf("error in getting github client %s", err), - 2, + exitFailure, ) } + count, err := writeIssues(clt, gclient, writer) if err != nil { - return cli.NewExitError(err.Error(), 2) + return cli.NewExitError(err.Error(), exitFailure) } + logger.GetLogger(clt).Infof("wrote %d records in the report", count) return nil @@ -107,6 +126,7 @@ func writeIssues( writer *csv.Writer, ) (int, error) { count := 0 + err := writer.Write([]string{ "Issue ID", "Title", "Total Comments", "Status", "Created On", "Closed On", @@ -114,6 +134,7 @@ func writeIssues( if err != nil { return count, fmt.Errorf("error in writing file header %s", err) } + opt := issueOpts(clt) for { issues, resp, err := gclient.Issues.ListByRepo( @@ -125,14 +146,17 @@ func writeIssues( if err != nil { return count, fmt.Errorf("error in fetching issues %s", err) } + for _, iss := range issues { if iss.IsPullRequest() { continue } + var closedStr string if iss.GetState() == "closed" { closedStr = iss.GetClosedAt().Format(layout) } + err := writer.Write([]string{ strconv.Itoa(iss.GetNumber()), iss.GetTitle(), @@ -147,14 +171,19 @@ func writeIssues( err, ) } + count++ } + if resp.NextPage == 0 { break } + opt.Page = resp.NextPage } + writer.Flush() + if err := writer.Error(); err != nil { return count, fmt.Errorf("error in writing %s", err) } @@ -166,6 +195,6 @@ func issueOpts(c *cli.Context) *github.IssueListByRepoOptions { return &github.IssueListByRepoOptions{ State: c.String("state"), Sort: "comments", - ListOptions: github.ListOptions{PerPage: 30}, + ListOptions: github.ListOptions{PerPage: commentsPerPage}, } } diff --git a/internal/app/repository/commit.go b/internal/app/repository/commit.go index ec86635..7205a1b 100644 --- a/internal/app/repository/commit.go +++ b/internal/app/repository/commit.go @@ -11,22 +11,26 @@ import ( "github.com/urfave/cli" ) +const exitFailure = 2 + func FilesCommited(clt *cli.Context) error { gclient, err := client.GetLegacyGithubClient(clt.GlobalString("token")) if err != nil { return cli.NewExitError( fmt.Sprintf("error in getting github client %s", err), - 2, + exitFailure, ) } + inp, err := os.Open(clt.String("payload-file")) if err != nil { return cli.NewExitError( fmt.Sprintf("error in reading content from file %s", err), - 2, + exitFailure, ) } defer inp.Close() + files, err := gh.FilterCommittedFiles(&gh.CommittedFilesParams{ Client: gclient, Input: inp, @@ -35,15 +39,18 @@ func FilesCommited(clt *cli.Context) error { SkipDeleted: clt.BoolT("skip-deleted"), }) if err != nil { - return cli.NewExitError(err.Error(), 2) + return cli.NewExitError(err.Error(), exitFailure) } + log := logger.GetLogger(clt) if len(files) == 0 { log.Warn("no committed file found matching the criteria") return nil } + out := os.Stdout + if len(clt.String("output")) > 0 { wout, err := os.Create(clt.String("output")) if err != nil { @@ -51,10 +58,12 @@ func FilesCommited(clt *cli.Context) error { fmt.Errorf( "error in creating file %s %s", clt.String("output"), - err), 2) + err), exitFailure) } + out = wout } + fmt.Fprint(out, strings.Join(files, "\n")) log.Infof("%d files has changed in the push", len(files)) diff --git a/internal/app/repository/file.go b/internal/app/repository/file.go index d897262..f87bc05 100644 --- a/internal/app/repository/file.go +++ b/internal/app/repository/file.go @@ -21,16 +21,19 @@ func BatchMultiRepo(clt *cli.Context) error { gclient, err := client.GetGithubClient(clt.GlobalString("token")) if err != nil { return cli.NewExitError( - fmt.Sprintf("error in getting github client %s", err), 2) + fmt.Sprintf("error in getting github client %s", err), exitFailure) } + rfl, wbc, err := readFiles(clt) if err != nil { - return cli.NewExitError(err.Error(), 2) + return cli.NewExitError(err.Error(), exitFailure) } + path := fmt.Sprintf( "%s/%s", clt.String("repository-path"), filepath.Base(clt.String("input-file")), ) + msg := github.String( fmt.Sprintf("adding %s file", filepath.Base(clt.String("input-file")), @@ -51,9 +54,10 @@ func BatchMultiRepo(clt *cli.Context) error { fmt.Sprintf("error in adding file %s to repository %s %s", path, fmt.Sprintf("%s/%s", rpo.owner, rpo.name), err, ), - 2, + exitFailure, ) } + logger.GetLogger(clt).Debugf( "uploaded file %s to repository %s", path, fmt.Sprintf("%s/%s", rpo.owner, rpo.name), @@ -65,6 +69,7 @@ func BatchMultiRepo(clt *cli.Context) error { func readFiles(clt *cli.Context) ([]byte, []byte, error) { var byr []byte + rpl, err := os.ReadFile(clt.String("repository-list")) if err != nil { return rpl, byr, fmt.Errorf( @@ -73,6 +78,7 @@ func readFiles(clt *cli.Context) ([]byte, []byte, error) { err, ) } + wnc, err := os.ReadFile(clt.String("input-file")) if err != nil { return rpl, wnc, fmt.Errorf( @@ -87,7 +93,8 @@ func readFiles(clt *cli.Context) ([]byte, []byte, error) { func parseOwnerRepo(str string) []*repo { rpo := make([]*repo, 0) - for _, f := range strings.Split(str, "\n") { + + for f := range strings.SplitSeq(str, "\n") { v := strings.Split(f, "/") rpo = append(rpo, &repo{owner: v[0], name: v[1]}) } diff --git a/internal/app/repository/migrate.go b/internal/app/repository/migrate.go index 771243d..1bf5a05 100644 --- a/internal/app/repository/migrate.go +++ b/internal/app/repository/migrate.go @@ -27,6 +27,7 @@ type poll struct { func (p *poll) forRepo() error { ticker := time.NewTicker(p.pollInterval) defer ticker.Stop() + OUTER: for { select { @@ -38,6 +39,7 @@ OUTER: ) if err == nil { p.repoShare <- rpg + p.log.Debugf( "polling finished for repo %s/%s", rpg.GetName(), rpg.GetOwner().GetLogin(), @@ -45,10 +47,12 @@ OUTER: break OUTER } + errResp, ok := err.(*gh.ErrorResponse) if !ok { return fmt.Errorf("unexpected github error %s", err) } + if errResp.Response.StatusCode != http.StatusNotFound { return fmt.Errorf("unexpected github error %s", err) } @@ -74,7 +78,9 @@ type migration struct { func (m *migration) createFork() error { defer close(m.repoShare) + rgr := new(errgroup.Group) + for _, repo := range m.repositories { rfc, _, err := m.client.Repositories.CreateFork( context.Background(), @@ -90,6 +96,7 @@ func (m *migration) createFork() error { repo, ) } + if _, ok := err.(*gh.AcceptedError); !ok { return fmt.Errorf( "error in creating fork for repo %s %v", @@ -97,6 +104,7 @@ func (m *migration) createFork() error { err, ) } + m.log.Debugf( "created fork for repo %s on organization %s\n", repo, rfc.GetOwner().GetLogin(), @@ -111,6 +119,7 @@ func (m *migration) createFork() error { } rgr.Go(pol.forRepo) } + if err := rgr.Wait(); err != nil { return fmt.Errorf("error after waiting %s", err) } @@ -120,8 +129,10 @@ func (m *migration) createFork() error { func (m *migration) makeArchive() error { defer close(m.repoNameShare) + for repo := range m.repoShare { repo.Archived = gh.Bool(true) + _, _, err := m.client.Repositories.Edit( context.Background(), repo.GetOwner().GetLogin(), @@ -131,7 +142,9 @@ func (m *migration) makeArchive() error { if err != nil { return fmt.Errorf("error in setting archive status %s", err) } + m.repoNameShare <- repo.GetName() + m.log.Debugf( "created archive for repo %s/%s", repo.GetName(), repo.GetOwner().GetLogin(), @@ -151,6 +164,7 @@ func (m *migration) delRepo() error { if err != nil { return fmt.Errorf("error in deleting repo %s %s", repo, err) } + m.log.Debugf("deleted repo %s", repo) } @@ -162,14 +176,17 @@ func MigrateRepositories(clt *cli.Context) error { if err != nil { return cli.NewExitError( fmt.Sprintf("error in getting github client %s", err), - 2, + exitFailure, ) } + log := logger.GetLogger(clt) deadline := time.Now(). Add(time.Duration(clt.Int64("poll-for")) * time.Second) + ctx, cancelFn := context.WithDeadline(context.Background(), deadline) defer cancelFn() + mgn := &migration{ repositories: clt.StringSlice("repo-to-move"), from: clt.GlobalString("owner"), @@ -185,12 +202,14 @@ func MigrateRepositories(clt *cli.Context) error { fgr.Go(mgn.createFork) fgr.Go(mgn.makeArchive) fgr.Go(mgn.delRepo) + if err := fgr.Wait(); err != nil { return cli.NewExitError( fmt.Sprintf("error in migrating repository %s", err), - 2, + exitFailure, ) } + log.Infof("migrated %d repositories", len(clt.StringSlice("repo-to-move"))) return nil diff --git a/internal/app/repository/migrate_test.go b/internal/app/repository/migrate_test.go index 8d934cc..aea60aa 100644 --- a/internal/app/repository/migrate_test.go +++ b/internal/app/repository/migrate_test.go @@ -16,12 +16,16 @@ import ( func TestMigrateRepositories(t *testing.T) { t.Parallel() assert := require.New(t) + server, client := fake.GhServerClient() defer server.Close() + grp := new(errgroup.Group) deadline := time.Now().Add(4 * time.Second) + ctx, cancelFn := context.WithDeadline(context.Background(), deadline) defer cancelFn() + mgn := &migration{ repositories: []string{"abc", "cde", "efg"}, from: "vandeley", diff --git a/internal/app/storage/s3.go b/internal/app/storage/s3.go index 8215b27..f25645a 100644 --- a/internal/app/storage/s3.go +++ b/internal/app/storage/s3.go @@ -8,6 +8,8 @@ import ( "github.com/urfave/cli" ) +const exitFailure = 2 + func getS3Host(clt *cli.Context) string { if len(clt.String("s3-server-port")) > 0 { return fmt.Sprintf( @@ -30,15 +32,19 @@ func SaveInS3(clt *cli.Context) error { if err != nil { return cli.NewExitError( fmt.Sprintf("error in getting minio client %s", err), - 2, + exitFailure, ) } + log := logger.GetLogger(clt) + path := clt.String("upload-path") if len(path) == 0 { path = clt.String("input") } + log.Debugf("upload path %s", path) + _, err = s3Client.FPutObject( clt.String("s3-bucket"), path, @@ -48,9 +54,10 @@ func SaveInS3(clt *cli.Context) error { if err != nil { return cli.NewExitError( fmt.Sprintf("unable to upload file %s", err), - 2, + exitFailure, ) } + log.Infof("save file %s to s3 storage", clt.String("input")) return nil diff --git a/internal/cmd/migrate.go b/internal/cmd/migrate.go index c3dda38..8ba8cf0 100644 --- a/internal/cmd/migrate.go +++ b/internal/cmd/migrate.go @@ -5,6 +5,11 @@ import ( "github.com/urfave/cli" ) +const ( + defaultPollFor = 60 + defaultPollInterval = 2 +) + func MigrateRepositories() cli.Command { return cli.Command{ Name: "migrate-repos", @@ -25,12 +30,12 @@ func MigrateRepositories() cli.Command { cli.Int64Flag{ Name: "poll-for", Usage: "threshold for polling forked repository(in seconds)", - Value: 60, + Value: defaultPollFor, }, cli.Int64Flag{ Name: "poll-interval", Usage: "polling interval for forked repository(in seconds)", - Value: 2, + Value: defaultPollInterval, }, }, } diff --git a/internal/fake/github.go b/internal/fake/github.go index 9dbbc3f..eb9454f 100644 --- a/internal/fake/github.go +++ b/internal/fake/github.go @@ -12,17 +12,21 @@ import ( func GithubCommitComparison() (*gh.CommitsComparison, error) { ccg := &gh.CommitsComparison{} + dir, err := os.Getwd() if err != nil { return ccg, fmt.Errorf("unable to get current dir %s", err) } + path := filepath.Join( filepath.Dir(dir), "../testdata", "commit-diff.json", ) + b, err := os.ReadFile(path) if err != nil { return ccg, errors.New("unable to read test file") } + if err := json.Unmarshal(b, ccg); err != nil { return ccg, fmt.Errorf("error in decoding json %s", err) } diff --git a/internal/fake/http.go b/internal/fake/http.go index 26f526e..e07b913 100644 --- a/internal/fake/http.go +++ b/internal/fake/http.go @@ -97,6 +97,7 @@ func delRoute() []*route { func routeTable() []*route { var route []*route + route = append(route, fetchRoute()...) route = append(route, postRoute()...) @@ -105,6 +106,7 @@ func routeTable() []*route { func handleNoContent(file string, w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusNoContent) + if _, err := fmt.Fprint(w, file); err != nil { http.Error( w, @@ -125,6 +127,7 @@ func handleAccepted(file string, wrt http.ResponseWriter, _ *http.Request) { return } + wrt.WriteHeader(http.StatusAccepted) fmt.Fprint(wrt, string(bfl)) } @@ -140,6 +143,7 @@ func handleSuccess(file string, wrt http.ResponseWriter, _ *http.Request) { return } + if _, err := wrt.Write(bfl); err != nil { http.Error( wrt, @@ -154,13 +158,16 @@ func router(wrt http.ResponseWriter, req *http.Request) { if req.Method != rtbl.method { continue } + if !rtbl.regexp.MatchString(req.URL.Path) { continue } + rtbl.fn(rtbl.file, wrt, req) return } + http.NotFound(wrt, req) } @@ -169,6 +176,7 @@ func payloadFile(file string) ([]byte, error) { if err != nil { return []byte(""), fmt.Errorf("unable to get current file %s", err) } + b, err := os.ReadFile(path) if err != nil { return []byte(""), fmt.Errorf("unable to read test file %s", path) diff --git a/internal/fake/payload.go b/internal/fake/payload.go index 4455bdf..806c0b9 100644 --- a/internal/fake/payload.go +++ b/internal/fake/payload.go @@ -35,15 +35,18 @@ func OntoErrorFile() (string, error) { func PullReqPayload(name string) (io.Reader, error) { var r io.Reader + dir, err := os.Getwd() if err != nil { return r, fmt.Errorf("unable to get current dir %s", err) } + path := filepath.Join( filepath.Dir(dir), "../testdata", name, ) + fhd, err := os.Open(path) if err != nil { return fhd, fmt.Errorf("error in opening file %s", err) @@ -54,11 +57,14 @@ func PullReqPayload(name string) (io.Reader, error) { func PushPayload() (io.Reader, error) { var r io.Reader + dir, err := os.Getwd() if err != nil { return r, fmt.Errorf("unable to get current dir %s", err) } + path := filepath.Join(filepath.Dir(dir), "../testdata", "push.json") + fhd, err := os.Open(path) if err != nil { return fhd, fmt.Errorf("error in opening file %s", err) diff --git a/internal/file/io.go b/internal/file/io.go index ef7a378..b6f0845 100644 --- a/internal/file/io.go +++ b/internal/file/io.go @@ -8,18 +8,24 @@ import ( ) func InputOutput(clt *cli.Context) (*os.File, *os.File, error) { - var inp *os.File - var out *os.File + var ( + inp *os.File + out *os.File + ) + r, err := os.Open(clt.String("payload-file")) if err != nil { return inp, out, fmt.Errorf("error in reading content from file %s", err) } + inp = r + if len(clt.String("output")) > 0 { w, err := os.Create(clt.String("output")) if err != nil { return inp, out, fmt.Errorf("error in creating file %s %s", clt.String("output"), err) } + out = w } else { out = os.Stdout diff --git a/internal/github/builder.go b/internal/github/builder.go index 3df1150..bc4980c 100644 --- a/internal/github/builder.go +++ b/internal/github/builder.go @@ -18,7 +18,9 @@ func (b *ChangedFilesBuilder) FilterSuffix(suffix string) *ChangedFilesBuilder { if len(b.files) == 0 { return b } + var afl []*ChangedFiles + for _, v := range b.files { if strings.HasSuffix(v.Name, suffix) { afl = append(afl, v) @@ -36,11 +38,14 @@ func (b *ChangedFilesBuilder) FilterDeleted( if len(b.files) == 0 { return b } + afl := make([]*ChangedFiles, 0) + for _, vfl := range b.files { if vfl.Change == "deleted" { continue } + afl = append(afl, vfl) } @@ -51,8 +56,10 @@ func (b *ChangedFilesBuilder) FilterUniqueByName() *ChangedFilesBuilder { if len(b.files) <= 1 { return b } + mnt := make(map[string]int) afl := make([]*ChangedFiles, 0) + for _, v := range b.files { n := path.Base(v.Name) if _, ok := mnt[n]; !ok { diff --git a/internal/github/builder_test.go b/internal/github/builder_test.go index b7fcaad..ec7ac22 100644 --- a/internal/github/builder_test.go +++ b/internal/github/builder_test.go @@ -15,6 +15,7 @@ func TestFilterUnique(t *testing.T) { err, "should not receive any error for parsing push event data", ) + files := CommittedFiles(cc).FilterUniqueByName().List() assert.Len(files, 11, "should have committed 11 unique files") assert.Contains( @@ -32,6 +33,7 @@ func TestFilterDeleted(t *testing.T) { err, "should not receive any error for parsing push event data", ) + files := CommittedFiles(cc).FilterDeleted(true).List() assert.Len(files, 14, "should have committed 14 unique files") assert.Contains( @@ -49,6 +51,7 @@ func TestFilterSuffix(t *testing.T) { err, "should not receive any error for parsing push event data", ) + files := CommittedFiles(cc).FilterSuffix("obo").List() assert.Len(files, 3, "should have committed 3 unique files") assert.Contains( @@ -66,8 +69,8 @@ func TestCommitedFiles(t *testing.T) { err, "should not receive any error for parsing push event data", ) - assert.Equal(ccg.GetStatus(), "ahead", "should match the status") - assert.Equal(ccg.GetAheadBy(), 31, "should match ahead by value") + assert.Equal("ahead", ccg.GetStatus(), "should match the status") + assert.Equal(31, ccg.GetAheadBy(), "should match ahead by value") assert.Equal( ccg.GetTotalCommits(), ccg.GetAheadBy(), @@ -90,6 +93,7 @@ func TestFilterChain(t *testing.T) { err, "should not receive any error for parsing push event data", ) + files := CommittedFiles( ccg, ).FilterSuffix("txt"). diff --git a/internal/github/manager.go b/internal/github/manager.go index d393145..17434b5 100644 --- a/internal/github/manager.go +++ b/internal/github/manager.go @@ -29,11 +29,14 @@ func (g *Manager) CommittedFilesInPull( r io.Reader, ) (*ChangedFilesBuilder, error) { var bcf *ChangedFilesBuilder + pev := &gh.PullRequestEvent{} if err := json.NewDecoder(r).Decode(pev); err != nil { return bcf, fmt.Errorf("error in decoding json %s", err) } + var after, before string + switch pev.GetAction() { case "synchronize": before = pev.GetBefore() @@ -42,6 +45,7 @@ func (g *Manager) CommittedFilesInPull( before = pev.GetPullRequest().GetBase().GetSHA() after = pev.GetPullRequest().GetHead().GetSHA() } + comc, _, err := g.client.Repositories.CompareCommits( context.Background(), pev.GetRepo().GetOwner().GetLogin(), @@ -60,10 +64,12 @@ func (g *Manager) CommittedFilesInPush( r io.Reader, ) (*ChangedFilesBuilder, error) { var bfl *ChangedFilesBuilder + pev := &gh.PushEvent{} if err := json.NewDecoder(r).Decode(pev); err != nil { return bfl, fmt.Errorf("error in decoding json %s", err) } + comc, _, err := g.client.Repositories.CompareCommits( context.Background(), pev.GetRepo().GetOwner().GetLogin(), @@ -91,8 +97,11 @@ func CommittedFiles(event *gh.CommitsComparison) *ChangedFilesBuilder { } func FilterCommittedFiles(args *CommittedFilesParams) ([]string, error) { - var fbl *ChangedFilesBuilder - var err error + var ( + fbl *ChangedFilesBuilder + err error + ) + switch args.Event { case "push": fbl, err = NewGithubManager( @@ -105,6 +114,7 @@ func FilterCommittedFiles(args *CommittedFilesParams) ([]string, error) { default: err = fmt.Errorf("event type %s not supported", args.Event) } + if err != nil { return []string{}, err } diff --git a/internal/github/manager_test.go b/internal/github/manager_test.go index 57e46b9..8b74d57 100644 --- a/internal/github/manager_test.go +++ b/internal/github/manager_test.go @@ -23,8 +23,10 @@ func TestCommittedFilesInpush(t *testing.T) { assert := require.New(t) r, err := fake.PushPayload() assert.NoError(err, "should not receive any error from reading push payload") + server, client := fake.GhServerClient() defer server.Close() + b, err := NewGithubManager(client).CommittedFilesInPush(r) assert.NoError( err, @@ -78,8 +80,10 @@ func testPull(t *testing.T, name string) { err, "should not receive any error from reading payload for push", ) + server, client := fake.GhServerClient() defer server.Close() + b, err := NewGithubManager(client).CommittedFilesInPull(reqp) assert.NoError( err, diff --git a/internal/logger/logger.go b/internal/logger/logger.go index e6cfbd0..ba31027 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -10,6 +10,7 @@ import ( func GetLogger(clt *cli.Context) *logrus.Entry { log := logrus.New() log.Out = os.Stderr + switch clt.GlobalString("log-format") { case "text": log.Formatter = &logrus.TextFormatter{ @@ -20,6 +21,7 @@ func GetLogger(clt *cli.Context) *logrus.Entry { TimestampFormat: "02/Jan/2006:15:04:05", } } + l := clt.GlobalString("log-level") switch l { case "debug": diff --git a/internal/ontology/report.go b/internal/ontology/report.go index 4c81e5e..b8269cc 100644 --- a/internal/ontology/report.go +++ b/internal/ontology/report.go @@ -13,26 +13,34 @@ func ParseViolations(path string, level string) ([]string, error) { if err != nil { return []string{}, fmt.Errorf("error in parsing json file %s", err) } + hasLevel := false + var violCont *gabs.Container + for _, child := range cont.Children() { v, ok := child.Search("level").Data().(string) if !ok { return []string{""}, errors.New("incompatible report format, level key not found") } + if v != level { continue } + violCont = child hasLevel = true break } + children := violCont.S("violations").Children() if !hasLevel || len(children) == 0 { return []string{}, &ViolationNotFoundError{Level: level} } + var slc []string + for _, child := range children { for k := range child.ChildrenMap() { slc = append(slc, strings.ReplaceAll(k, "_", " ")) diff --git a/internal/ontology/report_test.go b/internal/ontology/report_test.go index ec0a811..ae006b8 100644 --- a/internal/ontology/report_test.go +++ b/internal/ontology/report_test.go @@ -43,6 +43,7 @@ func TestParseViolations(t *testing.T) { "missing ontology title", "should have missing ontology title violation", ) + _, err = ParseViolations(fhd, "FATAL") assert.True(IsViolationNotFound(err), "should be violation not found error") } diff --git a/internal/runner/helm.go b/internal/runner/helm.go index a992d9b..4d265f8 100644 --- a/internal/runner/helm.go +++ b/internal/runner/helm.go @@ -84,11 +84,13 @@ func (h *Helm) IsChartDeployed(name string) (bool, error) { fmt.Sprintf("^%s$", name), "--short", ) + output, err := cmd.Output() if err != nil { return false, fmt.Errorf("error %s in running command %s", err, cmd.String()) } + trimmed := bytes.TrimSpace(output) if name == string(trimmed) { return true, nil @@ -111,7 +113,9 @@ func (h *Helm) ServerVersion() (string, error) { "--server", "--short", ) + var out bytes.Buffer + cmd.Stdout = &out if err := cmd.Run(); err != nil { return "", fmt.Errorf("error in getting helm version %s", err)