diff --git a/.github/lychee.toml b/.github/lychee.toml new file mode 100644 index 00000000000..976d16ee418 --- /dev/null +++ b/.github/lychee.toml @@ -0,0 +1,86 @@ +# .github/lychee.toml + +############################# Display ############################# +# Verbose program output +# Accepts log level: "error", "warn", "info", "debug", "trace" +verbose = "info" + +# Don't show interactive progress bar while checking links. +no_progress = false + +############################# Cache ############################### +# Enable link caching. This can be helpful to avoid checking the same links on +# multiple runs. +cache = false + +############################# Runtime ############################# +# Maximum number of concurrent link checks. +max_concurrency = 12 + +# Maximum number of allowed redirects. +max_redirects = 10 + +# Maximum number of allowed retries before a link is declared dead. +max_retries = 5 + +############################# Requests ############################ +# Website timeout from connect to response finished. +timeout = 60 + +# Minimum wait time in seconds between retries of failed requests. +retry_wait_time = 3 + +# Accept more status codes (follow redirects automatically) +accept = ["200..=204", "301..=308", "429"] + +# Avoid false fragment errors +include_fragments = false + +# Only test links with the given schemes (e.g. https). +# Omit to check links with any other scheme. +# At the moment, we support http, https, file, and mailto. +scheme = ["https"] + +# When links are available using HTTPS, treat HTTP links as errors. +require_https = false + +# Fallback extensions to apply when a URL does not specify one. +# This is common in documentation tools that cross-reference files without extensions. +fallback_extensions = ["md", "html"] + +############################# Exclusions ########################## +# Check links inside `` and `
` blocks as well as Markdown code
+# blocks.
+include_verbatim = false
+
+# Ignore case of paths when matching glob patterns.
+glob_ignore_case = false
+
+# Exclude URLs and mail addresses from checking (supports regex).
+exclude = [
+  '^mailto:',
+  '^https?://localhost',
+  '^https?://127\\.0\\.0\\.1',
+  '^https://www\.linkedin\.com',
+  '^https?://web\\.archive\\.org/web/'
+]
+
+# Exclude these filesystem paths from getting checked.
+exclude_path = [
+  '(^|/)node_modules/',
+  '(^|/)dist/',
+  '(^|/)bin/',
+  '\\.txt$',      # skip .txt extensions
+  '(^|/)test/'   # skip directories named "test"
+]
+
+# URLs to check (supports regex). Has preference over all excludes.
+include = ['gist\.github\.com.*']
+
+# Skip checking mail addresses
+include_mail = true
+
+############################# Content Checks ######################
+# Mark pages as broken if the body contains "page not found" or "404"
+[content]
+deny = ["(?i)page not found", "(?i)404"]
diff --git a/.github/workflows/check-pr-links.yml b/.github/workflows/check-pr-links.yml
new file mode 100644
index 00000000000..ce7d91a6ff1
--- /dev/null
+++ b/.github/workflows/check-pr-links.yml
@@ -0,0 +1,59 @@
+name: Check Links In Pull Requests
+
+on:
+  pull_request:
+    branches:
+      - main
+    paths:
+      - '**/*.md'
+
+jobs:
+  check-links:
+    runs-on: ubuntu-latest
+    steps:
+      # Checkout repo
+      - uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+
+      # Find changed Markdown files
+      - name: Get changed Markdown files
+        id: changed-files
+        run: |
+          files=$(git diff --name-only origin/${{ github.event.pull_request.base.ref }} ${{ github.head_ref }} | grep '\.md$' || true)
+          echo "changed_files=$files" >> $GITHUB_ENV
+          echo "Changed Markdown files:"
+          echo "$files"
+
+      # Skip if no Markdown files
+      - name: Skip if no Markdown files changed
+        if: ${{ env.changed_files == '' }}
+        run: |
+          echo "No Markdown files changed. Skipping link check."
+          exit 0
+
+      # Run Lychee
+      - name: Run Lychee link checker
+        id: lychee
+        continue-on-error: true
+        run: |
+          rm -f lychee-full-report.txt
+          for f in ${{ env.changed_files }}; do
+            echo "Checking links in $f"
+            lychee --no-progress --include-fragments "$f" >> lychee-full-report.txt || true
+          done
+
+          # Extract only broken links for log
+          if grep -qE "❌|ERROR" lychee-full-report.txt; then
+            echo "❌ Broken links found:"
+            grep -E "❌|ERROR" lychee-full-report.txt
+          else
+            echo "✅ No broken links found."
+          fi
+
+      # Upload full report as artifact
+      - name: Upload Lychee full report
+        uses: actions/upload-artifact@v4
+        with:
+          name: lychee-full-report
+          path: lychee-full-report.txt
diff --git a/.gitignore b/.gitignore
index 655ba93439d..946cb65a602 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@
 *.orig
 .vscode
 .idea
+.lycheecache
diff --git a/.lycheeignore b/.lycheeignore
new file mode 100644
index 00000000000..c149333be85
--- /dev/null
+++ b/.lycheeignore
@@ -0,0 +1,31 @@
+# These links are ignored by lychee link checker: https://github.com/lycheeverse/lychee
+# The file allows you to list multiple regular expressions for exclusion (one pattern per line).
+# The `.lycheeignore` file is only used for excluding URLs, not paths. Use the `exclude_path` key in the `lychee.toml` file. ref: https://lychee.cli.rs/recipes/excluding-paths/
+
+# GitHub blob/tree fragment links
+^https://github\.com/umbraco/Umbraco-CMS/blob/.*/.*#L.*
+^https://github\.com/umbraco/Umbraco-CMS/tree/.*
+^https://github\.com/Shazwazza/Articulate/blob/.*/.*#L.*
+^https://github\.com/umbraco/Umbraco-CMS/blob/.*
+
+# Anchor/fragment links causing false positives
+^https://apidocs\.umbraco\.com/.*/#.*
+^https://tinymce\.github\.io/.*/#.*
+^https://openid\.net/.*/#.*
+^https://docs\.microsoft\.com/.*#.*
+^https://learn\.microsoft\.com/.*#.*
+^https://developer\.mozilla\.org/.*/#.*
+^https://learning\.postman\.com/docs/.*/#.*
+^https://nginx\.org/.*/#.*
+^https://azure\.microsoft\.com/en-gb/services/media-services/.*
+^https://www\.tiny\.cloud/docs/.*
+
+# TinyMCE anchors
+^https://github\.com/tinymce/tinymce/issues/.*#.*
+
+# NIST FIPS and other static docs
+^https://csrc\.nist\.gov/publications/PubsFIPS\.html#.*
+
+# Timeout-prone Umbraco issue links
+^https://issues\.umbraco\.org/issue/.*
+^https://issues\.umbraco\.org/issues/.*
diff --git a/16/umbraco-cms/fundamentals/code/source-control.md b/16/umbraco-cms/fundamentals/code/source-control.md
index 7b78737f58a..2da8a96152a 100644
--- a/16/umbraco-cms/fundamentals/code/source-control.md
+++ b/16/umbraco-cms/fundamentals/code/source-control.md
@@ -8,7 +8,7 @@ description: >-
 
 ## Umbraco Cloud
 
-When you are running your site on Umbraco Cloud, source control is a part of the experience. Have a look at the ['Technical overview of an Umbraco Cloud Environment'](https://docs.umbraco.com/umbraco-cloud/getting-started/environments) and the information on ['Working with your Umbraco Cloud project'](https://docs.umbraco.com/umbraco-cloud/setup/set-up#working-with-your-umbraco-cloud-project) for a steer on Source/Version Control good practices.
+When you are running your site on Umbraco Cloud, source control is a part of the experience. Have a look at the ['Technical overview of an Umbraco Cloud Environment'](https://docs.umbraco.com/umbraco-cloud/getting-started/environments). Additionally, look at ['Working with a Local Clone'](https://docs.umbraco.com/umbraco-cloud/build-and-customize-your-solution/handle-deployments-and-environments/working-locally) for a steer on Source/Version Control good practices.
 
 ## Outside of Umbraco Cloud
 
diff --git a/16/umbraco-cms/fundamentals/setup/upgrading/version-specific/upgrade-from-8-to-latest.md b/16/umbraco-cms/fundamentals/setup/upgrading/version-specific/upgrade-from-8-to-latest.md
index 8da11efdc80..52f91e0d0d9 100644
--- a/16/umbraco-cms/fundamentals/setup/upgrading/version-specific/upgrade-from-8-to-latest.md
+++ b/16/umbraco-cms/fundamentals/setup/upgrading/version-specific/upgrade-from-8-to-latest.md
@@ -43,7 +43,7 @@ A video tutorial guiding you through the steps of upgrading from version 8 to th
 If you use Umbraco Forms, make sure to have [`StoreUmbracoFormsInDbset`](https://docs.umbraco.com/umbraco-forms/developer/forms-in-the-database#enable-storing-forms-definitions-in-the-database)to `True` before **step 1**.
 {% endhint %}
 
-1. Create a backup of the database from your Umbraco 8 project (after you have upgraded to the latest version of v8). For this, you can use the [database backup guide](https://docs.umbraco.com/umbraco-cloud/databases/backups#backup-with-sql-server-management-studio).
+1. Create a backup of the database from your Umbraco 8 project (after you have upgraded to the latest version of v8). For this, you can use the [Database backups Guide](https://docs.umbraco.com/umbraco-cloud/build-and-customize-your-solution/set-up-your-project/databases/backups#restoring-a-cloud-backup-to-a-sql-server-database).
 2. Import the database backup into SQL Server Management Studio.
 3. Update the connection string in the new projects `appsettings.json` file so that it connects to the Umbraco 8 database:
 
@@ -108,3 +108,27 @@ This concludes this tutorial. Find related information and further reading in th
 * [Issue tracker for known issues with Content Migration](https://github.com/umbraco/UmbracoDocs/issues)
 * [Configuration in modern Umbraco](../../../../reference/configuration/)
 * [Configuration in legacy Umbraco](https://our.umbraco.com/documentation/Reference/Configuration-for-Umbraco-7-and-8/)
+
+## Lychee Test Links 
+
+### ✅ Valid links 
+
+- [Google](https://www.google.com) 
+- [GitHub](https://github.com) 
+- [Upgrading Deploy](https://docs.umbraco.com/umbraco-deploy/upgrading/upgrades) 
+
+### ❌ Broken links 
+
+- [Broken domain](https://thisdomaindoesnotexist.openai) 
+- [404 page](https://docs.umbraco.com/umbraco-forms/editor/attaching-workflows/workflows-types) 
+- [Typo in domain](https://www.googlle.com) 
+- [Commerce Configuration](https://docs.umbraco.com/umbraco-commerce/getting-started/umbraco-configurations) 
+
+### ❌ Wrong anchors 
+
+- [Wrong anchor](https://docs.umbraco.com/umbraco-forms/editor/attaching-workflows#non-existing-anchor) 
+
+### ⚠️ Timeout-prone links (optional tests) 
+
+- [Umbraco Issues](https://issues.umbraco.org/issue/U4-1234) 
+- [Example CAPTCHA site](https://www.linkedin.com)
\ No newline at end of file