@dean-houston I'm sure that fixing the syntax would not be straightforward, but introducing @ListSet
and %MapSet
variable functions in lieu is probably a good enough workaround for nearly all use cases where someone would want to do this.
jimbobmcgee
@jimbobmcgee
Best posts made by jimbobmcgee
-
RE: Suggestion: allow for setting list or map elements by dynamic index or key (@ListSet, %MapSet)
Latest posts made by jimbobmcgee
-
RE: Otter server receives thousands of connections from agent after reboot
@stevedennis I understand not wanting to go in blind on something so low-level. For what it is worth, the monkey-patch I applied has been stable, so far, in my lab environment.
Let me know if you need me to test a build, prior to Otter 2025.
-
RE: PSEval can be called as $PSEval, @PSEval or %PSEval, but null/empty returns only make sense for $PSEval
@dean-houston said in PSEval can be called as $PSEval, @PSEval or %PSEval, but null/empty returns only make sense for $PSEval:
a variable prefix (
$
,@
,%
) is more of a convenience/convention, and the prefix isn't really available in any useful context. I'm almost certain you can do stuff like$MyVar = @(1,2,3)
for example.For what it is worth, if this is the intent, then it does not match what actually occurs. The execution engine throws exceptions when you mismatch the variable types:
# mixed sigils { set $ok = ""; set $no = ""; try { set $a = "blah"; set $ok = $ok: scalar; } catch { set $no = $no: scalar; force normal; } try { set $b = @(1,2,3); set $ok = $ok: vector-as-scalar; } catch { set $no = $no: vector-as-scalar; force normal; } try { set $c = %(a: 1, b: 2); set $ok = $ok: map-as-scalar; } catch { set $no = $no: map-as-scalar; force normal; } try { set @d = "blah"; set $ok = $ok: scalar-as-vector; } catch { set $no = $no: scalar-as-vector; force normal; } try { set @e = @(1,2,3); set $ok = $ok: vector; } catch { set $no = $no: vector; force normal; } try { set @f = %(a: 1, b: 2); set $ok = $ok: map-as-vector; } catch { set $no = $no: map-as-vector; force normal; } try { set %g = "blah"; set $ok = $ok: scalar-as-map; } catch { set $no = $no: scalar-as-map; force normal; } try { set %h = @(1,2,3); set $ok = $ok: vector-as-map; } catch { set $no = $no: vector-as-map; force normal; } try { set %i = %(a: 1, b: 2); set $ok = $ok: map; } catch { set $no = $no: map; force normal; } Log-Information Mixed sigils: success${ok}, fail${no}; }
DEBUG: Beginning execution run... ERROR: Unhandled exception: System.ArgumentException: Cannot assign a Vector value to a Scalar variable. at Inedo.ExecutionEngine.Executer.ExecuterThread.InitializeVariable(RuntimeVariableName name, RuntimeValue value, VariableAssignmentMode mode) at Inedo.ExecutionEngine.Executer.ExecuterThread.ExecuteAsync(AssignVariableStatement assignVariableStatement) at Inedo.ExecutionEngine.Executer.ExecuterThread.ExecuteNextAsync() ERROR: Unhandled exception: System.ArgumentException: Cannot assign a Map value to a Scalar variable. at Inedo.ExecutionEngine.Executer.ExecuterThread.InitializeVariable(RuntimeVariableName name, RuntimeValue value, VariableAssignmentMode mode) at Inedo.ExecutionEngine.Executer.ExecuterThread.ExecuteAsync(AssignVariableStatement assignVariableStatement) at Inedo.ExecutionEngine.Executer.ExecuterThread.ExecuteNextAsync() ERROR: Unhandled exception: System.ArgumentException: Cannot assign a Scalar value to a Vector variable. at Inedo.ExecutionEngine.Executer.ExecuterThread.InitializeVariable(RuntimeVariableName name, RuntimeValue value, VariableAssignmentMode mode) at Inedo.ExecutionEngine.Executer.ExecuterThread.ExecuteAsync(AssignVariableStatement assignVariableStatement) at Inedo.ExecutionEngine.Executer.ExecuterThread.ExecuteNextAsync() ERROR: Unhandled exception: System.ArgumentException: Cannot assign a Map value to a Vector variable. at Inedo.ExecutionEngine.Executer.ExecuterThread.InitializeVariable(RuntimeVariableName name, RuntimeValue value, VariableAssignmentMode mode) at Inedo.ExecutionEngine.Executer.ExecuterThread.ExecuteAsync(AssignVariableStatement assignVariableStatement) at Inedo.ExecutionEngine.Executer.ExecuterThread.ExecuteNextAsync() ERROR: Unhandled exception: System.ArgumentException: Cannot assign a Scalar value to a Map variable. at Inedo.ExecutionEngine.Executer.ExecuterThread.InitializeVariable(RuntimeVariableName name, RuntimeValue value, VariableAssignmentMode mode) at Inedo.ExecutionEngine.Executer.ExecuterThread.ExecuteAsync(AssignVariableStatement assignVariableStatement) at Inedo.ExecutionEngine.Executer.ExecuterThread.ExecuteNextAsync() ERROR: Unhandled exception: System.ArgumentException: Cannot assign a Vector value to a Map variable. at Inedo.ExecutionEngine.Executer.ExecuterThread.InitializeVariable(RuntimeVariableName name, RuntimeValue value, VariableAssignmentMode mode) at Inedo.ExecutionEngine.Executer.ExecuterThread.ExecuteAsync(AssignVariableStatement assignVariableStatement) at Inedo.ExecutionEngine.Executer.ExecuterThread.ExecuteNextAsync() INFO : Mixed sigils: success: scalar: vector: map, fail: vector-as-scalar: map-as-scalar: scalar-as-vector: map-as-vector: scalar-as-map: vector-as-map
There are also syntax elements which require specific context, such as
foreach
requiring a@vec
(Iteration source must be a vector value).I can certainly understand why you would not want to update the base classes so they provide the context (scalar, vector, map) to implementations but I expect it is probably the safest solution for maintaining backwards compatibility with existing authored scripts (if that information is available to you at parse-time).
The alternative is to let the script author pass it along as an optional property to
$PSEval()
(similar to$GetVariableValue()
), but this introduces another character which would then need to be escaped within the embedded Powershell (i.e.,
), and that probably would break authored scripts. -
RE: Suggestion: allow Execute-Powershell to return output stream and/or capture output variables
PSExec
(i.e.Execute-Powershell
) can capture variables, but not output streams.Understood with regards to output stream.
However, no joy with this (i.e. capturing variables) either...
set $In = 12345; set $Out = '<unset>'; Execute-PowerShell ( Text: >-|> Write-Verbose "Got some input: $In here..."; $Out = "This is nice"; Write-Verbose "inside the script, we have: $Out"; >-|>, Verbose: true ); Log-Information Outside the script, we have $Out;
DEBUG: Using Windows PowerShell 5.1... DEBUG: Importing Out... DEBUG: Importing In... DEBUG: Got some input: 12345 here... DEBUG: inside the script, we have: This is nice INFO : Outside the script, we have <unset>
So something like this:
set $hello = world; $PSExec >> $hello = 'dears'; >>; Log-Information Hello $hello;
Calling
$PSExec
like this throws Unexpected token $. I suspect you meant to writePSExec
(the operation) or$PSEval
(the variable function).However, substituting
PSExec >>
does not capture the output variable.set $hello = world; PSExec >> $hello = 'dears'; >>; Log-Information 1: Hello $hello;
DEBUG Using Windows PowerShell 5.1... DEBUG Importing hello... INFO 1: Hello world
Substituting
set $x = $PSEval(>>...>>)
does not even execute: The term '>>' is not recognized as the name of a cmdlet, function, script file, or operable programset $hello = world; set $x = $PSEval(>> $hello = 'dears'; >>); Log-Information 2: Hello $hello;
DEBUG: Using Windows PowerShell 5.1... ERROR: The term '>>' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. ERROR: The term 'world' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. ERROR: The term '>>' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. ERROR: PSEVal: PowerShell script failed with an error (see previous log messages).
set $hello = world; set $x = $PSEval(>> `$hello = 'dears'; >>); Log-Information 3: Hello $hello;
DEBUG: Using Windows PowerShell 5.1... DEBUG: Importing hello... ERROR: The term '>>' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. ERROR: The term '>>' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. ERROR: PSEVal: PowerShell script failed with an error (see previous log messages).
set $hello = world; set $x = $PSEval(>>`$hello = 'dears';>>); Log-Information 4: Hello $hello;
DEBUG: Using Windows PowerShell 5.1... DEBUG: Importing hello... ERROR: The term '>>' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. ERROR: The term '>>' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. ERROR: PSEVal: PowerShell script failed with an error (see previous log messages).
Substituting
set $x = $PSEval("...")
does not capture the output variable:set $hello = world; set $x = $PSEval(" $hello = 'dears'; "); Log-Information 5: Hello $hello;
DEBUG: Using Windows PowerShell 5.1... DEBUG: Importing hello... INFO : 5: Hello world
set $hello = world; set $x = $PSEval(" `$hello = 'dears'; "); Log-Information 6: Hello $hello;
DEBUG: Using Windows PowerShell 5.1... DEBUG: Importing hello... INFO : 6: Hello world
I think this might actually make my point for me. If you can get this wrong instinctively, what hope do I have (or my less code-savvy colleagues)?
-
RE: Suggestion: allow Execute-Powershell to return output stream and/or capture output variables
@dean-houston I hadn't noticed
Execute-Powershell
automatically capturing output variables. I'll do some more tests and see if it works for me.This is probably a reasonable candidate for the
/literal
proposal described in the documentation, if you are still taking votes on that:We're considering adding this using a /literal decorator at the end of a quoted or swim string. For example,
"Not $escaped"/literal
.If you have any interest in this, please submit a ticket with how you would plan to use this, and how it would be helpful; this is currently tracked as IEE-20, and we will link your ticket and seek to prioritize this.
-
RE: Suggestion: allow for setting list or map elements by dynamic index or key (@ListSet, %MapSet)
@dean-houston I'm sure that fixing the syntax would not be straightforward, but introducing
@ListSet
and%MapSet
variable functions in lieu is probably a good enough workaround for nearly all use cases where someone would want to do this. -
Suggestion: allow Execute-Powershell to return output stream and/or capture output variables
There does not appear to be a means to capture the output stream from
Execute-Powershell
, nor does it appear possible to obtain the value of a variable set inside the Powershell script back to the enclosing OtterScript.It is possible to get a result back from
$PSEval()
but the options to both$PSEval
andExecute-Powershell
are not equivalent. For instance:Execute-Powershell
uses aText
property which does not interpolate OtterScript variables at the script level (it extracts and dispatches them), which means it better handles the$variables
that are specific to the Powershell script$PSEval()
does interpolate OtterScript variables, which means all the variables in the Powershell script have to be backtick-escaped, whether they are specific to the Powershell script or defined in the OtterScript context$PSEval()
does not like parentheses; many of these have to be escaped as well, and it is not always clear which ones$PSEval()
does not like newlines; these can be within "swim" strings, but these still require escaping at least the variables$PSEval()
is not (currently) particularly supportive of scripts with varying output (see #4920).
Correctly escaping all the parentheses and variables in any Powershell longer than a couple of lines is an exercise in torture.
Execute-Powershell
is clearly the better choice for more complex scripts, but seems to lack the means to return anything back to the caller.As such, could
Execute-Powershell
be at least augmented with an output parameter, to capture anything in the Powershell output stream back to target variable? (noting it should be made aware of scalar, vector or map context)Alternatively/additionally, it would be useful for
Execute-Powershell
to export variables back to the calling OtterScript context. Clobbering existing variables across the board might not be the right approach (so as not to break existing scripts), but perhapsExecute-Powershell
could be given an optional input parameter which is a list of variable names to export, so the capture is opt-in?(I assume this was the intent of including them in ExecutePowerShellJob+Result, and that
Result
is correctly populated...)# context aware output.. Execute-Powershell ( Text: >> Write-Output "abc" >>, OutputVariable => $foo ); Execute-Powershell ( Text: >> Write-Output "abc" Write-Output "def" Write-Output "ghi" >>, OutputVariable => @bar ); Execute-Powershell ( Text: >> Write-Output @{a = 1; b = 2; c = 3} >>, OutputVariable => %baz ); # or, with capture... Execute-Powershell ( Text: >> $a = 10 + 5 $b = @($a, 10) $c = @{a = $a; b = $b} >>, CaptureVariables: @(a, b, c) ); Log-Information $a; # -> 15 Log-Information $ToJson(@b); # -> ["15", "10"] Log-Information $ToJson(%c); # -> { "a": "15", "b": ["15", "10"] }
PS: various crimes against humanity, trying to escape properly are below, to demonstrate the difficulties...
set $foo = "hello 'world'"; set @bar = @(); # 'natural' approach, newlines, no escapes: (otter compile error: 'Expected ;') { set @a = @PSEval( for ($i = 1; $i -le 5; $i++) { Write-Output ('{0} {1}' -f $foo,$i) } ); Log-Information `@a: $ToJson(@a); } # flatten newlines, no escapes: (otter runtime error: 'cannot resolve variable $i') set @a = @PSEval(for ($i = 1; $i -le 5; $i++) { Write-Output ('{0} {1}' -f $foo,$i) }); # flatten newlines, no escapes: (otter runtime error: 'invalid use of vector expression in scalar') set $a = $PSEval(for ($i = 1; $i -le 5; $i++) { Write-Output ('{0} {1}' -f $foo,$i) }); # flatten newlines, escape parens: (otter runtime error: 'cannot resolve variable $i') set @a = @PSEval(for `($i = 1; $i -le 5; $i++`) { Write-Output `('{0} {1}' -f $foo,$i`) }); # flatten newlines, escape Powershell sigils and parens: ($foo is interpolated, not captured; Powershell syntax error at '-f hello') set @a = @PSEval(for `(`$i = 1; `$i -le 5; `$i++`) { Write-Output `('{0} {1}' -f $foo,`$i`) }); # flatten newlines, escape all sigils and parens: ($foo is captured; powershell runtime error: 'missing closing ")"') set @a = @PSEval(for (`$i = 1; `$i -le 5; `$i++`) { Write-Output ('{0} {1}' -f `$foo,`$i`) }); # flatten newlines, escape all sigils and parens: ($foo is captured; powershell runtime error: 'missing closing ")"') set @a = @PSEval(for `(`$i = 1; `$i -le 5; `$i++`) { Write-Output `('{0`} {1`}' -f `$foo,`$i`) }); # as a swim string; same as above: ('cannot resolve $i' when unescaped; 'missing closing ")"' when escaped) set @a = @PSEval(>-|> for ($i = 1; $i -le 5; $i++) { Write-Output ('{0} {1}' -f $foo,$i) } >-|>); # as a variable, loaded by swim string; escaping all sigils: (captures $foo, this is the first one that works) set $ps = >-|> for (`$i = 1; `$i -le 5; `$i++) { Write-Output ('{0} {1}' -f `$foo,`$i) } >-|>; set @a = @PSEval($ps); # executes with the 'natural' syntactic approach; captures $foo but has no means to return output Execute-Powershell( Text: >-|> for ($i = 1; $i -le 5; $i++) { Write-Output ('{0} {1}' -f $foo,$i) } >-|> ); # similarly 'natural'; captures $foo and $bar, but does not populate @bar Execute-Powershell( Text: >-|> $bar = @() for ($i = 1; $i -le 5; $i++) { $bar += ('{0} {1}' -f $foo,$i) } >-|> );
(Reposted from Github on request)
-
PSEval can be called as $PSEval, @PSEval or %PSEval, but null/empty returns only make sense for $PSEval
Consider the arbitrary OtterScript...
set @fileLines = @PSEval(Get-Content -LiteralPath 'X:\PathTo\File') if $ListCount(@fileLines) != 0 { Log-Debug "OK" }
If the
Get-Content
PowerShell cmdlet returns no lines, then the current approach is to attempt to set the return value of@PSEval
to be an empty string, rather than an empty array. This in turn causes Otter to throw a rather nasty exception:Unhandled exception: System.ArgumentException: Cannot assign a Scalar value to a Vector variable. at Inedo.ExecutionEngine.Executer.ExecuterThread.InitializeVariable(RuntimeVariableName name, RuntimeValue value, VariableAssignmentMode mode) at Inedo.ExecutionEngine.Executer.ExecuterThread.ExecuteAsync(AssignVariableStatement assignVariableStatement) at Inedo.ExecutionEngine.Executer.ExecuterThread.ExecuteNextAsync()
From an end-user perspective, trying to resolve whether a
PSEval
would return scalar, vector or hash is therefore fraught -- you have to know in advance whether the script will return 0, 1 or many items, and cannot safely deduce this by simply calling in vector context and testing the resulting length.The best I can think of is
try { set global @r = @PSEval(...); } catch { set global @r = @(); }
, which technically proceeds, but the exception is still thrown and the overall job still results in an error state (assuming no subsequentforce normal
).Ideally,
PSEval
should behave appropriately on a null/empty value, depending on whether it was called as$PSEval
,@PSEval
or%PSEval
-- in scalar context, it is fine to return the empty string; in vector context, it would be better if it returned an empty vector; in hash context, an empty hash.I appreciate that this might not be resolvable straight away, as you probably need assistance from whatever parses and invokes the script line upstream (maybe
ExecuterThread
?) to pass the desiredRuntimeValueType
to you. I expect it either needs to be suppled by extendingIVariableFunctionContext
(which may have side-effects on implementors of that interface); or—more likely—be introduced as aprotected internal
settable property ofVariableFunction
itself.Alternatively, if it is possible to alter
RuntimeValue
so it has a dedicated constructor/static sentinel value for empty values,ExecuterThread.InitializeVariable
might be better-placed to handle the scalar/vector/hash decision, and you can just return the sentinel fromEvaluateAsync
.(Reposted from Github on request)
-
Suggestion: allow for setting list or map elements by dynamic index or key (@ListSet, %MapSet)
It does not appear to be possible to set the value of a list or map element, where the index or key is stored in a variable.
The formal grammar suggests...
<assign_variable_statement> ::= set [local | global] ( /variable_expression/ | /indexed_expression/ ) = /literal_expression/;
variable_expression
:
A variable type identifier ($
,@
, or%
) immediately followed by one of:- simple name - follows same rules as any_name
- explicit name - a left curly brace (
{
), followed by of characters with the same rules as any_name but that also allow spaces, followed by a right curly brace (}
)
indexed_expression
:
A variable_expression for a vector (@) or map (%) type, immediately followed one of:- left bracket (
[
), scalar_expression, then right bracket (]
) - dot (
.
) then scalar_expression
...which implies that both...
set %Map.$k = something; set @Vec[$i] = something;
...should be possible, but these throw various errors at the execution engine level, e.g.:
Unhandled exception: System.InvalidCastException: Unable to cast object of type 'System.ArrayEnumerator' to type 'System.Collections.Generic.IEnumerator`1[Inedo.ExecutionEngine.RuntimeValue]'. at Inedo.ExecutionEngine.RuntimeListValue.GetEnumerator() at Inedo.ExecutionEngine.Mapping.CoreScriptPropertyMapper.CoerceValue(RuntimeValue value, PropertyInfo property, Type type) at Inedo.Otter.Service.PlanExecuter.ExecutionVariableEvaluationContext.GetVariableFunctionInternal(RuntimeVariableName functionName, IList`1 arguments) at Inedo.Otter.Service.PlanExecuter.ExecutionVariableEvaluationContext.TryEvaluateFunctionAsync(RuntimeVariableName functionName, IList`1 arguments) at Inedo.ExecutionEngine.Variables.FunctionTextValue.EvaluateAsync(IVariableEvaluationContext context) at Inedo.ExecutionEngine.Variables.ProcessedString.EvaluateValueAsync(IVariableEvaluationContext context) at Inedo.ExecutionEngine.Executer.ExecuterThread.EvaluateAsync(EqualityPredicate equalityPredicate) at Inedo.ExecutionEngine.Executer.ExecuterThread.ExecuteAsync(PredicateStatement predicateStatement) at Inedo.ExecutionEngine.Executer.ExecuterThread.ExecuteNextAsync()
In the absence of syntax support, the sanest workaround is probably a pair of variable functions -- e.g.
@ListSet(@Vec, $i, value)
and%MapSet(%Map, $k, value)
, which could perform this operation.My current workaround is to...
set %Map = %MapAdd(%MapRemove(%Map, $key), $key, value); set @copy = @(); foreach $j in @Range(0, $ListCount(@Vec)) { if ($i == $j) { set @copy = @ListInsert(@copy, value); } else { set @copy = @ListInsert(@copy, $($ListItem(@Vec, $j))); } } set @Vec = @copy;
...the latter of which is so tortuous I've probably got it wrong just typing it in here (and does not handle anything other than lists of scalars).
Looking at the existing similar
%MapAdd
and@ListInsert
functions, I expect the meat of these is probably fairly straightforward:ListSetVariableFunction
protected override IEnumerable EvaluateVector(IVariableFunctionContext context) { var list = this.List.ToList(); var index = this.Index; // bounds checking if (index >= list.Count) { // allow for growing the list to fit new index list.AddRange(Enumerable.Range(0, 1+index-list.Count).Select(_ => string.Empty)); } else if (index < 0) { // allow for negative indexing from end of array (but not growth) if (-index >= list.Count) throw new ArgumentOutOfRangeException(nameof(this.Index)); index = list.Count + (index % list.Count); } list[index] = this.Value; return list; }
MapSetVariableFunction
public override RuntimeValue Evaluate(IVariableFunctionContext context) { if (String.IsNullOrEmpty(this.Key)) throw new ArgumentNullException(nameof(this.Key)); var map = new Dictionary<string, RuntimeValue>(this.Map); map[this.Key] = this.Value; return new RuntimeValue(map); }
(Reposted from GitHub on request)
-
"Log scope Execution has already been completed" exception after OSCall
I've been trying to understand how
OSCall
is supposed to work, so I can reuse some common OtterScript snippets across jobs.However, after successfully calling a script with an input variable, I am trying to log its output variable, and am receiving the exception "An unhandled error occurred during execution phase: Log scope Execution has already been completed.. See the error logs for more details."
I can find no more details.
Using a minimal example, the common/inner/child script I am trying to
OSCall
is:set $Result = Hello $Username; Log-Information in child script: $Result;
The calling/outer/parent script is:
set $Result = "<not set>"; OSCall( Name: Spikes/Hello.otter, Variables: %(Username: fred), OutputVariables: @(Result) ); Log-Information in parent script: $Result;
(I don't know if it is required to initialize
$Result
in the outer scope or whether it would be set automatically in the parent; nor whether this was even the correct usage of theOutputVariables
parameter -- these were the things I was testing. The documentation onOSCall
is extremely light on detail.)The resulting execution log is:
DEBUG: Job will be run against servers sequentially (not asynchronously). DEBUG: No servers, server roles, or environments specified, and thus no servers will be targeted. DEBUG: Beginning execution run... DEBUG: Beginning execution run... INFO: in child script: Hello fred INFO: Execution run succeeded. ERROR: An unhandled error occurred during execution phase: Log scope Execution has already been completed.. See the error logs for more details.
I can see from the log that it at least enters the child script and accepts the input variable (as evidenced by the
in child script
message), but it does not complete the subsequent log statement in the parent scope after theOSCall
.If I comment out either the
OSCall
or theLog-Information
in the parent script, the error does not occur.If I try the slightly more complicated parent script...
for server localhost { set $Result = "<not set>"; Log-Information before oscall: $Result; Create-File ( Name: "C:\ProgramData\InedoOutput.1.txt", Text: "in parent script: $Result", Overwrite: true ); OSCall( Name: Spikes/Hello.otter, Variables: %(Username: fred), OutputVariables: @(Result) ); Create-File ( Name: "C:\ProgramData\InedoOutput.2.txt", Text: "in parent script: $Result", Overwrite: true ); Log-Information in parent script: $Result; }
...I don't get the Log Scope Execution... message, but I do get Execution run failed, and only the first file (InedoOutput.1.txt) is created:
DEBUG: Job will be run against servers sequentially (not asynchronously). DEBUG: No servers, server roles, or environments specified, and thus no servers will be targeted. DEBUG: Beginning execution run... INFO: before oscall: <not set> INFO: Creating file... DEBUG: Creating directories for C:\ProgramData\InedoOutput.1.txt... DEBUG: Creating C:\ProgramData\InedoOutput.1.txt... INFO: C:\ProgramData\InedoOutput.1.txt file created. DEBUG: Beginning execution run... INFO: in child script: Hello fred DEBUG: Execution run succeeded. DEBUG: Cleaning up temporary files on Local Server... ERROR: Execution run failed. DEBUG: Cleaning up temporary files on Local Server...
It seems nothing will run after an
OSCall
...?