public static AutomationMacro Copy(AutomationMacro macroToCopy) { return(new AutomationMacro() { Name = macroToCopy.Name, Value = macroToCopy.Value, MacroType = macroToCopy.MacroType }); }
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); }