mirror of
https://github.com/sbrow/envr.git
synced 2025-12-29 15:47:38 -05:00
refactor: Rewrote in golang.
This commit is contained in:
55
cmd/backup.go
Normal file
55
cmd/backup.go
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
Copyright © 2025 NAME HERE <EMAIL ADDRESS>
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/sbrow/envr/app"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// backupCmd represents the backup command
|
||||
var backupCmd = &cobra.Command{
|
||||
Use: "backup <path>",
|
||||
Short: "Import a .env file into envr",
|
||||
Args: cobra.ExactArgs(1),
|
||||
// Long: `Long desc`
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
path := args[0]
|
||||
if len(strings.TrimSpace(path)) == 0 {
|
||||
return fmt.Errorf("No path provided")
|
||||
}
|
||||
|
||||
db, err := app.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer db.Close(app.Write)
|
||||
record := app.NewEnvFile(path)
|
||||
|
||||
if err := db.Insert(record); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
fmt.Printf("Saved %s into the database", path)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(backupCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// backupCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// backupCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
||||
51
cmd/check.go
Normal file
51
cmd/check.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/sbrow/envr/app"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var checkCmd = &cobra.Command{
|
||||
Use: "check",
|
||||
Short: "Check for missing binaries",
|
||||
Long: `envr relies on external binaries for certain functionality.
|
||||
|
||||
The check command reports on which binaries are available and which are not.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
db, err := app.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer db.Close(app.ReadOnly)
|
||||
features := db.Features()
|
||||
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.Header([]string{"Feature", "Status"})
|
||||
|
||||
// Check Git
|
||||
if features&app.Git == 1 {
|
||||
table.Append([]string{"Git", "✓ Available"})
|
||||
} else {
|
||||
table.Append([]string{"Git", "✗ Missing"})
|
||||
}
|
||||
|
||||
// Check fd
|
||||
if features&app.Fd == 1 {
|
||||
table.Append([]string{"fd", "✓ Available"})
|
||||
} else {
|
||||
table.Append([]string{"fd", "✗ Missing"})
|
||||
}
|
||||
|
||||
table.Render()
|
||||
|
||||
return nil
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(checkCmd)
|
||||
}
|
||||
55
cmd/edit_config.go
Normal file
55
cmd/edit_config.go
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
Copyright © 2025 NAME HERE <EMAIL ADDRESS>
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var editConfigCmd = &cobra.Command{
|
||||
Use: "edit-config",
|
||||
Short: "Edit your config with your default editor",
|
||||
// Long: ``,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
editor := os.Getenv("EDITOR")
|
||||
if editor == "" {
|
||||
fmt.Println("Error: $EDITOR environment variable is not set")
|
||||
return
|
||||
}
|
||||
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
fmt.Printf("Error getting home directory: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
configPath := filepath.Join(homeDir, ".envr", "config.json")
|
||||
|
||||
// Check if config file exists
|
||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
||||
fmt.Printf("Config file does not exist at %s. Run 'envr init' first.\n", configPath)
|
||||
return
|
||||
}
|
||||
|
||||
// Execute the editor
|
||||
execCmd := exec.Command(editor, configPath)
|
||||
execCmd.Stdin = os.Stdin
|
||||
execCmd.Stdout = os.Stdout
|
||||
execCmd.Stderr = os.Stderr
|
||||
|
||||
if err := execCmd.Run(); err != nil {
|
||||
fmt.Printf("Error running editor: %v\n", err)
|
||||
return
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(editConfigCmd)
|
||||
}
|
||||
95
cmd/init.go
Normal file
95
cmd/init.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/sbrow/envr/app"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// TODO: Add --force (-f) flag.
|
||||
var initCmd = &cobra.Command{
|
||||
Use: "init",
|
||||
DisableFlagsInUseLine: true,
|
||||
Short: "Set up envr",
|
||||
Long: `The init command generates your initial config and saves it to
|
||||
~/.envr/config in JSON format.
|
||||
|
||||
During setup, you will be prompted to select one or more ssh keys with which to
|
||||
encrypt your databse. **Make 100% sure** that you have **a remote copy** of this
|
||||
key somewhere, otherwise your data could be lost forever.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
config, _ := app.LoadConfig()
|
||||
|
||||
if config != nil {
|
||||
return fmt.Errorf("You have already initialized envr")
|
||||
} else {
|
||||
keys, err := selectSSHKeys()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error selecting SSH keys: %v", err)
|
||||
}
|
||||
|
||||
if len(keys) == 0 {
|
||||
return fmt.Errorf("No SSH keys selected - Config not created")
|
||||
}
|
||||
|
||||
cfg := app.NewConfig(keys)
|
||||
if err := cfg.Save(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Config initialized with %d SSH key(s). You are ready to use envr.\n", len(keys))
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(initCmd)
|
||||
}
|
||||
|
||||
func selectSSHKeys() ([]string, error) {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: Support reading from ssh-agent
|
||||
sshDir := filepath.Join(homeDir, ".ssh")
|
||||
entries, err := os.ReadDir(sshDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read ~/.ssh directory: %w", err)
|
||||
}
|
||||
|
||||
var privateKeys []string
|
||||
for _, entry := range entries {
|
||||
name := entry.Name()
|
||||
if !entry.IsDir() && !strings.HasSuffix(name, ".pub") &&
|
||||
!strings.Contains(name, "known_hosts") && !strings.Contains(name, "config") {
|
||||
privateKeys = append(privateKeys, filepath.Join(sshDir, name))
|
||||
}
|
||||
}
|
||||
|
||||
if len(privateKeys) == 0 {
|
||||
return nil, fmt.Errorf("no SSH private keys found in ~/.ssh")
|
||||
}
|
||||
|
||||
var selected []string
|
||||
|
||||
prompt := &survey.MultiSelect{
|
||||
Message: "Select SSH private keys:",
|
||||
Options: privateKeys,
|
||||
}
|
||||
|
||||
err = survey.AskOne(prompt, &selected)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return selected, nil
|
||||
}
|
||||
69
cmd/list.go
Normal file
69
cmd/list.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/sbrow/envr/app"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type listEntry struct {
|
||||
Directory string `json:"directory"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
var listCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "View your tracked files",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
db, err := app.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer db.Close(app.ReadOnly)
|
||||
|
||||
rows, err := db.List()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.Header([]string{"Directory", "Path"})
|
||||
|
||||
for _, row := range rows {
|
||||
path, err := filepath.Rel(row.Dir, row.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
table.Append([]string{row.Dir + "/", path})
|
||||
}
|
||||
table.Render()
|
||||
} else {
|
||||
var entries []listEntry
|
||||
for _, row := range rows {
|
||||
path, err := filepath.Rel(row.Dir, row.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
entries = append(entries, listEntry{
|
||||
Directory: row.Dir + "/",
|
||||
Path: path,
|
||||
})
|
||||
}
|
||||
|
||||
encoder := json.NewEncoder(os.Stdout)
|
||||
return encoder.Encode(entries)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(listCmd)
|
||||
}
|
||||
79
cmd/mod.nu
Normal file
79
cmd/mod.nu
Normal file
@@ -0,0 +1,79 @@
|
||||
# envr command extern definitions for Nushell
|
||||
# A tool for managing environment files and backups
|
||||
|
||||
export def tracked-paths [] {
|
||||
(
|
||||
^envr list
|
||||
| from json
|
||||
| each {
|
||||
[$in.directory $in.path] | path join
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export def untracked-paths [] {
|
||||
(
|
||||
^envr scan
|
||||
| from json
|
||||
)
|
||||
}
|
||||
|
||||
# Complete shell types for completion command
|
||||
def shells [] {
|
||||
["bash", "zsh", "fish", "powershell"]
|
||||
}
|
||||
|
||||
export extern envr [
|
||||
...args: any
|
||||
--help(-h) # Show help information
|
||||
--toggle(-t) # Help message for toggle
|
||||
]
|
||||
|
||||
export extern "envr backup" [
|
||||
--help(-h) # Show help for backup command
|
||||
path: path@untracked-paths # Path to .env file to backup
|
||||
]
|
||||
#TODO: envr backup path.
|
||||
|
||||
export extern "envr check" [
|
||||
--help(-h) # Show help for check command
|
||||
]
|
||||
|
||||
export extern "envr completion" [
|
||||
shell: string@shells # Shell to generate completion for
|
||||
--help(-h) # Show help for completion command
|
||||
]
|
||||
|
||||
export extern "envr edit-config" [
|
||||
--help(-h) # Show help for edit-config command
|
||||
]
|
||||
|
||||
export extern "envr help" [
|
||||
command?: string # Show help for specific command
|
||||
]
|
||||
|
||||
export extern "envr init" [
|
||||
--help(-h) # Show help for init command
|
||||
]
|
||||
|
||||
export extern "envr list" [
|
||||
--help(-h) # Show help for list command
|
||||
]
|
||||
|
||||
export extern "envr remove" [
|
||||
--help(-h) # Show help for remove command
|
||||
path: path@tracked-paths
|
||||
]
|
||||
|
||||
export extern "envr restore" [
|
||||
--help(-h) # Show help for restore command
|
||||
path: path@tracked-paths
|
||||
]
|
||||
|
||||
export extern "envr scan" [
|
||||
--help(-h) # Show help for scan command
|
||||
]
|
||||
|
||||
export extern "envr sync" [
|
||||
--help(-h) # Show help for sync command
|
||||
]
|
||||
26
cmd/nushell_completion.go
Normal file
26
cmd/nushell_completion.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
//go:embed mod.nu
|
||||
var completion string
|
||||
|
||||
// nushellCompletionCmd represents the nushellCompletion command
|
||||
var nushellCompletionCmd = &cobra.Command{
|
||||
Use: "nushell-completion",
|
||||
Short: "Generate custom completions for nushell",
|
||||
Long: `At time of writing, cobra does not natively support nushell,
|
||||
so a custom command had to be written`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println(completion)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(nushellCompletionCmd)
|
||||
}
|
||||
51
cmd/remove.go
Normal file
51
cmd/remove.go
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright © 2025 NAME HERE <EMAIL ADDRESS>
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/sbrow/envr/app"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var removeCmd = &cobra.Command{
|
||||
Use: "remove",
|
||||
Short: "Remove a .env file from your database",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
path := args[0]
|
||||
if len(strings.TrimSpace(path)) == 0 {
|
||||
return fmt.Errorf("No path provided")
|
||||
}
|
||||
|
||||
db, err := app.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer db.Close(app.Write)
|
||||
if err := db.Delete(path); err != nil {
|
||||
return err
|
||||
} else {
|
||||
fmt.Printf("Removed %s from the database", path)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(removeCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// removeCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// removeCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
||||
60
cmd/restore.go
Normal file
60
cmd/restore.go
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
Copyright © 2025 NAME HERE <EMAIL ADDRESS>
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/sbrow/envr/app"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// restoreCmd represents the restore command
|
||||
var restoreCmd = &cobra.Command{
|
||||
Use: "restore",
|
||||
Short: "Install a .env file from the database into your file system",
|
||||
// Long: ``,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
path := args[0]
|
||||
if len(strings.TrimSpace(path)) == 0 {
|
||||
return fmt.Errorf("No path provided")
|
||||
}
|
||||
|
||||
db, err := app.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer db.Close(app.ReadOnly)
|
||||
record, err := db.Fetch(path)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
err := record.Restore()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(restoreCmd)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
|
||||
// Cobra supports Persistent Flags which will work for this command
|
||||
// and all subcommands, e.g.:
|
||||
// restoreCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||
|
||||
// Cobra supports local flags which will only run when this command
|
||||
// is called directly, e.g.:
|
||||
// restoreCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
||||
66
cmd/root.go
Normal file
66
cmd/root.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "envr",
|
||||
Short: "Manage your .env files.",
|
||||
Long: `envr keeps your .env synced to a local, age encrypted database.
|
||||
Is a safe and eay way to gather all your .env files in one place where they can
|
||||
easily be backed by another tool such as restic or git.
|
||||
|
||||
All your data is stored in ~/data.age
|
||||
|
||||
Getting started is easy:
|
||||
|
||||
1. Create your configuration file and set up encrypted storage:
|
||||
|
||||
> envr init
|
||||
|
||||
2. Scan for existing .env files:
|
||||
|
||||
> envr scan
|
||||
|
||||
Select the files you want to back up from the interactive list.
|
||||
|
||||
3. Verify that it worked:
|
||||
|
||||
> envr list
|
||||
|
||||
4. After changing any of your .env files, update the backup with:
|
||||
|
||||
> envr sync
|
||||
|
||||
5. If you lose a repository, after re-cloning the repo into the same path it was
|
||||
at before, restore your backup with:
|
||||
|
||||
> envr restore ~/<path to repository>/.env`,
|
||||
}
|
||||
|
||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||
func Execute() {
|
||||
err := rootCmd.Execute()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Here you will define your flags and configuration settings.
|
||||
// Cobra supports persistent flags, which, if defined here,
|
||||
// will be global for your application.
|
||||
|
||||
// rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.envr.yaml)")
|
||||
|
||||
// Cobra also supports local flags, which will only run
|
||||
// when this action is called directly.
|
||||
// rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||
}
|
||||
|
||||
// Expose the root command for our generators.
|
||||
func Root() *cobra.Command { return rootCmd }
|
||||
104
cmd/scan.go
Normal file
104
cmd/scan.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/sbrow/envr/app"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var scanCmd = &cobra.Command{
|
||||
Use: "scan",
|
||||
Short: "Find and select .env files for backup",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
db, err := app.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if db == nil {
|
||||
return fmt.Errorf("No db was loaded")
|
||||
}
|
||||
|
||||
if err := db.CanScan(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
files, err := db.Scan()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(files) == 0 {
|
||||
return fmt.Errorf("No .env files found to add.")
|
||||
}
|
||||
|
||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||
selectedFiles, err := selectEnvFiles(files)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Insert selected files into database
|
||||
var addedCount int
|
||||
for _, file := range selectedFiles {
|
||||
envFile := app.NewEnvFile(file)
|
||||
err := db.Insert(envFile)
|
||||
if err != nil {
|
||||
fmt.Printf("Error adding %s: %v\n", file, err)
|
||||
} else {
|
||||
addedCount++
|
||||
}
|
||||
}
|
||||
|
||||
// Close database with write mode to persist changes
|
||||
if addedCount > 0 {
|
||||
err = db.Close(app.Write)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error saving changes: %v\n", err)
|
||||
} else {
|
||||
fmt.Printf("Successfully added %d file(s) to backup.\n", addedCount)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
err = db.Close(app.ReadOnly)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error closing database: %v\n", err)
|
||||
}
|
||||
fmt.Println("No files were added.")
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
output, err := json.Marshal(files)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error marshaling files to JSON: %v", err)
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
return nil
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(scanCmd)
|
||||
}
|
||||
|
||||
func selectEnvFiles(files []string) ([]string, error) {
|
||||
var selectedFiles []string
|
||||
|
||||
prompt := &survey.MultiSelect{
|
||||
Message: "Select .env files to backup:",
|
||||
Options: files,
|
||||
}
|
||||
|
||||
err := survey.AskOne(prompt, &selectedFiles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return selectedFiles, nil
|
||||
}
|
||||
62
cmd/sync.go
Normal file
62
cmd/sync.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/sbrow/envr/app"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// TODO: Detect when file paths have moved and update accordingly.
|
||||
var syncCmd = &cobra.Command{
|
||||
Use: "sync",
|
||||
Short: "Update or restore your env backups",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
db, err := app.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
defer db.Close(app.Write)
|
||||
files, err := db.List()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
for _, file := range files {
|
||||
fmt.Printf("%s\n", file.Path)
|
||||
|
||||
// Syncronize the filesystem with the database.
|
||||
changed, err := file.Sync()
|
||||
|
||||
switch changed {
|
||||
case app.Updated:
|
||||
fmt.Printf("File updated - changes saved\n")
|
||||
if err := db.Insert(file); err != nil {
|
||||
return err
|
||||
}
|
||||
case app.Restored:
|
||||
fmt.Printf("File missing - restored backup\n")
|
||||
case app.Error:
|
||||
if err == nil {
|
||||
panic("err cannot be nil when Sync returns Error")
|
||||
} else {
|
||||
fmt.Printf("%s\n", err)
|
||||
}
|
||||
case app.Noop:
|
||||
fmt.Println("Nothing to do")
|
||||
default:
|
||||
panic("Unknown result")
|
||||
}
|
||||
|
||||
fmt.Println("")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(syncCmd)
|
||||
}
|
||||
35
cmd/version.go
Normal file
35
cmd/version.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
version = "dev"
|
||||
commit = "none"
|
||||
date = "unknown"
|
||||
)
|
||||
|
||||
var long bool
|
||||
|
||||
// versionCmd represents the version command
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Show envr's version",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if long {
|
||||
fmt.Printf("envr version %s\n", version)
|
||||
fmt.Printf("commit: %s\n", commit)
|
||||
fmt.Printf("built: %s\n", date)
|
||||
} else {
|
||||
fmt.Printf("%s\n", version)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
versionCmd.Flags().BoolVarP(&long, "long", "l", false, "Show all version information")
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
}
|
||||
Reference in New Issue
Block a user