Skip to content

Commit

Permalink
New error handling for 'config.json'. WrapError and WrapErrorf can pr…
Browse files Browse the repository at this point in the history
…int their locations.
  • Loading branch information
Nusiq committed Feb 6, 2022
1 parent 4a0ee83 commit 6429c5d
Show file tree
Hide file tree
Showing 20 changed files with 265 additions and 183 deletions.
14 changes: 12 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,18 @@ func main() {
if regolith.StringArrayContains(c.FlagNames(), "add") {
regolith.AddFilters(c.StringSlice("add"), c.Bool("force"))
} else {
config := regolith.ConfigFromObject(regolith.LoadConfigAsMap())
return config.InstallFilters(c.Bool("force"))
configJson, err := regolith.LoadConfigAsMap()
if err != nil {
return regolith.WrapError(err, "could not load config.json")
}
config, err := regolith.ConfigFromObject(configJson)
if err != nil {
return regolith.WrapError(err, "could not load config.json")
}
err = config.InstallFilters(c.Bool("force"))
if err != nil {
return regolith.WrapError(err, "could not install filters")
}
}
return nil
},
Expand Down
6 changes: 3 additions & 3 deletions regolith/compatibility_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ func copyFileSecurityInfo(source string, target string) error {
windows.SE_FILE_OBJECT,
windows.DACL_SECURITY_INFORMATION)
if err != nil {
return wrapErrorf(
return WrapErrorf(
err, "Unable to get security info of %q.", source)
}
dacl, _, err := securityInfo.DACL()
if err != nil {
return wrapErrorf(
return WrapErrorf(
err, "Unable to get DACL of %q.", source)
}
err = windows.SetNamedSecurityInfo(
Expand All @@ -36,7 +36,7 @@ func copyFileSecurityInfo(source string, target string) error {
windows.DACL_SECURITY_INFORMATION, nil, nil, dacl, nil,
)
if err != nil {
return wrapErrorf(
return WrapErrorf(
err, "Unable to set DACL of %q.", target)
}
return nil
Expand Down
150 changes: 100 additions & 50 deletions regolith/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,51 +47,66 @@ type RegolithProject struct {
}

// LoadConfigAsMap loads the config.json file as map[string]interface{}
func LoadConfigAsMap() map[string]interface{} {
func LoadConfigAsMap() (map[string]interface{}, error) {
file, err := ioutil.ReadFile(ManifestName)
if err != nil {
Logger.Fatalf(
"Couldn't find %s! Consider running 'regolith init'.",
return nil, WrapErrorf(
err,
"%q not found (use 'regolith init' to initialize the project)",
ManifestName)
}
var configJson map[string]interface{}
err = json.Unmarshal(file, &configJson)
if err != nil {
Logger.Fatalf(
"Couldn't load %s! Does the file contain correct json?",
ManifestName)
return nil, WrapErrorf(
err, "could not load %s as a JSON file", ManifestName)
}
return configJson
return configJson, nil
}

// ConfigFromObject creates a "Config" object from map[string]interface{}
func ConfigFromObject(obj map[string]interface{}) *Config {
func ConfigFromObject(obj map[string]interface{}) (*Config, error) {
result := &Config{}
// Name
name, ok := obj["name"].(string)
if !ok {
Logger.Fatal("Could not find name in config.json")
return nil, WrapError(nil, "missing 'name' property")
}
result.Name = name
// Author
author, ok := obj["author"].(string)
if !ok {
Logger.Fatal("Could not find author in config.json")
return nil, WrapError(nil, "missing 'author' property")
}
result.Author = author
// Packs
packs, ok := obj["packs"].(map[string]interface{})
if !ok {
Logger.Fatal("Could not find packs in config.json")
if packs, ok := obj["packs"]; ok {
packs, ok := packs.(map[string]interface{})
if !ok {
return nil, WrapErrorf(
nil, "'packs' property is a %T, not a map", packs)
}
// Packs can be empty, no need to check for errors
result.Packs = PacksFromObject(packs)
} else {
return nil, WrapError(nil, "missing 'packs' property")
}
result.Packs = PacksFromObject(packs)
// Regolith
regolith, ok := obj["regolith"].(map[string]interface{})
if !ok {
Logger.Fatal("Could not find regolith in config.json")
if regolith, ok := obj["regolith"]; ok {
regolith, ok := regolith.(map[string]interface{})
if !ok {
return nil, WrapErrorf(
nil, "'regolith' property is a %T, not a map", regolith)
}
regolithProject, err := RegolithProjectFromObject(regolith)
if err != nil {
return nil, WrapError(err, "could not parse 'regolith' property")
}
result.RegolithProject = regolithProject
} else {
return nil, WrapError(nil, "missing 'regolith' property")
}
result.RegolithProject = RegolithProjectFromObject(regolith)
return result
return result, nil
}

// ProfileFromObject creates a "Profile" object from map[string]interface{}
Expand All @@ -108,15 +123,21 @@ func PacksFromObject(obj map[string]interface{}) Packs {

// RegolithProjectFromObject creates a "RegolithProject" object from
// map[string]interface{}
func RegolithProjectFromObject(obj map[string]interface{}) RegolithProject {
func RegolithProjectFromObject(
obj map[string]interface{},
) (RegolithProject, error) {
result := RegolithProject{
Profiles: make(map[string]Profile),
FilterDefinitions: make(map[string]FilterInstaller),
}
// DataPath
if _, ok := obj["dataPath"]; !ok {
return result, WrapError(nil, "missing 'dataPath' property")
}
dataPath, ok := obj["dataPath"].(string)
if !ok {
Logger.Fatal("Could not parse dataPath property from 'regolith' in config.json")
return result, WrapErrorf(
nil, "'dataPath' is a %T, not a string", obj["dataPath"])
}
result.DataPath = dataPath
// Filter definitions
Expand All @@ -125,9 +146,9 @@ func RegolithProjectFromObject(obj map[string]interface{}) RegolithProject {
for filterDefinitionName, filterDefinition := range filterDefinitions {
filterDefinitionMap, ok := filterDefinition.(map[string]interface{})
if !ok {
Logger.Fatal(
"invalid format of filter definition %s",
filterDefinitionName)
return result, WrapErrorf(
nil, "filter definition %q is a %T not a map",
filterDefinitionName, filterDefinitions[filterDefinitionName])
}
result.FilterDefinitions[filterDefinitionName] = FilterInstallerFromObject(
filterDefinitionName, filterDefinitionMap)
Expand All @@ -141,24 +162,30 @@ func RegolithProjectFromObject(obj map[string]interface{}) RegolithProject {
for profileName, profile := range profiles {
profileMap, ok := profile.(map[string]interface{})
if !ok {
Logger.Fatal("Could not find profile in config.json")
return result, WrapErrorf(
nil, "profile %q is a %T not a map",
profileName, profiles[profileName])
}
result.Profiles[profileName] = ProfileFromObject(
profileName, profileMap, result.FilterDefinitions)

profileValue, err := ProfileFromObject(
profileMap, result.FilterDefinitions)
if err != nil {
return result, WrapErrorf(
err, "could not parse profile %q", profileName)
}
result.Profiles[profileName] = profileValue
}
return result
return result, nil
}

// ExportTargetFromObject creates a "ExportTarget" object from
// map[string]interface{}
func ExportTargetFromObject(obj map[string]interface{}) ExportTarget {
func ExportTargetFromObject(obj map[string]interface{}) (ExportTarget, error) {
// TODO - implement in a proper way
result := ExportTarget{}
// Target
target, ok := obj["target"].(string)
if !ok {
Logger.Fatal("Could not find target in config.json")
return result, WrapError(nil, "could not find 'target' in config.json")
}
result.Target = target
// RpPath
Expand All @@ -176,7 +203,7 @@ func ExportTargetFromObject(obj map[string]interface{}) ExportTarget {
// ReadOnly
readOnly, _ := obj["readOnly"].(bool)
result.ReadOnly = readOnly
return result
return result, nil
}

// IsProjectInitialized checks if the project is initialized by testing if
Expand All @@ -194,8 +221,10 @@ func IsProjectInitialized() bool {
func InitializeRegolithProject(isForced bool) error {
// Do not attempt to initialize if project is already initialized (can be forced)
if !isForced && IsProjectInitialized() {
Logger.Errorf("Could not initialize Regolith project. File %s already exists.", ManifestName)
return nil
return WrapErrorf(
nil,
"Could not initialize Regolith project. File %s already exists.",
ManifestName)
} else {
Logger.Info("Initializing Regolith project...")

Expand All @@ -206,7 +235,7 @@ func InitializeRegolithProject(isForced bool) error {
// Delete old configuration if it exists
if err := os.Remove(ManifestName); !os.IsNotExist(err) {
if err != nil {
return err
return WrapError(err, "Could not remove old config.json")
}
}

Expand Down Expand Up @@ -237,13 +266,13 @@ func InitializeRegolithProject(isForced bool) error {
jsonBytes, _ := json.MarshalIndent(jsonData, "", " ")
err := ioutil.WriteFile(ManifestName, jsonBytes, 0666)
if err != nil {
return wrapError(err, "Failed to write project file contents")
return WrapError(err, "Failed to write project file contents")
}

// Create default gitignore file
err = ioutil.WriteFile(".gitignore", []byte(GitIgnore), 0666)
if err != nil {
return wrapError(err, "Failed to write .gitignore file contents")
return WrapError(err, "Failed to write .gitignore file contents")
}

foldersToCreate := []string{
Expand All @@ -268,16 +297,17 @@ func InitializeRegolithProject(isForced bool) error {
}
}

// CleanCache removes all contents of ".regolith" folder.
// CleanCache handles "regolith clean" command it removes all contents of
// ".regolith" folder.
func CleanCache() error {
Logger.Infof("Cleaning cache...")
err := os.RemoveAll(".regolith")
if err != nil {
return wrapError(err, "Failed to remove .regolith folder")
return WrapError(err, "failed to remove .regolith folder")
}
err = os.Mkdir(".regolith", 0666)
if err != nil {
return wrapError(err, "Failed to recreate .regolith folder")
return WrapError(err, "failed to recreate .regolith folder")
}
Logger.Infof("Cache cleaned.")
return nil
Expand All @@ -290,12 +320,26 @@ func CleanCache() error {
// dependencies. Non-force mode only installs dependencies that are not
// already installed.
func (c *Config) InstallFilters(isForced bool) error {
CreateDirectoryIfNotExists(".regolith/cache/filters", true)
CreateDirectoryIfNotExists(".regolith/cache/venvs", true)
err := CreateDirectoryIfNotExists(".regolith/cache/filters", true)
if err != nil {
return WrapError(nil, err.Error())
}
err = CreateDirectoryIfNotExists(".regolith/cache/venvs", true)
if err != nil {
return WrapError(nil, err.Error())
}

c.DownloadRemoteFilters(isForced)
for _, filterDefinition := range c.FilterDefinitions {
filterDefinition.InstallDependencies(nil)
err = c.DownloadRemoteFilters(isForced)
if err != nil {
return WrapError(err, "failed to download filters")
}
for filterName, filterDefinition := range c.FilterDefinitions {
err = filterDefinition.InstallDependencies(nil)
if err != nil {
return WrapErrorf(
err, "failed to install dependencies for filter %q",
filterName)
}
}
Logger.Infof("All filters installed.")
return nil
Expand All @@ -305,20 +349,26 @@ func (c *Config) InstallFilters(isForced bool) error {
// "filter_definitions" of the Confing.
// isForced toggles the force mode described in InstallFilters.
func (c *Config) DownloadRemoteFilters(isForced bool) error {
CreateDirectoryIfNotExists(".regolith/cache/filters", true)
CreateDirectoryIfNotExists(".regolith/cache/venvs", true)
err := CreateDirectoryIfNotExists(".regolith/cache/filters", true)
if err != nil {
return WrapError(nil, err.Error())
}
err = CreateDirectoryIfNotExists(".regolith/cache/venvs", true)
if err != nil {
return WrapError(nil, err.Error())
}

for name := range c.FilterDefinitions {
filterDefinition := c.FilterDefinitions[name]
Logger.Infof("Downloading %q...", name)
switch remoteFilter := filterDefinition.(type) {
case *RemoteFilterDefinition:
err := remoteFilter.Download(isForced)
remoteFilter.CopyFilterData(c.DataPath)
if err != nil {
return wrapErrorf(
err, "Could not download %q!", name)
return WrapErrorf(
err, "could not download %q!", name)
}
remoteFilter.CopyFilterData(c.DataPath)
}
}
Logger.Infof("All remote filters installed.")
Expand Down
16 changes: 8 additions & 8 deletions regolith/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func GetExportPaths(exportTarget ExportTarget, name string) (bpPath string, rpPa
if exportTarget.Target == "development" {
comMojang, err := FindMojangDir()
if err != nil {
return "", "", wrapError(err, "Failed to find com.mojang directory")
return "", "", WrapError(err, "Failed to find com.mojang directory")
}
// TODO - I don't like the _rp and _bp sufixes. Can we get rid of that?
// I for example always name my packs "0".
Expand All @@ -36,11 +36,11 @@ func GetExportPaths(exportTarget ExportTarget, name string) (bpPath string, rpPa
} else if exportTarget.WorldName != "" {
dir, err := FindMojangDir()
if err != nil {
return "", "", wrapError(err, "Failed to find com.mojang directory")
return "", "", WrapError(err, "Failed to find com.mojang directory")
}
worlds, err := ListWorlds(dir)
if err != nil {
return "", "", wrapError(err, "Failed to list worlds")
return "", "", WrapError(err, "Failed to list worlds")
}
for _, world := range worlds {
if world.Name == exportTarget.WorldName {
Expand Down Expand Up @@ -87,17 +87,17 @@ func ExportProject(profile Profile, name string, dataPath string) error {
// Spooky, I hope file protection works, and it won't do any damage
err = os.RemoveAll(bpPath)
if err != nil {
return wrapError(err, "Failed to clear behavior pack build output")
return WrapError(err, "Failed to clear behavior pack build output")
}
err = os.RemoveAll(rpPath)
if err != nil {
return wrapError(err, "Failed to clear resource pack build output")
return WrapError(err, "Failed to clear resource pack build output")
}
// TODO - this code is dangerous. You can put any dataPath into the config
// file and regolith will delete it
err = os.RemoveAll(dataPath)
if err != nil {
return wrapError(err, "Failed to clear filter data path")
return WrapError(err, "Failed to clear filter data path")
}
Logger.Info("Exporting project to ", bpPath)
err = MoveOrCopy(".regolith/tmp/BP", bpPath, exportTarget.ReadOnly, true)
Expand Down Expand Up @@ -133,7 +133,7 @@ func MoveOrCopy(
copyOptions := copy.Options{PreserveTimes: false, Sync: false}
err := copy.Copy(source, destination, copyOptions)
if err != nil {
return wrapErrorf(err, "Couldn't copy data files to %s, aborting.", destination)
return WrapErrorf(err, "Couldn't copy data files to %s, aborting.", destination)
}
} else if copyParentAcl { // No errors with moving files but needs ACL copy
parent := filepath.Dir(destination)
Expand All @@ -142,7 +142,7 @@ func MoveOrCopy(
}
err = copyFileSecurityInfo(parent, destination)
if err != nil {
return wrapErrorf(
return WrapErrorf(
err,
"Counldn't set ACL to the target file %s, aborting.",
destination,
Expand Down
Loading

0 comments on commit 6429c5d

Please sign in to comment.