mirror of
https://github.com/actions/runner.git
synced 2026-07-03 11:06:08 +08:00
Force LF line breaks in DAP YAML emitters
`StringWriter` defaults to `Environment.NewLine`, which is CRLF on Windows. YamlDotNet's `Emitter` calls `WriteLine` internally, so the emitted YAML mixes CRLF (from the emitter) with the explicit `\n` we append in the renderer skeleton. That breaks the document-end stripping in `FormatScalar` and `TemplateTokenYamlAdapter.Serialize` and corrupts the rendered view on Windows. Set `sw.NewLine = "\n"` in both emitter entry points so the output is always LF, regardless of host. Add regression tests asserting no `\r` appears in the rendered view or in adapter output. The tests are noop guards on Linux (where `Environment.NewLine` is already \n) but catch any future regression on the Windows CI matrix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -367,6 +367,11 @@ namespace GitHub.Runner.Worker.Dap
|
||||
}
|
||||
|
||||
using var sw = new StringWriter(CultureInfo.InvariantCulture);
|
||||
// Force LF line breaks; YamlDotNet's Emitter calls WriteLine,
|
||||
// which would otherwise produce CRLF on Windows and break
|
||||
// both our document-end stripping below and downstream
|
||||
// consumers that assume a single line-break convention.
|
||||
sw.NewLine = "\n";
|
||||
var emitter = new Emitter(sw);
|
||||
emitter.Emit(new StreamStart());
|
||||
emitter.Emit(new DocumentStart(null, null, true));
|
||||
|
||||
@@ -95,6 +95,11 @@ namespace GitHub.Runner.Worker.Dap
|
||||
}
|
||||
|
||||
using var sw = new StringWriter(CultureInfo.InvariantCulture);
|
||||
// Force LF line breaks; YamlDotNet's Emitter calls WriteLine,
|
||||
// which would otherwise produce CRLF on Windows and corrupt
|
||||
// both the document-end stripping below and the per-line
|
||||
// indentation pass that follows.
|
||||
sw.NewLine = "\n";
|
||||
var emitter = new Emitter(sw);
|
||||
var adapter = new TemplateTokenYamlAdapter(emitter);
|
||||
adapter.WriteStart();
|
||||
|
||||
@@ -613,5 +613,31 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
// entry gets an inline skipped annotation.
|
||||
Assert.Equal(unmarked.EntryStartLines[1], marked.EntryStartLines[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void Render_AlwaysUsesLfLineBreaks()
|
||||
{
|
||||
// Regression: YamlDotNet's Emitter calls WriteLine, which on
|
||||
// Windows produces CRLF (the host's Environment.NewLine).
|
||||
// FormatScalar / TemplateTokenYamlAdapter.Serialize must force
|
||||
// LF so the rendered view round-trips regardless of platform.
|
||||
var entry = new JobExecutionViewEntry(JobExecutionPhase.Main, "with: colon", id: "step-1", uses: "actions/checkout@v4");
|
||||
var result = JobExecutionViewRenderer.Render("job-1", new[] { entry });
|
||||
Assert.DoesNotContain("\r", result.Yaml);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void FormatScalar_AlwaysUsesLfLineBreaks()
|
||||
{
|
||||
// Direct check on FormatScalar to guard against future refactors
|
||||
// that bypass the full Render path but still emit through
|
||||
// YamlDotNet.
|
||||
Assert.DoesNotContain("\r", JobExecutionViewRenderer.FormatScalar("with: colon"));
|
||||
Assert.DoesNotContain("\r", JobExecutionViewRenderer.FormatScalar("hello"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,5 +170,22 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
string yaml = TemplateTokenYamlAdapter.Serialize(token, 0);
|
||||
Assert.False(yaml.EndsWith("\n"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Trait("Level", "L0")]
|
||||
[Trait("Category", "Worker")]
|
||||
public void Serialize_AlwaysUsesLfLineBreaks()
|
||||
{
|
||||
// Regression: YamlDotNet's Emitter calls WriteLine, which on
|
||||
// Windows produces CRLF (the host's Environment.NewLine).
|
||||
// Serialize must force LF so the rendered view round-trips
|
||||
// regardless of platform.
|
||||
var map = new MappingToken(null, null, null);
|
||||
map.Add(Str("k1"), Str("v1"));
|
||||
map.Add(Str("k2"), Num(2));
|
||||
map.Add(Str("k3"), Bool(true));
|
||||
string yaml = TemplateTokenYamlAdapter.Serialize(map, indentSpaces: 2);
|
||||
Assert.DoesNotContain("\r", yaml);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user