github.com/dvln/wkspc@v0.0.0-20150922010055-31dce5e9918e/wkspc.go (about)

     1  // Copyright © 2015 Erik Brady <brady@dvln.org>
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  // Package wkspc works with 'dvln' workspaces.  This typically means routines
    15  // to query or update a workspaces dvln "meta-data" can be found here.
    16  package wkspc
    17  
    18  import (
    19  	"os"
    20  	"path/filepath"
    21  
    22  	"github.com/dvln/codebase"
    23  	"github.com/dvln/devline"
    24  	"github.com/dvln/out"
    25  	"github.com/dvln/pkg"
    26  	"github.com/dvln/util/dir"
    27  	"github.com/dvln/util/file"
    28  	//"github.com/dvln/vcs"
    29  	globs "github.com/dvln/viper"
    30  )
    31  
    32  // Reader is targeted at reading workspace information (no wkspc meta-data
    33  // updates will occur during read operations although logging of info is
    34  // possible during such operations, eg: debugging/tracing)
    35  type Reader interface {
    36  	RootDir(path ...string) (string, error)
    37  	LogDir() (string, error)
    38  	TmpDir() (string, error)
    39  	Codebase() (codebase.Defn, error)
    40  	CodebaseRev() (pkg.Revision, error)
    41  	Devline() (devline.Revision, error)
    42  	Pkg(pkgName string, pkgID int) (pkg.Defn, error)
    43  	PkgRev(pkgName string, pkgID int) (pkg.Revision, error)
    44  	PkgDevline(pkgRev pkg.Revision, pkgID int) (devline.Revision, error)
    45  	//FIXME: it's likely if workspace ops come through this pkg that these
    46  	//       can be internal functions that are used within the context of
    47  	//       the operation and not needed in the interface
    48  	//DBDir() (string, error)
    49  	//DB() (string, error)
    50  	//VCSDir() (string, error)
    51  	//VCSDataDir() (string, error)
    52  	//VCSDataRepo() (vcs.Repo, error)
    53  }
    54  
    55  // Writer is meant for writing workspace data
    56  type Writer interface {
    57  	SetRootDir(rootDir string) error
    58  	SetCodebase(codebase.Defn) error
    59  	UpdCodebase(codebase.Defn) error
    60  	//CommitCodebase() error
    61  	//PushCodebase() error
    62  	SetDevline(devline.Revision) error
    63  	CommitDevline() error
    64  	//PushDevline() error
    65  	GetPkgRev(pkg.Revision) error // "Get" here relates to '% dvln get ..'
    66  	PullPkgRev(pkg.Revision) error
    67  	//CommitPkgRev(pkg.Defn) error
    68  	//PushPkgRev(pkg.Defn) error
    69  	RmPkg(pkg.Defn) error
    70  }
    71  
    72  // Info contains, well, information about the workspace (eg: wkspc.Info)
    73  type Info struct {
    74  	RootDir string `json:"rootDir,omitempty"`
    75  	MetaDir string `json:"metaDir,omitempty"`
    76  	LogDir  string `json:"logDir,omitempty"`
    77  	TmpDir  string `json:"tmpDir,omitempty"`
    78  	VCSDir  string `json:"vcsDir,omitempty"`
    79  	DBDir   string `json:"dbDir,omitempty"`
    80  	DB      string `json:"db,omitempty"`
    81  	//multipkg.Hierarchy
    82  	//multipkg.Pkgs
    83  }
    84  
    85  // doPkgWkspcGlobsInit sets up default settings for any variables/opts used
    86  // for the dvln wkspc pkg... "globals" so to speak.  These are currently
    87  // stashed in the 'globs' (viper) package at the default level (lowest
    88  // priority essentially) and can be overriden via config file, CLI
    89  // flags, and codebase, package and devline level overrides (in time).
    90  // - Any dvln specific package can use globs to store "globals" which are then
    91  //   effectively visible across all dvln commands and non-generic packages
    92  // - Generic packages (eg: lib/dvln/out, or: github.com/dvln/out) should *NOT*
    93  //   use globs (viper) for config so as to remain extremely generic (and usable
    94  //   by the global community)... for such generic packages (or 3rd party/vendor
    95  //   packages dvln is using the dvln or dvln get cmd can use globs (vipers) to
    96  //   get DVLN configuration or opts/etc... and then it can use API's for those
    97  //   generic package (or package variables) to config that pkg for dvln's use
    98  //   as one can see with dvln/cmd/dvln.go init'ing the 'out' pkg on startup.
    99  //
   100  // Note: for any new CLI focused option for the dvln meta-cmd please check out
   101  //       setupDvlnCmdCLIArgs() in cmds/dvln.go and for subcommands see related
   102  //       setup<Name>CmdCLIArgs() located within each cmds/<name>.go file, I'd
   103  //       suggest searching on the string "NewCLIOpts" to find those locations.
   104  func doPkgWkspcGlobsInit() {
   105  	// Note: this is currently in sections related to the scope of how the
   106  	//       variable can be set, feel free to set subsections within those
   107  	//       sections if needed (eg: path variables, clitool name vars)...
   108  	//       essentially any grouping you see fit at this point but try and
   109  	//       at least get the top level Section right
   110  
   111  	// Section: ConstGlobal variables to store data (default value only, no overrides)
   112  	// - please add them alphabetically and don't reuse existing opts/vars
   113  	globs.SetDefault("wkspcMetaDirName", ".dvln")
   114  	globs.SetDesc("wkspcMetaDirName", "name of dir under wkspc root where dvln cfg lives", globs.InternalUse, globs.ConstGlobal)
   115  
   116  	// Section: InternalGlobal variables to store data (default value, can be changed by this pkg)
   117  	// - please add them alphabetically and don't reuse existing opts/vars
   118  	globs.SetDefault("wkspcRootDir", "none")
   119  	globs.SetDesc("wkspcRootDir", "the workspace root directory, if one exists", globs.InternalUse, globs.InternalGlobal)
   120  
   121  	globs.SetDefault("wkspcMetaDir", "")
   122  	globs.SetDesc("wkspcMetaDir", "the .dvln dir under the workspace root dir, empty if no root", globs.InternalUse, globs.InternalGlobal)
   123  
   124  	globs.SetDefault("wkspcLogDir", "")
   125  	globs.SetDesc("wkspcLogDir", "the .dvln/log dir under the workspace root dir, empty if no root", globs.InternalUse, globs.InternalGlobal)
   126  
   127  	globs.SetDefault("wkspcTmpDir", "")
   128  	globs.SetDesc("wkspcTmpDir", "the .dvln/tmp dir under the workspace root dir, empty if no root", globs.InternalUse, globs.InternalGlobal)
   129  
   130  	globs.SetDefault("wkspcVCSDir", "")
   131  	globs.SetDesc("wkspcVCSDir", "the .dvln/vcs dir under the workspace root dir, empty if no root", globs.InternalUse, globs.InternalGlobal)
   132  
   133  	globs.SetDefault("wkspcVCSDataDir", "")
   134  	globs.SetDesc("wkspcVCSDataDir", "the .dvln/vcs/wkspc dir under the workspace root dir, empty if no root", globs.InternalUse, globs.InternalGlobal)
   135  
   136  	globs.SetDefault("wkspcDBDir", "")
   137  	globs.SetDesc("wkspcDBDir", "the .dvln/db dir under the workspace root dir, empty if no root", globs.InternalUse, globs.InternalGlobal)
   138  
   139  	globs.SetDefault("wkspcDB", "")
   140  	globs.SetDesc("wkspcDB", "the .dvln/db/wkspc.db file under the workspace root dir, empty if no root", globs.InternalUse, globs.InternalGlobal)
   141  
   142  	// Section: BasicGlobal variables to store data (env, config file, default)
   143  	// - please add them alphabetically and don't reuse existing opts/vars
   144  
   145  	// Section: CLIGlobal class options, vars that can come in from the CLI
   146  	// - Don't put these here, see cmds/dlvn.go and the cmds/cmdglobs.go file
   147  
   148  	// Section: <add more sections as needed>
   149  }
   150  
   151  // At startup time we'll initialize settings for the workspace, same may be
   152  // "constant"-like, others may be overridable via the users config file and env
   153  func init() {
   154  	doPkgWkspcGlobsInit()
   155  }
   156  
   157  // RootDir will return the path to the top-most workspace root directory
   158  // based on our current path unless a "starting directory" path is given
   159  // (all other params are ignored).  It will return the path to the
   160  // workspace root if it found one, otherwise "" (a non-nil error implies
   161  // there was an unexpected problem... ie: not being able to find a workspace
   162  // root directory is NOT an error condition).  Note: if you wish to bypass
   163  // cached workspace root info then use RootDirFind() directly instead.
   164  func RootDir(path ...string) (string, error) {
   165  	// wkspcRootDir starts out as "none", if something else we've calc'd it..
   166  	wkspcRootDir := globs.GetString("wkspcrootDir")
   167  	if wkspcRootDir != "none" {
   168  		return wkspcRootDir, nil
   169  	}
   170  	// otherwise find it (this will always try and find it and cache it)
   171  	return RootDirFind(path...)
   172  }
   173  
   174  // RootDirFind gets the workspaces root dir (if there is one), note that this will
   175  // not use any "cached" values and it will store the result in globs (viper)
   176  // under the "wkspcRootDir" key (you can access that with any upper/lower case
   177  // as viper is case insensitive).
   178  func RootDirFind(path ...string) (string, error) {
   179  	startDir := ""
   180  	var err error
   181  	if path == nil || path[0] == "" {
   182  		startDir, err = os.Getwd()
   183  		if err != nil {
   184  			return "", out.WrapErr(err, "Unable to find the workspace root directory (get current working dir failed)", 4100)
   185  		}
   186  	} else {
   187  		startDir = path[0]
   188  	}
   189  	rootDir, err := dir.FindDirInOrAbove(startDir, globs.GetString("wkspcMetaDirName"))
   190  	if err == nil {
   191  		// Cache the root dir in "globs" (viper) memory if no error
   192  		globs.Set("wkspcRootDir", rootDir)
   193  	}
   194  	return rootDir, err
   195  }
   196  
   197  // prepIfNotThere will create the dir if it doesn't exist and store info about
   198  // it in viper (if a name is given), returns the full dir name and any
   199  // error that occurs.  Note that "item" can be "" to indicate no sub dir.
   200  func prepIfNotThere(directory bool, parent, item, name string) (string, error) {
   201  	var err error
   202  	fullPath := ""
   203  	if item != "" {
   204  		fullPath = filepath.Join(parent, item)
   205  	} else {
   206  		fullPath = parent
   207  	}
   208  	if name != "" {
   209  		globs.Set(name, fullPath)
   210  	}
   211  	if fullPath != "" {
   212  		if directory {
   213  			err = dir.CreateIfNotExists(fullPath)
   214  		} else {
   215  			err = file.CreateIfNotExists(fullPath)
   216  		}
   217  	}
   218  	return fullPath, err
   219  }
   220  
   221  // SetRootDir will set the given dir as the workspace root directory, it will also
   222  // set some "derived" settings in globs (viper) such as:
   223  //   wkspcMetaDir:    $wsroot/.dvln
   224  //   wkspcLogDir:     $wsroot/.dvln/log
   225  //   wkspcTmpDir:     $wsroot/.dvln/tmp
   226  //   wkspcVCSDir:     $wsroot/.dvln/vcs
   227  //   wkspcVCSDataDir: $wsroot/.dvln/vcs/wkspc
   228  //   wkspcDBDir:      $wsroot/.dvln/db
   229  //   wkspcDB:         $wsroot/.dvln/db/wkspc.db
   230  // As other dirs or files are added relative to the .dvln/ wkspc meta-data dir
   231  // they can be added here to "bootstap" them.  Note that if
   232  func SetRootDir(rootDir string) error {
   233  	if rootDir == "" {
   234  		// if there is no root dir found allow it to be set to "" and bail
   235  		globs.Set("wkspcRootDir", rootDir)
   236  		return nil
   237  	}
   238  	directory := true
   239  	_, err := prepIfNotThere(directory, rootDir, "", "wkspcRootDir")
   240  	if err != nil {
   241  		return err
   242  	}
   243  	wkspcMetaDir, err := prepIfNotThere(directory, rootDir, globs.GetString("wkspcMetaDirName"), "wkspcMetaDir")
   244  	if err != nil {
   245  		return err
   246  	}
   247  	_, err = prepIfNotThere(directory, wkspcMetaDir, "log", "wkspcLogDir")
   248  	if err != nil {
   249  		return err
   250  	}
   251  	_, err = prepIfNotThere(directory, wkspcMetaDir, "tmp", "wkspcTmpDir")
   252  	if err != nil {
   253  		return err
   254  	}
   255  	wkspcVCSDir, err := prepIfNotThere(directory, wkspcMetaDir, "vcs", "wkspcVCSDir")
   256  	if err != nil {
   257  		return err
   258  	}
   259  	wkspcVCSDataDir, err := prepIfNotThere(directory, wkspcVCSDir, "wkspc", "wkspcVCSDataDir")
   260  	if err != nil {
   261  		return err
   262  	}
   263  	_, err = prepIfNotThere(!directory, wkspcVCSDataDir, "static.dvln", "wkspcStaticDvln")
   264  	if err != nil {
   265  		return err
   266  	}
   267  	wkspcDBDir, err := prepIfNotThere(directory, wkspcMetaDir, "db", "wkspcDBDir")
   268  	if err != nil {
   269  		return err
   270  	}
   271  	_, err = prepIfNotThere(!directory, wkspcDBDir, "wkspc.db", "wkspcDB")
   272  	if err != nil {
   273  		return err
   274  	}
   275  	return nil
   276  }