Skip to content

Commit b95ccb2

Browse files
Add TypeScript linting script and update dependencies
- Introduced `lint-markdown.ts` for markdown linting with TypeScript. - Updated `lint.sh` to utilize the new TypeScript linting script. - Added necessary TypeScript type definitions in `package.json`. - Updated `yarn.lock` to resolve new dependencies. Improve error messages in markdown linting script and optimize error aggregation logic. Add new markdown linter script and update existing lint functionality - Created `find-markdown.ts` to detect markdown files and identify new files using git or GitHub Actions. - Refactored `lint-markdown.ts` to enhance front matter validation with structured validation results and improved error handling. - Introduced TypeScript configuration for streamlined development. - Added `markdownlint.d.ts` for type definitions of the markdownlint library. - Added a detailed plan for future modernization of markdown linting processes in `plan.md`. Add markdown linting script and runner with customizable rules Add comprehensive list of JavaScript files and blog posts to js_files.txt Add Zod and Zod-Matter for improved front matter parsing in Markdown files Refactor markdown linting and validation scripts - Replace `lint-markdown.js` and `lint-markdown.ts` with new linting logic using `validate-frontmatter.ts`. - Update `lint.sh` to include front matter validation and utilize `yarn` for prettier checks. - Extract front matter validation logic using `zod` for structured validation. - Introduce improvements to script organization and error handling. Enhance linting scripts to support different modes (git, gha) for markdown validation. Enhance linting scripts with debug logging and improve file handling. Refactor front matter validation functions for improved readability and performance; remove async/await where unnecessary. Refactor front matter validation: streamline error handling, improve readability, and update interface definitions. Update metadata and front matter validation for blog posts and events. Implemented `allow_long_title` feature and improved meta description handling. Remove commented-out line from frontmatter validation script Update lint script path in Makefile Remove deprecated lint script and update lint command in the new directory. Set DEBUG flag to 0 in lint.sh script
1 parent b1e8fba commit b95ccb2

File tree

19 files changed

+4349
-461
lines changed

19 files changed

+4349
-461
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ new-blog-post:
146146

147147
.PHONY: lint
148148
lint:
149-
./scripts/lint.sh
149+
./scripts/lint/lint.sh
150150

151151
.PHONY: format
152152
format:

content/blog/sam-cogan-testing-best-practices/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ draft: true
1818
# of the content of the post, which is useful for targeting search results or
1919
# social-media previews. This field is required or the build will fail the
2020
# linter test. Max length is 160 characters.
21-
meta_desc:
21+
meta_desc: Infrastructure Testing Best Practices of Sam Cogan, Puluminary & Azure MVP
2222

2323
# The meta_image appears in social-media previews and on the blog home page. A
2424
# placeholder image representing the recommended format, dimensions and aspect

content/events/build-dagger-module-pulumi-chainguard/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
# Name of the event, <= 60 characters
33
title: "Codefest: Build a Dagger Module with Pulumi & Chainguard"
4-
meta_desc: Join Pulumi, Dagger, and Chainguard for an evening of networking, coding, and talks. Learn basics with Dagger's tutorial, then showcase your module with lightning demos.
4+
meta_desc: Join Pulumi, Dagger, and Chainguard for an evening of networking, coding, and talks.
55
meta_image:
66

77
# A featured webinar will display first in the list.

content/events/connecting-securing-and-scaling-microservices-with-kubernetes/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
# Name of the event, <= 60 characters
33
title: Connecting, Securing and Scaling Microservices with Kubernetes
4+
allow_long_title: true
45
meta_desc: Listen in and you will learn practical insights on how organizations can ease growing pains and successfully take Kubernetes from test to production.
56
meta_image:
67

content/events/kubecon-meetup/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
# Name of the event, <= 60 characters
33
title: "RSVP to Meetup: Development & Data Productivity in the Age of AI"
4+
allow_long_title: true
45
meta_desc: Join Docker, Pulumi, Tailscale, and New Relic for drinks, snacks, and casual conversations.
56
meta_image:
67

js_files.txt

Lines changed: 1309 additions & 0 deletions
Large diffs are not rendered by default.

package.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,16 @@
3636
"turndown": "^7.2.0",
3737
"typedoc": "^0.25.11",
3838
"typedoc-plugin-script-inject": "^1.0.0",
39-
"typescript": "^4.9.5"
39+
"typescript": "^4.9.5",
40+
"zod": "^3.24.1",
41+
"zod-matter": "^0.1.1"
4042
},
4143
"devDependencies": {
44+
"@types/js-yaml": "^4.0.9",
45+
"@types/markdownlint": "^0.18.0",
46+
"@types/node": "^22.13.1",
4247
"husky": "^9.0.11",
43-
"prettier": "^2.6.2"
48+
"prettier": "^2.6.2",
49+
"ts-node": "^10.9.2"
4450
}
4551
}

scripts/lint.sh

Lines changed: 0 additions & 6 deletions
This file was deleted.

scripts/lint/find-markdown.ts

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import * as fs from 'fs';
2+
import * as path from 'path';
3+
import { execSync } from 'child_process';
4+
import { z } from 'zod';
5+
import { parse as parseMatter } from 'zod-matter';
6+
7+
// Define the front matter schema
8+
const frontMatterSchema = z.object({
9+
// Optional fields that affect file filtering
10+
no_edit_this_page: z.boolean().optional(),
11+
redirect_to: z.string().optional(),
12+
block_external_search_index: z.boolean().optional(),
13+
allow_long_title: z.boolean().optional(),
14+
// Other common fields
15+
title: z.string().optional(),
16+
meta_desc: z.string().optional(),
17+
meta_image: z.string().optional(),
18+
});
19+
20+
type FrontMatter = z.infer<typeof frontMatterSchema>;
21+
22+
export interface MarkdownFile {
23+
path: string;
24+
isNew: boolean;
25+
content: string;
26+
frontMatter?: FrontMatter;
27+
}
28+
29+
/**
30+
* Different modes for finding markdown files and determining if they're new
31+
*/
32+
type FinderMode = 'git' | 'gha' | 'standard';
33+
34+
const AUTO_GENERATED_HEADING_REGEX = /^> This page was automatically generated\./m;
35+
36+
/**
37+
* Parse front matter from a markdown file
38+
*/
39+
function parseFrontMatter(filePath: string): FrontMatter | undefined {
40+
try {
41+
const content = fs.readFileSync(filePath, 'utf8');
42+
43+
// Check for auto-generated heading in content
44+
if (content.match(AUTO_GENERATED_HEADING_REGEX)) {
45+
return { no_edit_this_page: true };
46+
}
47+
48+
// Parse front matter with zod-matter
49+
const { data } = parseMatter(content, frontMatterSchema);
50+
return data;
51+
} catch (e) {
52+
// If there's no front matter or it's invalid, return null
53+
return undefined;
54+
}
55+
}
56+
57+
/**
58+
* Check if a file should be excluded based on its front matter
59+
*/
60+
function shouldExcludeByFrontMatter(frontMatter: FrontMatter | undefined): boolean {
61+
if (!frontMatter) return false;
62+
63+
return frontMatter.no_edit_this_page === true ||
64+
typeof frontMatter.redirect_to === 'string' ||
65+
frontMatter.block_external_search_index === true ||
66+
!!frontMatter.allow_long_title;
67+
}
68+
69+
import { debug } from './utils';
70+
71+
/**
72+
* Get list of files modified in the latest git commit
73+
*/
74+
function getGitModifiedFiles(): Set<string> {
75+
try {
76+
debug('Getting git modified files...');
77+
const output = execSync('git diff-tree --no-commit-id --name-only -r HEAD', { encoding: 'utf8' });
78+
const set = new Set(output.split('\n').filter(Boolean));
79+
debug('Modified files:', set);
80+
return set;
81+
} catch (error) {
82+
console.warn('Failed to get git modified files, treating all files as existing');
83+
return new Set<string>();
84+
}
85+
}
86+
87+
/**
88+
* Get list of files modified in the current PR (GitHub Actions)
89+
*/
90+
function getGHAModifiedFiles(): Set<string> {
91+
const eventPath = process.env.GITHUB_EVENT_PATH;
92+
if (!eventPath) {
93+
console.warn('No GITHUB_EVENT_PATH found, treating all files as existing');
94+
return new Set<string>();
95+
}
96+
97+
try {
98+
const event = JSON.parse(fs.readFileSync(eventPath, 'utf8'));
99+
const files = event.pull_request?.changed_files || [];
100+
return new Set(files);
101+
} catch (error) {
102+
console.warn('Failed to get PR modified files, treating all files as existing');
103+
return new Set<string>();
104+
}
105+
}
106+
107+
/**
108+
* Find all markdown files in the given directory and its subdirectories.
109+
* Optionally marks files as new based on git/GHA context.
110+
*/
111+
export function findMarkdownFiles(
112+
rootDir: string,
113+
mode: FinderMode = 'standard',
114+
excludePaths: string[] = ['/content/docs/reference/pkg', '/content/registry']
115+
): MarkdownFile[] {
116+
const files: MarkdownFile[] = [];
117+
const modifiedFiles = mode === 'git' ? getGitModifiedFiles()
118+
: mode === 'gha' ? getGHAModifiedFiles()
119+
: new Set<string>();
120+
121+
function isExcluded(filePath: string): boolean {
122+
return excludePaths.some(excludePath => filePath.includes(excludePath));
123+
}
124+
125+
function searchDirectory(dir: string) {
126+
const entries = fs.readdirSync(dir);
127+
128+
for (const entry of entries) {
129+
const fullPath = path.join(dir, entry);
130+
const stat = fs.statSync(fullPath);
131+
132+
if (isExcluded(fullPath)) {
133+
continue;
134+
}
135+
136+
if (stat.isDirectory()) {
137+
searchDirectory(fullPath);
138+
continue;
139+
}
140+
141+
if (path.extname(fullPath) === '.md') {
142+
const content = fs.readFileSync(fullPath, 'utf8');
143+
const frontMatter = parseFrontMatter(fullPath);
144+
if (!shouldExcludeByFrontMatter(frontMatter)) {
145+
files.push({
146+
path: fullPath,
147+
isNew: modifiedFiles.has(fullPath),
148+
content,
149+
frontMatter
150+
});
151+
}
152+
}
153+
}
154+
}
155+
156+
searchDirectory(path.resolve(rootDir));
157+
return files;
158+
}
159+
160+
// If run directly, output found files
161+
if (require.main === module) {
162+
const mode = (process.argv[2] as FinderMode) || 'standard';
163+
const rootDir = process.argv[3] || '../../content';
164+
const files = findMarkdownFiles(rootDir, mode);
165+
console.log(JSON.stringify(files, null, 2));
166+
}

0 commit comments

Comments
 (0)