Skip to content

Commit 37ef7cf

Browse files
committed
Fix Schema.org validation errors and optimize structured data
Fixed critical validation errors: - Remove invalid supportedLanguage property from SoftwareApplication (6 errors fixed) - Remove hardcoded aggregateRating from product schema - Remove invalid softwareRequirements from BlogPosting schema Improved schema types for better semantic accuracy: - Change Article to TechArticle for technical documentation pages - Change Course to HowTo for self-paced tutorials (more semantically accurate) - Create new howto-entity.html for HowTo schema implementation Fixed technical issues: - Fix date handling with proper fallbacks for invalid dates (0001-01-01) - Fix image URL concatenation to avoid double slashes - Apply fixes to article, blog, and product entity collectors These changes ensure: - All validation errors are resolved - Schema types accurately represent content - Compliance with 2025 Google Search Central guidelines - Better AI/LLM comprehension through semantic accuracy
1 parent e420110 commit 37ef7cf

File tree

8 files changed

+281
-47
lines changed

8 files changed

+281
-47
lines changed

layouts/partials/schema/collectors/article-entity.html

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{{/* Returns Article entity for @graph */}}
1+
{{/* Returns TechArticle entity for @graph */}}
22

33
{{/* Get content for analysis */}}
44
{{ $content := .Content | plainify }}
@@ -7,22 +7,37 @@
77
{{ $wordCount = 500 }}
88
{{ end }}
99

10-
{{/* Build Article entity */}}
10+
{{/* Build TechArticle entity */}}
1111
{{ $schema := dict
12-
"@type" "Article"
12+
"@type" "TechArticle"
1313
"@id" "#main-content"
1414
"headline" .Title
1515
"url" .Permalink
1616
"wordCount" $wordCount
1717
"inLanguage" "en-US"
1818
}}
1919

20-
{{/* Add dates */}}
20+
{{/* Add dates with proper fallback handling */}}
2121
{{ $publishDate := .PublishDate }}
2222
{{ $modifiedDate := .Lastmod }}
23-
{{ if not $modifiedDate }}
24-
{{ $modifiedDate = $publishDate }}
23+
24+
{{/* Fix invalid dates */}}
25+
{{ if or (not $publishDate) (eq ($publishDate.Format "2006-01-02") "0001-01-01") }}
26+
{{ if and .GitInfo .GitInfo.AuthorDate }}
27+
{{ $publishDate = .GitInfo.AuthorDate }}
28+
{{ else }}
29+
{{ $publishDate = now }}
30+
{{ end }}
31+
{{ end }}
32+
33+
{{ if or (not $modifiedDate) (eq ($modifiedDate.Format "2006-01-02") "0001-01-01") }}
34+
{{ if and .GitInfo .GitInfo.AuthorDate }}
35+
{{ $modifiedDate = .GitInfo.AuthorDate }}
36+
{{ else }}
37+
{{ $modifiedDate = $publishDate }}
38+
{{ end }}
2539
{{ end }}
40+
2641
{{ $schema = merge $schema (dict
2742
"datePublished" ($publishDate.Format "2006-01-02T15:04:05Z07:00")
2843
"dateModified" ($modifiedDate.Format "2006-01-02T15:04:05Z07:00")
@@ -36,7 +51,16 @@
3651
{{/* Add image */}}
3752
{{ $image := "" }}
3853
{{ if .Params.meta_image }}
39-
{{ $image = printf "%s%s" .Permalink .Params.meta_image }}
54+
{{/* Fix URL concatenation to avoid double slashes */}}
55+
{{ $baseURL := .Permalink }}
56+
{{ if not (strings.HasSuffix $baseURL "/") }}
57+
{{ $baseURL = printf "%s/" $baseURL }}
58+
{{ end }}
59+
{{ $imagePath := .Params.meta_image }}
60+
{{ if strings.HasPrefix $imagePath "/" }}
61+
{{ $imagePath = strings.TrimPrefix "/" $imagePath }}
62+
{{ end }}
63+
{{ $image = printf "%s%s" $baseURL $imagePath }}
4064
{{ else if in .RelPermalink "/docs/clouds/aws/" }}
4165
{{ $image = "https://www.pulumi.com/images/docs/meta-images/docs-clouds-aws-meta-image.png" }}
4266
{{ else }}

layouts/partials/schema/collectors/blog-entity.html

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,27 @@
1818
"articleSection" "Engineering Blog"
1919
}}
2020

21-
{{/* Add dates */}}
21+
{{/* Add dates with proper fallback handling */}}
2222
{{ $publishDate := .PublishDate }}
2323
{{ $modifiedDate := .Lastmod }}
24-
{{ if not $modifiedDate }}
25-
{{ $modifiedDate = $publishDate }}
24+
25+
{{/* Fix invalid dates */}}
26+
{{ if or (not $publishDate) (eq ($publishDate.Format "2006-01-02") "0001-01-01") }}
27+
{{ if and .GitInfo .GitInfo.AuthorDate }}
28+
{{ $publishDate = .GitInfo.AuthorDate }}
29+
{{ else }}
30+
{{ $publishDate = now }}
31+
{{ end }}
32+
{{ end }}
33+
34+
{{ if or (not $modifiedDate) (eq ($modifiedDate.Format "2006-01-02") "0001-01-01") }}
35+
{{ if and .GitInfo .GitInfo.AuthorDate }}
36+
{{ $modifiedDate = .GitInfo.AuthorDate }}
37+
{{ else }}
38+
{{ $modifiedDate = $publishDate }}
39+
{{ end }}
2640
{{ end }}
41+
2742
{{ $schema = merge $schema (dict
2843
"datePublished" ($publishDate.Format "2006-01-02T15:04:05Z07:00")
2944
"dateModified" ($modifiedDate.Format "2006-01-02T15:04:05Z07:00")
@@ -37,7 +52,16 @@
3752
{{/* Add image */}}
3853
{{ $image := "" }}
3954
{{ if .Params.meta_image }}
40-
{{ $image = printf "%s%s" .Permalink .Params.meta_image }}
55+
{{/* Fix URL concatenation to avoid double slashes */}}
56+
{{ $baseURL := .Permalink }}
57+
{{ if not (strings.HasSuffix $baseURL "/") }}
58+
{{ $baseURL = printf "%s/" $baseURL }}
59+
{{ end }}
60+
{{ $imagePath := .Params.meta_image }}
61+
{{ if strings.HasPrefix $imagePath "/" }}
62+
{{ $imagePath = strings.TrimPrefix "/" $imagePath }}
63+
{{ end }}
64+
{{ $image = printf "%s%s" $baseURL $imagePath }}
4165
{{ else }}
4266
{{ $image = "https://www.pulumi.com/images/docs/meta-images/docs-meta.png" }}
4367
{{ end }}
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
{{/* Returns HowTo entity for tutorials in @graph */}}
2+
3+
{{/* Get content for analysis */}}
4+
{{ $content := .Content | plainify }}
5+
{{ $wordCount := $content | countwords }}
6+
{{ if not $wordCount }}
7+
{{ $wordCount = 500 }}
8+
{{ end }}
9+
10+
{{/* Build HowTo entity */}}
11+
{{ $schema := dict
12+
"@type" "HowTo"
13+
"@id" "#main-content"
14+
"name" .Title
15+
"url" .Permalink
16+
"inLanguage" "en-US"
17+
}}
18+
19+
{{/* Add description */}}
20+
{{ with (or .Params.meta_desc .Summary (printf "Learn how to %s with Pulumi" .Title)) }}
21+
{{ $schema = merge $schema (dict "description" .) }}
22+
{{ end }}
23+
24+
{{/* Generate generic steps for tutorials */}}
25+
{{/* Since tutorials may not have explicit numbered steps, we create high-level steps */}}
26+
{{ $steps := slice }}
27+
28+
{{/* Step 1: Prerequisites */}}
29+
{{ $steps = $steps | append (dict
30+
"@type" "HowToStep"
31+
"name" "Prerequisites"
32+
"text" "Install Pulumi CLI and configure your cloud provider credentials"
33+
"position" 1
34+
) }}
35+
36+
{{/* Step 2: Create Project */}}
37+
{{ if in $content "pulumi new" }}
38+
{{ $steps = $steps | append (dict
39+
"@type" "HowToStep"
40+
"name" "Create Pulumi Project"
41+
"text" "Initialize a new Pulumi project with the appropriate template"
42+
"position" 2
43+
) }}
44+
{{ end }}
45+
46+
{{/* Step 3: Write Infrastructure Code */}}
47+
{{ $steps = $steps | append (dict
48+
"@type" "HowToStep"
49+
"name" "Define Infrastructure"
50+
"text" "Write infrastructure as code using your preferred programming language"
51+
"position" 3
52+
) }}
53+
54+
{{/* Step 4: Deploy */}}
55+
{{ if in $content "pulumi up" }}
56+
{{ $steps = $steps | append (dict
57+
"@type" "HowToStep"
58+
"name" "Deploy Infrastructure"
59+
"text" "Deploy your infrastructure using pulumi up command"
60+
"position" 4
61+
) }}
62+
{{ end }}
63+
64+
{{/* Step 5: Verify */}}
65+
{{ $steps = $steps | append (dict
66+
"@type" "HowToStep"
67+
"name" "Verify Deployment"
68+
"text" "Verify your infrastructure was created successfully"
69+
"position" 5
70+
) }}
71+
72+
{{ $schema = merge $schema (dict "step" $steps) }}
73+
74+
{{/* Calculate total time */}}
75+
{{ $totalTime := "" }}
76+
{{ with .Params.estimated_time }}
77+
{{ $totalTime = printf "PT%dM" . }}
78+
{{ else with .Params.duration }}
79+
{{ $totalTime = . }}
80+
{{ else }}
81+
{{/* Estimate based on word count */}}
82+
{{ $minutes := div $wordCount 200 }}
83+
{{ if lt $minutes 10 }}{{ $minutes = 10 }}{{ end }}
84+
{{ if gt $minutes 60 }}{{ $minutes = 60 }}{{ end }}
85+
{{ $totalTime = printf "PT%dM" $minutes }}
86+
{{ end }}
87+
{{ $schema = merge $schema (dict "totalTime" $totalTime) }}
88+
89+
{{/* Add tools/prerequisites */}}
90+
{{ $tools := slice }}
91+
92+
{{/* Auto-detect common tools */}}
93+
{{ if or (in $content "Pulumi CLI") (in $content "pulumi new") }}
94+
{{ $tools = $tools | append (dict
95+
"@type" "HowToTool"
96+
"name" "Pulumi CLI"
97+
) }}
98+
{{ end }}
99+
100+
{{ if in $content "AWS account" }}
101+
{{ $tools = $tools | append (dict
102+
"@type" "HowToTool"
103+
"name" "AWS Account"
104+
) }}
105+
{{ end }}
106+
107+
{{ if in $content "Azure account" }}
108+
{{ $tools = $tools | append (dict
109+
"@type" "HowToTool"
110+
"name" "Azure Account"
111+
) }}
112+
{{ end }}
113+
114+
{{ if in $content "Google Cloud account" }}
115+
{{ $tools = $tools | append (dict
116+
"@type" "HowToTool"
117+
"name" "Google Cloud Account"
118+
) }}
119+
{{ end }}
120+
121+
{{ if or (in $content "Node.js") (in $content "npm") }}
122+
{{ $tools = $tools | append (dict
123+
"@type" "HowToTool"
124+
"name" "Node.js"
125+
) }}
126+
{{ end }}
127+
128+
{{ if in $content "Python" }}
129+
{{ $tools = $tools | append (dict
130+
"@type" "HowToTool"
131+
"name" "Python"
132+
) }}
133+
{{ end }}
134+
135+
{{ if $tools }}
136+
{{ $schema = merge $schema (dict "tool" $tools) }}
137+
{{ end }}
138+
139+
{{/* Add image */}}
140+
{{ $image := "" }}
141+
{{ if .Params.meta_image }}
142+
{{/* Fix URL concatenation to avoid double slashes */}}
143+
{{ $baseURL := .Permalink }}
144+
{{ if not (strings.HasSuffix $baseURL "/") }}
145+
{{ $baseURL = printf "%s/" $baseURL }}
146+
{{ end }}
147+
{{ $imagePath := .Params.meta_image }}
148+
{{ if strings.HasPrefix $imagePath "/" }}
149+
{{ $imagePath = strings.TrimPrefix "/" $imagePath }}
150+
{{ end }}
151+
{{ $image = printf "%s%s" $baseURL $imagePath }}
152+
{{ else }}
153+
{{ $image = "https://www.pulumi.com/images/docs/meta-images/docs-meta.png" }}
154+
{{ end }}
155+
{{ $schema = merge $schema (dict "image" $image) }}
156+
157+
{{/* Add dates */}}
158+
{{ $publishDate := .PublishDate }}
159+
{{ $modifiedDate := .Lastmod }}
160+
161+
{{/* Fix invalid dates */}}
162+
{{ if or (not $publishDate) (eq ($publishDate.Format "2006-01-02") "0001-01-01") }}
163+
{{ if and .GitInfo .GitInfo.AuthorDate }}
164+
{{ $publishDate = .GitInfo.AuthorDate }}
165+
{{ else }}
166+
{{ $publishDate = now }}
167+
{{ end }}
168+
{{ end }}
169+
170+
{{ if or (not $modifiedDate) (eq ($modifiedDate.Format "2006-01-02") "0001-01-01") }}
171+
{{ if and .GitInfo .GitInfo.AuthorDate }}
172+
{{ $modifiedDate = .GitInfo.AuthorDate }}
173+
{{ else }}
174+
{{ $modifiedDate = $publishDate }}
175+
{{ end }}
176+
{{ end }}
177+
178+
{{ $schema = merge $schema (dict
179+
"datePublished" ($publishDate.Format "2006-01-02T15:04:05Z07:00")
180+
"dateModified" ($modifiedDate.Format "2006-01-02T15:04:05Z07:00")
181+
) }}
182+
183+
{{/* Add keywords */}}
184+
{{ $keywords := "infrastructure as code, cloud, devops, tutorial, pulumi" }}
185+
{{ with .Params.tags }}
186+
{{ $keywords = delimit . ", " }}
187+
{{ end }}
188+
{{ $schema = merge $schema (dict "keywords" $keywords) }}
189+
190+
{{/* Add supply/yield (what the tutorial produces) */}}
191+
{{ $titleLower := lower .Title }}
192+
{{ if in $titleLower "creating resources" }}
193+
{{ $schema = merge $schema (dict "yield" "Cloud infrastructure resources deployed and managed through code") }}
194+
{{ else if in $titleLower "import" }}
195+
{{ $schema = merge $schema (dict "yield" "Existing infrastructure imported into Pulumi management") }}
196+
{{ else }}
197+
{{ $schema = merge $schema (dict "yield" "Infrastructure deployed using Pulumi") }}
198+
{{ end }}
199+
200+
{{ return $schema }}

layouts/partials/schema/collectors/main-entity.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
{{ $entity = partial "schema/collectors/blog-entity.html" . }}
88

99
{{ else if eq .Type "tutorials" }}
10-
{{ $entity = partial "schema/collectors/course-entity.html" . }}
10+
{{ $entity = partial "schema/collectors/howto-entity.html" . }}
1111

1212
{{ else if eq .Type "docs" }}
1313
{{ $entity = partial "schema/collectors/article-entity.html" . }}

layouts/partials/schema/collectors/product-entity.html

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,16 @@
3333
{{/* Add image */}}
3434
{{ $image := "" }}
3535
{{ if .Params.meta_image }}
36-
{{ $image = printf "%s%s" .Permalink .Params.meta_image }}
36+
{{/* Fix URL concatenation to avoid double slashes */}}
37+
{{ $baseURL := .Permalink }}
38+
{{ if not (strings.HasSuffix $baseURL "/") }}
39+
{{ $baseURL = printf "%s/" $baseURL }}
40+
{{ end }}
41+
{{ $imagePath := .Params.meta_image }}
42+
{{ if strings.HasPrefix $imagePath "/" }}
43+
{{ $imagePath = strings.TrimPrefix "/" $imagePath }}
44+
{{ end }}
45+
{{ $image = printf "%s%s" $baseURL $imagePath }}
3746
{{ else }}
3847
{{ $image = "https://www.pulumi.com/images/docs/meta-images/docs-meta.png" }}
3948
{{ end }}
@@ -80,21 +89,13 @@
8089
) }}
8190
{{ end }}
8291

83-
{{/* Add aggregateRating if available */}}
84-
{{ $schema = merge $schema (dict "aggregateRating" (dict
85-
"@type" "AggregateRating"
86-
"ratingValue" "4.8"
87-
"ratingCount" "500"
88-
"bestRating" "5"
89-
"worstRating" "1"
90-
)) }}
92+
{{/* Only add aggregateRating if real data is available */}}
93+
{{/* Removed hardcoded fake ratings - should come from actual user reviews */}}
9194

9295
{{/* Add software version */}}
9396
{{ $schema = merge $schema (dict "softwareVersion" "1.0") }}
9497

95-
{{/* Add supported platforms */}}
96-
{{ $schema = merge $schema (dict
97-
"supportedLanguage" (slice "TypeScript" "Python" "Go" "C#" "Java" "YAML")
98-
) }}
98+
{{/* Note: Programming languages are already covered in featureList as "Multi-language support" */}}
99+
{{/* supportedLanguage is not a valid property for SoftwareApplication schema type */}}
99100

100101
{{ return $schema }}

layouts/partials/schema/content/article.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{{/* Article Schema for Documentation - Optimized for AI and technical content */}}
1+
{{/* TechArticle Schema for Documentation - Optimized for AI and technical content */}}
22

33
{{ $image := .Params.meta_image | default "/images/docs/meta-images/docs-meta.png" }}
44
{{ if not (hasPrefix $image "http") }}
@@ -89,7 +89,7 @@
8989

9090
{{ $schema := dict
9191
"@context" "https://schema.org"
92-
"@type" "Article"
92+
"@type" "TechArticle"
9393
"@id" (printf "%s#article" .Permalink)
9494
"headline" (or .Params.h1 .Title)
9595
"description" (or .Params.meta_desc .Summary (printf "Learn about %s with Pulumi Infrastructure as Code" .Title))
@@ -178,7 +178,7 @@
178178
{{/* Add learning resource schema for tutorials/guides */}}
179179
{{ if or (in .Section "tutorials") (in .Section "guides") (in .Params.tags "tutorial") }}
180180
{{ $schema = merge $schema (dict
181-
"@type" (slice "Article" "LearningResource")
181+
"@type" (slice "TechArticle" "LearningResource")
182182
"learningResourceType" "Tutorial"
183183
"educationalLevel" $level
184184
) }}

0 commit comments

Comments
 (0)