gitee.com/mysnapcore/mysnapd@v0.1.0/cmd/snap/last.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package main 21 22 import ( 23 "errors" 24 "fmt" 25 26 "gitee.com/mysnapcore/mysnapd/client" 27 "gitee.com/mysnapcore/mysnapd/i18n" 28 "gitee.com/mysnapcore/mysnapd/strutil" 29 ) 30 31 type changeIDMixin struct { 32 clientMixin 33 LastChangeType string `long:"last"` 34 Positional struct { 35 ID changeID `positional-arg-name:"<id>"` 36 } `positional-args:"yes"` 37 } 38 39 var changeIDMixinOptDesc = mixinDescs{ 40 // TRANSLATORS: This should not start with a lowercase letter. 41 "last": i18n.G("Select last change of given type (install, refresh, remove, try, auto-refresh, etc.). A question mark at the end of the type means to do nothing (instead of returning an error) if no change of the given type is found. Note the question mark could need protecting from the shell."), 42 } 43 44 var changeIDMixinArgDesc = []argDesc{{ 45 // TRANSLATORS: This needs to begin with < and end with > 46 name: i18n.G("<change-id>"), 47 // TRANSLATORS: This should not start with a lowercase letter. 48 desc: i18n.G("Change ID"), 49 }} 50 51 // should not be user-visible, but keep it clear and polite because mistakes happen 52 var noChangeFoundOK = errors.New("no change found but that's ok") 53 54 func (l *changeIDMixin) GetChangeID() (string, error) { 55 if l.Positional.ID == "" && l.LastChangeType == "" { 56 return "", fmt.Errorf(i18n.G("please provide change ID or type with --last=<type>")) 57 } 58 59 if l.Positional.ID != "" { 60 if l.LastChangeType != "" { 61 return "", fmt.Errorf(i18n.G("cannot use change ID and type together")) 62 } 63 64 return string(l.Positional.ID), nil 65 } 66 67 cli := l.client 68 // note that at this point we know l.LastChangeType != "" 69 kind := l.LastChangeType 70 optional := false 71 if l := len(kind) - 1; kind[l] == '?' { 72 optional = true 73 kind = kind[:l] 74 } 75 // our internal change types use "-snap" postfix but let user skip it and use short form. 76 shortForms := []string{ 77 // see api_snaps.go:snapInstructionDispTable 78 "install", "refresh", "remove", "revert", "enable", "disable", "switch", 79 // see api_interfaces.go:changeInterfaces 80 "connect", "disconnect", 81 // see api_snap_conf.go:setSnapConf 82 "configure", 83 // see api_sideload_n_try.go:trySnap 84 "try", 85 } 86 if strutil.ListContains(shortForms, kind) { 87 kind += "-snap" 88 } 89 changes, err := queryChanges(cli, &client.ChangesOptions{Selector: client.ChangesAll}) 90 if err != nil { 91 return "", err 92 } 93 if len(changes) == 0 { 94 if optional { 95 return "", noChangeFoundOK 96 } 97 return "", fmt.Errorf(i18n.G("no changes found")) 98 } 99 chg := findLatestChangeByKind(changes, kind) 100 if chg == nil { 101 if optional { 102 return "", noChangeFoundOK 103 } 104 return "", fmt.Errorf(i18n.G("no changes of type %q found"), l.LastChangeType) 105 } 106 107 return chg.ID, nil 108 } 109 110 func findLatestChangeByKind(changes []*client.Change, kind string) (latest *client.Change) { 111 for _, chg := range changes { 112 if chg.Kind == kind && (latest == nil || latest.SpawnTime.Before(chg.SpawnTime)) { 113 latest = chg 114 } 115 } 116 return latest 117 }