Skip to content

Commit

Permalink
Merge 22e3353 into 238781a
Browse files Browse the repository at this point in the history
  • Loading branch information
saheljalal committed Jan 17, 2022
2 parents 238781a + 22e3353 commit 70e1463
Show file tree
Hide file tree
Showing 10 changed files with 142 additions and 20 deletions.
8 changes: 5 additions & 3 deletions cmd/get.go
@@ -1,9 +1,10 @@
package cmd

import (
"os"

"github.com/pokanop/nostromo/task"
"github.com/spf13/cobra"
"os"
)

// getCmd represents the get command
Expand All @@ -14,8 +15,9 @@ var getCmd = &cobra.Command{
Nostromo config items are saved in the manifest.
Use this command to get keys to examine these settings:
verbose: true | false
aliasesOnly: true | false`,
verbose: boolean
aliasesOnly: boolean
backupCount: number`,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
os.Exit(task.GetConfig(args[0]))
Expand Down
12 changes: 7 additions & 5 deletions cmd/set.go
@@ -1,9 +1,10 @@
package cmd

import (
"os"

"github.com/pokanop/nostromo/task"
"github.com/spf13/cobra"
"os"
)

// setCmd represents the set command
Expand All @@ -14,11 +15,12 @@ var setCmd = &cobra.Command{
Nostromo config items are saved in the manifest.
Use this command to set values for these settings:
verbose: true | false
aliasesOnly: true | false
mode: concatenate | independent | exclusive`,
verbose: boolean
aliasesOnly: boolean
mode: concatenate | independent | exclusive
backupCount: number`,
Args: cobra.MinimumNArgs(2),
ValidArgs: []string{"verbose", "aliasesOnly", "mode"},
ValidArgs: []string{"verbose", "aliasesOnly", "mode", "backupCount"},
Run: func(cmd *cobra.Command, args []string) {
os.Exit(task.SetConfig(args[0], args[1]))
},
Expand Down
99 changes: 98 additions & 1 deletion config/config.go
Expand Up @@ -5,8 +5,12 @@ import (
"io/ioutil"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"time"

"github.com/pokanop/nostromo/log"
"github.com/pokanop/nostromo/model"
"github.com/pokanop/nostromo/pathutil"
"gopkg.in/yaml.v2"
Expand All @@ -16,6 +20,7 @@ import (
const (
DefaultManifestFile = "manifest.yaml"
DefaultBaseDir = "~/.nostromo"
DefaultBackupsDir = "backups"
)

// Config manages working with nostromo configuration files
Expand Down Expand Up @@ -61,7 +66,12 @@ func Parse(path string) (*Config, error) {
return nil, err
}

var m *model.Manifest
// Initialize manifest with some defaults
m := &model.Manifest{
Config: &model.Config{
BackupCount: 10,
},
}
ext := filepath.Ext(path)
if ext == ".yaml" {
err = yaml.Unmarshal(b, &m)
Expand Down Expand Up @@ -89,6 +99,10 @@ func (c *Config) Manifest() *model.Manifest {

// Save nostromo config to file
func (c *Config) Save() error {
return c.save(true)
}

func (c *Config) save(backup bool) error {
if len(c.path) == 0 {
return fmt.Errorf("invalid path to save")
}
Expand All @@ -110,6 +124,13 @@ func (c *Config) Save() error {
return err
}

// Save backup if requested
if backup {
if err = c.Backup(); err != nil {
return err
}
}

err = ioutil.WriteFile(pathutil.Abs(c.path), b, 0644)
if err != nil {
return err
Expand Down Expand Up @@ -150,6 +171,8 @@ func (c *Config) Get(key string) string {
return strconv.FormatBool(c.manifest.Config.AliasesOnly)
case "mode":
return c.manifest.Config.Mode.String()
case "backupCount":
return strconv.FormatInt(int64(c.manifest.Config.BackupCount), 10)
}
return "key not found"
}
Expand Down Expand Up @@ -177,6 +200,80 @@ func (c *Config) Set(key, value string) error {
}
c.manifest.Config.Mode = model.ModeFromString(value)
return nil
case "backupCount":
count, err := strconv.ParseInt(value, 10, 0)
if err != nil {
return err
}
c.manifest.Config.BackupCount = int(count)
return nil
}
return fmt.Errorf("key not found")
}

// Backup a manifest based on timestamp
func (c *Config) Backup() error {
// Before saving backup, prune old files
c.pruneBackups()

// Create backups under base dir in a folder
backupDir, err := ensureBackupDir()
if err != nil {
return err
}

// Copy existing manifest to backup path
ts := time.Now().UTC().Format("20060102150405")
basename := strings.TrimSuffix(DefaultManifestFile, filepath.Ext(DefaultManifestFile))
destinationFile := filepath.Join(backupDir, basename+"_"+ts+".yaml")
sourceFile := pathutil.Abs(GetConfigPath())

input, err := ioutil.ReadFile(sourceFile)
if err != nil {
return err
}

err = ioutil.WriteFile(destinationFile, input, 0644)
if err != nil {
return err
}

return nil
}

func (c *Config) pruneBackups() {
backupDir, err := ensureBackupDir()
if err != nil {
return
}

// Read all files, sort by timestamp, and drop items > max count
files, err := ioutil.ReadDir(backupDir)
if err != nil {
log.Warningf("unable to read backup dir: %s\n", err)
return
}

sort.SliceStable(files, func(i, j int) bool {
return files[i].ModTime().After(files[j].ModTime())
})

// Add one more to backup count since a new backup will be created
maxCount := c.manifest.Config.BackupCount - 1
if maxCount > 0 && len(files) > maxCount {
for _, file := range files[maxCount:] {
filename := filepath.Join(backupDir, file.Name())
if err := os.Remove(filename); err != nil {
log.Warningf("failed to prune backup file %s: %s\n", filename, err)
}
}
}
}

func ensureBackupDir() (string, error) {
backupDir := filepath.Join(DefaultBaseDir, DefaultBackupsDir)
if err := pathutil.EnsurePath(backupDir); err != nil {
return "", err
}
return pathutil.Abs(backupDir), nil
}
7 changes: 4 additions & 3 deletions config/config_test.go
Expand Up @@ -57,7 +57,7 @@ func TestSave(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
c := NewConfig(test.path, test.manifest)
err := c.Save()
err := c.save(false)
if test.expErr && err == nil {
t.Errorf("expected error but got none")
} else if !test.expErr && err != nil {
Expand All @@ -83,7 +83,7 @@ func TestDelete(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
c := NewConfig(test.path, test.manifest)
if test.manifest != nil {
err := c.Save()
err := c.save(false)
if err != nil {
t.Errorf("unable to save temporary manifest: %s", err)
}
Expand Down Expand Up @@ -189,7 +189,7 @@ func TestKeys(t *testing.T) {
config *Config
expected []string
}{
{"keys", NewConfig("path", fakeManifest()), []string{"verbose", "aliasesOnly", "mode"}},
{"keys", NewConfig("path", fakeManifest()), []string{"verbose", "aliasesOnly", "mode", "backupCount"}},
}

for _, test := range tests {
Expand All @@ -214,6 +214,7 @@ func TestFields(t *testing.T) {
"verbose": false,
"aliasesOnly": false,
"mode": model.ConcatenateMode.String(),
"backupCount": 10,
},
},
}
Expand Down
11 changes: 10 additions & 1 deletion model/config.go
Expand Up @@ -5,11 +5,19 @@ type Config struct {
Verbose bool `json:"verbose"`
AliasesOnly bool `json:"aliasesOnly"`
Mode Mode `json:"mode"`
BackupCount int `json:"backupCount"`
}

// Create a new config model with default values
func NewConfig() *Config {
return &Config{
BackupCount: 10,
}
}

// Keys as ordered list of fields for logging
func (c *Config) Keys() []string {
return []string{"verbose", "aliasesOnly", "mode"}
return []string{"verbose", "aliasesOnly", "mode", "backupCount"}
}

// Fields interface for logging
Expand All @@ -18,5 +26,6 @@ func (c *Config) Fields() map[string]interface{} {
"verbose": c.Verbose,
"aliasesOnly": c.AliasesOnly,
"mode": c.Mode.String(),
"backupCount": c.BackupCount,
}
}
10 changes: 9 additions & 1 deletion model/config_test.go
Expand Up @@ -5,13 +5,20 @@ import (
"testing"
)

func TestNewConfig(t *testing.T) {
c := NewConfig()
if c.BackupCount != 10 {
t.Errorf("unexpected default values for config")
}
}

func TestConfigKeys(t *testing.T) {
tests := []struct {
name string
manifest *Manifest
expected []string
}{
{"keys", fakeManifest(1, 1), []string{"verbose", "aliasesOnly", "mode"}},
{"keys", fakeManifest(1, 1), []string{"verbose", "aliasesOnly", "mode", "backupCount"}},
}

for _, test := range tests {
Expand All @@ -36,6 +43,7 @@ func TestConfigFields(t *testing.T) {
"verbose": true,
"aliasesOnly": false,
"mode": "concatenate",
"backupCount": 10,
},
},
}
Expand Down
2 changes: 1 addition & 1 deletion model/manifest.go
Expand Up @@ -22,7 +22,7 @@ type Manifest struct {
func NewManifest() *Manifest {
return &Manifest{
Version: "1.0",
Config: &Config{},
Config: NewConfig(),
Commands: map[string]*Command{},
}
}
Expand Down
7 changes: 4 additions & 3 deletions model/manifest_test.go
@@ -1,14 +1,15 @@
package model

import (
"github.com/shivamMg/ppds/tree"
"io/ioutil"
"os"
"reflect"
"strconv"
"strings"
"testing"

"github.com/shivamMg/ppds/tree"

"github.com/pokanop/nostromo/keypath"
"github.com/pokanop/nostromo/pathutil"
)
Expand Down Expand Up @@ -316,7 +317,7 @@ func TestManifestData(t *testing.T) {
fields fields
want interface{}
}{
{"data", fields{"1.0", &Config{true, true, ConcatenateMode}, map[string]*Command{"foo": {}}}, "manifest"},
{"data", fields{"1.0", &Config{true, true, ConcatenateMode, 10}, map[string]*Command{"foo": {}}}, "manifest"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -347,7 +348,7 @@ func TestManifestChildren(t *testing.T) {
fields fields
want []tree.Node
}{
{"children", fields{"1.0", &Config{true, true, ConcatenateMode}, commands}, []tree.Node{commands["foo"], commands["bar"]}},
{"children", fields{"1.0", &Config{true, true, ConcatenateMode, 10}, commands}, []tree.Node{commands["foo"], commands["bar"]}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
5 changes: 3 additions & 2 deletions testdata/manifest.json
Expand Up @@ -3,7 +3,8 @@
"config": {
"verbose": true,
"aliasesOnly": false,
"mode": 0
"mode": 0,
"backupCount": 10
},
"commands": {
"0-one-alias": {
Expand Down Expand Up @@ -165,4 +166,4 @@
"mode": 0
}
}
}
}
1 change: 1 addition & 0 deletions testdata/manifest.yaml
Expand Up @@ -3,6 +3,7 @@ config:
verbose: true
aliasesonly: false
mode: 0
backupcount: 10
commands:
0-one-alias:
keypath: 0-one-alias
Expand Down

0 comments on commit 70e1463

Please sign in to comment.