示例#1
0
 public static AutomationMacro Copy(AutomationMacro macroToCopy)
 {
     return(new AutomationMacro()
     {
         Name = macroToCopy.Name, Value = macroToCopy.Value, MacroType = macroToCopy.MacroType
     });
 }
示例#2
0
        public async Task <bool> RunTasksAsync()
        {
            if (Package == null || AutomationSequencer == null || AutomationRunnerSettings == null)
            {
                throw new NullReferenceException();
            }

            Logging.Debug(Logfiles.AutomationRunner, "Setting up macro list before task run");
            MacrosListForTask.Clear();
            MacrosListForTask.AddRange(ApplicationMacros);
            foreach (AutomationMacro macro in GlobalMacros)
            {
                MacrosListForTask.Add(AutomationMacro.Copy(macro));
            }

            Logging.Debug(Logfiles.AutomationRunner, "Setting up working directory");
            string workingDirectory = Path.Combine(ApplicationConstants.RelhaxTempFolderPath, Package.PackageName);

            if (Directory.Exists(workingDirectory))
            {
                Directory.Delete(workingDirectory, true);
                Directory.CreateDirectory(workingDirectory);
            }
            else
            {
                Directory.CreateDirectory(workingDirectory);
            }

            foreach (AutomationTask task in this.AutomationTasks)
            {
                Logging.Info(Logfiles.AutomationRunner, LogOptions.MethodName, "Running task: {0}", task.ID);
                await task.Execute();

                if (task.ExitCode != 0)
                {
                    Logging.Error(Logfiles.AutomationRunner, LogOptions.MethodName, "The task, '{0}', failed to execute. Check the task error output above for more details. You may want to enable verbose logging.");
                    return(false);
                }
            }
            return(true);
        }
        protected void ProcessMacro(string argName, ref string arg)
        {
            Logging.Info(Logfiles.AutomationRunner, LogOptions.MethodName, "Processing arg '{0}'", argName);
            Logging.Debug(Logfiles.AutomationRunner, LogOptions.MethodName, "Before processing: '{0}'", arg);

            //run regex on the arg to get the type of replacement to do. if it's recursive, then we need to process the inner one first
            Match result = Regex.Match(arg, AutomationMacro.MacroReplaceRegex);

            if (!result.Success)
            {
                //check if any "{" exist at all
                Match startBracketsMatch = Regex.Match(arg, "{");
                if (startBracketsMatch.Captures.Count > 0)
                {
                    Match endBracketsMatch = Regex.Match(arg, "}");
                    Logging.Error(Logfiles.AutomationRunner, LogOptions.MethodName, "Macros were detected in the command argument, but the syntax was incorrect. Most likely is the number of start and end brackets are unbalanced.");
                    Logging.Error(Logfiles.AutomationRunner, LogOptions.None, "Examine the number of brackets starting and ending in the command, and try again. For debug, here's what was parsed:");
                    Logging.Info(Logfiles.AutomationRunner, LogOptions.None, "Command value: {0}", arg);
                    Logging.Info(Logfiles.AutomationRunner, LogOptions.None, "Start brackets count: {0}", startBracketsMatch.Captures.Count);
                    foreach (Capture capture in startBracketsMatch.Captures)
                    {
                        Logging.Info(Logfiles.AutomationRunner, LogOptions.None, "Capture location in string: {0}", capture.Index);
                    }
                    Logging.Info(Logfiles.AutomationRunner, LogOptions.None, "End brackets count: {0}", endBracketsMatch.Captures.Count);
                    foreach (Capture capture in endBracketsMatch.Captures)
                    {
                        Logging.Info(Logfiles.AutomationRunner, LogOptions.None, "Capture location in string: {0}", capture.Index);
                    }
                }
                Logging.Info(Logfiles.AutomationRunner, LogOptions.MethodName, "The command {0} has no macros, continue", argName);
                return;
            }

            int inner1Count = result.Groups[AutomationMacro.RegexGroupInner1].Captures.Count;
            int inner2Count = result.Groups[AutomationMacro.RegexGroupInner2].Captures.Count;
            int inner3Count = result.Groups[AutomationMacro.RegexGroupInner3].Captures.Count;

            //verify that 2 and 3 have the same number
            if (inner2Count != inner3Count)
            {
                Logging.Error(Logfiles.AutomationRunner, LogOptions.MethodName, "Inner2Count ({0}) != Inner3Count ({1})! The macro engine is not designed for this!", inner2Count, inner3Count);
                throw new NotImplementedException("soon tm");
                //return;
            }
            else if (inner2Count > inner1Count)
            {
                int countDifference = inner2Count - inner1Count;
                //this means that the regex must recurse x levels (the difference) into the string to solve the inner values first
                Logging.Debug(Logfiles.AutomationRunner, LogOptions.None, "Inner2Count ({0}) > Inner1Count ({1}), required to recuse {2} levels to solve inner macros", inner2Count, inner1Count, countDifference);

                //use the matches of inner1 to send each section into the regex engine again
                int captureCount = 0;
                foreach (Capture capture in result.Groups[AutomationMacro.RegexGroupInner1].Captures)
                {
                    string capturedValue = capture.Value;
                    Logging.Debug(Logfiles.AutomationRunner, LogOptions.None, "Running regex on Inner1Count: {0}", capturedValue);
                    //get a count of how many "{" characters exist in this string. If it's 1, then just run this string through and call it good
                    //if it's more then 1, then need to parse out the extra level via a new greedy regex
                    int numStarts = capturedValue.Count(ch_ => ch_.Equals('{'));
                    if (numStarts > 1)
                    {
                        Logging.Debug(Logfiles.AutomationRunner, LogOptions.None, "This match is {0} level of brackets, split out the brackets before recursively processing", numStarts);
                        //we need to use the new value as the 'starting value', as if it didn't have a macro around it. for example, consider:
                        //name_{use_{date}_val}_thing
                        //after we resolve {date}, it will become part of the name for {use_{date}_val}.
                        //the way to do this is to treat {use_{date}_val} as a word by itself, i.e. 'use_{date}_val'
                        Regex subRegex     = new Regex(@"{.+}");
                        Match sectionMatch = subRegex.Match(capturedValue);

                        //following the example above, we have the section {use_{date}_val}
                        //strip off the brackets and send it through
                        string splitValue = sectionMatch.Value;
                        splitValue = splitValue.Remove(0, 1);
                        splitValue = splitValue.Remove(splitValue.Length - 1, 1);
                        //use_{date}_val
                        string innerResult = ProcessMacro(string.Format("{0}_capture{1}_level{2}", argName, captureCount, countDifference), splitValue);
                        //use_the_date_val, if {date} = the_date
                        innerResult = "{" + innerResult + "}";
                        //{use_the_date_val}
                        Regex replaceRegex2 = new Regex(sectionMatch.Value);
                        capturedValue = replaceRegex2.Replace(capturedValue, innerResult, 1);
                    }
                    else if (numStarts == 0)
                    {
                        throw new BadMemeException("whoa. didn't see that coming.");
                    }
                    else
                    {
                        Logging.Debug(Logfiles.AutomationRunner, LogOptions.None, "This match is 1 level of brackets, perform direct recursive replacement");
                    }

                    string processedValue = ProcessMacro(string.Format("{0}_capture{1}_level{2}", argName, captureCount, countDifference), capturedValue);
                    Regex  replaceRegex   = new Regex(capture.Value);
                    arg = replaceRegex.Replace(arg, processedValue, 1);
                    captureCount++;
                }
            }
            else
            {
                //macros are at the same level, we can just replace as we see them
                //use inner3 as the method to determine what's inside
                foreach (Capture capture in result.Groups[AutomationMacro.RegexGroupInner3].Captures)
                {
                    if (string.IsNullOrEmpty(capture.Value))
                    {
                        continue;
                    }

                    Logging.Debug(Logfiles.AutomationRunner, LogOptions.None, "Processing macro {0}, string location {1}, length {2}", capture.Value, capture.Index, capture.Length);
                    AutomationMacro resultMacro = Macros.Find(macro => macro.Name.Equals(capture.Value));
                    if (resultMacro == null)
                    {
                        Logging.Warning(Logfiles.AutomationRunner, LogOptions.None, "The macro with name '{0}', does not exist, skipping. (Is this intended?)", capture.Value);
                        continue;
                    }

                    //perform a single replace on the specified location of the string
                    //https://stackoverflow.com/a/6372134/3128017
                    Regex replaceRegex = new Regex("{" + capture.Value + "}");
                    arg = replaceRegex.Replace(arg, resultMacro.Value, 1);
                    Logging.Debug(Logfiles.AutomationRunner, LogOptions.None, "A single replace was done on the command string. Result: {0}", arg);
                }
            }

            Logging.Debug(Logfiles.AutomationRunner, LogOptions.MethodName, "After processing: {0}", arg);
        }
        public async Task <bool> RunTasksAsync()
        {
            ExecutionTimeStopwatch.Restart();
            RunningTask = null;
            ExitCode    = SequencerExitCode.NotRun;
            if (Package == null || AutomationSequencer == null || AutomationRunnerSettings == null)
            {
                throw new NullReferenceException();
            }

            Logging.Debug(Logfiles.AutomationRunner, "Setting up macro list before task run");
            AllMacros.Clear();
            AllMacros.AddRange(ApplicationMacros);
            foreach (AutomationMacro macro in GlobalMacros)
            {
                AllMacros.Add(AutomationMacro.Copy(macro));
            }

            //process local macros in case they were added with macros inside them
            foreach (AutomationMacro macro in SequenceMacros)
            {
                macro.Value = AutomationTask.ProcessMacro(macro.Name, macro.Value, SequenceMacros);
                AllMacros.Add(AutomationMacro.Copy(macro));
            }

            Logging.Debug(Logfiles.AutomationRunner, "Setting up working directory");
            string workingDirectory = Path.Combine(ApplicationConstants.RelhaxTempFolderPath, Package.PackageName);

            if (Directory.Exists(workingDirectory))
            {
                Logging.Debug("Directory exits, delete");
                if (!await FileUtils.DirectoryDeleteAsync(workingDirectory, true, true))
                {
                    Logging.Error(LogOptions.ClassName, "Failed to clear the working directory");
                    return(false);
                }
            }
            Directory.CreateDirectory(workingDirectory);

            bool taskReturnGood = true;

            for (int index = 0; index < AutomationTasks.Count; index++)
            {
                AutomationTask task = AutomationTasks[index];
                RunningTask = task;
                bool breakLoop = false;
                Logging.Info(Logfiles.AutomationRunner, LogOptions.MethodName, "Running task: {0}", task.ID);
                try
                {
                    await task.Execute();
                }
                catch (Exception ex)
                {
                    if (task is ICancelOperation taskThatNeedsCancel)
                    {
                        taskThatNeedsCancel.Cancel();
                    }

                    if (!(ex is OperationCanceledException))
                    {
                        Logging.Exception(ex.ToString());
                    }
                }

                switch (task.ExitCode)
                {
                case AutomationExitCode.None:
                    breakLoop      = false;
                    taskReturnGood = true;
                    ExitCode       = SequencerExitCode.NoErrors;
                    break;

                case AutomationExitCode.Cancel:
                    breakLoop      = true;
                    taskReturnGood = true;
                    ExitCode       = SequencerExitCode.Cancel;
                    break;

                case AutomationExitCode.ComparisonNoFilesToUpdate:
                    breakLoop      = true;
                    taskReturnGood = true;
                    ExitCode       = SequencerExitCode.NoErrors;
                    break;

                case AutomationExitCode.ComparisonManualFilesToUpdate:
                    breakLoop      = true;
                    taskReturnGood = true;
                    ExitCode       = SequencerExitCode.NoErrors;
                    break;

                default:
                    Logging.Error(Logfiles.AutomationRunner, LogOptions.MethodName, "The task, '{0}', failed to execute. Check the task error output above for more details. You may want to enable verbose logging.", task.ID);
                    breakLoop      = true;
                    taskReturnGood = false;
                    ExitCode       = SequencerExitCode.Errors;
                    break;
                }

                if (CancellationToken.IsCancellationRequested)
                {
                    breakLoop      = true;
                    taskReturnGood = true;
                    ExitCode       = SequencerExitCode.Cancel;
                }

                if (breakLoop)
                {
                    break;
                }
            }

            //dispose/cleanup the tasks
            AutomationTasks.Clear();
            RunningTask = null;
            Logging.Info("Sequence {0} completed in {1} ms", PackageName, ExecutionTimeStopwatch.ElapsedMilliseconds);
            Dispose();
            return(taskReturnGood);
        }