Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show updated plugin manifests and available upgrades #457

Merged
merged 5 commits into from
Jan 26, 2020

Conversation

artmello
Copy link
Contributor

@artmello artmello commented Jan 4, 2020

Closes #443

@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Jan 4, 2020
@k8s-ci-robot
Copy link
Contributor

Welcome @artmello!

It looks like this is your first PR to kubernetes-sigs/krew 🎉. Please refer to our pull request process documentation to help your PR have a smooth ride to approval.

You will be prompted by a bot to use commands during the review process. Do not be afraid to follow the prompts! It is okay to experiment. Here is the bot commands documentation.

You can also check if kubernetes-sigs/krew has its own contribution guidelines.

You may want to refer to our testing guide if you run into trouble with your tests not passing.

If you are having difficulty getting your pull request seen, please follow the recommended escalation practices. Also, for tips and tricks in the contribution process you may want to read the Kubernetes contributor cheat sheet. We want to make sure your contribution gets all the attention it needs!

Thank you, and welcome to Kubernetes. 😃

@k8s-ci-robot k8s-ci-robot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Jan 4, 2020
@corneliusweig
Copy link
Contributor

First of all, thanks for taking a shot at this!
The function ensureIndexUpdated is actually called in three places: install, update, and upgrade. I'm not sure if it really makes sense to show this for each of these cases.
Also, I wonder if we can get away without scanning the manifest index each time. We could also use git diff --name-only to restrict the number of scanned manifests. WDYT?

@artmello
Copy link
Contributor Author

artmello commented Jan 5, 2020

Hi @corneliusweig, thanks for taking a look on this!
On the original issue, it was mentioned that for other commands (i.e install) we could have a short version of the updated plugins output. Maybe we can have either a verbose/quiet option to control when that is displayed or not. WDYT?

I agree that we should avoid scanning index when not necessary. I tried to implement suggested approach using diff --name-only let me know if it does look any better.

This is current output:

$ cd -
~/.krew/index

$ git reset --hard dc075b6355c02e30aa0a43bebf4587bab2eafb9d                                                                                     HEAD is now at dc075b6 update neat to v1.1.0 (#399)

$ cd -
~/code/krew

$ ./out/bin/krew-darwin_amd64 update
Updated the local copy of plugin index.
  New plugins available:
    * tree v0.2.0
  The following plugins have new version:
    * pod-dive v0.1.3 -> v0.1.4 (!)
    * fleet v0.1.3 -> v0.1.4
    * open-svc v2.2.0 -> v2.3.0
    * view-allocations v0.6.0 -> v0.6.2
    * view-serviceaccount-kubeconfig v2.0.3 -> v2.1.1

$ ./out/bin/krew-darwin_amd64 update
Updated the local copy of plugin index.

@ahmetb
Copy link
Member

ahmetb commented Jan 5, 2020

For now let’s just show the same output for all (install/update/upgrade). I initially proposed we only show a summary (items combined into single line). Maybe it’s not worth it. Let’s get a feel for it.

@aimbot31
Copy link
Contributor

aimbot31 commented Jan 14, 2020

Hello, would like to help if it's possible, still ongoing ?

Copy link
Contributor

@corneliusweig corneliusweig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for my response time. I must have lost the notification and only stumbled over this today.

You did a great job integrating git into this logic. But I think you can use git even more to simplify the logic (see my inline comment).

In general, I like the index summary and would love to see that merged at some point. In order to get there fast, please add tests as soon as you can. This will also force you to structure the code in a testable way.

Lastly, I think we should switch to a oneline report for starters (maybe one line per added/updated but not more :) ). Otherwise we take up too much space and people will hate the feature, because they don't see what is most relevant to them anymore.

return modifiedFiles, nil
}

func exec(pwd string, args ...string) (string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add an exec and execOut variant, so that the out return argument does not have to be ignored in so many places?


var modifiedFiles []string
for _, f := range strings.Split(output, "\n") {
if len(f) > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simpler: if f == "" {

}

var modifiedFiles []string
for _, f := range strings.Split(output, "\n") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only empty line is the last one, right? Then you can do this simpler as

modifiedFiles := strings.Split(strings.Trim(output), "\n")

@@ -69,7 +70,27 @@ func EnsureUpdated(uri, destinationPath string) error {
return updateAndCleanUntracked(destinationPath)
}

func exec(pwd string, args ...string) error {
// ListModifiedFiles will fetch origin and list modified files
func ListModifiedFiles(uri, destinationPath string) ([]string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ListModifiedFiles is a slight misnomer, because no files are actually modified. What about ListUpstreamChanges?

m := make(map[string]string, len(names))
for _, n := range names {
plugin, err := indexscanner.LoadPluginByName(paths.IndexPluginsPath(), n)
if err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should only continue for os.IsNoExist(err). All other errors should abort the function.

Comment on lines 110 to 116
if !ok {
newPluginList = append(newPluginList, newPlugin{
name: name,
version: version,
})
continue
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could also be a

newVersionPlugin{
  ...,
  oldVersion: "",
  installed:  false,
}

That could simplify things a little. WDYT?

if len(newPluginList) > 0 {
fmt.Fprintln(os.Stderr, " New plugins available:")
for _, np := range newPluginList {
fmt.Fprintf(os.Stderr, " * %s %s\n", np.name, np.version)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a TabWriter would produce more legible output here.

But as far as I understand, a one-line report would be preferable anyways.

cmd/krew/cmd/update.go Show resolved Hide resolved
}

var oldMap map[string]string
if len(updatedPlugins) > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If len(updatedPlugins) == 0, you can return early here, because this means the index is already up-to-date.

return []string{}, errors.Wrapf(err, "fetch index at %q failed", destinationPath)
}

output, err := exec(destinationPath, "diff", "--name-only", "@{upstream}", "--", ".")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if I come up with those ideas bit by bit. But you can also use git diff --name-status which will add a mark each change as modified/added/deleted...

You can use that to further simplify the logic you need to build the report. I suggest that you play around with a toy git repo to see how this works. Try to offload as much as possible to git :)

@corneliusweig
Copy link
Contributor

Hi @aimbot31, only @artmello can answer that :).

@artmello
Copy link
Contributor Author

Hey @corneliusweig thanks a lot for reviewing it. I will work on your comments and let you know when it is good for another round.

Hey @aimbot31, thanks for the interest. I plan to work on Cornelius comments and on adding tests (as mentioned this may need some refactor). Not sure how we could split it, but let me know if you have any suggestion :)

@ahmetb ahmetb changed the title Show information about updated plugin during update command Show updated plugin manifests and available upgrades Jan 16, 2020
func retrieveInstalledPluginMap() (map[string]bool, error) {
plugins, err := installation.ListInstalledPlugins(paths.InstallReceiptsPath())
if err != nil {
return map[string]bool{}, err
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can use nil here.
consider wrapping err. (e.g. "failed during listing installed plugins")

return m
}

func retrieveInstalledPluginMap() (map[string]bool, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why's the return type a map here?
you only need to just return names, i.e. []string?


func showUpdatedPlugins(newPluginList []newPlugin, newVersionList []newVersionPlugin) {
if len(newPluginList) > 0 {
fmt.Fprintln(os.Stderr, " New plugins available:")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be better if you just accept out io.Writer here, so that you don't repeat os.Stderr everywhere.

return plugins, nil
}

func retrievePluginNameVersionMap(names []string) map[string]string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not convinced this should be map[string]string.
It's not great for extensibility or readability.

Feel free to either,

  • just create a type pluginInfo struct {name, version string }, or
  • just return the []*Plugin objects.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually it seems like you have a type newPlugin struct, so let's follow suit here. either create a new type or find a way of reusing.

Comment on lines 130 to 144
sort.Slice(newPluginList, func(i, j int) bool {
return newPluginList[i].name < newPluginList[j].name
})

sort.Slice(newVersionList, func(i, j int) bool {
if newVersionList[i].installed && !newVersionList[j].installed {
return true
}

if !newVersionList[i].installed && newVersionList[j].installed {
return false
}

return newVersionList[i].name < newVersionList[j].name
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pro-tip:

if you don't do maps (which I don't see a reason why you'd need maps here), you don't need to worry about sorting at all.

all the methods we have already are returning *index.Plugin types sorted by name. :)

return newVersionList[i].name < newVersionList[j].name
})

return newPluginList, newVersionList
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shout-out to my comment above, this looks odd.

just create an internal type (struct) to carry these.

Comment on lines 105 to 106
var newPluginList []newPlugin
var newVersionList []newVersionPlugin
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

something I noticed throughout your coding style is how you prefix things with their types (e.g. xxxMap xxxList). let's avoid that. :)

you can simply use newPlugins, updatedPlugins here.

version string
}

type newVersionPlugin struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename: updatedPlugin

"sigs.k8s.io/krew/pkg/constants"
)

type newPlugin struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like you can simply have 1 type to carry out everything

type pluginInfo struct {
    name, version string
}

If you simply return []pluginInfo from everywhere, when the time comes to printing, just diff two []pluginInfo slices to find the "new" and "updated" plugins.

Correct me if I'm wrong but this can even help reduce some of the code.

internal/gitutil/git.go Outdated Show resolved Hide resolved
@k8s-ci-robot k8s-ci-robot added size/M Denotes a PR that changes 30-99 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Jan 21, 2020
@artmello
Copy link
Contributor Author

@ahmetb / @corneliusweig

Thanks a lot for the detailed review. I tried to address all your comments, please, let me know if I missed some.
I reverted gitutil changes and used suggested approach of executing a diff on indexes from pre/pos update. Also I moved to use a less verbose output as suggested.

Here is a sample output:

$ cd -
~/.krew/index

$ git reset --hard dc075b6355c02e30aa0a43bebf4587bab2eafb9d
HEAD is now at dc075b6 update neat to v1.1.0 (#399)

$ cd -
~/dump/krew

$ ./out/bin/krew-darwin_amd64 update
Updated the local copy of plugin index.
  New plugins available: advise-psp v1.4.6, deprecations v0.2.4, duck v0.0.1, net-forward v1.0.3, profefe v0.9.0, resource-snapshot v0.1.2, spy v0.3.1, tree v0.3.1
  Upgrades available: pod-dive v0.1.3 -> v0.1.4

@ahmetb
Copy link
Member

ahmetb commented Jan 21, 2020

Can I just say: I LOVEEEEE how small this PR is.

I have only 2 concerns atm:

  • when first ever “update” is done, does it print everything as new (ideally should not)
  • it seems like the indentation in the output isnt that useful if the plugin list is gonna just flow over to the next line. I wonder if we can do something like print max 4-5 on each line and just add \n\t after each 5?

I am on the fence about whether we should add integration tests for this? Probably not in this PR since it’s not critical functionality.

@ahmetb
Copy link
Member

ahmetb commented Jan 23, 2020

Also it seems like there are a couple lint and test errors.

@ahmetb
Copy link
Member

ahmetb commented Jan 25, 2020

@artmello pinging if you've still got time to pursue. it seems like we're pretty close :)

- Do not raise an error if no index was found before an update
- Do not print updated plugins output if no index was found before an update
- Split output lines on chuncks of 5 plugins
- Fix complains from linter
@codecov-io
Copy link

codecov-io commented Jan 26, 2020

Codecov Report

Merging #457 into master will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##           master     #457   +/-   ##
=======================================
  Coverage   56.52%   56.52%           
=======================================
  Files          19       19           
  Lines         927      927           
=======================================
  Hits          524      524           
  Misses        350      350           
  Partials       53       53

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update d2bb438...93529ee. Read the comment docs.

@artmello
Copy link
Contributor Author

Hi @ahmetb, once again thanks for reviewing it and sorry for the sparse answers. I believe all your last comments are addressed with latest commits. Output has changed, now we are splitting plugins list on chuncks of 5.

$ cd -
~/.krew/index

$ git reset --hard dc075b6355c02e30aa0a43bebf4587bab2eafb9d
HEAD is now at dc075b6 update neat to v1.1.0 (#399)

$ cd -
~/dump/krew

$ ./out/bin/krew-darwin_amd64 update
Updated the local copy of plugin index.
  New plugins available: advise-psp v1.4.6, deprecations v0.2.4, duck v0.0.1, net-forward v1.0.3, profefe v0.9.0
	resource-snapshot v0.1.2, spy v0.3.3, tree v0.4.0

@ahmetb
Copy link
Member

ahmetb commented Jan 26, 2020

/lgtm
/approve
Thanks a bunch. This was cool work.

I might write a few integration tests for this.

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Jan 26, 2020
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: ahmetb, artmello

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jan 26, 2020
@k8s-ci-robot k8s-ci-robot merged commit 99b9cdf into kubernetes-sigs:master Jan 26, 2020
@corneliusweig
Copy link
Contributor

This is great! Thanks for your contribution @artmello

@artmello
Copy link
Contributor Author

Thanks a lot @ahmetb and @corneliusweig for all the help on it. Looking forward to contribute again soon :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. size/M Denotes a PR that changes 30-99 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Show what's updated in "update" command (or its invocations indirectly)
6 participants