From 362b5060c2921e9669c5225dc94ba15e7c990a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Otto=20Boly=C3=B3s?= Date: Thu, 4 Jun 2026 14:39:25 +0200 Subject: [PATCH 1/2] chore(repo): normalize MTConnect.NET.sln to CRLF line endings Add `*.sln text eol=crlf` to `.gitattributes` so Git always delivers CRLF line endings in the working tree for Visual Studio solution files. Visual Studio for Windows generates a dirty diff each time it opens an LF-terminated `.sln`; pinning CRLF in the attributes eliminates that noise for Windows contributors without affecting the normalized LF blob stored in the object database. --- .gitattributes | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitattributes b/.gitattributes index bdb0cabc8..956bc5edf 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,12 @@ # Auto detect text files and perform LF normalization * text=auto +# Visual Studio solution files are canonically CRLF. Pin the line-ending +# so cross-platform edits (mixed-OS contributors, scripted regenerators +# that emit LF, editors that drop the trailing CR on save) do not drift +# the file off the format Visual Studio + MSBuild expect. +*.sln text eol=crlf + # Custom for Visual Studio *.cs diff=csharp From 323339b9cf74c721da6b62f5d665f9009e0eb990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Otto=20Boly=C3=B3s?= Date: Thu, 4 Jun 2026 14:40:47 +0200 Subject: [PATCH 2/2] docs(config): derive absolute og:image URL from DOCS_CANONICAL_URL LinkedIn, Slack unfurl, and the classic X crawler reject root-relative paths for social-card images; an absolute https:// URL is required for unfurl rendering on those venues. Replace the hard-coded '/logo.png' value in both og:image and twitter:image with a URL derived from the DOCS_CANONICAL_URL environment variable, defaulting to the GitHub Pages host for this repository. Operators deploying to a custom domain override the default via DOCS_CANONICAL_URL at workflow level. The landing-page house-style test gains two StartsWith("https://") assertions for og:image and twitter:image so a future regression back to a root-relative path fails the E2E suite. --- docs/.vitepress/config.ts | 12 ++++++++++-- tests/MTConnect.NET-Docs-Tests/RouteCheckTests.cs | 6 ++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 7e59f6be6..233313d10 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -2,6 +2,14 @@ import { defineConfig } from 'vitepress'; import { withMermaid } from 'vitepress-plugin-mermaid'; import { apiSidebar, referenceSidebar } from './sidebar'; +// Absolute base URL for the deployed site. Social-card crawlers (LinkedIn, +// Slack unfurl, the classic X crawler) require absolute `https://…` URLs in +// og:image / twitter:image; root-relative paths are silently discarded. +// Override at workflow level via DOCS_CANONICAL_URL for custom domains. +const docsCanonicalUrl = + process.env.DOCS_CANONICAL_URL ?? 'https://trakhound.github.io/MTConnect.NET'; +const ogImage = `${docsCanonicalUrl}/logo.png`; + /** * VitePress config for the MTConnect.NET documentation site. * @@ -62,7 +70,7 @@ export default withMermaid( 'The .NET implementation of the MTConnect Standard — 100% public-surface API coverage, 100% MTConnect Standard compliance.', }, ], - ['meta', { property: 'og:image', content: '/logo.png' }], + ['meta', { property: 'og:image', content: ogImage }], ['meta', { name: 'twitter:card', content: 'summary_large_image' }], ['meta', { name: 'twitter:title', content: 'MTConnect.NET' }], [ @@ -72,7 +80,7 @@ export default withMermaid( content: 'The .NET implementation of the MTConnect Standard.', }, ], - ['meta', { name: 'twitter:image', content: '/logo.png' }], + ['meta', { name: 'twitter:image', content: ogImage }], ], themeConfig: { diff --git a/tests/MTConnect.NET-Docs-Tests/RouteCheckTests.cs b/tests/MTConnect.NET-Docs-Tests/RouteCheckTests.cs index 7b0486cc1..87d479ec5 100644 --- a/tests/MTConnect.NET-Docs-Tests/RouteCheckTests.cs +++ b/tests/MTConnect.NET-Docs-Tests/RouteCheckTests.cs @@ -302,9 +302,13 @@ public async Task Landing_Page_Carries_The_House_Style_Surfaces() "favicon type attribute is not 'image/png'"); // Open Graph — title + image surface so social previews render. + // og:image must be an absolute https:// URL; LinkedIn, Slack unfurl, + // and the classic X crawler reject root-relative paths for social cards. Assert.That(probes.OgTitle, Is.EqualTo("MTConnect.NET"), "og:title meta does not match the expected site title"); Assert.That(probes.OgImage, Is.Not.Null.And.Not.Empty, "og:image meta missing"); + Assert.That(probes.OgImage, Does.StartWith("https://"), + $"og:image must be an absolute https:// URL for social-card crawlers — got '{probes.OgImage}'"); Assert.That(probes.OgImage, Does.EndWith("/logo.png"), $"og:image does not point at /logo.png — got '{probes.OgImage}'"); @@ -312,6 +316,8 @@ public async Task Landing_Page_Carries_The_House_Style_Surfaces() Assert.That(probes.TwitterCard, Is.EqualTo("summary_large_image"), "twitter:card meta is not 'summary_large_image'"); Assert.That(probes.TwitterImage, Is.Not.Null.And.Not.Empty, "twitter:image meta missing"); + Assert.That(probes.TwitterImage, Does.StartWith("https://"), + $"twitter:image must be an absolute https:// URL for social-card crawlers — got '{probes.TwitterImage}'"); Assert.That(probes.TwitterImage, Does.EndWith("/logo.png"), $"twitter:image does not point at /logo.png — got '{probes.TwitterImage}'");