Skip to content

Commit

Permalink
Feature/dependency pinning and update automation (kubernetes-sigs#5451)
Browse files Browse the repository at this point in the history
* * handle local flag
* add managerfactory handling for local flag
* add shortName handling for local flag
* add dot git file handling for local flag
* add tests

* fix normal listing

* add ParseGitRepository function, add viper, add testing for utils

* add latest tag logic, add auto pinning and auto fetching

* makke gorepomod list works with --local

* make pinning works with local flag, enable auto update on fork and non-fork repo

* fix: refactor to pass linter

* refactor code and fix comments

* edit README

* refactor code to pass linting

* refactor code

* refactor code and enable patch branch label

* ru add license

* fbackward compatibility for unpin
  • Loading branch information
antoooks authored Jan 16, 2024
1 parent f3fedac commit ab519fd
Show file tree
Hide file tree
Showing 18 changed files with 1,186 additions and 85 deletions.
2 changes: 2 additions & 0 deletions cmd/gorepomod/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ do nothing but log commands
unless you add the `--doIt` flag,
allowing the change._

_If you want to run `gorepomod` on your fork or outside of `$GOSRC` directory, add `--local` flag to your command._

#### `gorepomod list`

Lists modules and intra-repo dependencies.
Expand Down
25 changes: 24 additions & 1 deletion cmd/gorepomod/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,27 @@ module sigs.k8s.io/kustomize/cmd/gorepomod

go 1.20

require golang.org/x/mod v0.9.0
require golang.org/x/mod v0.12.0

require (
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.3.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.10.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.17.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
483 changes: 483 additions & 0 deletions cmd/gorepomod/go.sum

Large diffs are not rendered by default.

86 changes: 86 additions & 0 deletions cmd/gorepomod/gorepomod_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright 2023 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package main

import (
"os/exec"
"testing"

"github.com/stretchr/testify/assert"
)

func TestListCommand(t *testing.T) {
// Assuming gorepomod is installed
var testCases = map[string]struct {
isFork bool
cmd string
expected string
}{
"upstreamWithLocalFlag": {
isFork: false,
cmd: "cd ../.. && gorepomod list --local",
},
"upstreamWithNoLocalFlag": {
isFork: false,
cmd: "cd ../.. && gorepomod list",
},
"forkWithLocalFlag": {
isFork: true,
cmd: "cd ../.. && gorepomod list --local",
},
"forkWithNoLocalFlag": {
isFork: true,
cmd: "cd ../.. && gorepomod list",
},
}

for _, tc := range testCases {
bash, err := exec.LookPath("bash")
if err != nil {
t.Error("bash not found")
}
out, err := exec.Command(bash, "-c", tc.cmd).Output()
if err != nil {
assert.Error(t, err, "exit status 1")
}
assert.Greater(t, len(string(out)), 1)
}
}

func TestPinCommand(t *testing.T) {
// Assuming gorepomod is installed
var testCases = map[string]struct {
isFork bool
cmd string
}{
"upstreamWithLocalFlag": {
isFork: false,
cmd: "cd ../.. && gorepomod pin kyaml --local",
},
"upstreamWithNoLocalFlag": {
isFork: false,
cmd: "cd ../.. && gorepomod pin kyaml",
},
"forkWithLocalFlag": {
isFork: true,
cmd: "cd ../.. && gorepomod pin kyaml --local",
},
"forkWithNoLocalFlag": {
isFork: true,
cmd: "cd ../.. && gorepomod pin kyaml",
},
}

for _, tc := range testCases {
bash, err := exec.LookPath("bash")
if err != nil {
t.Error("bash not found")
}
out, err := exec.Command(bash, "-c", tc.cmd).Output()
if err != nil {
assert.Error(t, err, "exit status 1")
}
assert.Greater(t, len(string(out)), 1)
}
}
14 changes: 12 additions & 2 deletions cmd/gorepomod/internal/arguments/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

const (
doItFlag = "--doIt"
localFlag = "--local"
cmdPin = "pin"
cmdUnPin = "unpin"
cmdTidy = "tidy"
Expand Down Expand Up @@ -64,6 +65,7 @@ type Args struct {
version semver.SemVer
bump semver.SvBump
doIt bool
localFlag bool
}

func (a *Args) GetCommand() Command {
Expand Down Expand Up @@ -106,9 +108,14 @@ func (a *Args) DoIt() bool {
return a.doIt
}

func (a *Args) LocalFlag() bool {
return a.localFlag
}

type myArgs struct {
args []string
doIt bool
args []string
doIt bool
localFlag bool
}

func (a *myArgs) next() (result string) {
Expand All @@ -129,6 +136,8 @@ func newArgs() *myArgs {
for _, a := range os.Args[1:] {
if a == doItFlag {
result.doIt = true
} else if a == localFlag {
result.localFlag = true
} else {
result.args = append(result.args, a)
}
Expand All @@ -140,6 +149,7 @@ func Parse() (result *Args, err error) {
result = &Args{}
clArgs := newArgs()
result.doIt = clArgs.doIt
result.localFlag = clArgs.localFlag

result.moduleName = misc.ModuleUnknown
result.conditionalModule = misc.ModuleUnknown
Expand Down
12 changes: 6 additions & 6 deletions cmd/gorepomod/internal/edit/editor.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,19 @@ func (e *Editor) Tidy() error {
func (e *Editor) Pin(target misc.LaModule, oldV, newV semver.SemVer) error {
err := e.run(
"edit",
"-dropreplace="+target.ImportPath(),
"-dropreplace="+target.ImportPath()+"@"+oldV.String(),
"-require="+target.ImportPath()+"@"+newV.String(),
"-dropreplace=sigs.k8s.io/kustomize/"+string(target.ShortName()),
"-dropreplace=sigs.k8s.io/kustomize/"+string(target.ShortName())+"@"+oldV.String(),
"-require=sigs.k8s.io/kustomize/"+string(target.ShortName())+"@"+newV.String(),
)
if err != nil {
return err
return fmt.Errorf("%w", err)
}
return e.run("tidy")
}

func (e *Editor) UnPin(target misc.LaModule, oldV semver.SemVer) error {
var r strings.Builder
r.WriteString(target.ImportPath())
r.WriteString("sigs.k8s.io/kustomize/" + string(target.ShortName()))
// Don't specify the old version.
// r.WriteString("@")
// r.WriteString(oldV.String())
Expand All @@ -81,7 +81,7 @@ func (e *Editor) UnPin(target misc.LaModule, oldV semver.SemVer) error {
"-replace="+r.String(),
)
if err != nil {
return err
return fmt.Errorf("%w", err)
}
return e.run("tidy")
}
62 changes: 54 additions & 8 deletions cmd/gorepomod/internal/git/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,22 @@ type Runner struct {
workDir string
// Run commands, or merely print commands.
doIt bool
// Indicate local execution to adapt with path.
localFlag bool
// Run commands, or merely print commands.
verbosity Verbosity
}

func NewLoud(wd string, doIt bool) *Runner {
return newRunner(wd, doIt, High)
func NewLoud(wd string, doIt bool, localFlag bool) *Runner {
return newRunner(wd, doIt, High, localFlag)
}

func NewQuiet(wd string, doIt bool) *Runner {
return newRunner(wd, doIt, Low)
func NewQuiet(wd string, doIt bool, localFlag bool) *Runner {
return newRunner(wd, doIt, Low, localFlag)
}

func newRunner(wd string, doIt bool, v Verbosity) *Runner {
return &Runner{workDir: wd, doIt: doIt, verbosity: v}
func newRunner(wd string, doIt bool, v Verbosity, localFlag bool) *Runner {
return &Runner{workDir: wd, doIt: doIt, verbosity: v, localFlag: localFlag}
}

func (gr *Runner) comment(f string) {
Expand Down Expand Up @@ -165,8 +167,20 @@ func (gr *Runner) LoadLocalTags() (result misc.VersionMap, err error) {

func (gr *Runner) LoadRemoteTags(
remote misc.TrackedRepo) (result misc.VersionMap, err error) {
gr.comment("loading remote tags")
var out string

// Update latest tags from upstream
gr.comment("updating tags from upstream")
_, err = gr.run(noHarmDone, "fetch", "-t", string(remoteUpstream), string(mainBranch))
if err != nil {
// Handle if repo is not a fork
_, err = gr.run(noHarmDone, "fetch", "-t", string(mainBranch))
if err != nil {
_ = fmt.Errorf("failed to fetch tags from %s", string(mainBranch))
}
}

gr.comment("loading remote tags")
out, err = gr.run(noHarmDone, "ls-remote", "--ref", string(remote))
if err != nil {
return nil, err
Expand Down Expand Up @@ -250,7 +264,15 @@ func (gr *Runner) CheckoutMainBranch() error {
// FetchRemote does that.
func (gr *Runner) FetchRemote(remote misc.TrackedRepo) error {
gr.comment("fetching remote")
return gr.runNoOut(noHarmDone, "fetch", string(remote))
err := gr.runNoOut(noHarmDone, "fetch", string(remote))
if err != nil {
// If current repo is fork
err = gr.runNoOut(noHarmDone, "fetch", string(remoteUpstream))
if err != nil {
return fmt.Errorf("%w", err)
}
}
return nil
}

// MergeFromRemoteMain does a fast forward only merge with main branch.
Expand Down Expand Up @@ -342,3 +364,27 @@ func (gr *Runner) DeleteTagFromRemote(
gr.comment("deleting tags from remote")
return gr.runNoOut(undoPainful, "push", string(remote), ":"+refsTags+tag)
}

func (gr *Runner) GetLatestTag(releaseBranch string) (string, error) {
var latestTag string
// Assuming release branch has this format: release-path/to/module-vX.Y.Z
// and each release branch maintains tags, extract version from latest `releaseBranch`
gr.comment("extract version from latest release branch")
filteredBranchList, err := gr.run(noHarmDone, "branch", "-a", "--list", "*"+releaseBranch+"*", "--sort=-committerdate")
if len(filteredBranchList) < 1 {
_ = fmt.Errorf("latest tag not found for %s", releaseBranch)
return "", err
}
newestBranch := strings.Split(strings.ReplaceAll(filteredBranchList, "\r\n", "\n"), "\n")
split := strings.Split(newestBranch[0], "-")
latestTag = split[len(split)-1]
if err != nil {
_ = fmt.Errorf("error getting latest tag for %s", releaseBranch)
}

return latestTag, nil
}

func (gr *Runner) GetMainBranch() string {
return string(mainBranch)
}
3 changes: 2 additions & 1 deletion cmd/gorepomod/internal/mod/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ func (m *Module) AbsPath() string {

func (m *Module) DependsOn(target misc.LaModule) (bool, semver.SemVer) {
for _, r := range m.mf.Require {
if r.Mod.Path == target.ImportPath() {
targetImportPath := fmt.Sprintf("sigs.k8s.io/kustomize/%s", string(target.ShortName()))
if r.Mod.Path == targetImportPath {
v, err := semver.Parse(r.Mod.Version)
if err != nil {
panic(err)
Expand Down
Loading

0 comments on commit ab519fd

Please sign in to comment.