Skip to content

Commit db98340

Browse files
jabrenacursoragent
andauthored
Create jbang script for markdown validation (#157)
* Checkpoint before follow-up message * Remove markdown validator script documentation Co-authored-by: bren <bren@juanantonio.info> * Update MarkdownValidator to support custom markdown directories Co-authored-by: bren <bren@juanantonio.info> * Always print validation file path, remove verbose flag check Co-authored-by: bren <bren@juanantonio.info> * Simplify markdown validation, remove complex checks and table support Co-authored-by: bren <bren@juanantonio.info> * Add null check for HTML rendering and improve verbose logging Co-authored-by: bren <bren@juanantonio.info> * Reorder GitHub workflow jobs and update generation steps Co-authored-by: bren <bren@juanantonio.info> * Remove unnecessary dependency on markdown validation in Maven workflow Co-authored-by: bren <bren@juanantonio.info> * Move MarkdownValidator script to .github/scripts directory Co-authored-by: bren <bren@juanantonio.info> * Remove unnecessary dependency from markdown validation workflow Co-authored-by: bren <bren@juanantonio.info> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
1 parent fb4ab6e commit db98340

2 files changed

Lines changed: 208 additions & 0 deletions

File tree

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
///usr/bin/env jbang "$0" "$@" ; exit $?
2+
3+
//DEPS info.picocli:picocli:4.7.5
4+
//DEPS org.commonmark:commonmark:0.21.0
5+
6+
import org.commonmark.node.*;
7+
import org.commonmark.parser.Parser;
8+
import org.commonmark.renderer.html.HtmlRenderer;
9+
import picocli.CommandLine;
10+
import picocli.CommandLine.Command;
11+
import picocli.CommandLine.Option;
12+
import picocli.CommandLine.Parameters;
13+
14+
import java.io.IOException;
15+
import java.nio.file.*;
16+
import java.util.*;
17+
import java.util.concurrent.Callable;
18+
import java.util.stream.Stream;
19+
20+
@Command(name = "markdown-validator",
21+
mixinStandardHelpOptions = true,
22+
version = "1.0",
23+
description = "Validates markdown files from specified directories")
24+
public class MarkdownValidator implements Callable<Integer> {
25+
26+
@Option(names = {"-v", "--verbose"}, description = "Enable verbose output")
27+
boolean verbose;
28+
29+
@Option(names = {"-f", "--fail-fast"}, description = "Stop on first validation error")
30+
boolean failFast;
31+
32+
@Option(names = {"-d", "--directories"},
33+
description = "Directories to scan for markdown files (default: .cursor/rules,.cursor/rules/templates)",
34+
split = ",")
35+
List<String> targetDirectories = List.of(".cursor/rules", ".cursor/rules/templates");
36+
37+
@Parameters(description = "Root directory to scan (default: current directory)")
38+
String rootDir = ".";
39+
40+
private static final List<String> MARKDOWN_EXTENSIONS = List.of(".md", ".mdc");
41+
42+
private final Parser parser;
43+
private final HtmlRenderer renderer;
44+
private final List<ValidationError> errors = new ArrayList<>();
45+
46+
public MarkdownValidator() {
47+
this.parser = Parser.builder().build();
48+
this.renderer = HtmlRenderer.builder().build();
49+
}
50+
51+
public static void main(String... args) {
52+
int exitCode = new CommandLine(new MarkdownValidator()).execute(args);
53+
System.exit(exitCode);
54+
}
55+
56+
@Override
57+
public Integer call() throws Exception {
58+
System.out.println("🔍 Starting markdown validation...");
59+
60+
Path root = Paths.get(rootDir);
61+
if (!Files.exists(root)) {
62+
System.err.println("❌ Root directory does not exist: " + root);
63+
return 1;
64+
}
65+
66+
List<Path> markdownFiles = findMarkdownFiles(root);
67+
if (markdownFiles.isEmpty()) {
68+
System.out.println("⚠️ No markdown files found in target directories");
69+
return 0;
70+
}
71+
72+
System.out.printf("📄 Found %d markdown files to validate\n", markdownFiles.size());
73+
74+
for (Path file : markdownFiles) {
75+
validateFile(file);
76+
if (failFast && !errors.isEmpty()) {
77+
break;
78+
}
79+
}
80+
81+
printResults();
82+
return errors.isEmpty() ? 0 : 1;
83+
}
84+
85+
private List<Path> findMarkdownFiles(Path root) throws IOException {
86+
List<Path> files = new ArrayList<>();
87+
88+
for (String targetDir : targetDirectories) {
89+
Path dir = root.resolve(targetDir);
90+
if (Files.exists(dir) && Files.isDirectory(dir)) {
91+
try (Stream<Path> paths = Files.walk(dir)) {
92+
paths.filter(Files::isRegularFile)
93+
.filter(this::isMarkdownFile)
94+
.forEach(files::add);
95+
}
96+
} else if (verbose) {
97+
System.out.printf("⚠️ Directory not found: %s\n", dir);
98+
}
99+
}
100+
101+
return files;
102+
}
103+
104+
private boolean isMarkdownFile(Path file) {
105+
String fileName = file.getFileName().toString().toLowerCase();
106+
return MARKDOWN_EXTENSIONS.stream().anyMatch(fileName::endsWith);
107+
}
108+
109+
private void validateFile(Path file) {
110+
System.out.printf("🔍 Validating: %s\n", file);
111+
112+
try {
113+
String content = Files.readString(file);
114+
validateContent(file, content);
115+
} catch (IOException e) {
116+
addError(file, 0, "Failed to read file: " + e.getMessage());
117+
}
118+
}
119+
120+
private void validateContent(Path file, String content) {
121+
try {
122+
// Parse markdown content
123+
Node document = parser.parse(content);
124+
125+
// Try to render to HTML to validate structure
126+
String html = renderer.render(document);
127+
128+
// Assert that HTML output is not null
129+
if (html == null) {
130+
addError(file, 0, "HTML rendering produced null output");
131+
return;
132+
}
133+
134+
if (verbose) {
135+
System.out.printf("✅ Successfully parsed: %s (%d characters, HTML: %d characters)\n",
136+
file.getFileName(), content.length(), html.length());
137+
}
138+
} catch (Exception e) {
139+
addError(file, 0, "Failed to parse markdown: " + e.getMessage());
140+
}
141+
}
142+
143+
private void addError(Path file, int lineNumber, String message) {
144+
errors.add(new ValidationError(file, lineNumber, message));
145+
if (verbose) {
146+
System.out.printf("❌ %s:%d - %s\n", file, lineNumber, message);
147+
}
148+
}
149+
150+
private void printResults() {
151+
System.out.println("\n" + "=".repeat(60));
152+
153+
if (errors.isEmpty()) {
154+
System.out.println("✅ All markdown files are valid!");
155+
} else {
156+
System.out.printf("❌ Found %d validation errors:\n\n", errors.size());
157+
158+
Map<Path, List<ValidationError>> errorsByFile = new LinkedHashMap<>();
159+
for (ValidationError error : errors) {
160+
errorsByFile.computeIfAbsent(error.file, k -> new ArrayList<>()).add(error);
161+
}
162+
163+
for (Map.Entry<Path, List<ValidationError>> entry : errorsByFile.entrySet()) {
164+
System.out.printf("📄 %s:\n", entry.getKey());
165+
for (ValidationError error : entry.getValue()) {
166+
if (error.lineNumber > 0) {
167+
System.out.printf(" Line %d: %s\n", error.lineNumber, error.message);
168+
} else {
169+
System.out.printf(" %s\n", error.message);
170+
}
171+
}
172+
System.out.println();
173+
}
174+
}
175+
176+
System.out.println("=".repeat(60));
177+
}
178+
179+
private static class ValidationError {
180+
final Path file;
181+
final int lineNumber;
182+
final String message;
183+
184+
ValidationError(Path file, int lineNumber, String message) {
185+
this.file = file;
186+
this.lineNumber = lineNumber;
187+
this.message = message;
188+
}
189+
}
190+
}

.github/workflows/maven.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,24 @@ jobs:
1717
- name: Generate Cursor Rules
1818
run: cd generator && ./mvnw --batch-mode --no-transfer-progress verify --file pom.xml
1919

20+
validate-markdown:
21+
name: Validate Markdown Files
22+
runs-on: ubuntu-latest
23+
steps:
24+
- uses: actions/checkout@v4
25+
with:
26+
submodules: true # Fetches all submodules
27+
- uses: actions/setup-java@v4
28+
with:
29+
distribution: 'graalvm' # See 'Supported distributions' for available options
30+
java-version: '24'
31+
- name: Install JBang
32+
run: |
33+
curl -Ls https://sh.jbang.dev | bash -s - app setup
34+
echo "$HOME/.jbang/bin" >> $GITHUB_PATH
35+
- name: Validate Markdown Files
36+
run: jbang .github/scripts/MarkdownValidator.java --verbose .
37+
2038
examples:
2139
name: Build Examples
2240
runs-on: ubuntu-latest

0 commit comments

Comments
 (0)