Skip to content

Commit 000ddd1

Browse files
reakaleekMpdreamzcotti
authored
Add matrix to pr workflow (#425)
* Add matrix * Split windows out of matrix and set shell explicitly * skip compile of build in release mode * Refactor * Fix Elastic.Markdown.Tests on Windows * DiagnosticLinkInlineParser.UpdateLinkUrl()'s Path traversal is adjusted before processing into the HTML payload * Mover received Windows-specific handling * Undo NavigationTests as they were working as intended. * Add prerelease step for Windows to configure line feed behavior. * Configure before checkout * Remove eol directive from Git Configuration * Remove PR as it breaks linting rules. Changing approach to individually handling tests. * Handle more tests. * Fix path inference on Windows for link references and MarkdownFile Url property. * Fix MarkdownResultsAssertions on Windows --------- Co-authored-by: Martijn Laarman <[email protected]> Co-authored-by: Felipe Cotti <[email protected]>
1 parent 0482d50 commit 000ddd1

File tree

15 files changed

+72
-46
lines changed

15 files changed

+72
-46
lines changed

.github/workflows/pr.yml

+12-16
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@ on:
55

66
permissions:
77
contents: read
8-
packages: read
9-
id-token: write
10-
pull-requests: write
11-
deployments: write
128

139
concurrency:
1410
group: ${{ github.workflow }}-${{ github.ref }}
@@ -19,26 +15,26 @@ env:
1915

2016
jobs:
2117
build:
22-
runs-on: ubuntu-latest
23-
steps:
18+
runs-on: ${{ matrix.os }}
19+
strategy:
20+
fail-fast: false
21+
matrix:
22+
os:
23+
- ubuntu-latest
24+
- macos-latest
25+
- windows-latest
26+
steps:
2427
- uses: actions/checkout@v4
2528

2629
- name: Bootstrap Action Workspace
2730
id: bootstrap
2831
uses: ./.github/actions/bootstrap
2932

3033
- name: Build
31-
run: ./build.sh
34+
run: dotnet run --project build -c release
3235

3336
- name: Test
34-
run: ./build.sh test
37+
run: dotnet run --project build -c release -- test
3538

3639
- name: Publish AOT
37-
run: ./build.sh publishbinaries
38-
39-
- uses: actions/upload-artifact@v4
40-
with:
41-
name: docs-builder-binary
42-
path: .artifacts/publish/docs-builder/release/docs-builder
43-
if-no-files-found: error
44-
retention-days: 1
40+
run: dotnet run --project build -c release -- publishbinaries

docs-builder.sln

-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ Global
102102
{10857974-6CF1-42B5-B793-AAA988BD7348}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
103103
{10857974-6CF1-42B5-B793-AAA988BD7348}.Debug|Any CPU.Build.0 = Debug|Any CPU
104104
{10857974-6CF1-42B5-B793-AAA988BD7348}.Release|Any CPU.ActiveCfg = Release|Any CPU
105-
{10857974-6CF1-42B5-B793-AAA988BD7348}.Release|Any CPU.Build.0 = Release|Any CPU
106105
{28350800-B44B-479B-86E2-1D39E321C0B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
107106
{28350800-B44B-479B-86E2-1D39E321C0B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
108107
{28350800-B44B-479B-86E2-1D39E321C0B4}.Release|Any CPU.ActiveCfg = Release|Any CPU

src/Elastic.Markdown.Refactor/Move.cs

+16
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information
44

55
using System.IO.Abstractions;
6+
using System.Runtime.InteropServices;
67
using System.Text.RegularExpressions;
78
using Elastic.Markdown.IO;
89
using Microsoft.Extensions.Logging;
@@ -64,6 +65,9 @@ private async Task SetupChanges(ChangeSet changeSet, Cancel ctx)
6465
var fullPath = Path.GetFullPath(Path.Combine(sourceDirectory, originalPath));
6566
var relativePath = Path.GetRelativePath(targetDirectory, fullPath);
6667

68+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
69+
relativePath = relativePath.Replace('\\', '/');
70+
6771
newPath = originalPath.StartsWith("./", OrdinalIgnoreCase) && !relativePath.StartsWith("./", OrdinalIgnoreCase)
6872
? "./" + relativePath
6973
: relativePath;
@@ -258,6 +262,15 @@ string targetPath
258262
var absolutStyleSource = $"/{relativeToDocsFolder}";
259263
var relativeToDocsFolderTarget = Path.GetRelativePath(documentationSet.SourceDirectory.FullName, targetPath);
260264
var absoluteStyleTarget = $"/{relativeToDocsFolderTarget}";
265+
266+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
267+
{
268+
relativeSource = relativeSource.Replace('\\', '/');
269+
relativeSourceWithDotSlash = relativeSourceWithDotSlash.Replace('\\', '/');
270+
absolutStyleSource = absolutStyleSource.Replace('\\', '/');
271+
absoluteStyleTarget = absoluteStyleTarget.Replace('\\', '/');
272+
}
273+
261274
return (
262275
relativeSource,
263276
relativeSourceWithDotSlash,
@@ -298,6 +311,9 @@ private string ReplaceLinks(
298311
: $"[{match.Groups[1].Value}]({relativeTarget}{anchor})";
299312
}
300313

314+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
315+
newLink = newLink.Replace('\\', '/');
316+
301317
var lineNumber = content[..match.Index].Count(c => c == '\n') + 1;
302318
var columnNumber = match.Index - content.LastIndexOf('\n', match.Index);
303319
if (!_linkModifications.ContainsKey(changeSet))

src/Elastic.Markdown/IO/MarkdownFile.cs

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information
44

55
using System.IO.Abstractions;
6+
using System.Runtime.InteropServices;
67
using Elastic.Markdown.Diagnostics;
78
using Elastic.Markdown.Helpers;
89
using Elastic.Markdown.IO.Navigation;
@@ -95,6 +96,8 @@ public string Url
9596
get
9697
{
9798
var relativePath = RelativePathUrl;
99+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
100+
relativePath = relativePath.Replace('\\', '/');
98101
return Path.GetFileName(relativePath) == "index.md"
99102
? $"{UrlPathPrefix}/{relativePath.Remove(relativePath.LastIndexOf("index.md", StringComparison.Ordinal), "index.md".Length)}"
100103
: $"{UrlPathPrefix}/{relativePath.Remove(relativePath.LastIndexOf(SourceFile.Extension, StringComparison.Ordinal), SourceFile.Extension.Length)}";

src/Elastic.Markdown/IO/State/LinkReference.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
33
// See the LICENSE file in the project root for more information
44

5+
using System.Runtime.InteropServices;
56
using System.Text.Json;
67
using System.Text.Json.Serialization;
78
using Elastic.Markdown.IO.Discovery;
@@ -70,7 +71,9 @@ public static LinkReference Create(DocumentationSet set)
7071
var crossLinks = set.Build.Collector.CrossLinks.ToHashSet().ToArray();
7172
var links = set.MarkdownFiles.Values
7273
.Select(m => (m.RelativePath, File: m))
73-
.ToDictionary(k => k.RelativePath, v =>
74+
.ToDictionary(k => RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
75+
? k.RelativePath.Replace('\\', '/')
76+
: k.RelativePath, v =>
7477
{
7578
var anchors = v.File.Anchors.Count == 0 ? null : v.File.Anchors.ToArray();
7679
return new LinkMetadata { Anchors = anchors, Hidden = v.File.Hidden };

src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Collections.Immutable;
66
using System.Diagnostics.CodeAnalysis;
77
using System.IO.Abstractions;
8+
using System.Runtime.InteropServices;
89
using System.Text.RegularExpressions;
910
using Elastic.Markdown.Diagnostics;
1011
using Elastic.Markdown.Helpers;
@@ -271,6 +272,10 @@ private static void UpdateLinkUrl(LinkInline link, string url, ParserContext con
271272
if (!string.IsNullOrWhiteSpace(url) && !string.IsNullOrWhiteSpace(urlPathPrefix))
272273
url = $"{urlPathPrefix.TrimEnd('/')}{url}";
273274

275+
// When running on Windows, path traversal results must be normalized prior to being used in a URL
276+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
277+
url = url.Replace('\\', '/');
278+
274279
link.Url = string.IsNullOrEmpty(anchor) ? url : $"{url}#{anchor}";
275280
}
276281

tests/Elastic.Markdown.Tests/CodeBlocks/CallOutTests.cs

+6-6
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public void ParsesMagicCallOuts() => Block!.CallOuts
118118

119119
[Fact]
120120
public void RendersExpectedHtml() =>
121-
Html.Should().Contain("""
121+
Html.ReplaceLineEndings().Should().Contain("""
122122
<div class="highlight-csharp notranslate">
123123
<div class="highlight">
124124
<pre><code class="language-csharp">var x = 1; <span class="code-callout" data-index="1">1</span>
@@ -132,7 +132,7 @@ public void RendersExpectedHtml() =>
132132
<li>Marking the first callout</li>
133133
<li>Marking the second callout</li>
134134
</ol>
135-
""");
135+
""".ReplaceLineEndings());
136136

137137

138138
[Fact]
@@ -367,12 +367,12 @@ public void ParsesCallouts() => Block!.CallOuts
367367

368368
[Fact]
369369
public void RenderedHtmlContainsCallouts() =>
370-
Html.Should().Contain("""
370+
Html.ReplaceLineEndings().Should().Contain("""
371371
<ol class="code-callouts">
372372
<li>First callout</li>
373373
<li>Second callout</li>
374374
</ol>
375-
""");
375+
""".ReplaceLineEndings());
376376
}
377377

378378
public class CodeBlockWithMultipleCommentTypesThenList(ITestOutputHelper output) : CodeBlockCallOutTests(output, "csharp",
@@ -426,10 +426,10 @@ public void ParsesCallouts() => Block!.CallOuts
426426

427427
[Fact]
428428
public void RendersIntermediateParagraph() =>
429-
Html.Should().Contain("""
429+
Html.ReplaceLineEndings().Should().Contain("""
430430
<p><strong>This is an intermediate paragraph</strong></p>
431431
<ol class="code-callouts">
432-
""");
432+
""".ReplaceLineEndings());
433433
}
434434

435435
public class CodeBlockWithCommentBlocksTwoParagraphsThenList(ITestOutputHelper output) : CodeBlockCallOutTests(output, "csharp",

tests/Elastic.Markdown.Tests/DocSet/BreadCrumbTests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class BreadCrumbTests(ITestOutputHelper output) : NavigationTestsBase(out
1212
[Fact]
1313
public void ParsesATableOfContents()
1414
{
15-
var doc = Generator.DocumentationSet.Files.FirstOrDefault(f => f.RelativePath == "testing/nested/index.md") as MarkdownFile;
15+
var doc = Generator.DocumentationSet.Files.FirstOrDefault(f => f.RelativePath == Path.Combine("testing", "nested", "index.md")) as MarkdownFile;
1616

1717
doc.Should().NotBeNull();
1818

tests/Elastic.Markdown.Tests/DocSet/NavigationTests.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ public void ParsesNestedFoldersAndPrefixesPaths()
1717
{
1818
Configuration.ImplicitFolders.Should().NotBeNullOrEmpty();
1919
Configuration.ImplicitFolders.Should()
20-
.Contain("testing/nested");
20+
.Contain(Path.Combine("testing", "nested"));
2121
}
2222

2323
[Fact]
2424
public void ParsesFilesAndPrefixesPaths() =>
2525
Configuration.Files.Should()
2626
.Contain("index.md")
27-
.And.Contain("syntax/index.md");
27+
.And.Contain(Path.Combine("syntax", "index.md"));
2828

2929
[Fact]
3030
public void ParsesRedirects()

tests/Elastic.Markdown.Tests/DocSet/NestedTocTests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class NestedTocTests(ITestOutputHelper output) : NavigationTestsBase(outp
1212
[Fact]
1313
public void InjectsNestedTocsIntoDocumentationSet()
1414
{
15-
var doc = Generator.DocumentationSet.Files.FirstOrDefault(f => f.RelativePath == "development/index.md") as MarkdownFile;
15+
var doc = Generator.DocumentationSet.Files.FirstOrDefault(f => f.RelativePath == Path.Combine("development", "index.md")) as MarkdownFile;
1616

1717
doc.Should().NotBeNull();
1818

tests/Elastic.Markdown.Tests/Inline/InlineAnchorTests.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,11 @@ public class InlineAnchorInHeading(ITestOutputHelper output) : BlockTest<Heading
111111
[Fact]
112112
public void GeneratesAttributesInHtml() =>
113113
// language=html
114-
Html.Should().Be(
114+
Html.ReplaceLineEndings().TrimEnd().Should().Be(
115115
"""
116116
<div class="heading-wrapper" id="hello-world"><h2><a class="headerlink" href="#hello-world">Hello world <a id="my-anchor"></a></a></h2>
117117
</div>
118-
""".TrimEnd()
118+
""".ReplaceLineEndings().TrimEnd()
119119
);
120120
}
121121

@@ -128,11 +128,11 @@ public class ExplicitSlugInHeader(ITestOutputHelper output) : BlockTest<HeadingB
128128
[Fact]
129129
public void GeneratesAttributesInHtml() =>
130130
// language=html
131-
Html.Should().Be(
131+
Html.ReplaceLineEndings().TrimEnd().Should().Be(
132132
"""
133133
<div class="heading-wrapper" id="my-anchor"><h2><a class="headerlink" href="#my-anchor">Hello world</a></h2>
134134
</div>
135-
""".TrimEnd()
135+
""".ReplaceLineEndings().TrimEnd()
136136
);
137137
}
138138

tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -267,15 +267,15 @@ public class CommentedNonExistingLinks2(ITestOutputHelper output) : LinkTestBase
267267
[Fact]
268268
public void GeneratesHtml() =>
269269
// language=html
270-
Html.TrimEnd().Should().Be("""
270+
Html.ReplaceLineEndings().TrimEnd().Should().Be("""
271271
<p>Links:</p>
272272
<ul>
273273
<li><a href="/docs/testing/req" hx-get="/docs/testing/req" hx-select-oob="#primary-nav,#secondary-nav,#content-container" hx-swap="none" hx-push-url="true" hx-indicator="#htmx-indicator" preload="true">Special Requirements</a></li>
274274
</ul>
275275
<ul>
276276
<li><a href="/docs/testing/req" hx-get="/docs/testing/req" hx-select-oob="#primary-nav,#secondary-nav,#content-container" hx-swap="none" hx-push-url="true" hx-indicator="#htmx-indicator" preload="true">Special Requirements</a></li>
277277
</ul>
278-
""");
278+
""".ReplaceLineEndings());
279279

280280
[Fact]
281281
public void HasErrors() => Collector.Diagnostics.Should().HaveCount(0);

tests/Elastic.Markdown.Tests/Inline/InlneBaseTests.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
33
// See the LICENSE file in the project root for more information
44
using System.IO.Abstractions.TestingHelpers;
5+
using System.Runtime.InteropServices;
56
using Elastic.Markdown.IO;
67
using FluentAssertions;
78
using JetBrains.Annotations;
@@ -103,8 +104,10 @@ protected InlineTest(
103104
// ReSharper disable once VirtualMemberCallInConstructor
104105
// nasty but sub implementations won't use class state.
105106
AddToFileSystem(FileSystem);
106-
107-
var root = FileSystem.DirectoryInfo.New(Path.Combine(Paths.Root.FullName, "docs/"));
107+
var baseRootPath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
108+
? Paths.Root.FullName.Replace('\\', '/')
109+
: Paths.Root.FullName;
110+
var root = FileSystem.DirectoryInfo.New($"{baseRootPath}/docs/");
108111
FileSystem.GenerateDocSetYaml(root, globalVariables);
109112

110113
Collector = new TestDiagnosticsCollector(output);

tests/Elastic.Markdown.Tests/Mover/MoverTests.cs

+9-9
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,15 @@ public async Task RelativeLinks()
2828
linkModifications.Should().HaveCount(3);
2929

3030

31-
Path.GetRelativePath(".", linkModifications[0].SourceFile).Should().Be("testing/mover/first-page.md");
31+
Path.GetRelativePath(".", linkModifications[0].SourceFile).Should().Be(Path.Combine("testing", "mover", "first-page.md"));
3232
linkModifications[0].OldLink.Should().Be("[Link to second page](second-page.md)");
3333
linkModifications[0].NewLink.Should().Be("[Link to second page](../testing/mover/second-page.md)");
3434

35-
Path.GetRelativePath(".", linkModifications[1].SourceFile).Should().Be("testing/mover/second-page.md");
35+
Path.GetRelativePath(".", linkModifications[1].SourceFile).Should().Be(Path.Combine("testing", "mover", "second-page.md"));
3636
linkModifications[1].OldLink.Should().Be("[Link to first page](first-page.md)");
3737
linkModifications[1].NewLink.Should().Be("[Link to first page](../../new-folder/hello-world.md)");
3838

39-
Path.GetRelativePath(".", linkModifications[2].SourceFile).Should().Be("testing/mover/second-page.md");
39+
Path.GetRelativePath(".", linkModifications[2].SourceFile).Should().Be(Path.Combine("testing", "mover", "second-page.md"));
4040
linkModifications[2].OldLink.Should().Be("[Absolut link to first page](/testing/mover/first-page.md)");
4141
linkModifications[2].NewLink.Should().Be("[Absolut link to first page](/new-folder/hello-world.md)");
4242
}
@@ -56,15 +56,15 @@ public async Task MoveToFolder()
5656
var linkModifications = mover.LinkModifications[changeSet];
5757
linkModifications.Should().HaveCount(3);
5858

59-
Path.GetRelativePath(".", linkModifications[0].SourceFile).Should().Be("testing/mover/first-page.md");
59+
Path.GetRelativePath(".", linkModifications[0].SourceFile).Should().Be(Path.Combine("testing", "mover", "first-page.md"));
6060
linkModifications[0].OldLink.Should().Be("[Link to second page](second-page.md)");
6161
linkModifications[0].NewLink.Should().Be("[Link to second page](../testing/mover/second-page.md)");
6262

63-
Path.GetRelativePath(".", linkModifications[1].SourceFile).Should().Be("testing/mover/second-page.md");
63+
Path.GetRelativePath(".", linkModifications[1].SourceFile).Should().Be(Path.Combine("testing", "mover", "second-page.md"));
6464
linkModifications[1].OldLink.Should().Be("[Link to first page](first-page.md)");
6565
linkModifications[1].NewLink.Should().Be("[Link to first page](../../new-folder/first-page.md)");
6666

67-
Path.GetRelativePath(".", linkModifications[2].SourceFile).Should().Be("testing/mover/second-page.md");
67+
Path.GetRelativePath(".", linkModifications[2].SourceFile).Should().Be(Path.Combine("testing", "mover", "second-page.md"));
6868
linkModifications[2].OldLink.Should().Be("[Absolut link to first page](/testing/mover/first-page.md)");
6969
linkModifications[2].NewLink.Should().Be("[Absolut link to first page](/new-folder/first-page.md)");
7070
}
@@ -84,15 +84,15 @@ public async Task MoveFolderToFolder()
8484
var linkModifications = mover.LinkModifications[changeSet];
8585
linkModifications.Should().HaveCount(3);
8686

87-
Path.GetRelativePath(".", linkModifications[0].SourceFile).Should().Be("testing/mover/first-page.md");
87+
Path.GetRelativePath(".", linkModifications[0].SourceFile).Should().Be(Path.Combine("testing", "mover", "first-page.md"));
8888
linkModifications[0].OldLink.Should().Be("[Link to second page](second-page.md)");
8989
linkModifications[0].NewLink.Should().Be("[Link to second page](../testing/mover/second-page.md)");
9090

91-
Path.GetRelativePath(".", linkModifications[1].SourceFile).Should().Be("testing/mover/second-page.md");
91+
Path.GetRelativePath(".", linkModifications[1].SourceFile).Should().Be(Path.Combine("testing", "mover", "second-page.md"));
9292
linkModifications[1].OldLink.Should().Be("[Link to first page](first-page.md)");
9393
linkModifications[1].NewLink.Should().Be("[Link to first page](../../new-folder/first-page.md)");
9494

95-
Path.GetRelativePath(".", linkModifications[2].SourceFile).Should().Be("testing/mover/second-page.md");
95+
Path.GetRelativePath(".", linkModifications[2].SourceFile).Should().Be(Path.Combine("testing", "mover", "second-page.md"));
9696
linkModifications[2].OldLink.Should().Be("[Absolut link to first page](/testing/mover/first-page.md)");
9797
linkModifications[2].NewLink.Should().Be("[Absolut link to first page](/new-folder/first-page.md)");
9898
}

tests/authoring/Framework/MarkdownResultsAssertions.fs

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ open DiffPlex.DiffBuilder.Model
1111
open FsUnit.Xunit
1212
open JetBrains.Annotations
1313
open Xunit.Sdk
14+
open System.IO
1415

1516
[<AutoOpen>]
1617
module ResultsAssertions =
@@ -50,7 +51,7 @@ module ResultsAssertions =
5051

5152
let result =
5253
results.MarkdownResults
53-
|> Seq.tryFind (fun m -> m.File.RelativePath = file)
54+
|> Seq.tryFind (fun m -> m.File.RelativePath = (string file).Replace('/', Path.DirectorySeparatorChar))
5455

5556
match result with
5657
| None ->

0 commit comments

Comments
 (0)