1---
2stage: none
3group: Documentation Guidelines
4info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
5description: Learn how to contribute to GitLab Documentation.
6---
7
8# Documentation testing
9
10GitLab documentation is stored in projects with code and treated like code. Therefore, we use
11processes similar to those used for code to maintain standards and quality of documentation.
12
13We have tests:
14
15- To lint the words and structure of the documentation.
16- To check the validity of internal links within the documentation suite.
17- To check the validity of links from UI elements, such as files in `app/views` files.
18
19For the specifics of each test run in our CI/CD pipelines, see the configuration for those tests
20in the relevant projects:
21
22- <https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/docs.gitlab-ci.yml>
23- <https://gitlab.com/gitlab-org/gitlab-runner/-/blob/main/.gitlab/ci/docs.gitlab-ci.yml>
24- <https://gitlab.com/gitlab-org/omnibus-gitlab/-/blob/master/gitlab-ci-config/gitlab-com.yml>
25- <https://gitlab.com/gitlab-org/charts/gitlab/-/blob/master/.gitlab-ci.yml>
26
27## Run tests locally
28
29Similar to [previewing your changes locally](index.md#previewing-the-changes-live), you can also
30run these tests on your local computer. This has the advantage of:
31
32- Speeding up the feedback loop. You can know of any problems with the changes in your branch
33  without waiting for a CI/CD pipeline to run.
34- Lowering costs. Running tests locally is cheaper than running tests on the cloud
35  infrastructure GitLab uses.
36
37To run tests locally, it's important to:
38
39- [Install the tools](#install-linters), and [keep them up to date](#update-linters).
40- Run [linters](#lint-checks), [documentation link tests](#documentation-link-tests), and
41  [UI link tests](#ui-link-tests) the same way they are run in CI/CD pipelines. It's important to use
42  same configuration we use in CI/CD pipelines, which can be different than the default configuration
43  of the tool.
44
45### Lint checks
46
47Lint checks are performed by the [`lint-doc.sh`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/scripts/lint-doc.sh)
48script and can be executed as follows:
49
501. Navigate to the `gitlab` directory.
511. Run:
52
53   ```shell
54   MD_DOC_PATH=path/to/my_doc.md scripts/lint-doc.sh
55   ```
56
57Where `MD_DOC_PATH` points to the file or directory you would like to run lint checks for.
58If you omit it completely, it defaults to the `doc/` directory.
59The output should be similar to:
60
61```plaintext
62=> Linting documents at path /path/to/gitlab as <user>...
63=> Checking for cURL short options...
64=> Checking for CHANGELOG.md duplicate entries...
65=> Checking /path/to/gitlab/doc for executable permissions...
66=> Checking for new README.md files...
67=> Linting markdown style...
68=> Linting prose...
69✔ 0 errors, 0 warnings and 0 suggestions in 1 file.
70✔ Linting passed
71```
72
73This requires you to either:
74
75- Have the [required lint tools installed](#local-linters) on your computer.
76- A working Docker installation, in which case an image with these tools pre-installed is used.
77
78### Documentation link tests
79
80To execute documentation link tests locally:
81
821. Navigate to the [`gitlab-docs`](https://gitlab.com/gitlab-org/gitlab-docs) directory.
831. Run the following commands:
84
85   ```shell
86   # Check for broken internal links
87   bundle exec nanoc check internal_links
88
89   # Check for broken external links (might take a lot of time to complete).
90   # This test is set to be allowed to fail and is run only in the gitlab-docs project CI
91   bundle exec nanoc check internal_anchors
92   ```
93
94### UI link tests
95
96The `ui-docs-links lint` job uses `haml-lint` to test that all documentation links from
97UI elements (`app/views` files, for example) are linking to valid pages and anchors.
98
99To run the `ui-docs-links` test locally:
100
1011. Open the `gitlab` directory in a terminal window.
1021. Run:
103
104   ```shell
105   bundle exec haml-lint -i DocumentationLinks
106   ```
107
108If you receive an error the first time you run this test, run `bundle install`, which
109installs the dependencies for GitLab, and try again.
110
111If you don't want to install all of the dependencies to test the links, you can:
112
1131. Open the `gitlab` directory in a terminal window.
1141. Install `haml-lint`:
115
116   ```shell
117   gem install haml_lint
118   ```
119
1201. Run:
121
122   ```shell
123   haml-lint -i DocumentationLinks
124   ```
125
126If you manually install `haml-lint` with this process, it does not update automatically
127and you should make sure your version matches the version used by GitLab.
128
129## Local linters
130
131To help adhere to the [documentation style guidelines](styleguide/index.md), and improve the content
132added to documentation, [install documentation linters](#install-linters) and
133[integrate them with your code editor](#configure-editors).
134
135At GitLab, we mostly use:
136
137- [markdownlint](#markdownlint)
138- [Vale](#vale)
139
140### markdownlint
141
142[markdownlint](https://github.com/DavidAnson/markdownlint) checks that Markdown syntax follows
143[certain rules](https://github.com/DavidAnson/markdownlint/blob/master/doc/Rules.md#rules), and is
144used by the `docs-lint` test.
145
146Our [Documentation Style Guide](styleguide/index.md#markdown) and
147[Markdown Guide](https://about.gitlab.com/handbook/markdown-guide/) elaborate on which choices must
148be made when selecting Markdown syntax for GitLab documentation. This tool helps catch deviations
149from those guidelines.
150
151markdownlint configuration is found in the following projects:
152
153- [`gitlab`](https://gitlab.com/gitlab-org/gitlab)
154- [`gitlab-runner`](https://gitlab.com/gitlab-org/gitlab-runner)
155- [`omnibus-gitlab`](https://gitlab.com/gitlab-org/omnibus-gitlab)
156- [`charts`](https://gitlab.com/gitlab-org/charts/gitlab)
157- [`gitlab-development-kit`](https://gitlab.com/gitlab-org/gitlab-development-kit)
158
159This configuration is also used in build pipelines.
160
161You can use markdownlint:
162
163- [On the command line](https://github.com/igorshubovych/markdownlint-cli#markdownlint-cli--).
164- [In a code editor](#configure-editors).
165- [In a `pre-push` hook](#configure-pre-push-hooks).
166
167### Vale
168
169[Vale](https://docs.errata.ai/vale/about/) is a grammar, style, and word usage linter for the
170English language. Vale's configuration is stored in the
171[`.vale.ini`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.vale.ini) file located in the root
172directory of projects.
173
174Vale supports creating [custom tests](https://errata-ai.github.io/vale/styles/) that extend any of
175several types of checks, which we store in the `.linting/vale/styles/gitlab` directory in the
176documentation directory of projects.
177
178You can find Vale configuration in the following projects:
179
180- [`gitlab`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/doc/.vale/gitlab)
181- [`gitlab-runner`](https://gitlab.com/gitlab-org/gitlab-runner/-/tree/main/docs/.vale/gitlab)
182- [`omnibus-gitlab`](https://gitlab.com/gitlab-org/omnibus-gitlab/-/tree/master/doc/.vale/gitlab)
183- [`charts`](https://gitlab.com/gitlab-org/charts/gitlab/-/tree/master/doc/.vale/gitlab)
184- [`gitlab-development-kit`](https://gitlab.com/gitlab-org/gitlab-development-kit/-/tree/master/doc/.vale/gitlab)
185
186This configuration is also used in build pipelines, where
187[error-level rules](#vale-result-types) are enforced.
188
189You can use Vale:
190
191- [On the command line](https://docs.errata.ai/vale/cli).
192- [In a code editor](#configure-editors).
193- [In a Git hook](#configure-pre-push-hooks). Vale only reports errors in the Git hook (the same
194  configuration as the CI/CD pipelines), and does not report suggestions or warnings.
195
196#### Vale result types
197
198Vale returns three types of results: `suggestion`, `warning`, and `error`:
199
200- **Suggestion**-level results are writing tips and aren't displayed in CI
201  job output. Suggestions don't break CI. See a list of
202  [suggestion-level rules](https://gitlab.com/search?utf8=✓&snippets=false&scope=&repository_ref=master&search=path%3Adoc%2F.vale%2Fgitlab+Suggestion%3A&group_id=9970&project_id=278964).
203- **Warning**-level results are [Style Guide](styleguide/index.md) violations, aren't displayed in CI
204  job output, and should contain clear explanations of how to resolve the warning.
205  Warnings may be technical debt, or can be future error-level test items
206  (after the Technical Writing team completes its cleanup). Warnings don't break CI. See a list of
207  [warning-level rules](https://gitlab.com/search?utf8=✓&snippets=false&scope=&repository_ref=master&search=path%3Adoc%2F.vale%2Fgitlab+Warning%3A&group_id=9970&project_id=278964).
208- **Error**-level results are Style Guide violations, and should contain clear explanations
209  of how to resolve the error. Errors break CI and are displayed in CI job output. See a list of
210  [error-level rules](https://gitlab.com/search?utf8=✓&snippets=false&scope=&repository_ref=master&search=path%3Adoc%2F.vale%2Fgitlab+Error%3A&group_id=9970&project_id=278964).
211
212#### Vale spelling test
213
214When Vale flags a valid word as a spelling mistake, you can fix it following these
215guidelines:
216
217| Flagged word                                         | Guideline |
218|------------------------------------------------------|-----------|
219| jargon                                               | Rewrite the sentence to avoid it. |
220| *correctly-capitalized* name of a product or service | Add the word to the [vale spelling exceptions list](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/spelling-exceptions.txt). |
221| name of a person                                     | Remove the name if it's not needed, or [add the vale exception code in-line](#disable-vale-tests). |
222| a command, variable, code, or similar                | Put it in backticks or a code block. For example: ``The git clone command can be used with the CI_COMMIT_BRANCH variable.`` -> ``The `git clone` command can be used with the `CI_COMMIT_BRANCH` variable.`` |
223| UI text from GitLab                                  | Verify it correctly matches the UI, then: If it does not match the UI, update it. If it matches the UI, but the UI seems incorrect, create an issue to see if the UI needs to be fixed. If it matches the UI and seems correct, add it to the [vale spelling exceptions list](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/spelling-exceptions.txt). |
224| UI text from a third-party product                   | Rewrite the sentence to avoid it, or [add the vale exception code in-line](#disable-vale-tests). |
225
226#### Vale readability score
227
228In [`ReadingLevel.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/ReadingLevel.yml),
229we have implemented
230[the Flesch-Kincaid grade level test](https://readable.com/readability/flesch-reading-ease-flesch-kincaid-grade-level/)
231to determine the readability of our documentation.
232
233As a general guideline, the lower the score, the more readable the documentation.
234For example, a page that scores `12` before a set of changes, and `9` after, indicates an iterative improvement to readability. The score is not an exact science, but is meant to help indicate the
235general complexity level of the page.
236
237The readability score is calculated by using the following formula:
238
239```plaintext
240(.39 x ASL) + (11.8 x ASW) – 15.59
241```
242
243- `ASL` is average sentence length (the number of words divided by the number of sentences).
244- `ASW` is the average number of syllables per word (the number of syllables divided by the number of words).
245- The score excludes headings, code blocks, and lists.
246
247### Install linters
248
249At a minimum, install [markdownlint](#markdownlint) and [Vale](#vale) to match the checks run in
250build pipelines:
251
2521. Install `markdownlint-cli`:
253
254   ```shell
255   yarn global add markdownlint-cli
256   ```
257
258   We recommend installing the version of `markdownlint-cli`
259   [used (see `variables:` section)](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/main/.gitlab-ci.yml) when building
260   the `image:docs-lint-markdown`.
261
2621. Install [`vale`](https://github.com/errata-ai/vale/releases). For example, to install using
263   `brew` for macOS, run:
264
265   ```shell
266   brew install vale
267   ```
268
269These tools can be [integrated with your code editor](#configure-editors).
270
271### Update linters
272
273It's important to use linter versions that are the same or newer than those run in
274CI/CD. This provides access to new features and possible bug fixes.
275
276To match the versions of `markdownlint-cli` and `vale` used in the GitLab projects, refer to the
277[versions used (see `variables:` section)](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/main/.gitlab-ci.yml)
278when building the `image:docs-lint-markdown` Docker image containing these tools for CI/CD.
279
280| Tool               | Version   | Command                                   | Additional information |
281|--------------------|-----------|-------------------------------------------|------------------------|
282| `markdownlint-cli` | Latest    | `yarn global add markdownlint-cli`        | n/a                    |
283| `markdownlint-cli` | Specific  | `yarn global add markdownlint-cli@0.23.2` | The `@` indicates a specific version, and this example updates the tool to version `0.23.2`. |
284| Vale               | Latest    | `brew update && brew upgrade vale`        | This command is for macOS only. |
285| Vale               | Specific  | n/a                                       | Not possible using `brew`, but can be [directly downloaded](https://github.com/errata-ai/vale/releases). |
286
287### Configure editors
288
289Using linters in your editor is more convenient than having to run the commands from the
290command line.
291
292To configure markdownlint in your editor, install one of the following as appropriate:
293
294- Sublime Text [`SublimeLinter-contrib-markdownlint` package](https://packagecontrol.io/packages/SublimeLinter-contrib-markdownlint).
295- Visual Studio Code [`DavidAnson.vscode-markdownlint` extension](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint).
296- Atom [`linter-node-markdownlint` package](https://atom.io/packages/linter-node-markdownlint).
297- Vim [ALE plugin](https://github.com/dense-analysis/ale).
298
299To configure Vale in your editor, install one of the following as appropriate:
300
301- Sublime Text [`SublimeLinter-contrib-vale` package](https://packagecontrol.io/packages/SublimeLinter-contrib-vale).
302- Visual Studio Code [`errata-ai.vale-server` extension](https://marketplace.visualstudio.com/items?itemName=errata-ai.vale-server).
303  You can configure the plugin to
304  [display only a subset of alerts](#show-subset-of-vale-alerts).
305
306  In the extension's settings:
307
308  <!-- vale gitlab.Spelling = NO -->
309
310  - Select the **Use CLI** checkbox.
311  - In the  **Config** setting, enter an absolute
312    path to [`.vale.ini`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.vale.ini)
313    in one of the cloned GitLab repositories on your computer.
314
315  <!-- vale gitlab.Spelling = YES -->
316
317  - In the **Path** setting, enter the absolute path to the Vale binary. In most
318    cases, `vale` should work. To find the location, run `which vale` in a terminal.
319
320- Vim [ALE plugin](https://github.com/dense-analysis/ale).
321- Emacs [Flycheck extension](https://github.com/flycheck/flycheck).
322  This requires some configuration:
323
324  - `Flycheck` supports `markdownlint-cli` out of the box, but you must point it
325  to the `.markdownlint.yml` at the base of the project directory. A `.dir-locals.el`
326  file can accomplish this:
327
328  ```lisp
329  ;; Place this code in a file called `.dir-locals.el` at the root of the gitlab project.
330  ((markdown-mode . ((flycheck-markdown-markdownlint-cli-config . ".markdownlint.yml"))))
331
332  ```
333
334  - A minimal configuration for Flycheck to work with Vale could look like this:
335
336  ```lisp
337  (flycheck-define-checker vale
338    "A checker for prose"
339    :command ("vale" "--output" "line" "--no-wrap"
340              source)
341    :standard-input nil
342    :error-patterns
343      ((error line-start (file-name) ":" line ":" column ":" (id (one-or-more (not (any ":")))) ":" (message)   line-end))
344    :modes (markdown-mode org-mode text-mode)
345    :next-checkers ((t . markdown-markdownlint-cli))
346  )
347
348  (add-to-list 'flycheck-checkers 'vale)
349  ```
350
351  In this setup the `markdownlint` checker is set as a "next" checker from the defined `vale` checker.
352  Enabling this custom Vale checker provides error linting from both Vale and markdownlint.
353
354We don't use [Vale Server](https://errata-ai.github.io/vale/#using-vale-with-a-text-editor-or-another-third-party-application).
355
356### Configure pre-push hooks
357
358Git [pre-push hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) allow Git users to:
359
360- Run tests or other processes before pushing a branch.
361- Avoid pushing a branch if failures occur with these tests.
362
363[`lefthook`](https://github.com/Arkweid/lefthook) is a Git hooks manager, making configuring,
364installing, and removing Git hooks easy.
365
366Configuration for `lefthook` is available in the [`lefthook.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lefthook.yml)
367file for the [`gitlab`](https://gitlab.com/gitlab-org/gitlab) project.
368
369To set up `lefthook` for documentation linting, see
370[Pre-push static analysis](../contributing/style_guides.md#pre-push-static-analysis-with-lefthook).
371
372### Show subset of Vale alerts
373
374You can set Visual Studio Code to display only a subset of Vale alerts when viewing files:
375
3761. Go to **Preferences > Settings > Extensions > Vale**.
3771. In **Vale CLI: Min Alert Level**, select the minimum alert level you want displayed in files.
378
379To display only a subset of Vale alerts when running Vale from the command line, use
380the `--minAlertLevel` flag, which accepts `error`, `warning`, or `suggestion`. Combine it with `--config`
381to point to the configuration file within the project, if needed:
382
383```shell
384vale --config .vale.ini --minAlertLevel error doc/**/*.md
385```
386
387Omit the flag to display all alerts, including `suggestion` level alerts.
388
389### Disable Vale tests
390
391You can disable a specific Vale linting rule or all Vale linting rules for any portion of a
392document:
393
394- To disable a specific rule, add a `<!-- vale gitlab.rulename = NO -->` tag before the text, and a
395  `<!-- vale gitlab.rulename = YES -->` tag after the text, replacing `rulename` with the filename
396  of a test in the
397  [GitLab styles](https://gitlab.com/gitlab-org/gitlab/-/tree/master/doc/.linting/vale/styles/gitlab)
398  directory.
399- To disable all Vale linting rules, add a `<!-- vale off -->` tag before the text, and a
400  `<!-- vale on -->` tag after the text.
401
402Whenever possible, exclude only the problematic rule and lines.
403
404For more information, see
405[Vale's documentation](https://docs.errata.ai/vale/scoping#markup-based-configuration).
406
407### Disable markdownlint tests
408
409To disable all markdownlint rules, add a `<!-- markdownlint-disable -->` tag before the text, and a
410`<!-- markdownlint-enable -->` tag after the text.
411
412To disable only a [specific rule](https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#rules),
413add the rule number to the tag, for example `<!-- markdownlint-disable MD044 -->`
414and `<!-- markdownlint-enable MD044 -->`.
415
416Whenever possible, exclude only the problematic lines.
417