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 }