From 27e84884d6fa0fe7809b1e5a42a250cef3fd43f9 Mon Sep 17 00:00:00 2001
From: Fran Garcia-Linares <14157-fjgarlin@users.noreply.drupalcode.org>
Date: Thu, 7 Mar 2024 10:19:25 +0000
Subject: [PATCH] Issue #3423238 by fjgarlin, jonathan1055, mparker17: Document
 how to use and customize GitLabCI with mkdocs and a Pages job

---
 .gitignore                                   |   1 +
 .gitlab-ci.yml                               |  26 ++++++
 .spelling                                    |  55 ++++++++++++
 CHANGELOG.md                                 |   8 ++
 README.MD                                    |  85 ++-----------------
 CONTRIBUTING.md => docs/help/contributing.md |  17 ++--
 docs/help/documentation.md                   |  23 +++++
 docs/help/help.md                            |   7 ++
 docs/index.md                                |  42 +++++++++
 docs/info/common.md                          |  41 +++++++++
 docs/info/customizations.md                  |  35 ++++++++
 docs/info/drupal7.md                         |  12 +++
 docs/info/setup.md                           |  44 ++++++++++
 docs/info/templates-version.md               |  24 ++++++
 docs/info/test-locally.md                    |  67 +++++++++++++++
 docs/info/testing-mrs.md                     |  16 ++++
 docs/jobs.md                                 |  82 ++++++++++++++++++
 docs/jobs/composer-lint.md                   |  10 +++
 docs/jobs/composer.md                        |  54 ++++++++++++
 docs/jobs/cspell.md                          |  25 ++++++
 docs/jobs/eslint.md                          |   7 ++
 docs/jobs/nightwatch.md                      |   9 ++
 docs/jobs/pages.md                           |   8 ++
 docs/jobs/phpcs.md                           |   9 ++
 docs/jobs/phpstan.md                         |   9 ++
 docs/jobs/phpunit.md                         |  25 ++++++
 docs/jobs/stylelint.md                       |   5 ++
 docs/jobs/test-only-changes.md               |  14 +++
 docs/logo.png                                | Bin 0 -> 12547 bytes
 includes/include.drupalci.main-d7.yml        |   2 +-
 includes/include.drupalci.main.yml           |   2 +-
 includes/include.drupalci.workflows.yml      |  13 ++-
 mkdocs.yml                                   |  60 +++++++++++++
 33 files changed, 737 insertions(+), 100 deletions(-)
 create mode 100644 .gitignore
 create mode 100644 .spelling
 rename CONTRIBUTING.md => docs/help/contributing.md (76%)
 create mode 100644 docs/help/documentation.md
 create mode 100644 docs/help/help.md
 create mode 100644 docs/index.md
 create mode 100644 docs/info/common.md
 create mode 100644 docs/info/customizations.md
 create mode 100644 docs/info/drupal7.md
 create mode 100644 docs/info/setup.md
 create mode 100644 docs/info/templates-version.md
 create mode 100644 docs/info/test-locally.md
 create mode 100644 docs/info/testing-mrs.md
 create mode 100644 docs/jobs.md
 create mode 100644 docs/jobs/composer-lint.md
 create mode 100644 docs/jobs/composer.md
 create mode 100644 docs/jobs/cspell.md
 create mode 100644 docs/jobs/eslint.md
 create mode 100644 docs/jobs/nightwatch.md
 create mode 100644 docs/jobs/pages.md
 create mode 100644 docs/jobs/phpcs.md
 create mode 100644 docs/jobs/phpstan.md
 create mode 100644 docs/jobs/phpunit.md
 create mode 100644 docs/jobs/stylelint.md
 create mode 100644 docs/jobs/test-only-changes.md
 create mode 100644 docs/logo.png
 create mode 100644 mkdocs.yml

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..16d3c4db
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.cache
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 41c42dba..3cea2cfa 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -72,3 +72,29 @@ check_versions:
     - if: $CI_PIPELINE_SOURCE == 'merge_request_event'
   script:
     - php $CI_PROJECT_DIR/scripts/check-versions.php
+
+# Build documentation page.
+pages:
+  stage: deploy
+  needs: [markdown-spellcheck]
+  image: python:latest
+  script:
+    - pip install mkdocs-material
+    - mkdocs build --site-dir public
+    - echo "Pages accessible through ${CI_PAGES_URL}"
+  artifacts:
+    paths:
+      - public
+  rules:
+    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+
+markdown-spellcheck:
+  stage: test
+  rules:
+    - changes:
+      - docs/**
+      - mkdocs.yml
+  image: registry.gitlab.com/pipeline-components/markdown-spellcheck:latest
+  script:
+    - mdspell --en-us -n -a --report 'docs/**/*.md'
+    - echo "***** Results are not colored in the GitLab job output. You can install the tool locally via \"npm i markdown-spellcheck -g\" and then run \"mdspell --en-us -n -a --report 'docs/**/*.md'\" locally. *****"
diff --git a/.spelling b/.spelling
new file mode 100644
index 00000000..af98b895
--- /dev/null
+++ b/.spelling
@@ -0,0 +1,55 @@
+# markdown-spellcheck spelling configuration file
+GitLab
+Drupal
+phpunit
+stylelint
+cspell
+nightwatch
+phpcs
+DrupalCI
+phpstan
+MkDocs
+eslint
+Drupal.org
+mglaman
+composer-drupal-lenient
+php-parallel-lint
+gitlab-ci-local
+env
+firecow
+chromedriver
+locahost
+MacOS
+Homebrew
+npm
+codebase
+ddev-drupal-contrib
+semver
+yml
+gitlab-ci
+drupalci_environments
+cron
+ie
+PHP_CodeSniffer
+X.Y.Z
+OPT_IN
+localhost
+URLs
+readme
+uncomment
+SKIP_COMPOSER_LINT
+SKIP_PHPCS
+SKIP_STYLELINT
+SKIP_ESLINT
+SKIP_CSPELL
+SKIP_PHPSTAN
+SKIP_NIGHTWATCH
+SKIP_PHPUNIT
+SKIP_TEST_ONLY_CHANGES
+SKIP_PAGES
+OPT_IN_TEST_CURRENT
+OPT_IN_TEST_MAX_PHP
+OPT_IN_TEST_PREVIOUS_MINOR
+OPT_IN_TEST_PREVIOUS_MAJOR
+OPT_IN_TEST_NEXT_MINOR
+OPT_IN_TEST_NEXT_MAJOR
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7490df55..69a090b6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
 # GitLab Templates Changelog
 
+## x.y.z - 2024-...
+
+#3423238 - Documentation site using GitLab pages.\
+#3426104 - Bump D7 version.\
+#3426013 - Bump core stable.\
+#3397699 - Preserve original composer.json in the modules directory.\
+#3425414 - Remove experimental flags.
+
 ## 1.2.2 - 2024-03-04
 
 #3423236 - Unify symlink approach, allow folder argument.\
diff --git a/README.MD b/README.MD
index 0713b38d..83d316fb 100644
--- a/README.MD
+++ b/README.MD
@@ -1,86 +1,13 @@
 # GitLab Templates
 
-## Usage
-
 For usage documentation, please see the [GitLab CI](https://www.drupal.org/node/3356364/)
 guide in [Drupal.org](https://www.drupal.org).
 
+Visit the [GitLab Templates](https://project.pages.drupalcode.org/gitlab_templates/) documentation page for more technical and detailed information.
 
-## Templates behaviour in contrib modules
-
-If a project wants to change the way to retrieve the default templates, go to
-the `.gitlab-ci.yml` file and find the line `ref: $_GITLAB_TEMPLATES_REF`. The
-value set in that line will determine _which_ version of the templates to use.
-
-- `ref: $_GITLAB_TEMPLATES_REF` - This is the recommended (and default) value.
-The Drupal Association will update this value to the recommended tag.
-- `ref: main` - Get the very latest additions and bug fixes as soon as they are
-merged into the templates.
-- `ref: 1.0.0` - a fixed known tag value. This will not move and not get any
-updates. It is up to the maintainer to change this when necessary.
-
-There are also some special _moving_ tags. With these you can get the latest
-changes without jumping a minor or major version. These tags are suffixed with
-`-latest` and can be checked in [this tags listing](https://git.drupalcode.org/project/gitlab_templates/-/tags).
-
-For example:
-- `ref: 1.0.x-latest` - moving tag for the latest bug fixes. It will always
-point to the latest *existing* `1.0.number` release. This mimics the `~1.0`
-semver constraint.
-- `ref: 1.x-latest` - moving tag for the latest minor features. It will always
-point to the latest *existing* `1.minor.number` release. This mimics the `~1`
-semver constraint.
-
-
-## Testing MRs
-
-If a project wants to test using a branch/MR of this repo, go to your project's CI/CD settings page and add the variables below. All pipelines in the project will use the specified repo+branch until the variables are removed.
-  - KEY = `_GITLAB_TEMPLATES_REPO` VALUE = `issue/gitlab_templates-nnnnnnn`
-  - KEY = `_GITLAB_TEMPLATES_REF` VALUE = `branch name`, often the format is `nnnnnnn-some-text`
-
-The variables should have the following attributes:
-  - Type = Variable (default)
-  - Environment Scope = All (default)
-  - Protected = No
-  - Masked = No
-  - Expanded = Yes
-
-
-## When do the jobs run?
-
-Each job will run or not run according to the following rules:
-
-| JOB NAME                    | Conditions when it will run                             | Conditions when it will NOT run                           |
-| --------------------------- | ------------------------------------------------------- | --------------------------------------------------------- |
-| composer                    | SKIP_COMPOSER_LINT=0 \|\| SKIP_PHPCS=0 \|\|<br>SKIP_STYLELINT=0 \|\| SKIP_ESLINT=0 \|\|<br>SKIP_CSPELL=0 \|\| OPT_IN_TEST_CURRENT=1 | SKIP_COMPOSER_LINT=1 && SKIP_PHPCS=1 &&<br>SKIP_STYLELINT=1 && SKIP_ESLINT=1 &&<br>SKIP_CSPELL=1 && OPT_IN_TEST_CURRENT=0 |
-| composer (max PHP version)  | OPT_IN_TEST_MAX_PHP=1                                   | OPT_IN_TEST_MAX_PHP=0                                     |
-| composer (previous minor)   | OPT_IN_TEST_PREVIOUS_MINOR=1                            | OPT_IN_TEST_PREVIOUS_MINOR=0                              |
-| composer (previous major)   | OPT_IN_TEST_PREVIOUS_MAJOR=1                            | OPT_IN_TEST_PREVIOUS_MAJOR=0                              |
-| composer (next minor)       | OPT_IN_TEST_NEXT_MINOR=1                                | OPT_IN_TEST_NEXT_MINOR=0                                  |
-| composer (next major)       | OPT_IN_TEST_NEXT_MAJOR=1                                | OPT_IN_TEST_NEXT_MAJOR=0                                  |
-| composer-lint               | SKIP_COMPOSER_LINT=0                                    | SKIP_COMPOSER_LINT=1                                      |
-| cspell                      | SKIP_CSPELL=0                                           | SKIP_CSPELL=1                                             |
-| eslint                      | SKIP_ESLINT=0                                           | SKIP_ESLINT=1                                             |
-| phpcs                       | SKIP_PHPCS=0                                            | SKIP_PHPCS=1                                              |
-| stylelint                   | SKIP_STYLELINT=0                                        | SKIP_STYLELINT=1                                          |
-| phpstan                     | SKIP_PHPSTAN=0 && OPT_IN_TEST_CURRENT=1                 | SKIP_PHPSTAN=1 \|\| OPT_IN_TEST_CURRENT=0                 |
-| phpstan (next minor)        | SKIP_PHPSTAN=0 && OPT_IN_TEST_NEXT_MINOR=1              | SKIP_PHPSTAN=1 \|\| OPT_IN_TEST_NEXT_MINOR=0              |
-| phpstan (next major)        | SKIP_PHPSTAN=0 && OPT_IN_TEST_NEXT_MAJOR=1              | SKIP_PHPSTAN=1 \|\| OPT_IN_TEST_NEXT_MAJOR=0              |
-| nightwatch                  | SKIP_NIGHTWATCH=0 && OPT_IN_TEST_CURRENT=1              | SKIP_NIGHTWATCH=1 \|\| OPT_IN_TEST_CURRENT=0              |
-| nightwatch (max PHP version)| SKIP_NIGHTWATCH=0 && OPT_IN_TEST_MAX_PHP=1              | SKIP_NIGHTWATCH=1 \|\| OPT_IN_TEST_MAX_PHP=0              |
-| nightwatch (previous minor) | SKIP_NIGHTWATCH=0 && OPT_IN_TEST_PREVIOUS_MINOR=1       | SKIP_NIGHTWATCH=1 \|\| OPT_IN_TEST_PREVIOUS_MINOR=0       |
-| nightwatch (previous major) | SKIP_NIGHTWATCH=0 && OPT_IN_TEST_PREVIOUS_MAJOR=1       | SKIP_NIGHTWATCH=1 \|\| OPT_IN_TEST_PREVIOUS_MAJOR=0       |
-| nightwatch (next minor)     | SKIP_NIGHTWATCH=0 && OPT_IN_TEST_NEXT_MINOR=1           | SKIP_NIGHTWATCH=1 \|\| OPT_IN_TEST_NEXT_MINOR=0           |
-| nightwatch (next major)     | SKIP_NIGHTWATCH=0 && OPT_IN_TEST_NEXT_MAJOR=1           | SKIP_NIGHTWATCH=1 \|\| OPT_IN_TEST_NEXT_MAJOR=0           |
-| phpunit                     | SKIP_PHPUNIT=0 && OPT_IN_TEST_CURRENT=1                 | SKIP_PHPUNIT=1 \|\| OPT_IN_TEST_CURRENT=0                 |
-| phpunit (max PHP version)   | SKIP_PHPUNIT=0 && OPT_IN_TEST_MAX_PHP=1                 | SKIP_PHPUNIT=1 \|\| OPT_IN_TEST_MAX_PHP=0                 |
-| phpunit (previous minor)    | SKIP_PHPUNIT=0 && OPT_IN_TEST_PREVIOUS_MINOR=1          | SKIP_PHPUNIT=1 \|\| OPT_IN_TEST_PREVIOUS_MINOR=0          |
-| phpunit (previous major)    | SKIP_PHPUNIT=0 && OPT_IN_TEST_PREVIOUS_MAJOR=1          | SKIP_PHPUNIT=1 \|\| OPT_IN_TEST_PREVIOUS_MAJOR=0          |
-| phpunit (next minor)        | SKIP_PHPUNIT=0 && OPT_IN_TEST_NEXT_MINOR=1              | SKIP_PHPUNIT=1 \|\| OPT_IN_TEST_NEXT_MINOR=0              |
-| phpunit (next major)        | SKIP_PHPUNIT=0 && OPT_IN_TEST_NEXT_MAJOR=1              | SKIP_PHPUNIT=1 \|\| OPT_IN_TEST_NEXT_MAJOR=0              |
-| pages                       | SKIP_PAGES=0                                            | SKIP_PAGES=1                                              |
-
-
-## Contribute
+Useful quicklinks:
 
-If you want to contribute, read [the contributing file](CONTRIBUTING.md).
+- [Templates version](https://project.pages.drupalcode.org/gitlab_templates/info/templates-version.md)
+- [Testing MRs](https://project.pages.drupalcode.org/gitlab_templates/info/testing-mrs.md)
+- [Jobs (and when they run)](https://project.pages.drupalcode.org/gitlab_templates/jobs.md)
+- [Contributing](https://project.pages.drupalcode.org/gitlab_templates/contributing.md)
diff --git a/CONTRIBUTING.md b/docs/help/contributing.md
similarity index 76%
rename from CONTRIBUTING.md
rename to docs/help/contributing.md
index 2765ab41..f3ad79d3 100644
--- a/CONTRIBUTING.md
+++ b/docs/help/contributing.md
@@ -4,9 +4,6 @@ If you want to contribute to the GitLab templates, use the `main` branch as
 reference, but work the [issue queue](https://www.drupal.org/project/issues/gitlab_templates?categories=All),
 creating forks and merge requests so they can be tested and reviewed by others.
 
-These templates are the default set up for all contrib space, from Drupal 7 to
-the latest Drupal 10+ compatible modules.
-
 ## Tags
 
 **Branch name**: `main`
@@ -25,22 +22,18 @@ We follow a linear development, but with tags so people can pin things:
 - If new features were added, we increase the Y. ie: from 1.0.11 to 1.1.0
 - Once a new "minor" tag is created (ie: 1.1.0), we don't port anything to
 1.0.11
-- Breaking changes or big refactorings increase X
+- Breaking changes or a big refactoring increase X
 
 For additional information on how contrib maintainers might use the above tags
-see the [readme](readme.md) file.
+see the [templates version](info/templates-version.md) page.
+
+**Maintainers only - semver tags and default tag**
 
-There is a script to help with that located at `scripts/do-git-tags.sh`.
+There is a script to help with setting up the tags located at `scripts/do-git-tags.sh`.
 Using this script will ensure that the desired tag is set and that also the
 moving tags for minor and major updates are updated too.
 
-**Maintainers only - default tag**
-
 There is a special tag called `default-ref` that is the value set in
 `$_GITLAB_TEMPLATES_REF`. That tag can be set using the
 `scripts/set-default-tag` script. It will point to the recommended tag for the
 contrib space, so make sure that is a stable one.
-
-## If you need help
-
-Use the `#gitlab` channel on [Drupal Slack](https://www.drupal.org/slack) or the issue queue to ask questions.
diff --git a/docs/help/documentation.md b/docs/help/documentation.md
new file mode 100644
index 00000000..d3c87b28
--- /dev/null
+++ b/docs/help/documentation.md
@@ -0,0 +1,23 @@
+# Documentation
+
+This documentation site has been put together by the community and it is a constant work in progress.
+
+If you find a typo or want to help with documentation in general, you are welcome to open and issue and work on it.
+
+## Run the site locally
+
+To run the documentation site locally, run the following commands (you will need `docker`):
+```
+docker pull squidfunk/mkdocs-material
+docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material
+```
+
+That will serve a site locally.
+
+## Check for spelling errors
+
+You can check the site for spelling errors before committing with this commands:
+```
+npm i markdown-spellcheck -g
+mdspell --en-us -n -a --report 'docs/**/*.md'
+```
diff --git a/docs/help/help.md b/docs/help/help.md
new file mode 100644
index 00000000..b48a01df
--- /dev/null
+++ b/docs/help/help.md
@@ -0,0 +1,7 @@
+# Help us
+
+You can [contribute](help/contributing.md) in the code or help with the [documentation](help/documenation.md).
+
+You can help by [submitting an issue](https://www.drupal.org/node/add/project-issue/gitlab_templates). Explain what the issue is and feel free to open a Merge Request with the suggested changes.
+
+Use the `#gitlab` channel on [Drupal Slack](https://www.drupal.org/slack) or the issue queue to ask questions.
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 00000000..0108eda6
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,42 @@
+---
+hide:
+  - toc
+---
+
+# GitLab Templates
+
+The GitLab Templates project drives all the GitLab CI integration for all Drupal contrib modules.
+
+## Documentation
+
+The [GitLab CI](https://www.drupal.org/docs/develop/git/using-gitlab-to-contribute-to-drupal/gitlab-ci) page in [Drupal.org](https://www.drupal.org) offers a great introduction to most of the topis that you might be interested in, so visit that first if you are interested in overall documentation.
+
+This documentation site offers further insight into individual jobs, best practices, etc. Navigate through the site or use the search feature to find additional information.
+
+## Jobs
+
+[Jobs](jobs.md): Learn about the different jobs, how to turn them on/off and the different variants available to test past and future versions of Drupal in your module.
+
+Jump straight to the individual job's documentation page:
+
+- [composer](jobs/composer.md)
+- [cspell](jobs/cspell.md)
+- [phpstan](jobs/phpstan.md)
+- [stylelint](jobs/stylelint.md)
+- [phpcs](jobs/phpcs.md)
+- [composer-lint](jobs/composer-lint.md)
+- [phpunit](jobs/phpunit.md)
+- [nightwatch](jobs/nightwatch.md)
+- [test-only](jobs/test-only-changes.md)
+- [pages](jobs/pages.md)
+
+## Basic information
+
+Look for further information on the following topics:
+
+- [Set up](info/setup.md)
+- [Common tasks](info/common.md)
+- [Customizations](info/customizations.md)
+- [Test locally](info/test-locally.md)
+- [Templates version](info/templates-version.md)
+- [Drupal 7](info/drupal7.md)
diff --git a/docs/info/common.md b/docs/info/common.md
new file mode 100644
index 00000000..9c267f0e
--- /dev/null
+++ b/docs/info/common.md
@@ -0,0 +1,41 @@
+# Common tasks
+
+## Scheduling pipelines
+
+If you would like to schedule pipelines to run on automatically, separately from the triggers defined by the template above, you can do so with [Scheduled Pipelines](https://docs.gitlab.com/ee/ci/pipelines/schedules.html).
+
+- Navigate to your project on Drupal GitLab
+- Navigate to Build -> Pipelines schedules in the sidebar.
+- Click New Schedule button.
+
+From here, you can set an interval pattern, such as every day, week, or month, or a custom pattern based on standard cron syntax. You can then choose a target branch or tag, and set any variable values.
+
+## Gathering results
+
+There are multiple ways to gather GitLab CI results, depending on what data you need.
+
+- You can use the test result summaries from the pipeline, which will highlight test failures and summarize successes.
+- You can download the test artifacts.
+- You can view the full console output of any particular job by clicking through it.
+
+If you need additional debugging, you can also leverage GitLab CI features to capture additional logs from some of the services that are running, by setting `CI_DEBUG_SERVICES` variable to `"true"`. You can read more about this feature on the GitLab documentation page [here](https://docs.gitlab.com/ee/ci/services/#capturing-service-container-logs). We only recommend this while debugging, as the volume and size of artifacts can grow very rapidly with this feature enabled.
+
+## Including a non-required module for testing.
+
+Add the module to the `require-dev` section of your module's `composer.json` file. It will be installed only when requested by developers and the CI process.
+
+## Test multiple database combinations
+
+You can test multiple database types and versions leveraging the `parallel:matrix` feature. You can see an example in the `date_point` module.
+
+Remember that core already tests the different database types and versions, so this is only recommended if your module does some operations that can be database-specific and you need to support multiple database drivers (check the allowed images in [drupalci_environments](https://git.drupalcode.org/project/drupalci_environments/-/tree/production/db?ref_type=heads) project). It can be as easy as doing this in the template:
+
+```
+phpunit:
+  parallel:
+    matrix:
+      - _TARGET_DB_TYPE: 'mysql'
+        _TARGET_DB_VERSION: '8'
+      - _TARGET_DB_TYPE: 'pgsql'
+        _TARGET_DB_VERSION: '16'
+```
\ No newline at end of file
diff --git a/docs/info/customizations.md b/docs/info/customizations.md
new file mode 100644
index 00000000..e0dc9474
--- /dev/null
+++ b/docs/info/customizations.md
@@ -0,0 +1,35 @@
+# Customizations
+
+## Overriding parts of the template
+
+You can override any part of the template, like `variables`, `workflows` or individual job definitions.
+
+## Variables
+
+You can override any variable. The default values can be seen [here](https://git.drupalcode.org/project/gitlab_templates/-/blob/main/includes/include.drupalci.variables.yml).
+
+```
+variables:
+  _TARGET_CORE: "10.2.0"
+  _SHOW_ENVIRONMENT_VARIABLES: "1"
+```
+
+## Jobs
+
+If you want to change the default behavior of some jobs, you just need to override that part of the job.
+
+```
+# Linting jobs are allowed to fail by default, let's change that below.
+cspell:
+  allow_failure: false
+phpcs:
+  allow_failure: false
+phpstan:
+  allow_failure: false
+```
+
+## References
+
+If the part of the template that you are overriding uses in-template references, you don't need to replicate them in your overrides, you can just use the [!reference notation](https://docs.gitlab.com/ee/ci/yaml/yaml_optimization.html#reference-tags).
+
+For example: `!reference [ .setup-webserver ]`
diff --git a/docs/info/drupal7.md b/docs/info/drupal7.md
new file mode 100644
index 00000000..a9f508ab
--- /dev/null
+++ b/docs/info/drupal7.md
@@ -0,0 +1,12 @@
+# Drupal 7
+
+All you need to do for your Drupal 7 module is to comment one line out in the default template, and uncomment another one.
+
+```
+      # COMMENT THIS ONE OUT
+      # '/includes/include.drupalci.main.yml'
+      # UNCOMMENT THIS ONE
+      - '/includes/include.drupalci.main-d7.yml'
+```
+
+That's it!
diff --git a/docs/info/setup.md b/docs/info/setup.md
new file mode 100644
index 00000000..453089f0
--- /dev/null
+++ b/docs/info/setup.md
@@ -0,0 +1,44 @@
+# Set up
+
+Your first step is to add a `.gitlab-ci.yml` file to the root of your project.
+
+The Drupal Association and the community have provided everything you need in a template to make it easy to get started. By using the Drupal Association `.gitlab-ci.yml` template, your project will be configured to run the most commonly needed test scenarios for you. This template also 'includes' supplementary configuration which can be updated by the Drupal Association on an ongoing basis. This means we can update 'variables' keywords to keep your testing up to date with the latest versions of Drupal and supported environments, and you do not need to make any changes to stay up to date!
+
+The template is configured to work by default with contrib modules for newer versions of Drupal (9 and above), so note that if you are adding this to your Drupal 7 contrib module, you will need to comment out one line and uncomment another. Everything is explained in the "`.gitlab-ci.yml`" file.
+
+## Using the Drupal Association .gitlab-ci.yml template
+
+The easiest way to do this is through the GitLab UI.
+
+If you are a maintainer, you can do this directly against the repository. But if you are a contributor, or even as a maintainer too, we recommend creating an issue in the project, open a fork and a Merge Request, and do the following:
+
+- From the fork page, navigate to the source code on `git.drupalcode.org/issue/fork-name/fork-branch-used-in-mr`.
+- Then use the repository UI (not the Web IDE) to add a new file.
+- Add a new file and type as file name `.gitlab-ci.yml`.
+- You will be presented with the option to select a template, select the Drupal Association's `template.gitlab-ci.yml` from the top of the list.
+- Commit that file.
+
+You can also do all the above manually via `git`.
+- Checkout your project or fork.
+- Create a new file called `.gitlab-ci.yml` in the root of the project.
+- Copy in the contents of the [template](https://git.drupalcode.org/project/gitlab_templates/-/blob/main/gitlab-ci/template.gitlab-ci.yml).
+- Commit and push your changes.
+
+## Confirm that it works
+
+When you first commit your `.gitlab-ci.yml` file the pipeline should be triggered automatically, either in the branch (if you are committing straight to it) or in the Merge Request (if you are doing the integration via an issue).
+
+## When will pipelines run?
+
+The default configuration from the provided template will run jobs when:
+
+- There is a merge request event
+- There is a commit tag
+- When an upstream pipeline calls it
+- There is a commit to the default branch
+- If triggered manually from the 'Run pipelines' function
+- If triggered manually from the Web IDE
+
+## Using your own .gitlab-ci.yml file
+
+If you would prefer to use your own `.gitlab-ci.yml` file, or to modify the template, you are certainly welcome to do so. GitLab provides extensive [documentation for GitLab CI](https://docs.gitlab.com/ee/ci/), or you can join the `#gitLab` channel in [Drupal Slack](https://www.drupal.org/join-slack).
diff --git a/docs/info/templates-version.md b/docs/info/templates-version.md
new file mode 100644
index 00000000..85781acc
--- /dev/null
+++ b/docs/info/templates-version.md
@@ -0,0 +1,24 @@
+# Templates behavior in contrib modules
+
+If a project wants to change the way to retrieve the default templates, go to
+the `.gitlab-ci.yml` file and find the line `ref: $_GITLAB_TEMPLATES_REF`. The
+value set in that line will determine _which_ version of the templates to use.
+
+- `ref: $_GITLAB_TEMPLATES_REF` - This is the recommended (and default) value.
+The Drupal Association will update this value to the recommended tag.
+- `ref: main` - Get the very latest additions and bug fixes as soon as they are
+merged into the templates.
+- `ref: 1.0.0` - a fixed known tag value. This will not move and not get any
+updates. It is up to the maintainer to change this when necessary.
+
+There are also some special _moving_ tags. With these you can get the latest
+changes without jumping a minor or major version. These tags are suffixed with
+`-latest` and can be checked in [this tags listing](https://git.drupalcode.org/project/gitlab_templates/-/tags).
+
+For example:
+- `ref: 1.0.x-latest` - moving tag for the latest bug fixes. It will always
+point to the latest *existing* `1.0.number` release. This mimics the `~1.0`
+semver constraint.
+- `ref: 1.x-latest` - moving tag for the latest minor features. It will always
+point to the latest *existing* `1.minor.number` release. This mimics the `~1`
+semver constraint.
diff --git a/docs/info/test-locally.md b/docs/info/test-locally.md
new file mode 100644
index 00000000..1a0daa07
--- /dev/null
+++ b/docs/info/test-locally.md
@@ -0,0 +1,67 @@
+# Running GitLab CI tests locally
+
+## ddev-drupal-contrib
+
+DDEV users working on contrib modules are encouraged to use the [ddev-drupal-contrib service](https://github.com/ddev/ddev-drupal-contrib). This assembles a codebase and provides composer scripts that mimic GitLab CI. See the repository README for detailed installation and usage instructions.
+
+## Running GitLab CI containers locally
+
+An alternative is to run our GitLab containers and tests locally. Unfortunately, the local runner from GitLab itself has been deprecated and [doesn't support a lot of features](https://docs.gitlab.com/runner/commands/#limitations-of-gitlab-runner-exec). But there is a package available on GitHub, [firecow/gitlab-ci-local](https://github.com/firecow/gitlab-ci-local), which supports most features.
+
+### Installation
+
+The easiest way to install the tool is to use npm: `npm install -g gitlab-ci-local`. You can also install the tool with apt on Debian-based systems or use Homebrew to install on MacOS.
+
+You also need to have Docker available, the best way to run docker depends on your environment.
+
+The package requires a few variables to be set so it understands the Drupal GitLab templates. To make it easier to run commands, use a bash script or an alias.
+
+For example, put this code in `/usr/local/bin/drupal-ci-local`
+
+```
+#!/bin/bash
+gitlab-ci-local \
+  --remote-variables git@git.drupal.org:project/gitlab_templates=includes/include.drupalci.variables.yml=main \
+  --variable="_GITLAB_TEMPLATES_REPO=project/gitlab_templates" \
+  "$@"
+```
+
+or as an alternative create an alias in your shell:
+```
+alias drupal-ci-local='gitlab-ci-local --remote-variables git@git.drupal.org:project/gitlab_templates=includes/include.drupalci.variables.yml=main --variable="_GITLAB_TEMPLATES_REPO=project/gitlab_templates"'
+```
+
+This way you can just call `drupal-ci-local` in your shell and run GitLab CI tests or test your `.gitlab.ci.yml`.
+
+### Running tests
+
+Now that you have the tooling installed, you can run the tests locally. To test if everything works. So for example:
+
+```
+$ git clone git@git.drupal.org:project/keycdn
+$ cd keycdn
+$ drupal-ci-local composer # runs the composer step
+$ drupal-ci-local # runs all steps!
+
+Warning: cloning from https:// urls does not work completly without issues.
+```
+
+All the changes that GitLab makes also are made to your project. So should you run into something really weird, you can actually debug it!
+
+This behavior can also be adjusted by using `--shell-isolation`. This option also makes sure artifact are handled correctly.
+
+If you want to inspect the templates, artifact or anything there is a directory in your project called `.gitlab-ci-local`. There you will find an artifacts directory. But that directory also contains the included templates and some other goodies.
+
+### Chromedriver tests issues
+
+There are still some quirks, when using chromedriver the configuration needs some changes to know where to go. There are multiple containers:
+
+- The main container is called: “build”
+- The database is called: “database”
+- The chromedriver is called: “chrome”
+
+The current implementation cheats. It makes all services available on localhost on the runner. This is not default behavior and uses a feature flag. The proper implementation would be to use the container names to get things up and running. This should be possible, but will need some work to make sure testing URLs and chromedriver URLs are correct.
+
+### Read the manual, there is more to discover
+
+Read the readme on the repository: [firecow/gitlab-ci-local](https://github.com/firecow/gitlab-ci-local). There are loads of other things you can do like global and local variables, use a .env file for settings, add bash completion, list jobs, and more.
\ No newline at end of file
diff --git a/docs/info/testing-mrs.md b/docs/info/testing-mrs.md
new file mode 100644
index 00000000..fb5843dc
--- /dev/null
+++ b/docs/info/testing-mrs.md
@@ -0,0 +1,16 @@
+# Testing Merge Requests
+
+If a project wants to test using a branch/MR of this repository, go to your project's CI/CD settings page and add the variables below. All pipelines in the project will use the specified repository+branch until the variables are removed.
+
+- KEY = `_GITLAB_TEMPLATES_REPO` VALUE = `issue/gitlab_templates-nnnnnnn`
+- KEY = `_GITLAB_TEMPLATES_REF` VALUE = `branch name`, often the format is `nnnnnnn-some-text`
+
+If your MR is testing assets provided by the fork (like modifications to the `.cspell.json` or `phpcs.xml.dist` file, or the `symlink_project.php` file), you might need to set up two additional variables: `_CURL_TEMPLATES_REF` and `_CURL_TEMPLATES_REPO`. This is only needed if you are modified the `project` and `ref` value directly in the `.gitlab-ci.yml` file.
+
+The variables should have the following attributes:
+
+- Type = Variable (default)
+- Environment Scope = All (default)
+- Protected = No
+- Masked = No
+- Expanded = Yes
diff --git a/docs/jobs.md b/docs/jobs.md
new file mode 100644
index 00000000..44dc00af
--- /dev/null
+++ b/docs/jobs.md
@@ -0,0 +1,82 @@
+---
+hide:
+  - toc
+---
+
+# Jobs
+
+We run multiple set up jobs (`composer`), lint jobs (`cspell`, `phpcs`...) and testing jobs (`phpunit`, `nightwatch`), and some of these also have the option to be run against previous or future Drupal versions.
+
+Learn more about the different options regarding jobs below.
+
+## SKIP and OPT_IN variables
+
+You can control which jobs run or not by setting up a series of `SKIP_` and `OPT_IN_` variables.
+
+You can skip:
+
+- Any of the validation steps: `cspell`, `phpcs`, `phpstan`, `eslint`, `stylelint`.
+- Any of the testing steps: `phpunit`, `nightwatch`, `test-only`.
+
+The current available skip-variables are:
+
+- `SKIP_COMPOSER_LINT`
+- `SKIP_PHPCS`
+- `SKIP_STYLELINT`
+- `SKIP_ESLINT`
+- `SKIP_CSPELL`
+- `SKIP_PHPSTAN`
+- `SKIP_NIGHTWATCH`
+- `SKIP_PHPUNIT`
+- `SKIP_TEST_ONLY_CHANGES`
+- `SKIP_PAGES`
+
+The _current_ version of all the above will be run by default, and you can skip on demand.
+
+The templates also support testing not only the current version (on by default), but also the next minor and major, or previous minor and major.
+All of these variants can be opted-in via the `OPT_IN_` variables.
+
+The current available opt-in variables are:
+
+- `OPT_IN_TEST_CURRENT`
+- `OPT_IN_TEST_MAX_PHP`
+- `OPT_IN_TEST_PREVIOUS_MINOR`
+- `OPT_IN_TEST_PREVIOUS_MAJOR`
+- `OPT_IN_TEST_NEXT_MINOR`
+- `OPT_IN_TEST_NEXT_MAJOR`
+
+
+## When do the jobs run?
+
+Each job will run or not run according to the following rules:
+
+| JOB NAME                    | Conditions when it will run                             | Conditions when it will NOT run                           |
+| --------------------------- | ------------------------------------------------------- | --------------------------------------------------------- |
+| composer                    | SKIP_COMPOSER_LINT=0 \|\| SKIP_PHPCS=0 \|\|<br>SKIP_STYLELINT=0 \|\| SKIP_ESLINT=0 \|\|<br>SKIP_CSPELL=0 \|\| OPT_IN_TEST_CURRENT=1 | SKIP_COMPOSER_LINT=1 && SKIP_PHPCS=1 &&<br>SKIP_STYLELINT=1 && SKIP_ESLINT=1 &&<br>SKIP_CSPELL=1 && OPT_IN_TEST_CURRENT=0 |
+| composer (max PHP version)  | OPT_IN_TEST_MAX_PHP=1                                   | OPT_IN_TEST_MAX_PHP=0                                     |
+| composer (previous minor)   | OPT_IN_TEST_PREVIOUS_MINOR=1                            | OPT_IN_TEST_PREVIOUS_MINOR=0                              |
+| composer (previous major)   | OPT_IN_TEST_PREVIOUS_MAJOR=1                            | OPT_IN_TEST_PREVIOUS_MAJOR=0                              |
+| composer (next minor)       | OPT_IN_TEST_NEXT_MINOR=1                                | OPT_IN_TEST_NEXT_MINOR=0                                  |
+| composer (next major)       | OPT_IN_TEST_NEXT_MAJOR=1                                | OPT_IN_TEST_NEXT_MAJOR=0                                  |
+| composer-lint               | SKIP_COMPOSER_LINT=0                                    | SKIP_COMPOSER_LINT=1                                      |
+| cspell                      | SKIP_CSPELL=0                                           | SKIP_CSPELL=1                                             |
+| eslint                      | SKIP_ESLINT=0                                           | SKIP_ESLINT=1                                             |
+| phpcs                       | SKIP_PHPCS=0                                            | SKIP_PHPCS=1                                              |
+| stylelint                   | SKIP_STYLELINT=0                                        | SKIP_STYLELINT=1                                          |
+| phpstan                     | SKIP_PHPSTAN=0 && OPT_IN_TEST_CURRENT=1                 | SKIP_PHPSTAN=1 \|\| OPT_IN_TEST_CURRENT=0                 |
+| phpstan (next minor)        | SKIP_PHPSTAN=0 && OPT_IN_TEST_NEXT_MINOR=1              | SKIP_PHPSTAN=1 \|\| OPT_IN_TEST_NEXT_MINOR=0              |
+| phpstan (next major)        | SKIP_PHPSTAN=0 && OPT_IN_TEST_NEXT_MAJOR=1              | SKIP_PHPSTAN=1 \|\| OPT_IN_TEST_NEXT_MAJOR=0              |
+| nightwatch                  | SKIP_NIGHTWATCH=0 && OPT_IN_TEST_CURRENT=1              | SKIP_NIGHTWATCH=1 \|\| OPT_IN_TEST_CURRENT=0              |
+| nightwatch (max PHP version)| SKIP_NIGHTWATCH=0 && OPT_IN_TEST_MAX_PHP=1              | SKIP_NIGHTWATCH=1 \|\| OPT_IN_TEST_MAX_PHP=0              |
+| nightwatch (previous minor) | SKIP_NIGHTWATCH=0 && OPT_IN_TEST_PREVIOUS_MINOR=1       | SKIP_NIGHTWATCH=1 \|\| OPT_IN_TEST_PREVIOUS_MINOR=0       |
+| nightwatch (previous major) | SKIP_NIGHTWATCH=0 && OPT_IN_TEST_PREVIOUS_MAJOR=1       | SKIP_NIGHTWATCH=1 \|\| OPT_IN_TEST_PREVIOUS_MAJOR=0       |
+| nightwatch (next minor)     | SKIP_NIGHTWATCH=0 && OPT_IN_TEST_NEXT_MINOR=1           | SKIP_NIGHTWATCH=1 \|\| OPT_IN_TEST_NEXT_MINOR=0           |
+| nightwatch (next major)     | SKIP_NIGHTWATCH=0 && OPT_IN_TEST_NEXT_MAJOR=1           | SKIP_NIGHTWATCH=1 \|\| OPT_IN_TEST_NEXT_MAJOR=0           |
+| phpunit                     | SKIP_PHPUNIT=0 && OPT_IN_TEST_CURRENT=1                 | SKIP_PHPUNIT=1 \|\| OPT_IN_TEST_CURRENT=0                 |
+| phpunit (max PHP version)   | SKIP_PHPUNIT=0 && OPT_IN_TEST_MAX_PHP=1                 | SKIP_PHPUNIT=1 \|\| OPT_IN_TEST_MAX_PHP=0                 |
+| phpunit (previous minor)    | SKIP_PHPUNIT=0 && OPT_IN_TEST_PREVIOUS_MINOR=1          | SKIP_PHPUNIT=1 \|\| OPT_IN_TEST_PREVIOUS_MINOR=0          |
+| phpunit (previous major)    | SKIP_PHPUNIT=0 && OPT_IN_TEST_PREVIOUS_MAJOR=1          | SKIP_PHPUNIT=1 \|\| OPT_IN_TEST_PREVIOUS_MAJOR=0          |
+| phpunit (next minor)        | SKIP_PHPUNIT=0 && OPT_IN_TEST_NEXT_MINOR=1              | SKIP_PHPUNIT=1 \|\| OPT_IN_TEST_NEXT_MINOR=0              |
+| phpunit (next major)        | SKIP_PHPUNIT=0 && OPT_IN_TEST_NEXT_MAJOR=1              | SKIP_PHPUNIT=1 \|\| OPT_IN_TEST_NEXT_MAJOR=0              |
+| test-only changes           | SKIP_TEST_ONLY_CHANGES=0 && OPT_IN_TEST_CURRENT=1       | SKIP_TEST_ONLY_CHANGES=1 \|\| OPT_IN_TEST_CURRENT=0       |
+| pages                       | SKIP_PAGES=0                                            | SKIP_PAGES=1                                              |
diff --git a/docs/jobs/composer-lint.md b/docs/jobs/composer-lint.md
new file mode 100644
index 00000000..591683f5
--- /dev/null
+++ b/docs/jobs/composer-lint.md
@@ -0,0 +1,10 @@
+# Composer Validation and PHP Parallel Lint
+
+This job validates the `composer.json` file via `composer validate`.
+
+It also uses the [php-parallel-lint](https://github.com/php-parallel-lint/PHP-Parallel-Lint) package to check the module's files.
+
+It is called like this at the moment of writing:
+`vendor/bin/parallel-lint --no-progress -e php,module,install,inc $_PARALLEL_LINT_EXTRA --exclude $_WEB_ROOT --exclude ./vendor .`
+
+If you need extra options for the command you can use the `_PARALLEL_LINT_EXTRA` variable.
diff --git a/docs/jobs/composer.md b/docs/jobs/composer.md
new file mode 100644
index 00000000..41ebce2d
--- /dev/null
+++ b/docs/jobs/composer.md
@@ -0,0 +1,54 @@
+# Composer
+
+The composer jobs are run automatically every time a related job needs something from `composer`.
+
+You cannot skip them unless you skip testing for a given Drupal variant (current, previous, next).
+
+## Updating dependencies when testing against future releases
+
+When testing against future releases, dependencies may not be compatible with the target release, so will need help to enable the test.
+
+### Lenient support
+
+The variable `LENIENT_ALLOW_LIST` can specify a comma-separated list of modules that composer dependencies on the core version will be ignored for. This is achieved using the [mglaman/composer-drupal-lenient](https://github.com/mglaman/composer-drupal-lenient) composer extension.
+
+An example of this can be:
+```
+variables:
+  OPT_IN_TEST_NEXT_MINOR: '1'
+  OPT_IN_TEST_NEXT_MAJOR: '1'
+  OPT_IN_TEST_MAX_PHP: '1'
+
+composer (next minor):
+  variables:
+    LENIENT_ALLOW_LIST: token
+
+composer (next major):
+  variables:
+    LENIENT_ALLOW_LIST: entity_reference_revisions,inline_entity_form,paragraphs,token
+```
+
+In this example, the lenient plugin will NOT be used in the regular composer task. But it will be used for the next minor and next major with different lists of projects that are allowed, even if those projects are not compatible with the new release.
+
+
+### Composer patches
+
+Sometimes, you may need to install a patch for a dependent module to allow tests to pass.
+
+To this, add a JSON file containing patches, and specify it in the `COMPOSER_PATCHES_FILE` variable. For example:
+```
+composer (next major):
+  variables:
+    LENIENT_ALLOW_LIST: token
+    COMPOSER_PATCHES_FILE: .gitlab-ci/nextmajor_patches.json
+```
+
+We recommend placing the patches within the `.gitlab-ci` folder inside your module.
+
+Additional information on the patch file can be found in the [Composer Patches documentation](https://docs.cweagans.net/composer-patches//usage/defining-patches/#patches-file).
+
+### Package versions
+
+The templates will favor the `drupal/core-recommended` package when installing Drupal, which does NOT include a `composer.lock` file. This is expected and is the recommended usage for most cases.
+
+If your module relies on specific versions of certain packages from core you need to update your module's `composer.json` to declare this.
diff --git a/docs/jobs/cspell.md b/docs/jobs/cspell.md
new file mode 100644
index 00000000..3bb66404
--- /dev/null
+++ b/docs/jobs/cspell.md
@@ -0,0 +1,25 @@
+# CSpell
+
+It will run a [cspell](https://cspell.org/) check against the module's code.
+
+This job has some related variables that are available:
+
+- `_CSPELL_WORDS` for a list of allowed words
+- `_CSPELL_IGNORE_PATHS` for a list of paths to ignore
+
+If the module does not have a `.cspell.json` file, it will take [this one](https://git.drupalcode.org/project/gitlab_templates/-/blob/main/assets/.cspell.json), which uses the `drupal` dictionary.
+
+## How do I ignore words that CSpell thinks are spelling mistakes?
+
+The simplest solution is to either fill up the `_CSPELL_WORDS` variable or to add a `.cspell-project-words.txt` file to your project, which follows [CSpell's Words List Syntax](https://cspell.org/docs/dictionaries-custom/#words-list-syntax) (ie: one word per line; lines starting with `#` are comments, words prefixed with `~` are matched case-insensitively, etc.).
+
+To ignore one or more paths, add a `_CSPELL_IGNORE_PATHS` variable to your project's `.gitlab-ci.yml`. Use double quotes and comma separation:
+
+```
+variables:
+  _CSPELL_IGNORE_PATHS: "\"tests/data/*\", \"**/*.log\""
+```
+
+For more advanced configuration, you can add a `.cspell.json` file to your project, which follows [CSpell's configuration syntax](https://cspell.org/configuration/). Start by copying [Drupal.org's GitLab Template](https://git.drupalcode.org/project/gitlab_templates/-/blob/main/assets/.cspell.json) into your project, and customizing it to your needs.
+
+To skip CSpell checking altogether, add a `SKIP_CSPELL: "1"` variable to your project's `.gitlab-ci.yml`.
diff --git a/docs/jobs/eslint.md b/docs/jobs/eslint.md
new file mode 100644
index 00000000..70e02e5a
--- /dev/null
+++ b/docs/jobs/eslint.md
@@ -0,0 +1,7 @@
+# ESLint
+
+If the module has `.js` or `.yml` files, the [eslint](https://eslint.org/) too will run against them.
+
+You can use `.prettierrc.json` or `.prettierignore` files to configure the behavior of this job.
+
+If there are errors found in the job, you will get a useful artifact named `_eslint.patch` which you could run against your module's code to fix the issues that were found.
diff --git a/docs/jobs/nightwatch.md b/docs/jobs/nightwatch.md
new file mode 100644
index 00000000..175d028c
--- /dev/null
+++ b/docs/jobs/nightwatch.md
@@ -0,0 +1,9 @@
+# Nightwatch
+
+If your module contains [nightwatch](https://nightwatchjs.org/) tests, this job will run them.
+
+It will set up a chrome browser and run the tests against it.
+
+The results will be available as a `junit` artifact within the job.
+
+This job allows for multiple variants to check previous, current and future versions of Drupal.
diff --git a/docs/jobs/pages.md b/docs/jobs/pages.md
new file mode 100644
index 00000000..8c31f8f5
--- /dev/null
+++ b/docs/jobs/pages.md
@@ -0,0 +1,8 @@
+# GitLab Pages
+
+Projects can automatically publish a documentation site powered by [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) and [GitLab Pages](https://docs.gitlab.com/ee/user/project/pages/).
+
+- Get started by creating `mkdocs.yml` and `docs/index.md` files.
+- GitLab Pages has a [tutorial on how to write these files](https://docs.gitlab.com/ee/user/project/pages/getting_started/pages_from_scratch.html).
+- With those in place, a site gets re-published every time the default branch changes. We only auto-publish the default branch for now.
+- [Material for MkDocs has a great docs site](https://squidfunk.github.io/mkdocs-material/) and offers lots of plugins that should work on our GitLab pages. You may need to install their prerequisites in a `before_script` step in the pages job.
diff --git a/docs/jobs/phpcs.md b/docs/jobs/phpcs.md
new file mode 100644
index 00000000..28c5abaf
--- /dev/null
+++ b/docs/jobs/phpcs.md
@@ -0,0 +1,9 @@
+# PHP_CodeSniffer
+
+This job will validate the module's code against Drupal coding standards.
+
+It uses [PHP_CodeSniffer](https://github.com/PHPCSStandards/PHP_CodeSniffer/) library and configures the Drupal coding standards.
+
+If the module does not have a default `phpcs.xml` or `phpcs.xml.dist`, it will use [this one](https://git.drupalcode.org/project/gitlab_templates/-/blob/main/scripts/phpcs.xml.dist).
+
+If it has one, then it will use it.
diff --git a/docs/jobs/phpstan.md b/docs/jobs/phpstan.md
new file mode 100644
index 00000000..301697e5
--- /dev/null
+++ b/docs/jobs/phpstan.md
@@ -0,0 +1,9 @@
+# PHPStan
+
+This job runs [phpstan](https://phpstan.org/) checks on your module.
+
+If there is a baseline file in the module it will use it, but if there isn't, it will just run default values.
+
+It will offer a baseline file for module maintainers to add it to their projects to make adoption easier.
+
+If you do not want it to run in your contrib module, you just need to set `SKIP_PHPSTAN` to 1.
diff --git a/docs/jobs/phpunit.md b/docs/jobs/phpunit.md
new file mode 100644
index 00000000..fd94e823
--- /dev/null
+++ b/docs/jobs/phpunit.md
@@ -0,0 +1,25 @@
+# PHPUnit
+
+It will run the tests of your module using [phpunit](https://phpunit.de/index.html), but there are two flavors that you can choose from.
+
+## Using phpunit binary directly.
+
+This is the default option. The tests will be run calling the `vendor/bin/phpunit` binary directly and in a sequential order. As there is no concurrency, the tests here might take a bit longer.
+
+You can use the `_PHPUNIT_EXTRA` variable to add extra options to the `phpunit` binary.
+
+
+## Using run-tests.sh core script (aka concurrent mode)
+
+This script is included in Drupal core and leverages concurrency and also runs the tests in the same way that Drupal core tests are run. As there is concurrency, the tests here might run a little bit quicker than with the sequential option.
+
+You just need to set `_PHPUNIT_CONCURRENT` to `1` in the `variables` section and that's it. In this case, the `_PHPUNIT_EXTRA` variable will contain options compatible with the `run-test.sh` script. In this case, you can also add `parallel` options to the job definition, to split out the tests into multiple jobs running in parallel, like this:
+
+```
+phpunit:
+  parallel: 3
+```
+
+## Variants
+
+This job allows for multiple variants to check previous, current and future versions of Drupal.
diff --git a/docs/jobs/stylelint.md b/docs/jobs/stylelint.md
new file mode 100644
index 00000000..e0030473
--- /dev/null
+++ b/docs/jobs/stylelint.md
@@ -0,0 +1,5 @@
+# Stylelint
+
+This job will run [stylelint](https://stylelint.io/) checks on the CSS code of the module
+
+You can add extra options to the command using the `_STYLELINT_EXTRA` variable.
diff --git a/docs/jobs/test-only-changes.md b/docs/jobs/test-only-changes.md
new file mode 100644
index 00000000..0274d409
--- /dev/null
+++ b/docs/jobs/test-only-changes.md
@@ -0,0 +1,14 @@
+# Test-only changes
+
+The purpose of this job is to run only the tests that have been modified in a Merge Request.
+All the non-test files are being reverted back to their unchanged state.
+
+This is a way to demonstrate that the changes in the tests provide test coverage for the bug/problem/error which is being fixed. It is equivalent to the "test-only will fail" patches that have been used on DrupalCI for contrib projects and for core over the past years. Drupal Core has had a 'test-only changes' job step, but this feature is new for contrib projects.
+
+This job is only created in Merge Requests, it will not appear in branch pipelines. It will only be created if the merge request contains changes to phpunit test files.
+
+It is `manual`, which means it will not start automatically but will require you to click the `[>]` 'play' button.
+
+The log has extra information showing what files have been changed in the merge request and what files have been reverted before running the tests. The job is intended to fail with an amber warning, as this demonstrates that the modified tests do provide test coverage for the problem being fixed.
+
+If the job ends with a green pass this means that the bug does not have test coverage (because the tests pass when the error is still present).
diff --git a/docs/logo.png b/docs/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..6a81f415e1012f1dcafe3871d7779fee77fb6db2
GIT binary patch
literal 12547
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

literal 0
HcmV?d00001

diff --git a/includes/include.drupalci.main-d7.yml b/includes/include.drupalci.main-d7.yml
index 3a8929fd..69748628 100644
--- a/includes/include.drupalci.main-d7.yml
+++ b/includes/include.drupalci.main-d7.yml
@@ -186,7 +186,7 @@ stages:
 # Jobs define what scripts are actually executed in each stage.
 #
 # The 'rules' keyword can also be used to define conditions for each job.
-# # @TODO: Use rules to define core version to test against, allow overriding default on manual run with variables. (Perhaps with 'variables in variables')
+# Use rules to define core version to test against, allow overriding default on manual run with variables.
 #
 # Documentation: https://docs.gitlab.com/ee/ci/jobs/
 ################
diff --git a/includes/include.drupalci.main.yml b/includes/include.drupalci.main.yml
index 77dcadc2..617108b8 100644
--- a/includes/include.drupalci.main.yml
+++ b/includes/include.drupalci.main.yml
@@ -225,7 +225,7 @@ stages:
 # Jobs define what scripts are actually executed in each stage.
 #
 # The 'rules' keyword can also be used to define conditions for each job.
-# # @TODO: Use rules to define core version to test against, allow overriding default on manual run with variables. (Perhaps with 'variables in variables')
+# Use rules to define core version to test against, allow overriding default on manual run with variables.
 #
 # Documentation: https://docs.gitlab.com/ee/ci/jobs/
 ################
diff --git a/includes/include.drupalci.workflows.yml b/includes/include.drupalci.workflows.yml
index 5652a08a..c993dd33 100644
--- a/includes/include.drupalci.workflows.yml
+++ b/includes/include.drupalci.workflows.yml
@@ -6,14 +6,13 @@
 #     * On commit
 #     * On merge request
 #     * On manual trigger
-#     * etc. 
-# https://docs.gitlab.com/ee/ci/jobs/job_control.html#specify-when-jobs-run-with-rules 
-# 
-# Pipelines can also be configured to run on a schedule,though they still must meet the conditions defined in Workflow and Rules. This can be used, for example, to do nightly regression testing: 
-# https://gitlab.com/help/ci/pipelines/schedules 
+#     * etc.
+# https://docs.gitlab.com/ee/ci/jobs/job_control.html#specify-when-jobs-run-with-rules
+#
+# Pipelines can also be configured to run on a schedule,though they still must meet the conditions defined in Workflow and Rules. This can be used, for example, to do nightly regression testing:
+# https://gitlab.com/help/ci/pipelines/schedules
 ################
 
-# @TODO: Still not sure what the best defaults should be for this template
 workflow:
   rules:
   # These 3 rules from https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Workflows/MergeRequest-Pipelines.gitlab-ci.yml
@@ -32,4 +31,4 @@ workflow:
     # Run if triggered from Web using 'Run Pipelines'
     - if: $CI_PIPELINE_SOURCE == "web"
      # Run if triggered from WebIDE
-    - if: $CI_PIPELINE_SOURCE == "webide"   
+    - if: $CI_PIPELINE_SOURCE == "webide"
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 00000000..a94aa009
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,60 @@
+site_name: 'GitLab Templates'
+repo_url: https://git.drupalcode.org/project/gitlab_templates
+repo_name: drupal/gitlab_templates
+edit_uri: /-/edit/main/docs/
+theme:
+  name: material
+  logo: logo.png
+  favicon: logo.png
+  palette:
+    # Palette toggle for automatic mode
+    - media: "(prefers-color-scheme)"
+      primary: blue
+      toggle:
+        icon: material/brightness-auto
+        name: Switch to light mode
+    # Palette toggle for light mode
+    - media: "(prefers-color-scheme: light)"
+      primary: blue
+      scheme: default
+      toggle:
+        icon: material/brightness-7
+        name: Switch to dark mode
+    # Palette toggle for dark mode
+    - media: "(prefers-color-scheme: dark)"
+      primary: blue
+      scheme: slate
+      toggle:
+        icon: material/brightness-4
+        name: Switch to system preference
+  features:
+nav:
+  - Home: 'index.md'
+  - Get started:
+    - Set up: 'info/setup.md'
+    - Common tasks: 'info/common.md'
+    - Customizations: 'info/customizations.md'
+    - Test locally: 'info/test-locally.md'
+    - Templates version: 'info/templates-version.md'
+    - Drupal 7: 'info/drupal7.md'
+  - Jobs:
+    - When do they run: 'jobs.md'
+    - composer: 'jobs/composer.md'
+    - composer-lint: 'jobs/composer-lint.md'
+    - cspell: 'jobs/cspell.md'
+    - eslint: 'jobs/eslint.md'
+    - phpcs: 'jobs/phpcs.md'
+    - phpstan: 'jobs/phpstan.md'
+    - stylelint: 'jobs/stylelint.md'
+    - phpunit: 'jobs/phpunit.md'
+    - test-only changes: 'jobs/test-only-changes.md'
+    - nightwatch: 'jobs/nightwatch.md'
+    - pages: 'jobs/pages.md'
+  - Help us:
+    - Help us: 'help/help.md'
+    - With the code: 'help/contributing.md'
+    - With the documentation: 'help/documentation.md'
+plugins:
+  - search
+  - privacy:
+      enabled: !ENV [CI, false]
\ No newline at end of file
-- 
GitLab