| Server IP : 188.151.22.197 / Your IP : 216.73.217.74 Web Server : Apache/2.4.62 (Rocky Linux) OpenSSL/3.5.5 System : Linux wsten.se 5.14.0-687.30.1.el9_8.x86_64 #1 SMP PREEMPT_DYNAMIC Mon Jul 27 13:09:21 UTC 2026 x86_64 User : apache ( 48) PHP Version : 8.1.32 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /opt/gitlab/embedded/service/gitlab-rails/scripts/ |
Upload File : |
#!/usr/bin/env bash
# shellcheck disable=2059
set -o pipefail
ERROR_COLOR_SET="\e[1;31m"
INFO_COLOR_SET="\e[1;32m"
COLOR_RESET="\e[0m"
# Directories for projects that are included on the GitLab Docs website in addition to the 'gitlab' project
EXTERNAL_DOCS_PROJECTS="omnibus charts runner operator cli"
cd "$(dirname "$0")/.." || exit 1
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} Linting '$(pwd)' as $(whoami)...\n"
ERRORCODE=0
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} Checking '$(pwd)/doc' for files with cURL short options...\n"
FILES_WITH_CURL_SHORT_OPTIONS=$(grep --extended-regexp --recursive --color=auto 'curl (.+ )?-[^- ].*' doc/)
if [ "${FILES_WITH_CURL_SHORT_OPTIONS}" != "" ]; then
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} cURL short options found! Use long options in these files instead (for example, --header instead of -H):\n"
printf " ${FILES_WITH_CURL_SHORT_OPTIONS}\n"
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} For more information, see https://docs.gitlab.com/development/documentation/restful_api_styleguide/#curl-commands\n\n"
((ERRORCODE++))
fi
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} Checking '$(pwd)' for CHANGELOG.md with duplicate entries...\n"
DUPLICATE_CHANGELOG_VERSIONS=$(grep --extended-regexp '^## .+' CHANGELOG.md | sed -E 's| \(.+\)||' | sort -r | uniq -d)
if [ "${DUPLICATE_CHANGELOG_VERSIONS}" != "" ]; then
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} Duplicate versions found! Check these versions in CHANGELOG.md:\n"
printf " ${DUPLICATE_CHANGELOG_VERSIONS}\n\n"
((ERRORCODE++))
fi
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} Checking '$(pwd)/doc' for files with executable permissions...\n"
EXECUTABLE_PERMISSIONS_FILES=$(find doc -type f -perm 755)
if [ "${EXECUTABLE_PERMISSIONS_FILES}" != "" ]; then
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} Executable permissions found! Use 'chmod 644' on these files:\n"
printf " ${EXECUTABLE_PERMISSIONS_FILES}\n\n"
((ERRORCODE++))
fi
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} Checking '$(pwd)/doc' for README.md files...\n"
README_FILES=$(find doc -name "README.md")
if [ "${README_FILES}" != "" ]; then
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} README.md files found! Rename these files:\n"
printf " ${README_FILES}\n"
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} For more information, see https://docs.gitlab.com/development/documentation/site_architecture/folder_structure/#work-with-directories-and-files\n\n"
((ERRORCODE++))
fi
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} Checking '$(pwd)/doc' for directory names containing dashes...\n"
DIRECTORY_NAMES_WITH_DASHES=$(find doc -type d -name "*-*")
if [ "${DIRECTORY_NAMES_WITH_DASHES}" != "" ]; then
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} Directory names with dashes found! Use underscores instead of dashes for these directory names:\n"
printf " ${DIRECTORY_NAMES_WITH_DASHES}\n"
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} For more information, see https://docs.gitlab.com/development/documentation/site_architecture/folder_structure/#work-with-directories-and-files\n\n"
((ERRORCODE++))
fi
# Number of filenames with dashes as of 2025-10-14
FILE_NUMBER_DASHES=20
FILENAMES_WITH_DASHES=$(find doc -type f -name "*-*.md" | wc -l)
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} Checking '$(pwd)/doc' for filenames containing dashes...\n"
if [ "${FILENAMES_WITH_DASHES}" -ne $FILE_NUMBER_DASHES ]
then
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} The number of filenames containing dashes has changed! Use underscores instead of dashes for filenames.\n"
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} If removing a file with a filename containing dashes, update the variable FILE_NUMBER_DASHES in scripts/lint-doc.sh.\n"
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} For more information, see https://docs.gitlab.com/development/documentation/site_architecture/folder_structure/#work-with-directories-and-files\n\n"
((ERRORCODE++))
fi
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} Checking '$(pwd)/doc' for directory names containing an uppercase letter...\n"
DIRECTORY_NAMES_WITH_UPPERCASE=$(find doc -type d -name "*[[:upper:]]*")
if [ "${DIRECTORY_NAMES_WITH_UPPERCASE}" != "" ]; then
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} Directory names with an uppercase letter found! Use lowercase instead for these directories:\n"
printf " ${DIRECTORY_NAMES_WITH_UPPERCASE}\n"
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} For more information, see https://docs.gitlab.com/development/documentation/site_architecture/folder_structure/#work-with-directories-and-files\n\n"
((ERRORCODE++))
fi
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} Checking '$(pwd)/doc' for images without a milestone in the filename...\n"
VERSIONLESS_IMAGES=$(find doc -name "*.png" | grep -Ev '_v[0-9][0-9]?_.+\.png$')
if [ "${VERSIONLESS_IMAGES}" != "" ]; then
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} Images without a milestone in the filename found! Append a milestone ('_vXX_Y') to the filename of these images:\n"
printf " ${VERSIONLESS_IMAGES}\n"
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} For more information, see https://docs.gitlab.com/development/documentation/styleguide/#image-requirements\n\n"
((ERRORCODE++))
fi
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} Checking '$(pwd)/doc' for filenames containing an uppercase letter...\n"
# AGENTS.md is excluded because it follows the AI agent convention of uppercase filenames
FILENAMES_WITH_UPPERCASE=$(find doc -type f -name "*[[:upper:]]*.md" ! -name "AGENTS.md")
if [ "${FILENAMES_WITH_UPPERCASE}" != "" ]; then
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} Filenames with an uppercase letter found! Use lowercase letters instead for these filenames:\n"
printf " ${FILENAMES_WITH_UPPERCASE}\n"
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} For more information, see https://docs.gitlab.com/development/documentation/site_architecture/folder_structure/#work-with-directories-and-files\n\n"
((ERRORCODE++))
fi
# Run Vale and Markdownlint only on changed files. Only works on merged results
# pipelines, so first checks if a merged results CI/CD variable is present. If not present,
# runs test on all files.
if [ -n "$1" ]; then
MD_DOC_PATH="$@"
MD_DOC_PATH_VALE="$@"
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} List of files specified on command line. Running Markdownlint and Vale for only those files...\n"
elif [ -n "${CI_MERGE_REQUEST_IID}" ]; then
DOC_CHANGES_FILE=$(mktemp)
ruby -r './tooling/lib/tooling/find_changes' -e "Tooling::FindChanges.new(
from: :api,
changed_files_pathname: '${DOC_CHANGES_FILE}',
file_filter: ->(file) { !file['deleted_file'] && file['new_path'] =~ %r{\A(?:doc/(.*\.md|\.markdownlint|\.vale)|\.vale\.ini|\.markdownlint-cli2.yaml|scripts/lint-doc\.sh|\.gitlab/ci/docs\.gitlab-ci\.yml)} },
only_new_paths: true
).execute"
if grep -qE "\.vale|\.markdownlint|lint-doc\.sh|docs\.gitlab-ci\.yml" < $DOC_CHANGES_FILE; then
MD_DOC_PATH=${MD_DOC_PATH:-'doc/{*,**/*}.md'}
MD_DOC_PATH_VALE=${MD_DOC_PATH_VALE:-'doc/'}
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} Vale, Markdownlint, lint-doc.sh, or pipeline configuration changed. Testing all files.\n"
else
MD_DOC_PATH=$(cat $DOC_CHANGES_FILE)
MD_DOC_PATH_VALE=$(cat $DOC_CHANGES_FILE)
if [ -n "${MD_DOC_PATH}" ]; then
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} Merge request pipeline detected. Testing only the following files:\n${MD_DOC_PATH}\n"
fi
fi
rm $DOC_CHANGES_FILE
else
MD_DOC_PATH=${MD_DOC_PATH:-'doc/{*,**/*}.md'}
MD_DOC_PATH_VALE=${MD_DOC_PATH_VALE:-'doc/'}
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} No merge request pipeline detected. Running Markdownlint and Vale on all files...\n"
fi
function run_locally_or_in_container() {
local cmd=$1
local args=$2
local files=$3
local registry_url="registry.gitlab.com/gitlab-org/technical-writing/docs-gitlab-com/lint-markdown:alpine-3.22-vale-3.13.0-markdownlint2-0.19.0-lychee-0.21.0"
if hash "${cmd}" 2>/dev/null; then
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} Found locally installed ${cmd}! Running...\n"
$cmd $args $files
# When using software like Rancher Desktop, both nerdctl and docker binaries are available
# but only one is configured. To check which one to use, we need to probe each runtime
elif (hash nerdctl 2>/dev/null) && (nerdctl info > /dev/null 2>&1); then
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} Found nerdctl! Using linting image to run ${cmd}...\n"
nerdctl run -t -v "${PWD}:/gitlab" -w /gitlab --rm ${registry_url} ${cmd} ${args}
elif (hash docker 2>/dev/null) && (docker info > /dev/null 2>&1); then
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} Found docker! Using linting image to run ${cmd}...\n"
docker run -t -v "${PWD}:/gitlab" -w /gitlab --rm ${registry_url} ${cmd} ${args}
else
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} ${cmd} not found! Install ${cmd} locally, or install a container runtime (docker or nerdctl) and try again.\n"
((ERRORCODE++))
fi
if [ $? -ne 0 ]; then
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} ${cmd} failed with errors!\n"
((ERRORCODE++))
fi
}
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} Linting markdown style...\n"
if [ -z "${MD_DOC_PATH}" ]; then
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} Merge request pipeline detected, but no markdown files found. Skipping.\n"
else
if ! markdownlint-cli2 ${MD_DOC_PATH}; then
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} Markdownlint failed with errors!\n"
((ERRORCODE++))
fi
fi
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} Looking for Vale to lint prose, either installed locally or available in documentation linting image...\n"
run_locally_or_in_container 'vale' "--minAlertLevel error --output=doc/.vale/vale.tmpl" "${MD_DOC_PATH_VALE}"
# Check for restricted directory names that would conflict with other project's docs
printf "${INFO_COLOR_SET}INFO${COLOR_RESET} Checking for restricted directory names...\n"
for dir in $EXTERNAL_DOCS_PROJECTS; do
if [ -d "doc/$dir" ]; then
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} Found restricted directory name '${dir}' in doc/ directory!\n"
printf "${ERROR_COLOR_SET}ERROR${COLOR_RESET} This directory name conflicts with existing documentation repositories.\n"
((ERRORCODE++))
break
fi
done
if [ "$ERRORCODE" -ne 0 ]; then
printf "\n${ERROR_COLOR_SET}ERROR${COLOR_RESET} lint tests failed! Review the log carefully to see full listing.\n"
exit 1
else
printf "\n${INFO_COLOR_SET}INFO${COLOR_RESET} Linting passed.\n"
exit 0
fi