Пример #1
0
        private void SetCreateLogDirectory(bool value)
        {
            ParameterItem logParameter = new ParameterItem()
            {
                LongParameter       = "log-directory",
                ShortParameter      = "l",
                Description         = "directory for the log file.  the log file name will be based on the script name",
                VariableName        = "logDirectory",
                Default             = "\"./\"",
                RequiresInputString = true,
                RequiredParameter   = false,
                ValueIfSet          = "$2"
            };

            AddOptionParameter(logParameter, value);
        }
Пример #2
0
        /// <summary>
        ///     make sure we don't add exactly the same error twice
        /// </summary>
        /// <param name="findOrAdd"></param>
        private void AddValidationError(ParameterItem item, ParseErrorInfo findOrAdd)
        {
            foreach (var error in ParseErrors)
            {
                if (error.ErrorLevel == findOrAdd.ErrorLevel && error.Message == findOrAdd.Message)
                {
                    return;
                }
            }

            ParseErrors.Add(findOrAdd);
            if (item != null)
            {
                findOrAdd.Tag = item;
            }
        }
Пример #3
0
        private void SetAcceptsInputFile(bool newValue)
        {
            // i is the short name and input-file is the long name for the
            ParameterItem param = new ParameterItem()
            {
                ShortParameter      = "i",
                LongParameter       = "input-file",
                VariableName        = "inputFile",
                Description         = "filename that contains the JSON values to drive the script.  command line overrides file",
                RequiresInputString = true,
                Default             = "", // this needs to be empty because if it is set, the script will try to find the file...
                RequiredParameter   = false,
                ValueIfSet          = "$2"
            };

            AddOptionParameter(param, newValue);
        }
Пример #4
0
        private void AddOptionParameter(ParameterItem item, bool add)
        {
            ParameterItem param = null;

            foreach (ParameterItem p in Parameters)
            {
                if (p.VariableName == item.VariableName)
                {
                    param = p;
                    break;
                }
            }

            //
            //  need to have the right parameter for long line to work correctly -- make sure it is there, and if not, add it.
            if (add && param == null)
            {
                Parameters.Add(item);
            }
            else if (!add && param != null)
            {
                Parameters.Remove(param);
            }
        }
Пример #5
0
        /// <summary>
        ///     Given a bash file, create a ScriptData object.  This is the "parse a bash script" function
        /// </summary>
        /// <param name="bash"></param>

        public static ScriptData FromBash(string input)
        {
            ScriptData scriptData            = new ScriptData();
            bool       oldGenerateBashScript = scriptData.GenerateBashScript;

            try
            {
                scriptData.ClearParseErrors();
                scriptData.UpdateOnPropertyChanged = false; // this flag stops the NotifyPropertyChanged events from firing
                scriptData.GenerateBashScript      = false; // this flag tells everything that we are in the process of parsing
                scriptData.BashScript = input;
                //
                //  make sure that we deal with the case of getting a file with EOL == \n\r.  we only want \n
                //  I've also had scenarios where I get only \r...fix those too.
                if (input.IndexOf("\n") != -1)
                {
                    //
                    //  we have some new lines, just kill the \r
                    if (input.IndexOf("\r") != -1)
                    {
                        input = input.Replace("\r", string.Empty);
                    }
                }
                else if (input.IndexOf("\r") != -1)
                {
                    // no \n, but we have \r
                    input = input.Replace("\r", "\n");
                }
                else
                {
                    // no \r and no \n
                    scriptData.ParseErrors.Add(new ParseErrorInfo(ErrorLevel.Fatal, noNewLines));
                    return(scriptData);
                }


                //
                // make sure the file doesn't have GIT merge conflicts
                if (input.IndexOf("<<<<<<< HEAD") != -1)
                {
                    scriptData.ParseErrors.Add(new ParseErrorInfo(ErrorLevel.Fatal, unMergedGitFile));
                    return(scriptData);
                }

                /*
                 *      The general layout of the file is
                 *
                 #!/bin/bash
                 # bashWizard version <version>
                 #      <BashWizard Functions>
                 # --- BEGIN USER CODE ---
                 #
                 # --- END USER CODE ---
                 #      <optional BashWizard code>
                 #
                 #      the general parse strategy is to separate the user code and then to parse the Bash Wizard Functions to find all the parameter information
                 #      we *never* want to touch the user code
                 #
                 */

                string[] userComments   = new string[] { "# --- BEGIN USER CODE ---", "# --- END USER CODE ---" };
                string[] sections       = input.Split(userComments, StringSplitOptions.RemoveEmptyEntries);
                string   bashWizardCode = "";
                switch (sections.Length)
                {
                case 0:

                    //
                    //  this means we couldn't find any of the comments -- treat this as a non-BW file
                    scriptData.UserCode = input.Trim();     // make it all user code
                    scriptData.ParseErrors.Add(new ParseErrorInfo(ErrorLevel.Warning, missingComments));
                    scriptData.ParseErrors.Add(new ParseErrorInfo(ErrorLevel.Warning, addingComments));
                    return(scriptData);

                case 1:
                    scriptData.ParseErrors.Add(new ParseErrorInfo(ErrorLevel.Fatal, missingOneUserComment));
                    scriptData.ParseErrors.Add(new ParseErrorInfo(ErrorLevel.Fatal, pleaseFix));
                    return(scriptData);

                case 2:
                case 3:
                    bashWizardCode      = sections[0];
                    scriptData.UserCode = sections[1].Trim();
                    // ignore section[2], it is code after the "# --- END USER CODE ---" that will be regenerated
                    break;

                default:
                    scriptData.ParseErrors.Add(new ParseErrorInfo(ErrorLevel.Fatal, tooManyUserComments));
                    return(scriptData);
                }

                //
                //  first look for the bash version
                string   versionLine      = "# bashWizard version ";
                int      index            = bashWizardCode.IndexOf(versionLine);
                double   userBashVersion  = 0.1;
                string[] lines            = null;
                string   line             = "";
                bool     foundDescription = false;
                if (index > 0)
                {
                    bool ret = double.TryParse(bashWizardCode.Substring(index + versionLine.Length, 5), out userBashVersion);
                    if (!ret)
                    {
                        ret = double.TryParse(bashWizardCode.Substring(index + versionLine.Length, 3), out userBashVersion); // 0.9 is a version i have out there...
                        if (!ret)
                        {
                            scriptData.ParseErrors.Add(new ParseErrorInfo(ErrorLevel.Warning, missingVersionInfo));
                        }
                    }
                }

                scriptData.EchoInput = (bashWizardCode.IndexOf("    echoInput") > 0);

                //
                //  find the usage() function and parse it out - this gives us the 4 properties in the ParameterItem below
                if (scriptData.GetStringBetween(bashWizardCode, "usage() {", "}", out string bashFragment) == false)
                {
                    scriptData.ParseErrors.Add(new ParseErrorInfo(ErrorLevel.Fatal, bashFragment));
                }
                else
                {
                    bashFragment = bashFragment.Replace("echoWarning", "echo");
                    bashFragment = bashFragment.Replace("\n", "");
                    lines        = bashFragment.Split(new string[] { "echo ", "\"" }, StringSplitOptions.RemoveEmptyEntries);
                    line         = "";

                    foreach (string l in lines)
                    {
                        line = l.Trim();
                        if (line == "")
                        {
                            continue;
                        }
                        if (line == "exit 1")
                        {
                            break;
                        }


                        if (!foundDescription)
                        {
                            /*
                             * it look like:
                             *
                             * function usage() {
                             *  echoWarning
                             *  echo "<description>"
                             *  ...
                             *
                             * }
                             *
                             * but the echoWarning isn't always there -- only if the --input-file option was specified.
                             *
                             */
                            if (line.StartsWith("Parameters can be passed in the command line or in the input file."))
                            {
                                continue;
                            }
                            //
                            //  if the description is black, the next line echo's the usage -- so if we do NOT find the Usage statement
                            //  we have found the description.  and in any case, if the Description isn't there by now, it isn't there
                            //  so always set the flag saying we found it.

                            if (!line.StartsWith("Usage: $0"))
                            {
                                scriptData.Description = line.TrimEnd();
                            }

                            foundDescription = true;
                            continue;
                        }

                        if (line.Substring(0, 1) == "-") // we have a parameter!
                        {
                            string[] paramTokens = line.Split(new string[] { " ", "|" }, StringSplitOptions.RemoveEmptyEntries);
                            string   description = "";
                            for (int i = 3; i < paramTokens.Length; i++)
                            {
                                description += paramTokens[i] + " ";
                            }
                            description = description.Trim();
                            ParameterItem parameterItem = new ParameterItem()
                            {
                                ShortParameter    = paramTokens[0].Trim(),
                                LongParameter     = paramTokens[1].Trim(),
                                RequiredParameter = (paramTokens[2].Trim() == "Required") ? true : false,
                                Description       = description
                            };


                            scriptData.Parameters.Add(parameterItem);
                        }
                    }
                }

                //
                //  parse the echoInput() function to get script name - don't fail parsing on this one
                bashFragment = "";
                if (scriptData.GetStringBetween(bashWizardCode, "echoInput() {", "parseInput()", out bashFragment))
                {
                    lines = bashFragment.Split('\n');
                    foreach (string l in lines)
                    {
                        line = l.Trim();
                        if (line == "")
                        {
                            continue;
                        }
                        //
                        //  the line is in the form of: "echo "<scriptName>:"
                        if (scriptData.GetStringBetween(line, "echo \"", ":", out string name))
                        {
                            scriptData.ScriptName = name;
                        }
                        break;
                    }
                }


                //
                //  next parse out the "parseInput" function to get "valueWhenSet" and the "VariableName"

                bashFragment = "";
                if (scriptData.GetStringBetween(bashWizardCode, "eval set -- \"$PARSED\"", "--)", out bashFragment) == false)
                {
                    scriptData.ParseErrors.Add(new ParseErrorInfo(ErrorLevel.Warning, bashFragment));
                }
                else
                {
                    lines = bashFragment.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
                    for (index = 0; index < lines.Length; index++)
                    {
                        line = lines[index].Trim();
                        if (line == "")
                        {
                            continue;
                        }

                        if (line.Substring(0, 1) == "-") // we have a parameter!
                        {
                            string[] paramTokens = lines[index + 1].Trim().Split(new char[] { '=' });
                            if (paramTokens.Length != 2)
                            {
                                scriptData.ParseErrors.Add(new ParseErrorInfo(ErrorLevel.Warning, $"When parsing the parseInput() function to get the variable names, encountered the line {lines[index + 1].Trim()} which doesn't parse.  It should look like varName=$2 or the like."));
                            }
                            string[] nameTokens = line.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
                            if (nameTokens.Length != 2) // the first is the short param, second long param, and third is empty
                            {
                                scriptData.ParseErrors.Add(new ParseErrorInfo(ErrorLevel.Warning, $"When parsing the parseInput() function to get the variable names, encountered the line {lines[index].Trim()} which doesn't parse.  It should look like \"-l | --long-name)\" or the like."));
                            }
                            // nameTokens[1] looks like "--long-param)
                            string        longParam = nameTokens[1].Substring(3, nameTokens[1].Length - 4);
                            ParameterItem param     = scriptData.FindParameterByLongName(longParam);
                            if (param == null)
                            {
                                scriptData.ParseErrors.Add(new ParseErrorInfo(ErrorLevel.Warning, $"When parsing the parseInput() function to get the variable names, found a long parameter named {longParam} which was not found in the usage() function"));
                            }
                            else
                            {
                                param.VariableName = paramTokens[0].Trim();
                                param.ValueIfSet   = paramTokens[1].Trim();
                                if (lines[index + 2].Trim() == "shift 1")
                                {
                                    param.RequiresInputString = false;
                                }
                                else if (lines[index + 2].Trim() == "shift 2")
                                {
                                    param.RequiresInputString = true;
                                }
                                else
                                {
                                    scriptData.ParseErrors.Add(new ParseErrorInfo(ErrorLevel.Warning, $"When parsing the parseInput() function to see if {param.VariableName} requires input, found this line: {lines[index + 1]} which does not parse.  it should either be \"shift 1\" or \"shift 2\""));
                                }
                            }
                            index += 2;
                        }
                    }
                }
                // the last bit of info to figure out is the default value -- find these with a comment
                if (scriptData.GetStringBetween(bashWizardCode, "# input variables", "parseInput", out bashFragment) == false)
                {
                    scriptData.ParseErrors.Add(new ParseErrorInfo(ErrorLevel.Fatal, bashFragment));
                }
                else
                {
                    // throw away the "declare "
                    bashFragment = bashFragment.Replace("declare ", "");
                    lines        = bashFragment.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (string l in lines)
                    {
                        line = l.Trim();
                        if (line == "")
                        {
                            continue;
                        }
                        if (line.StartsWith("#"))
                        {
                            continue;
                        }

                        string[] varTokens = line.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries);
                        if (varTokens.Length == 0 || varTokens.Length > 2)
                        {
                            scriptData.ParseErrors.Add(new ParseErrorInfo(ErrorLevel.Warning, $"When parsing the variable declarations between the \"# input variables\" comment and the \"parseInput\" calls, the line {line} was encountered that didn't parse.  it should be in the form of varName=Default"));
                        }
                        string        varName = varTokens[0].Trim();
                        ParameterItem param   = scriptData.FindParameterByVarName(varName);
                        if (param == null)
                        {
                            scriptData.ParseErrors.Add(new ParseErrorInfo(ErrorLevel.Warning, $"When parsing the variable declarations between the \"# input variables\" comment and the \"parseInput\" calls, found a variable named {varName} which was not found in the usage() function"));
                            scriptData.ParseErrors.Add(new ParseErrorInfo(ErrorLevel.Information, $"\"{line}\" will be removed from the script.  If you want to declare it, put the declaration inside the user code comments"));
                        }
                        else
                        {
                            param.Default = varTokens.Length == 2 ? varTokens[1].Trim() : "";  // in bash "varName=" is a valid declaration
                        }
                    }
                }


                return(scriptData);
            }
            finally
            {
                //
                //  need to update everything that might have been changed by the parse
                scriptData.UpdateOnPropertyChanged = true; // force events to fire
                scriptData.NotifyPropertyChanged("Description");
                scriptData.NotifyPropertyChanged("ScriptName");


                //  "BashScript" also updates the ToggleButtons
                scriptData.GenerateBashScript = oldGenerateBashScript;
                scriptData.NotifyPropertyChanged("BashScript");

                //
                //  now go from Parameters back to bash script, unless there are fatal errors
                //  if there are, it will stay as the input set at the top of the FromBash() function
                if (!scriptData.HasFatalErrors)
                {
                    scriptData.ToBash();
                }
            }
        }
Пример #6
0
        private (bool ret, string msg) SetCreateVerifyDelete(bool addParameter)
        {
            string msg = "";

            if (!addParameter) // deselecting
            {
                string[] functions = new string[] { "onVerify", "onCreate", "onDelete" };
                string   err       = "";

                foreach (string f in functions)
                {
                    if (FunctionExists(UserCode, f))
                    {
                        err += f + "\n";
                    }
                }

                if (err != "")
                {
                    msg = $"You can unselected the Create, Verify, Delete pattern, but you have the following functions implemented:\n{err}\n\nManually fix the user code to not need these functions before removing this option.";
                    return(ret : false, msg : msg);
                }
            }



            ParameterItem param = new ParameterItem()
            {
                LongParameter       = "create",
                ShortParameter      = "c",
                VariableName        = "create",
                Description         = "creates the resource",
                RequiresInputString = false,
                Default             = "false",
                RequiredParameter   = false,
                ValueIfSet          = "true"
            };

            AddOptionParameter(param, addParameter);
            param = new ParameterItem()
            {
                LongParameter       = "verify",
                ShortParameter      = "v",
                VariableName        = "verify",
                Description         = "verifies the script ran correctly",
                RequiresInputString = false,
                Default             = "false",
                RequiredParameter   = false,
                ValueIfSet          = "true"
            };
            AddOptionParameter(param, addParameter);
            param = new ParameterItem()
            {
                LongParameter       = "delete",
                ShortParameter      = "d",
                VariableName        = "delete",
                Description         = "deletes whatever the script created",
                RequiresInputString = false,
                Default             = "false",
                RequiredParameter   = false,
                ValueIfSet          = "true"
            };
            AddOptionParameter(param, addParameter);
            return(ret : true, msg : "");
        }
Пример #7
0
        /// <summary>
        ///     this is like compiler errors/warnings when generating a bash script
        /// </summary>
        /// <returns></returns>
        public bool ValidateParameters(bool allowBlankParameters = true)
        {
            //verify short names are unique
            Dictionary <string, ParameterItem> nameDictionary     = new Dictionary <string, ParameterItem>();
            Dictionary <string, ParameterItem> variableDictionary = new Dictionary <string, ParameterItem>();

            ClearValidationErrors();
            ParameterItem item = null;

            foreach (var param in Parameters)
            {
                if (allowBlankParameters)
                {
                    if (param.ShortParameter == "" && param.LongParameter == "")
                    {
                        continue; // probably just getting started
                    }
                }
                else
                {
                    if (param.ShortParameter == "" || param.LongParameter == "" || param.VariableName == "")
                    {
                        AddValidationError(param, new ParseErrorInfo(ErrorLevel.Validation, $"Parameter[{Parameters.IndexOf(param)}]: All Long Names, Short Names, and Variable Names must be non-empty."));
                    }
                }

                if (nameDictionary.TryGetValue(param.ShortParameter, out item))
                {
                    var otherIndex = Parameters.IndexOf(item);
                    AddValidationError(param, new ParseErrorInfo(ErrorLevel.Validation, $"Parameter[{Parameters.IndexOf(param)}]: The name \"{param.ShortParameter}\" already exists for the parameter with index={otherIndex}. All Long Names and Short Names must be unique."));
                }
                else
                {
                    nameDictionary[param.ShortParameter] = param;
                }
                if (nameDictionary.TryGetValue(param.LongParameter, out item))
                {
                    var otherIndex = Parameters.IndexOf(item);
                    AddValidationError(param, new ParseErrorInfo(ErrorLevel.Validation, $"Parameter[{Parameters.IndexOf(param)}]: The name \"{param.LongParameter}\" already exists for the parameter with index={otherIndex}. All Long Names and Short Names must be unique."));
                }
                else
                {
                    nameDictionary[param.LongParameter] = param;
                }
                if (variableDictionary.TryGetValue(param.VariableName, out item))
                {
                    var otherIndex = Parameters.IndexOf(item);
                    AddValidationError(param, new ParseErrorInfo(ErrorLevel.Validation, $"Parameter[{Parameters.IndexOf(param)}]: The variable \"{param.VariableName}\" already exists for the parameter with index={otherIndex}. All Variable Names must be unique."));
                }
                else
                {
                    variableDictionary[param.VariableName] = param;
                }


                if (!param.RequiresInputString && param.ValueIfSet.Trim() == "$2")
                {
                    AddValidationError(param, new ParseErrorInfo(ErrorLevel.Validation, $"Parameter[{Parameters.IndexOf(param)}]: {param.LongParameter} has \"Require Input String\" set to False and the \"Value if Set\" to \"$2\".  \nThis combination is not allowed."));
                }
            }
            //
            //  I'm taking out these chars because they are "special" in JSON.  I found that the ":" messed up JQ processing
            //  and it seems a small price to pay to not take any risks with the names.  Note that we always Trim() the names
            //  in the ParameterOrScriptData_PropertyChanged method
            //
            string[] illegalNameChars = new string[] { ":", "{", "}", "[", "]", "\\", "'", "\"" };
            if (ScriptName != "")
            {
                foreach (string c in illegalNameChars)
                {
                    if (ScriptName.Contains(c))
                    {
                        AddValidationError(null, new ParseErrorInfo(ErrorLevel.Validation, "The following characters are illegal in the Script Name: :{}[]\"\'"));
                        break; // we only print one error anyway
                    }
                }
            }
            if (Description != "")
            {
                foreach (string c in illegalNameChars)
                {
                    if (Description.Contains(c))
                    {
                        AddValidationError(null, new ParseErrorInfo(ErrorLevel.Validation, "The following characters are illegal in the Description: :{}[]\"\'"));
                        break; // we only print one error anyway
                    }
                }
            }



            return(ParseErrors.Count == 0);
        }