grafana/docs/make-docs
Jack Baldry 560f49b3dc
Enable doc-validator for specific directories (#63524)
* Enable doc-validator for specific directories

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* Fix one linting error to trigger CI

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* Update doc-validator to latest release

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* Update make-docs procedure

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* Use doc-validator version from CI in local make target

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* Revert to 1.11.0

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* adds missing descriptions

* Fix titles and headings

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* Fix link formats

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* Fix easy to resolve anchors

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* Remove broken anchor link

This anchor appears to have been broken for a long time.

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* Use doc-validator image with support for numbered anchors

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* Update make-docs procedure to support doc-validator 2.0.x

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* Fix a bunch of broken anchors

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* Ignore old whatsnew content

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* Update doc-validator to v2.0.x and use reviewdog to report errors

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>

* removes broken links

---------

Signed-off-by: Jack Baldry <jack.baldry@grafana.com>
Co-authored-by: Chris Moyer <chris.moyer@grafana.com>
2023-05-18 15:50:20 +01:00

427 lines
12 KiB
Bash
Executable File

#!/bin/sh
# The source of this file is https://raw.githubusercontent.com/grafana/writers-toolkit/main/docs/make-docs.
# 2.0.0 (2023-05-18)
set -ef
readonly DOCS_CONTAINER="${DOCS_CONTAINER:-make-docs}"
readonly DOCS_HOST_PORT="${DOCS_HOST_PORT:-3002}"
readonly DOCS_IMAGE="${DOCS_IMAGE:-grafana/docs-base:latest}"
readonly DOC_VALIDATOR_INCLUDE="${DOC_VALIDATOR_INCLUDE:-.+\.md$}"
readonly DOC_VALIDATOR_SKIP_CHECKS="${DOC_VALIDATOR_SKIP_CHECKS:-^image-}"
readonly HUGO_REFLINKSERRORLEVEL="${HUGO_REFLINKSERRORLEVEL:-WARNING}"
readonly VALE_MINALERTLEVEL="${VALE_MINALERTLEVEL:-error}"
readonly WEBSITE_EXEC="${WEBSITE_EXEC:-make docs}"
# If set, the docs-base image will run a prebuild script that sets up Hugo mounts.
readonly WEBSITE_MOUNTS="${WEBSITE_MOUNTS:-}"
PODMAN="$(if command -v podman >/dev/null 2>&1; then echo podman; else echo docker; fi)"
about() {
cat <<EOF
Test documentation locally with multiple source repositories.
The REPOS_PATH environment variable is a colon (:) separated list of paths in which to look for project repositories.
EOF
}
usage() {
cat <<EOF
Usage:
REPOS_PATH=<PATH[:<PATH>...]> $0 [<PROJECT>[:<VERSION>[:<REPO>[:<DIR>]]]...]
Examples:
REPOS_PATH=~/ext/grafana/ $0 writers-toolkit tempo:latest helm-charts/mimir-distributed:latest:mimir:docs/sources/mimir-distributed
EOF
}
if [ $# -lt 1 ]; then
cat <<EOF >&2
ERRR: arguments required but not supplied.
$(about)
$(usage)
EOF
exit 1
fi
readonly REPOS_PATH="${REPOS_PATH:-$(realpath "$(git rev-parse --show-toplevel)/..")}"
if [ -z "${REPOS_PATH}" ]; then
cat <<EOF >&2
ERRR: REPOS_PATH environment variable is required but has not been provided.
$(usage)
EOF
exit 1
fi
SOURCES_as_code='as-code-docs'
SOURCES_enterprise_metrics='backend-enterprise'
SOURCES_enterprise_metrics_='backend-enterprise'
SOURCES_grafana_cloud='website'
SOURCES_grafana_cloud_k6='k6-docs'
SOURCES_grafana_cloud_data_configuration_integrations='cloud-onboarding'
SOURCES_grafana_cloud_frontend_observability_faro_web_sdk='faro-web-sdk'
SOURCES_grafana_cloud_machine_learning='machine-learning'
SOURCES_helm_charts_mimir_distributed='mimir'
SOURCES_helm_charts_tempo_distributed='tempo'
SOURCES_opentelemetry='opentelemetry-docs'
VERSIONS_as_code='UNVERSIONED'
VERSIONS_grafana_cloud='UNVERSIONED'
VERSIONS_grafana_cloud_k6='UNVERSIONED'
VERSIONS_grafana_cloud_data_configuration_integrations='UNVERSIONED'
VERSIONS_grafana_cloud_frontend_observability_faro_web_sdk='UNVERSIONED'
VERSIONS_grafana_cloud_machine_learning='UNVERSIONED'
VERSIONS_opentelemetry='UNVERSIONED'
VERSIONS_technical_documentation='UNVERSIONED'
VERSIONS_website='UNVERSIONED'
VERSIONS_writers_toolkit='UNVERSIONED'
PATHS_grafana_cloud='content/docs/grafana-cloud'
PATHS_helm_charts_mimir_distributed='docs/sources/helm-charts/mimir-distributed'
PATHS_helm_charts_tempo_distributed='docs/sources/helm-charts/tempo-distributed'
PATHS_mimir='docs/sources/mimir'
PATHS_tempo='docs/sources/tempo'
PATHS_website='content/docs'
# identifier STR
# Replace characters that are not valid in an identifier with underscores.
identifier() {
echo "$1" | tr -C '[:alnum:]_\n' '_'
}
# aget ARRAY KEY
# Get the value of KEY from associative array ARRAY.
# Characters that are not valid in an identifier are replaced with underscores.
aget() {
eval echo '$'"$(identifier "$1")_$(identifier "$2")"
}
# new_proj populates a new project structure.
new_proj() {
_project="$1"
_version="$2"
_repo="$3"
_path="$4"
# If version is not set, use the script mapping of project to default versions if it exists.
# Fallback to 'latest'.
if [ -z "${_version}" ]; then
if [ -z "$(aget VERSIONS "${_project}")" ]; then
_version=latest
else
_version="$(aget VERSIONS "${_project}")"
fi
fi
# If repo is not set, use the script mapping of project to repo name if it exists.
# Fallback to using the project name.
if [ -z "${_repo}" ]; then
if [ -z "$(aget SOURCES "${_project}")" ]; then
_repo="${_project}"
else
_repo="$(aget SOURCES "${_project}")"
fi
fi
# If path is not set, use the script mapping of project to docs sources path if it exists.
# Fallback to using 'docs/sources'.
if [ -z "${_path}" ]; then
if [ -z "$(aget PATHS "${_project}")" ]; then
_path="docs/sources"
else
_path="$(aget PATHS "${_project}")"
fi
fi
echo "${_project}:${_version}:${_repo}:${_path}"
unset _project _version _repo _path
}
# proj_url returns the webserver URL for a project.
# It expects a complete project structure as input.
proj_url() {
IFS=: read -r _project _version _ _ <<POSIX_HERESTRING
$1
POSIX_HERESTRING
if [ "${_project}" = 'website' ]; then
echo "http://localhost:${DOCS_HOST_PORT}/docs/"
unset _project _version
return
fi
if [ -z "${_version}" ] || [ "${_version}" = 'UNVERSIONED' ]; then
echo "http://localhost:${DOCS_HOST_PORT}/docs/${_project}/"
else
echo "http://localhost:${DOCS_HOST_PORT}/docs/${_project}/${_version}/"
fi
unset _project _version
}
# proj_ver returns the version for a project.
# It expects a complete project structure as input.
proj_ver() {
IFS=: read -r _ _ver _ _ <<POSIX_HERESTRING
$1
POSIX_HERESTRING
echo "${_ver}"
unset _ver
}
# proj_dst returns the container path to content source for a project.
# It expects a complete project structure as input.
proj_dst() {
IFS=: read -r _project _version _ _ <<POSIX_HERESTRING
$1
POSIX_HERESTRING
if [ "${_project}" = 'website' ]; then
echo '/hugo/content/docs'
unset _project _version
return
fi
if [ -z "${_version}" ] || [ "${_version}" = 'UNVERSIONED' ]; then
echo "/hugo/content/docs/${_project}"
else
echo "/hugo/content/docs/${_project}/${_version}"
fi
unset _project _version
}
# proj_src returns the host path to content source for a project.
# It expects a complete project structure as input.
# It looks for the provided repository name in each of the paths specified in the REPOS_PATH environment variable.
proj_src() {
IFS=: read -r _ _ _repo _path <<POSIX_HERESTRING
$1
POSIX_HERESTRING
IFS=:
for lookup in ${REPOS_PATH}; do
if [ -d "${lookup}/${_repo}" ]; then
echo "${lookup}/${_repo}/${_path}"
unset _path _repo
return
fi
done
unset IFS
echo "ERRR: could not find project '${_repo}' in any of the paths in REPOS_PATH '${REPOS_PATH}'." >&2
echo "NOTE: you must have a checkout of the project '${_repo}' at '${REPOS_PATH##:*}/${_repo}'." >&2
echo "NOTE: if you have cloned the repository into a directory with a different name, consider changing it to ${_repo}." >&2
unset _path _repo
exit 1
}
# proj_canonical returns the canonical absolute path partial URI for a project.
# It expects a complete project structure as input.
proj_canonical() {
IFS=: read -r _project _version _ _ <<POSIX_HERESTRING
$1
POSIX_HERESTRING
if [ "${_project}" = 'website' ]; then
echo '/docs'
unset _project _version
return
fi
if [ -z "${_version}" ] || [ "${_version}" = 'UNVERSIONED' ]; then
echo "/docs/${_project}"
else
echo "/docs/${_project}/${_version}"
fi
unset _project _version
}
proj_to_url_src_dst_ver() {
_url="$(proj_url "$1")"
_src="$(proj_src "$1")"
_dst="$(proj_dst "$1")"
_ver="$(proj_ver "$1")"
echo "${_url}^${_src}^${_dst}^${_ver}"
unset _url _src _dst _ver
}
url_src_dst_vers() {
for arg in "$@"; do
IFS=: read -r _project _version _repo _path <<POSIX_HERESTRING
$arg
POSIX_HERESTRING
case "${_project}" in
# Workaround for arbitrary mounts where the version field is expected to be the local directory
# and the repo field is expected to be the container directory.
arbitrary)
echo "${_project}^${_version}^${_repo}^" # TODO
;;
logs)
proj_to_url_src_dst_ver "$(new_proj loki "${_version}")"
proj_to_url_src_dst_ver "$(new_proj enterprise-logs "${_version}")"
;;
metrics)
proj_to_url_src_dst_ver "$(new_proj mimir "${_version}")"
proj_to_url_src_dst_ver "$(new_proj helm-charts/mimir-distributed "${_version}")"
proj_to_url_src_dst_ver "$(new_proj enterprise-metrics "${_version}")"
;;
traces)
proj_to_url_src_dst_ver "$(new_proj tempo "${_version}")"
proj_to_url_src_dst_ver "$(new_proj enterprise-traces "${_version}")"
;;
*)
proj_to_url_src_dst_ver "$(new_proj "${_project}" "${_version}" "${_repo}" "${_path}")"
;;
esac
done
unset _project _version _repo _path
}
url_src_dst_vers="$(url_src_dst_vers "$@")"
volumes=""
redirects=""
for x in ${url_src_dst_vers}; do
IFS='^' read -r _url _src _dst _ver <<POSIX_HERESTRING
$x
POSIX_HERESTRING
if [ "${_url}" != "arbitrary" ]; then
if [ ! -f "${_src}/_index.md" ]; then
echo "ERRR: Index file '${_src}/_index.md' does not exist." >&2
echo "Is '${_src}' the correct source directory?" >&2
exit 1
fi
fi
echo "DEBG: Mounting '${_src}' at container path '${_dst}'" >&2
if [ -z "${volumes}" ]; then
volumes="--volume=${_src}:${_dst}"
else
volumes="${volumes} --volume=${_src}:${_dst}"
fi
if [ -n "${_ver}" ] && [ "${_ver}" != 'UNVERSIONED' ]; then
if [ -z "${redirects}" ]; then
redirects="${_dst}^${_ver}"
else
redirects="${redirects} ${_dst}^${_ver}"
fi
fi
unset _url _src _dst _ver
done
IFS=':' read -r image _ <<POSIX_HERESTRING
${DOCS_IMAGE}
POSIX_HERESTRING
case "${image}" in
'grafana/doc-validator')
proj="$(new_proj "$1")"
echo
"${PODMAN}" run \
--init \
--interactive \
--name "${DOCS_CONTAINER}" \
--platform linux/amd64 \
--rm \
--tty \
${volumes} \
"${DOCS_IMAGE}" \
--include="${DOC_VALIDATOR_INCLUDE}" \
--skip-checks="${DOC_VALIDATOR_SKIP_CHECKS}" \
/hugo/content/docs \
"$(proj_canonical "${proj}")" | sed "s#$(proj_dst "${proj}")#sources#"
;;
'grafana/vale')
proj="$(new_proj "$1")"
echo
"${PODMAN}" run \
--init \
--interactive \
--name "${DOCS_CONTAINER}" \
--platform linux/amd64 \
--rm \
--tty \
${volumes} \
"${DOCS_IMAGE}" \
--minAlertLevel="${VALE_MINALERTLEVEL}" \
--config=/etc/vale/.vale.ini \
--output=line \
/hugo/content/docs | sed "s#$(proj_dst "${proj}")#sources#"
;;
*)
tempfile="$(mktemp -t make-docs.XXX)"
cat <<EOF >"${tempfile}"
#!/usr/bin/env bash
for redirect in ${redirects}; do
IFS='^' read -r path ver <<<"\${redirect}"
echo -e "---\\nredirectURL: \"\${path/\/hugo\/content/}\"\\ntype: redirect\\n---\\n" > "\${path/\${ver}/_index.md}"
done
for x in "${url_src_dst_vers}"; do
IFS='^' read -r _ _ dst _ <<<"\${x}"
while [[ -n "\${dst}" ]]; do
touch "\${dst}/_index.md"
dst="\${dst%/*}"
done
done
if [[ -n "${WEBSITE_MOUNTS}" ]]; then
unset WEBSITE_SKIP_MOUNTS
fi
${WEBSITE_EXEC}
EOF
chmod +x "${tempfile}"
volumes="${volumes} --volume=$(realpath "${tempfile}"):/entrypoint"
readonly volumes
echo
echo "Documentation will be served at the following URLs:"
for x in ${url_src_dst_vers}; do
IFS='^' read -r url _ _ <<POSIX_HERESTRING
$x
POSIX_HERESTRING
if [ -n "${url}" ]; then
if [ "${_url}" != "arbitrary" ]; then
echo " ${url}"
fi
fi
done
echo
"${PODMAN}" run \
--env "HUGO_REFLINKSERRORLEVEL=${HUGO_REFLINKSERRORLEVEL}" \
--init \
--interactive \
--name "${DOCS_CONTAINER}" \
--platform linux/amd64 \
--publish "${DOCS_HOST_PORT}:3002" \
--publish "3003:3003" \
--rm \
--tty \
${volumes} \
"${DOCS_IMAGE}" \
/entrypoint
;;
esac