diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3e446c3..f4cdf7e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -33,7 +33,3 @@ push: script: - filename=$(curl --upload-file ./xc-linux-amd64 https://t.bk.ru/xc) - icqnotify.py "a new release of XC is out:\n$filename" - artifacts: - name: release - paths: - - release.txt diff --git a/cli/cli.go b/cli/cli.go index 82b09d7..b4f4cbf 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -47,6 +47,7 @@ type Cli struct { progressBar bool debug bool usePasswordMgr bool + naturalSort bool interpreter string sudoInterpreter string @@ -113,6 +114,7 @@ func New(cfg *config.XCConfig, backend store.Backend) (*Cli, error) { cli.debug = cfg.Debug cli.connectTimeout = cfg.SSHConnectTimeout cli.remoteTmpDir = cfg.RemoteTmpdir + cli.naturalSort = true // output cli.outputFileName = "" @@ -466,9 +468,12 @@ func doOnOff(propName string, propRef *bool, args []string) bool { case "off": *propRef = false default: - term.Errorf("Invalid %s vaue. Please use either \"on\" or \"off\"\n", propName) + term.Errorf("Invalid %s value. Please use either \"on\" or \"off\"\n", propName) return false } + + term.Warnf("%s set to %s\n", propName, args[0]) + return prev != *propRef } diff --git a/cli/completer.go b/cli/completer.go index 1c947c3..85dfb65 100644 --- a/cli/completer.go +++ b/cli/completer.go @@ -25,6 +25,7 @@ func newCompleter(store *store.Store, commands []string) *completer { x.handlers["progressbar"] = onOffCompleter() x.handlers["prepend_hostnames"] = onOffCompleter() x.handlers["use_password_manager"] = onOffCompleter() + x.handlers["natural_sort"] = onOffCompleter() x.handlers["raise"] = staticCompleter([]string{"none", "su", "sudo"}) x.handlers["interpreter"] = staticCompleter([]string{"none", "su", "sudo"}) x.handlers["exec"] = x.completeExec diff --git a/cli/handlers.go b/cli/handlers.go index 0e4d08b..c7ef912 100644 --- a/cli/handlers.go +++ b/cli/handlers.go @@ -55,6 +55,7 @@ func (c *Cli) setupCmdHandlers() { c.handlers["_passmgr_debug"] = c.doPassmgrDebug c.handlers["version"] = c.doVersion c.handlers["goruntime"] = c.doGoruntime + c.handlers["natural_sort"] = c.doNaturalSort commands := make([]string, len(c.handlers)) i := 0 @@ -294,6 +295,12 @@ func (c *Cli) doDebug(name string, argsLine string, args ...string) { } } +func (c *Cli) doNaturalSort(name string, argsLine string, args ...string) { + if doOnOff("natural_sort", &c.naturalSort, args) { + c.store.SetNaturalSort(c.naturalSort) + } +} + func (c *Cli) doProgressBar(name string, argsLine string, args ...string) { if doOnOff("progressbar", &c.progressBar, args) { remote.SetProgressBar(c.progressBar) diff --git a/cli/help.go b/cli/help.go index 9b21691..3f1b289 100644 --- a/cli/help.go +++ b/cli/help.go @@ -294,6 +294,15 @@ Switching them off is useful for copy-pasting the results.`, usage: "[]", help: `Sets the progressbar on or off. If no value is given, prints the current value.`, }, + "natural_sort": &helpItem{ + usage: "[]", + help: `Sets natural sorting of host lists on or off. If no value is given, prints the current value. + +Important note: sorting works only within one expression token, that is, if you +explicitly type the hostlist like "host5,host3,host1", the order is preserved, +xc considers you know what you are doing. Sorting is applied only within a group, +a workgroup or a pattern like "host{1..50}"`, + }, "raise": &helpItem{ usage: "", @@ -383,7 +392,7 @@ func generalHelp() { List of commands: alias creates a local alias command cd changes current working directory - collapse shortcut for "mode collapse" + collapse shortcut for "mode collapse" debug one shouldn't use this delay sets a delay between hosts in serial mode distribute copies a file to a number of hosts in parallel @@ -395,6 +404,7 @@ List of commands: interpreter sets interpreter for each type of privileges raising local starts a local command mode switches between execution modes + natural_sort sets natural sorting on/off parallel shortcut for "mode parallel" passwd sets passwd for privilege raise progressbar controls progressbar diff --git a/go.mod b/go.mod index c95e485..307c75f 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ require ( github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect github.com/creack/pty v1.1.9 // indirect + github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb github.com/fatih/color v1.7.0 // indirect github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 // indirect github.com/kr/pty v1.1.8 diff --git a/go.sum b/go.sum index dac5c09..e7f36f9 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,8 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb h1:IT4JYU7k4ikYg1SCxNI1/Tieq/NFvh6dzLdgi7eu0tM= +github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb/go.mod h1:bH6Xx7IW64qjjJq8M2u4dxNaBiDfKK+z/3eGDpXEQhc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1:zN2lZNZRflqFyxVaTIU61KNKQ9C0055u9CAfpmqUvo4= github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU= diff --git a/store/store.go b/store/store.go index bf33189..8b7a29c 100644 --- a/store/store.go +++ b/store/store.go @@ -5,6 +5,7 @@ import ( "sort" "strings" + "github.com/facette/natsort" "github.com/viert/sekwence" "github.com/viert/xc/stringslice" ) @@ -17,6 +18,8 @@ type Store struct { workgroups *wgstore tags []string backend Backend + + naturalSort bool } func (s *Store) reinitStore() { @@ -146,6 +149,12 @@ func (s *Store) groupAllHosts(g *Group) []*Host { return hosts } +// SetNaturalSort enables/disables using of natural sorting +// within one expression token (i.e. group) +func (s *Store) SetNaturalSort(value bool) { + s.naturalSort = value +} + // HostList returns a list of host FQDNs according to a given // expression func (s *Store) HostList(expr []rune) ([]string, error) { @@ -273,9 +282,14 @@ func (s *Store) HostList(expr []rune) ([]string, error) { results := make([]string, 0) for _, sthosts := range hostlist { - // sorting withing one expression token only + // sorting within one expression token only // the order of tokens themselves should be respected - sort.Strings(sthosts) + if s.naturalSort { + natsort.Sort(sthosts) + } else { + sort.Strings(sthosts) + } + for _, host := range sthosts { results = append(results, host) } @@ -385,6 +399,7 @@ func (s *Store) apply() { func CreateStore(backend Backend) (*Store, error) { s := new(Store) s.backend = backend + s.naturalSort = true err := s.BackendLoad() return s, err }