diff --git a/.github/workflows/_main.yml b/.github/workflows/_main.yml index b59f16e..e14c900 100644 --- a/.github/workflows/_main.yml +++ b/.github/workflows/_main.yml @@ -7,19 +7,83 @@ on: - '**.md' jobs: + build: + runs-on: ubuntu-latest + permissions: + pull-requests: read + outputs: + config: ${{ steps.filter.outputs.config }} + milestone: ${{ steps.filter.outputs.milestone }} + labels: ${{ steps.filter.outputs.labels }} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + config: + - "pkg/config/config.yaml" + milestone: + - "cmd/milestones/**" + labels: + - "cmd/labels/**" + lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v3 - run: go install github.com/rhysd/actionlint/cmd/actionlint@latest - run: | GOPATH="$(go env GOPATH)" export PATH="${PATH}:${GOPATH}/bin" actionlint + tests: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v3 - run: go test ./... + + check-milestones: + needs: build + if: ${{ needs.build.outputs.config == 'true' || needs.build.outputs.milestone == 'true' }} + runs-on: ubuntu-latest + permissions: read-all + env: + GITHUB_TOKEN: ${{ github.token }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v3 + # - name: Get Token + # id: get_workflow_token + # uses: peter-murray/workflow-application-token-action@v3 + # with: + # application_id: ${{ vars.KONVEYOR_BOT_ID }} + # application_private_key: ${{ secrets.KONVEYOR_BOT_KEY }} + # - env: + # GITHUB_TOKEN: ${{ steps.get_workflow_token.outputs.token }} + # run: go run cmd/milestones/main.go -config pkg/config/config.yaml + - run: go run cmd/milestones/main.go -config pkg/config/config.yaml -log-level 8 + + check-labels: + needs: build + if: ${{ needs.build.outputs.config == 'true' || needs.build.outputs.milestone == 'true' }} + runs-on: ubuntu-latest + permissions: read-all + env: + GITHUB_TOKEN: ${{ github.token }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v3 + # - name: Get Token + # id: get_workflow_token + # uses: peter-murray/workflow-application-token-action@v3 + # with: + # application_id: ${{ vars.KONVEYOR_BOT_ID }} + # application_private_key: ${{ secrets.KONVEYOR_BOT_KEY }} + # - env: + # GITHUB_TOKEN: ${{ steps.get_workflow_token.outputs.token }} + # run: go run cmd/labels/main.go -config pkg/config/config.yaml + - run: go run cmd/labels/main.go -config pkg/config/config.yaml diff --git a/cmd/milestones/main.go b/cmd/milestones/main.go index b99cb7b..9d0f2e3 100644 --- a/cmd/milestones/main.go +++ b/cmd/milestones/main.go @@ -3,14 +3,15 @@ package main import ( "context" "flag" - "fmt" - "log" "os" + "strconv" "time" + "github.com/bombsimon/logrusr/v3" "github.com/google/go-github/v55/github" "github.com/konveyor/release-tools/pkg/action" "github.com/konveyor/release-tools/pkg/config" + "github.com/sirupsen/logrus" "gopkg.in/yaml.v2" ) @@ -24,16 +25,32 @@ type Update struct { Issues []int } +var ( + configPath = flag.String("config", "", "Path to config.yaml") + confirm = flag.Bool("confirm", false, "Make mutating changes to labels via GitHub API") + logLevel = flag.Int("log-level", 5, "Level to log") +) + func main() { - configPtr := flag.String("config", "", "Path to config.yaml") - confirmPtr := flag.Bool("confirm", false, "Make mutating changes to labels via GitHub API") flag.Parse() - configPath := *configPtr - confirm := *confirmPtr + + logrusLog := logrus.New() + logrusLog.SetOutput(os.Stdout) + logrusLog.SetFormatter(&logrus.TextFormatter{}) + logrusLog.SetLevel(logrus.Level(5)) + log := logrusr.New(logrusLog) + + if logLevel != nil && *logLevel != 5 { + logrusLog.SetLevel(logrus.Level(*logLevel)) + } + + configPath := *configPath + confirm := *confirm c, err := config.LoadConfig(configPath) if err != nil { - log.Fatal(err) + log.Error(err, "failed to load config") + os.Exit(1) } wantedMilestones := c.Milestones @@ -49,12 +66,14 @@ func main() { updates := []Update{} for _, r := range c.Repos { + log.V(2).Info("Getting milestone for repository", "org", r.Org, "repo", r.Repo) var currentMilestones []*github.Milestone for { milestones, resp, err := client.Issues.ListMilestones(context.Background(), r.Org, r.Repo, milestoneListOptions) if err != nil { action.ErrorCommand("Failed to get repo milestones") - log.Fatal(err) + log.Error(err, "failed to get repo milestones") + os.Exit(1) } currentMilestones = append(currentMilestones, milestones...) if resp.NextPage == 0 { @@ -65,18 +84,38 @@ func main() { currentMilestonesMap := make(map[string]*github.Milestone) for _, m := range currentMilestones { + log.V(2).Info("adding milestone to map", "milestone", struct { + Title string `json:"title"` + Description string `json:"description"` + Number int `json:"number"` + State string `json:"state"` + Due github.Timestamp `json:"due"` + OpenIssues int `json:"openIssues"` + ClosedIssues int `json:"closedIssues"` + }{ + Title: m.GetTitle(), + Description: m.GetDescription(), + Number: m.GetNumber(), + State: m.GetState(), + Due: m.GetDueOn(), + OpenIssues: m.GetOpenIssues(), + ClosedIssues: m.GetClosedIssues(), + }) currentMilestonesMap[m.GetTitle()] = m } for _, m := range wantedMilestones { wantMilestone := m + log.V(2).Info("wanted milestone", "milestone", wantMilestone) repoIssues := []int{} if wantMilestone.Replaces != "" { oldMilestone, oldMilestoneExists := currentMilestonesMap[wantMilestone.Replaces] - if oldMilestoneExists { + if oldMilestoneExists && oldMilestone.GetOpenIssues() > 0 { + log.V(2).Info("old milestone exists", "want milestone title", wantMilestone.Title, "replaces", wantMilestone.Replaces, "open issues", oldMilestone.GetOpenIssues()) issueListByRepoOpts := &github.IssueListByRepoOptions{ - Milestone: fmt.Sprintf("%x", oldMilestone.GetNumber()), + Milestone: strconv.Itoa(oldMilestone.GetNumber()), + State: "open", ListOptions: listOptions, } @@ -85,7 +124,8 @@ func main() { issues, resp, err := client.Issues.ListByRepo(context.Background(), r.Org, r.Repo, issueListByRepoOpts) if err != nil { action.ErrorCommand("Failed to get repo issues") - log.Fatal(err) + log.Error(err, "failed to get repo issues") + os.Exit(1) } for _, i := range issues { @@ -145,7 +185,7 @@ func main() { } y, _ := yaml.Marshal(updates) - log.Print(string(y)) + log.Info(string(y)) if !confirm { action.NoticeCommand("Running without confirm, no mutations will be made") @@ -157,7 +197,8 @@ func main() { if update.Wanted.Due != "" { parsedTime, err := time.Parse(time.DateOnly, update.Wanted.Due) if err != nil { - log.Fatal(err) + log.Error(err, "failed to parse time") + os.Exit(1) } // add some time to make sure it registers as correct day dueOn = &github.Timestamp{Time: parsedTime.Add(12 * time.Hour)} @@ -174,9 +215,10 @@ func main() { }) if err != nil { action.ErrorCommand("Error creating milestone") - log.Fatal(err) + log.Error(err, "error creating milestone") + os.Exit(1) } - log.Printf("[%v/%v] Milestone %v created", update.Org, update.Repo, update.Wanted) + log.Info("[%v/%v] Milestone %v created", update.Org, update.Repo, update.Wanted) case "changed": milestone, _, err = client.Issues.EditMilestone(context.Background(), update.Org, update.Repo, update.Current.Number, &github.Milestone{ Title: github.String(update.Wanted.Title), @@ -186,9 +228,10 @@ func main() { }) if err != nil { action.ErrorCommand("Error modifying milestone") - log.Fatal(err) + log.Error(err, "error modifying milestone") + os.Exit(1) } - log.Printf("[%v/%v] Milestone %v updated", update.Org, update.Repo, update.Wanted) + log.Info("Milestone updated", "org", update.Org, "repo", update.Repo, "milestone", update.Wanted) default: panic("Should not happen") } @@ -200,9 +243,10 @@ func main() { }) if err != nil { action.ErrorCommand("Failed to move issue to milestone") - log.Fatal(err) + log.Error(err, "error moving issue to milestone") + os.Exit(1) } - log.Printf("[%v/%v] Issue %v added to milestone %v", update.Org, update.Repo, i, update.Wanted.Title) + log.Info("Issue added to milestone", "org", update.Org, "repo", update.Repo, "issue", i, "milestone", update.Wanted.Title) } } diff --git a/go.mod b/go.mod index d046b35..eef5c90 100644 --- a/go.mod +++ b/go.mod @@ -4,14 +4,18 @@ go 1.19 require ( github.com/Netflix/go-env v0.0.0-20220526054621-78278af1949d + github.com/bombsimon/logrusr/v3 v3.1.0 github.com/google/go-github/v55 v55.0.0 + github.com/sirupsen/logrus v1.9.3 golang.org/x/oauth2 v0.12.0 + gopkg.in/yaml.v2 v2.4.0 sigs.k8s.io/yaml v1.3.0 ) require ( github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect github.com/cloudflare/circl v1.3.3 // indirect + github.com/go-logr/logr v1.2.3 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-querystring v1.1.0 // indirect golang.org/x/crypto v0.13.0 // indirect @@ -19,5 +23,4 @@ require ( golang.org/x/sys v0.12.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.31.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 7b363e7..997a55a 100644 --- a/go.sum +++ b/go.sum @@ -2,12 +2,17 @@ github.com/Netflix/go-env v0.0.0-20220526054621-78278af1949d h1:wvStE9wLpws31NiW github.com/Netflix/go-env v0.0.0-20220526054621-78278af1949d/go.mod h1:9XMFaCeRyW7fC9XJOWQ+NdAv8VLG7ys7l3x4ozEGLUQ= github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= +github.com/bombsimon/logrusr/v3 v3.1.0 h1:zORbLM943D+hDMGgyjMhSAz/iDz86ZV72qaak/CA0zQ= +github.com/bombsimon/logrusr/v3 v3.1.0/go.mod h1:PksPPgSFEL2I52pla2glgCyyd2OqOHAnFF5E+g8Ixco= github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= @@ -19,6 +24,13 @@ github.com/google/go-github/v55 v55.0.0 h1:4pp/1tNMB9X/LuAhs5i0KQAE40NmiR/y6prLN github.com/google/go-github/v55 v55.0.0/go.mod h1:JLahOTA1DnXzhxEymmFF5PP2tSS9JVNj68mSZNDwskA= 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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= 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.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= @@ -33,6 +45,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -51,5 +64,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/pkg/config/config.yaml b/pkg/config/config.yaml index bae31f4..4c59274 100644 --- a/pkg/config/config.yaml +++ b/pkg/config/config.yaml @@ -130,14 +130,16 @@ milestones: description: The v0.3.0 release of Konveyor state: closed due: 2024-01-24 - replaces: 0.3-beta.2 + replaces: v0.3-beta.2 - title: v0.3.1 description: The v0.3.1 release of Konveyor state: closed + replaces: v0.3.0 - title: v0.3.2 description: The v0.3.2 release of Konveyor state: open - replaces: 0.3.1 + replaces: v0.3.1 + due: 2024-05-02 - title: v0.4.0 description: The v0.4.0 release of Konveyor state: open