diff --git a/src/Runner.Worker/Dap/DapReplExecutor.cs b/src/Runner.Worker/Dap/DapReplExecutor.cs
index e7d9866d0..6977c3be3 100644
--- a/src/Runner.Worker/Dap/DapReplExecutor.cs
+++ b/src/Runner.Worker/Dap/DapReplExecutor.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -89,8 +89,9 @@ namespace GitHub.Runner.Worker.Dap
_trace.Info($"REPL shell: {shellCommand}, argFormat: {argFormat}");
- // 2. Prepare the script content
- var contents = command.Script;
+ // 2. Expand ${{ }} expressions in the script body, just like
+ // ActionRunner evaluates step inputs before ScriptHandler sees them
+ var contents = ExpandExpressions(command.Script, context);
contents = ScriptHandlerHelpers.FixUpScriptContents(shellCommand, contents);
// Write to a temp file (same pattern as ScriptHandler)
@@ -197,6 +198,73 @@ namespace GitHub.Runner.Worker.Dap
}
}
+ ///
+ /// Expands ${{ }} expressions in the input string using the
+ /// runner's template evaluator — the same evaluation path that processes
+ /// step inputs before runs them.
+ ///
+ /// Each ${{ expr }} occurrence is individually evaluated and
+ /// replaced with its masked string result, mirroring the semantics of
+ /// expression interpolation in a workflow run: step body.
+ ///
+ private string ExpandExpressions(string input, IExecutionContext context)
+ {
+ if (string.IsNullOrEmpty(input) || !input.Contains("${{"))
+ {
+ return input ?? string.Empty;
+ }
+
+ var result = new StringBuilder();
+ int pos = 0;
+
+ while (pos < input.Length)
+ {
+ var start = input.IndexOf("${{", pos, StringComparison.Ordinal);
+ if (start < 0)
+ {
+ result.Append(input, pos, input.Length - pos);
+ break;
+ }
+
+ // Append the literal text before the expression
+ result.Append(input, pos, start - pos);
+
+ var end = input.IndexOf("}}", start + 3, StringComparison.Ordinal);
+ if (end < 0)
+ {
+ // Unterminated expression — keep literal
+ result.Append(input, start, input.Length - start);
+ break;
+ }
+
+ var expr = input.Substring(start + 3, end - start - 3).Trim();
+ end += 2; // skip past "}}"
+
+ // Evaluate the expression
+ try
+ {
+ var templateEvaluator = context.ToPipelineTemplateEvaluator();
+ var token = new GitHub.DistributedTask.ObjectTemplating.Tokens.BasicExpressionToken(
+ null, null, null, expr);
+ var evaluated = templateEvaluator.EvaluateStepDisplayName(
+ token,
+ context.ExpressionValues,
+ context.ExpressionFunctions);
+ result.Append(_hostContext.SecretMasker.MaskSecrets(evaluated ?? string.Empty));
+ }
+ catch (Exception ex)
+ {
+ _trace.Warning($"Expression expansion failed for '{expr}': {ex.Message}");
+ // Keep the original expression literal on failure
+ result.Append(input, start, end - start);
+ }
+
+ pos = end;
+ }
+
+ return result.ToString();
+ }
+
///
/// Resolves the default shell the same way
/// does: check job defaults, then fall back to platform default.
@@ -270,12 +338,13 @@ namespace GitHub.Runner.Worker.Dap
}
}
- // Apply REPL-specific overrides last (so they win)
+ // Apply REPL-specific overrides last (so they win),
+ // expanding any ${{ }} expressions in the values
if (replEnv != null)
{
foreach (var pair in replEnv)
{
- env[pair.Key] = pair.Value;
+ env[pair.Key] = ExpandExpressions(pair.Value, context);
}
}