From b4ee425c80542c1377edff7f38b0de44ab40b8a1 Mon Sep 17 00:00:00 2001 From: Peter Leitzen <pleitzen@gitlab.com> Date: Mon, 19 Feb 2024 07:25:31 +0000 Subject: [PATCH] Document mermaidlint in documentation guidelines Run this script in lefthook and static analysis on CI. --- doc/development/documentation/testing.md | 13 ++++ lefthook.yml | 5 ++ scripts/lint/check_mermaid.mjs | 77 ++++++++++++++++++++++++ scripts/static-analysis | 1 + 4 files changed, 96 insertions(+) create mode 100755 scripts/lint/check_mermaid.mjs diff --git a/doc/development/documentation/testing.md b/doc/development/documentation/testing.md index 279793563e74..ce03aef664b4 100644 --- a/doc/development/documentation/testing.md +++ b/doc/development/documentation/testing.md @@ -308,6 +308,19 @@ included in backticks. For example: - `git clone` is a command, so it must be lowercase, while Git is the product, so it must have a capital G. +### Mermaid + +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/144328) in GitLab 16.10. + +[Mermaid](https://mermaid.js.org/) builds charts and diagrams from code. + +The `mermaidlint` job runs on merge requests that contain changes to Markdown files. +The script (`scripts/lint/check_mermaid.mjs`) returns an error if any Markdown +files return a Mermaid syntax error. + +To help debug your Mermaid charts, use the +[Mermaid Live Editor](https://mermaid-js.github.io/mermaid-live-editor/edit). + ### Vale [Vale](https://vale.sh/) is a grammar, style, and word usage linter for the diff --git a/lefthook.yml b/lefthook.yml index f5f2597e5bbb..a7147b559902 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -33,6 +33,11 @@ pre-push: files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD glob: '*.{yml,yaml}{,.*}' run: scripts/lint-yaml.sh {files} + mermaidlint: + tags: documentation style,backend style,frontend style + files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD + glob: '{app,lib,ee,spec,doc,scripts}/**/*.md' + run: scripts/lint/check_mermaid.mjs {files} stylelint: tags: stylesheet css style files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD diff --git a/scripts/lint/check_mermaid.mjs b/scripts/lint/check_mermaid.mjs new file mode 100755 index 000000000000..253cf58cc1f1 --- /dev/null +++ b/scripts/lint/check_mermaid.mjs @@ -0,0 +1,77 @@ +#!/usr/bin/env node + +// Lint mermaid code in markdown files. +// Usage: scripts/lint/check_mermaid.mjs [files ...] + +import fs from 'node:fs'; +import glob from 'glob'; +import mermaid from 'mermaid'; +import DOMPurify from 'dompurify'; +import { JSDOM } from 'jsdom'; + +const jsdom = new JSDOM('...', { + pretendToBeVisual: true, +}); +global.document = jsdom; +global.window = jsdom.window; +global.Option = window.Option; + +// Workaround to make DOMPurify not fail. +// See https://github.com/mermaid-js/mermaid/issues/5204 +DOMPurify.addHook = () => {}; +DOMPurify.sanitize = (x) => x; + +const defaultGlob = "{app,lib,ee,spec,doc,scripts}/**/*.md"; +const mermaidMatch = /```mermaid(.*?)```/gms; + +const argv = process.argv.length > 2 ? process.argv.slice(2) : [defaultGlob]; +const mdFiles = argv.flatMap((arg) => glob.sync(arg)) + +console.log(`Checking ${mdFiles.length} markdown files...`); + +// Mimicking app/assets/javascripts/lib/mermaid.js +mermaid.initialize({ + // mermaid core options + mermaid: { + startOnLoad: false, + }, + // mermaidAPI options + theme: 'neutral', + flowchart: { + useMaxWidth: true, + htmlLabels: true, + }, + secure: ['secure', 'securityLevel', 'startOnLoad', 'maxTextSize', 'htmlLabels'], + securityLevel: 'strict', +}); + +let errors = 0; + +await Promise.all( + mdFiles.map((path) => { + const data = fs.readFileSync(path, 'utf8'); + + const matched = [...data.matchAll(mermaidMatch)]; + + return Promise.all( + matched.map((match) => { + const matchIndex = match.index; + const mermaidText = match[1]; + + return mermaid.parse(mermaidText).catch((error) => { + const lineNumber = data.slice(0, matchIndex).split('\n').length; + + console.log(`${path}:${lineNumber}: Mermaid syntax error\nError: ${error}\n`); + errors += 1; + }); + }), + ); + }), +); + +if (errors > 0) { + console.log(`Total errors: ${errors}`); + // eslint-disable-next-line no-restricted-syntax + console.log(`To fix these errors, see https://docs.gitlab.com/ee/development/documentation/testing.html#mermaid.`); + process.exit(1); +} diff --git a/scripts/static-analysis b/scripts/static-analysis index 0c42bd38a3ca..185897b0133f 100755 --- a/scripts/static-analysis +++ b/scripts/static-analysis @@ -46,6 +46,7 @@ class StaticAnalysis Task.new(%w[bin/rake config_lint], 10), Task.new(%w[bin/rake gitlab:sidekiq:all_queues_yml:check], 15), (Gitlab.ee? ? Task.new(%w[bin/rake gitlab:sidekiq:sidekiq_queues_yml:check], 11) : nil), + Task.new(%w[scripts/lint/check_mermaid.mjs], 10), Task.new(%w[yarn run internal:stylelint], 8), Task.new(%w[scripts/lint-conflicts.sh], 1), Task.new(%w[yarn run block-dependencies], 1), -- GitLab