47f7b0e609
This change moves the workspace and Mufile detection logic out of the compiler package and into the workspace one. This also sketches out the overall workspace structure. A workspace is "delimited" by the presence of a .mu/ directory anywhere in the parent ancestry. Inside of that directory we have an optional .mu/clusters.yaml (or .json) file containing cluster settings shared among the whole workspace. We also have an optional .mu/stacks/ directory that contains dependencies used during package management. The notion of a "global" workspace will also be present, which is essentially just a .mu/ directory in your home, ~/.mu/, that has an equivalent structure, but can be shared among all workspaces on the same machine.
117 lines
3.2 KiB
Go
117 lines
3.2 KiB
Go
// Copyright 2016 Marapongo, Inc. All rights reserved.
|
|
|
|
package workspace
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/marapongo/mu/pkg/diag"
|
|
"github.com/marapongo/mu/pkg/errors"
|
|
"github.com/marapongo/mu/pkg/util"
|
|
)
|
|
|
|
// Mufile is the base name of a Mufile.
|
|
const Mufile = "Mu"
|
|
|
|
// Muspace is a directory containing settings, modules, etc., delimiting a workspace.
|
|
const Muspace = ".mu"
|
|
|
|
// MuspaceStacks is the directory in which dependency modules exist, either local to a workspace, or globally.
|
|
const MuspaceStacks = "stacks"
|
|
|
|
// MuspaceClusters is the base name of a clusters specification file.
|
|
const MuspaceClusters = "clusters"
|
|
|
|
// Exts contains a list of all the valid Mufile and Mucluster extensions.
|
|
var Exts = []string{
|
|
".json",
|
|
".yaml",
|
|
// Although ".yml" is not a sanctioned YAML extension, it is used quite broadly; so we will support it.
|
|
".yml",
|
|
}
|
|
|
|
// DetectMufile locates the closest Mufile from the given path, searching "upwards" in the directory hierarchy. If no
|
|
// Mufile is found, an empty path is returned. If problems are detected, they are logged to the diag.Sink.
|
|
func DetectMufile(from string, d diag.Sink) string {
|
|
abs, err := filepath.Abs(from)
|
|
util.AssertMF(err == nil, "An IO error occurred while searching for a Mufile: %v", err)
|
|
|
|
// It's possible the target is already the file we seek; if so, return right away.
|
|
if IsMufile(abs, d) {
|
|
return abs
|
|
}
|
|
|
|
curr := abs
|
|
for {
|
|
stop := false
|
|
|
|
// If the target is a directory, enumerate its files, checking each to see if it's a Mufile.
|
|
files, err := ioutil.ReadDir(curr)
|
|
util.AssertMF(err == nil, "An IO error occurred while searching for a Mufile: %v", err)
|
|
for _, file := range files {
|
|
name := file.Name()
|
|
path := filepath.Join(curr, name)
|
|
if IsMufile(path, d) {
|
|
return path
|
|
} else if name == Muspace {
|
|
// If we hit a .muspace file, stop looking.
|
|
stop = true
|
|
}
|
|
}
|
|
|
|
// If we encountered a stop condition, break out of the loop.
|
|
if stop {
|
|
break
|
|
}
|
|
|
|
// If neither succeeded, keep looking in our parent directory.
|
|
curr = filepath.Dir(curr)
|
|
if os.IsPathSeparator(curr[len(curr)-1]) {
|
|
break
|
|
}
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
// IsMufile returns true if the path references what appears to be a valid Mufile. If problems are detected -- like
|
|
// an incorrect extension -- they are logged to the provided diag.Sink (if non-nil).
|
|
func IsMufile(path string, d diag.Sink) bool {
|
|
info, err := os.Stat(path)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
// Directories can't be Mufiles.
|
|
if info.IsDir() {
|
|
return false
|
|
}
|
|
|
|
// Ensure the base name is expected.
|
|
name := info.Name()
|
|
ext := filepath.Ext(name)
|
|
base := strings.TrimSuffix(name, ext)
|
|
if base != Mufile {
|
|
if d != nil && strings.EqualFold(base, Mufile) {
|
|
// If the strings aren't equal, but case-insensitively match, issue a warning.
|
|
d.Warningf(errors.WarnIllegalMufileCasing.WithFile(name))
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Check all supported extensions.
|
|
for _, mufileExt := range Exts {
|
|
if name == Mufile+mufileExt {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// If we got here, it means the base name matched, but not the extension. Warn and return.
|
|
if d != nil {
|
|
d.Warningf(errors.WarnIllegalMufileExt.WithFile(name), ext)
|
|
}
|
|
return false
|
|
}
|