Permalink
Browse files

initial commit

  • Loading branch information...
jmoiron committed Aug 6, 2012
0 parents commit 4a7f2c783ef5521f4cc94fcd4e47d26a89aeb42a
Showing with 486 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +30 −0 README
  3. +13 −0 autobuild.sh
  4. +96 −0 cfg.go
  5. +162 −0 cm.go
  6. +184 −0 commands.go
@@ -0,0 +1 @@
+*.swp
30 README
@@ -0,0 +1,30 @@
+Usage: ./cm [options]:
+
+ --help, -h: show help
+ --version: show version
+ --verbose, -v: show more output
+
+cm is a (very) simple configuration manager designed to help keep and maintain
+system configs in an alternate overlay directory, which could then be separately
+backed up, distributed, or under version control. The optional file arguments
+given to cm can generally also be directories, in which case cm always behaves
+recursively. Usage follows typical version control interface, with 'cm' followed
+by a command and then arguments related to that command:
+
+ add <file> - add a file to management directory
+ rm <file> - remove file from management directory
+ show - show what files, if any, are added under cm
+ diff [file] - show diff between files under cm
+ sync [all] - sync cm overlay to cwd, or / if "all"
+ pkg [subcmd...] - package management subcommand
+
+cm can also keep a log of installed packages and install missing packages. This
+is done by using the command "pkg" (or "package") and using one of the following
+subcommands:
+
+ pkg add <name> - add package to management list
+ pkg rm <name> - remove package from management list
+ pkg sync - install packages from list
+ pkg diff [name] -
+ pkg show [name] - show packages and install status, matching optional name
+
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+cur=`pwd`
+
+inotifywait -mqr --timefmt '%d/%m/%y %H:%M' --format '%T %w %f' \
+ -e modify ./ | while read date time dir file; do
+ ext="${file##*.}"
+ if [[ "$ext" = "go" ]]; then
+ echo "$file changed @ $time $date, rebuilding..."
+ go build
+ fi
+done
+
96 cfg.go
@@ -0,0 +1,96 @@
+package main
+
+// Configuration management part of cm
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+const (
+ CONFIG_PATH = "/opt/cm"
+ CONFIG_PATH_MODE = 0755
+)
+
+// return the overlay version of a path
+func C(path string) string { return filepath.Join(CONFIG_PATH, path) }
+
+// return the non-overlay version of a path
+func X(path string) string {
+ if strings.HasPrefix(path, CONFIG_PATH) {
+ return path[len(CONFIG_PATH):]
+ }
+ return path
+}
+
+// copy the file `dst` to the file `src`, creating any direcsrcries necessary
+func Copy(dst, src string) error {
+ if !isFile(src) {
+ return fmt.Errorf("Source path %s must be a file.", src)
+ }
+ ss, _ := os.Stat(src)
+ s, err := os.Open(src)
+ if err != nil {
+ return err
+ }
+ defer s.Close()
+ dir := filepath.Dir(dst)
+ err = os.MkdirAll(dir, 0755)
+ if err != nil {
+ return err
+ }
+ d, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, ss.Mode())
+ if err != nil {
+ return err
+ }
+ defer d.Close()
+ _, err = io.Copy(d, s)
+ return err
+}
+
+// return true if path exists and is a file, false otherwise
+func isFile(path string) bool {
+ fi, err := os.Stat(path)
+ if err == nil && !fi.IsDir() {
+ return true
+ }
+ if err != nil && !os.IsNotExist(err) {
+ fmt.Printf("%s\n", err.Error())
+ }
+ return false
+}
+
+// initialize config path if not present, return an error if either the dir
+// exists and is not writable or the dir does not exist and cannot be created
+func configPathInit() error {
+ fi, err := os.Stat(CONFIG_PATH)
+ // if it doesn't exist
+ if err != nil && os.IsNotExist(err) {
+ err = os.MkdirAll(CONFIG_PATH, CONFIG_PATH_MODE)
+ return err
+ // if it isn't a dir or symlink
+ } else if !fi.IsDir() && (fi.Mode()&os.ModeSymlink) == 0 {
+ return fmt.Errorf("Path %s must be directory or symlink.", CONFIG_PATH)
+ }
+ // if it exists, check for writability
+ for i := 0; ; i++ {
+ path := C(fmt.Sprintf("__testfile%d", i))
+ if isFile(path) {
+ continue
+ }
+ _, err := os.Create(path)
+ if err == nil {
+ os.Remove(path)
+ return nil
+ }
+ if os.IsPermission(err) {
+ return fmt.Errorf("Path %s must be writable.", CONFIG_PATH)
+ }
+ return err
+ }
+ return errors.New("Unknown error setting up config path.")
+}
162 cm.go
@@ -0,0 +1,162 @@
+package main
+
+import (
+ "fmt"
+ "github.com/jmoiron/go-pkg-optarg"
+ "os"
+)
+
+const (
+ VERSION = "0.1"
+ ABOUT = `
+cm is a (very) simple configuration manager designed to help keep and maintain
+system configs in an alternate overlay directory, which could then be separately
+backed up, distributed, or under version control. The optional file arguments
+given to cm can generally also be directories, in which case cm always behaves
+recursively. Usage follows typical version control interface, with 'cm' followed
+by a command and then arguments related to that command:
+
+ add <file> - add a file to management directory
+ rm <file> - remove file from management directory
+ show - show what files, if any, are added under cm
+ diff [file] - show diff between files under cm
+ sync [all] - sync cm overlay to cwd, or / if "all"
+ pkg [subcmd...] - package management subcommand
+
+cm can also keep a log of installed packages and install missing packages. This
+is done by using the command "pkg" (or "package") and using one of the following
+subcommands:
+
+ pkg add <name> - add package to management list
+ pkg rm <name> - remove package from management list
+ pkg sync - install packages from list
+ pkg diff [name] - show diff between list and installed, matching optional name
+ pkg show [name] - show packages and install status, matching optional name
+
+`
+ COMMAND_HELP = `
+Valid commands:
+
+ add <file> - add a file to management directory
+ rm <file> - remove file from management directory
+ show - show what files, if any, are added under cm
+ diff [file] - show diff between files under cm
+ sync [all] - sync cm overlay to cwd, or / if "all"
+ pkg add <name> - add package to management list
+ pkg rm <name> - remove package from management list
+ pkg sync - install packages from list
+ pkg diff [name] - show diff between list and installed, matching optional name
+ pkg show [name] - show packages and install status, matching optional name
+
+`
+)
+
+type Opts struct {
+ Verbose bool
+}
+
+var opts Opts
+
+func vPrintf(s string, x ...interface{}) {
+ if opts.Verbose {
+ fmt.Printf(s, x...)
+ }
+}
+
+func init() {
+ optarg.HeaderFmt = "%s"
+ optarg.Header("")
+ optarg.Add("h", "help", "show help", false)
+ optarg.Add("", "version", "show version", false)
+ optarg.Add("v", "verbose", "show more output", false)
+ for opt := range optarg.Parse() {
+ switch opt.Name {
+ case "help":
+ optarg.Usage()
+ fmt.Printf(ABOUT)
+ os.Exit(0)
+ case "version":
+ fmt.Printf("%s\n", VERSION)
+ os.Exit(0)
+ case "verbose":
+ opts.Verbose = opt.Bool()
+ }
+ }
+}
+
+func main() {
+ args := optarg.Remainder
+ if len(args) == 0 {
+ vPrintf("Nothing to do. Run `%s --help` for usage.\n", os.Args[0])
+ return
+ }
+ // sanity check, initialize the config path and confirm it's writable
+ err := configPathInit()
+ if err != nil {
+ fmt.Printf("Error: %s\n", err.Error())
+ return
+ }
+
+ cmd := args[0]
+ args = args[1:]
+
+ // work on cwd if no args are given
+ if len(args) == 0 {
+ args = append(args, ".")
+ }
+
+ switch cmd {
+ case "add":
+ // FIXME: symlink behavior?
+ for _, arg := range args {
+ err := Add(arg)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error (Add): %s\n", err.Error())
+ }
+ }
+ case "rm", "remove":
+ for _, arg := range args {
+ err := Rm(arg)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error (Rm): %s\n", err.Error())
+ }
+ }
+ case "show":
+ for _, arg := range args {
+ err := Show(arg)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error (Show): %s\n", err.Error())
+ }
+ }
+ case "sync", "update":
+ for _, arg := range args {
+ err := Sync(arg)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error (Sync): %s\n", err.Error())
+ }
+ }
+ case "diff":
+ for _, arg := range args {
+ err := Diff(arg)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error (Diff): %s\n", err.Error())
+ }
+ }
+ case "pkg", "package":
+ subcmd := args[0]
+ if subcmd == "." {
+ fmt.Fprintf(os.Stderr, "Error: pkg command requires subcommand.\n")
+ return
+ }
+ args := args[1:]
+ switch subcmd {
+ default:
+ fmt.Fprintf(os.Stderr, "Error: pkg subcommand %s not recognized\n", cmd)
+ fmt.Fprintf(os.Stderr, COMMAND_HELP)
+ }
+ default:
+ fmt.Fprintf(os.Stderr, "Error: command %s not recognized\n", cmd)
+ fmt.Fprintf(os.Stderr, COMMAND_HELP)
+ }
+
+}
Oops, something went wrong.

0 comments on commit 4a7f2c7

Please sign in to comment.