Skip to content

Commit 153c7e8

Browse files
committed
Getting Swift test working on Windows
1 parent bdce29b commit 153c7e8

5 files changed

Lines changed: 67 additions & 21 deletions

File tree

.github/workflows/build.yml

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,7 @@ permissions:
1212

1313
jobs:
1414
build:
15-
strategy:
16-
matrix:
17-
os: [ubuntu-latest, windows-latest]
18-
runs-on: ${{ matrix.os }}
15+
runs-on: ubuntu-latest
1916
outputs:
2017
artifact-path: ./better-code/book
2118
steps:
@@ -37,19 +34,12 @@ jobs:
3734
restore-keys: |
3835
${{ runner.os }}-cargo-
3936
40-
- name: Install mdBook and plugins (Linux)
41-
if: runner.os != 'Windows'
37+
- name: Install mdBook and plugins
4238
run: |
4339
chmod +x scripts/install-tools.sh
4440
./scripts/install-tools.sh
4541
46-
- name: Install mdBook and plugins (Windows)
47-
if: runner.os == 'Windows'
48-
run: .\scripts\install-tools.ps1
49-
50-
# Install Swift for testing Swift code examples in the book (Ubuntu only; setup-swift does not support Windows)
5142
- name: Setup Swift
52-
if: runner.os != 'Windows'
5343
uses: swift-actions/setup-swift@v2
5444

5545
- name: Build with mdBook
@@ -58,6 +48,6 @@ jobs:
5848
- name: Upload build artifact
5949
uses: actions/upload-artifact@v4
6050
with:
61-
name: mdbook-build-${{ matrix.os }}
51+
name: mdbook-build-ubuntu-latest
6252
path: ./better-code/book
6353
retention-days: 1

better-code/book.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,5 @@ swift = "#"
2929
# supported-platforms: Only run on these platforms (python3 sys.platform values); omitted => run on all
3030
[output.swift-test]
3131
command = "python3 ../../../scripts/mdbook-swift-test.py"
32-
# Note: setup-swift@v3 does not support Windows
33-
supported-platforms = ["linux", "darwin"]
32+
# win32: runs when swiftc is on PATH; otherwise the script skips (e.g. GitHub Actions windows-latest)
33+
supported-platforms = ["linux", "darwin", "win32"]

better-code/src/SUMMARY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
- [Contracts](./chapter-2-contracts.md)
55
- [Errors](./chapter-3-errors.md)
66
- [Algorithms](./chapter-4-algorithms.md)
7-
- [Types](./chapter-4-types.md)
7+
- [Types](./chapter-5-types.md)
File renamed without changes.

scripts/mdbook-swift-test.py

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,20 @@
1111
Usage (book.toml):
1212
[output.swift-test]
1313
command = "python3 path/to/mdbook-swift-test.py"
14-
supported-platforms = ["linux", "darwin"] # optional; sys.platform values to run on
14+
supported-platforms = ["linux", "darwin", "win32"] # optional; sys.platform values to run on
15+
16+
Do not run this script directly in a terminal: mdbook pipes JSON into stdin.
17+
Use `mdbook build` from the book directory (or `mdbook-swift-test.py --help`).
18+
19+
Debugging (see what mdBook sent):
20+
Set MDBOOK_SWIFT_TEST_DUMP_CONTEXT to a file path, then run `mdbook build`.
21+
The full renderer context JSON is written there; search for "content" or ```swift.
1522
"""
1623

1724
import json
1825
import os
1926
import re
27+
import shutil
2028
import subprocess
2129
import sys
2230
import tempfile
@@ -79,11 +87,19 @@ def unhide_line(line: str) -> str:
7987

8088
def extract_blocks(content: str, path: str) -> Iterator[CodeBlock]:
8189
"""Yields Swift code blocks from markdown content."""
90+
# mdBook may pass CRLF on Windows; the fence regex only matches LF after ```info.
91+
if "\r" in content:
92+
content = content.replace("\r\n", "\n").replace("\r", "\n")
8293
for m in CODE_BLOCK.finditer(content):
8394
attrs = Attributes.parse(m.group(1))
8495
if attrs.language == "swift":
8596
start_line = content[: m.start()].count("\n") + 2
86-
yield CodeBlock(m.group(2).rstrip("\n"), start_line, path, attrs)
97+
yield CodeBlock(m.group(2).rstrip("\n\r"), start_line, path, attrs)
98+
99+
100+
def _book_roots(book: dict) -> list:
101+
"""Top-level spine entries: mdBook 0.5+ uses `items`, older versions use `sections`."""
102+
return book.get("items") or book.get("sections", [])
87103

88104

89105
def extract_from_chapter(item: dict) -> Iterator[CodeBlock]:
@@ -126,7 +142,7 @@ def compile_swift(source: str) -> TestResult:
126142

127143
def prepare_source(code: str) -> str:
128144
"""A code block's source code with hidden lines revealed and placeholder bodies expanded."""
129-
unhidden = "\n".join(unhide_line(line) for line in code.split("\n"))
145+
unhidden = "\n".join(unhide_line(line) for line in code.splitlines())
130146
return re.sub(r"\{\s*\.\.\.\s*\}", "{ fatalError() }", unhidden)
131147

132148

@@ -135,8 +151,40 @@ def line(char: str, count: int = 60) -> str:
135151
return char * count
136152

137153

138-
def main():
154+
def _usage_stderr() -> None:
155+
print(
156+
"mdbook-swift-test.py is an mdbook backend: it reads the book JSON from stdin.\n"
157+
"Run from the book directory:\n"
158+
" mdbook build\n"
159+
"mdbook will invoke this command when [output.swift-test] is set in book.toml.\n"
160+
"On Windows, use the same `command` as in book.toml (e.g. python path\\to\\mdbook-swift-test.py).",
161+
file=sys.stderr,
162+
)
163+
164+
165+
def main() -> None:
166+
if len(sys.argv) > 1 and sys.argv[1] in ("-h", "--help"):
167+
print(__doc__.strip(), file=sys.stderr)
168+
sys.exit(0)
169+
170+
# Interactive terminal has no piped JSON; json.load would block until EOF (Ctrl+C).
171+
if sys.stdin.isatty():
172+
_usage_stderr()
173+
sys.exit(2)
174+
139175
context = json.load(sys.stdin)
176+
177+
dump_path = os.environ.get("MDBOOK_SWIFT_TEST_DUMP_CONTEXT", "").strip()
178+
if dump_path:
179+
# ensure_ascii=True so lone surrogates from mdBook/Rust still serialize (UTF-8 cannot store them).
180+
with open(dump_path, "w", encoding="utf-8") as df:
181+
json.dump(context, df, indent=2, ensure_ascii=True)
182+
print(
183+
f"Wrote mdBook renderer context to {dump_path!r} "
184+
"(MDBOOK_SWIFT_TEST_DUMP_CONTEXT)",
185+
file=sys.stderr,
186+
)
187+
140188
cfg = (
141189
context.get("config", {})
142190
.get("output", {})
@@ -150,9 +198,17 @@ def main():
150198
)
151199
sys.exit(0)
152200

201+
# CI and many dev machines have no Swift on Windows; skip instead of failing every block.
202+
if sys.platform == "win32" and not shutil.which("swiftc"):
203+
print(
204+
"Skipping Swift code example tests (swiftc not on PATH; install Swift for Windows to enable)",
205+
file=sys.stderr,
206+
)
207+
sys.exit(0)
208+
153209
book = context.get("book", {})
154210
blocks = list(
155-
chain.from_iterable(extract_from_chapter(s) for s in book.get("sections", []))
211+
chain.from_iterable(extract_from_chapter(s) for s in _book_roots(book))
156212
)
157213

158214
results = [

0 commit comments

Comments
 (0)