github.com/grafana/pyroscope@v1.18.0/docs/make-docs (about)

     1  #!/bin/sh
     2  # shellcheck disable=SC2034
     3  #
     4  # The source of this file is https://raw.githubusercontent.com/grafana/writers-toolkit/main/docs/make-docs.
     5  # # `make-docs` procedure changelog
     6  #
     7  # Updates should conform to the guidelines in https://keepachangelog.com/en/1.1.0/.
     8  # [Semantic versioning](https://semver.org/) is used to help the reader identify the significance of changes.
     9  # Changes are relevant to this script and the support docs.mk GNU Make interface.
    10  #
    11  # ## 10.1.0 (2025-11-11)
    12  #
    13  # ### Fixed
    14  #
    15  # - Extend readiness probes to prevent confusing output.
    16  #
    17  #   Before, the probes could fail too soon and long an error message which contradicted the service starting up.
    18  #
    19  # ## 10.0.0 (2025-10-13)
    20  #
    21  # ### Changed
    22  #
    23  # - Hugo no longer supports the `--debug` option, use `--logLevel debug` instead.
    24  #
    25  #   Thank you to @karlskewes for their contribution!
    26  #
    27  # ## 9.0.0 (2025-04-05)
    28  #
    29  # ### Removed
    30  #
    31  # - doc-validator target and associated scripts.
    32  #
    33  #   Most useful rules have been migrated to Vale and the others are often false positives.
    34  #
    35  # ## 8.5.2 (2025-02-28)
    36  #
    37  # ### Fixed
    38  #
    39  # - topic/<KIND> targets are no longer no-ops as a result of 8.5.1.
    40  #
    41  # ## 8.5.1 (2025-02-18)
    42  #
    43  # ### Fixed
    44  #
    45  # - PHONY declaration for topic/<KIND> targets.
    46  #
    47  # ## 8.5.0 (2025-02-13)
    48  #
    49  # ### Added
    50  #
    51  # - make topic/<KIND> TOPIC_PATH=<PATH> target to create a new topic from the Writers' Toolkit templates.
    52  #
    53  # ## 8.4.0 (2025-01-27)
    54  #
    55  # ### Fixed
    56  #
    57  # - Correct mount for the /docs/grafana-cloud/send-data/fleet-management/ project.
    58  #
    59  # ## 8.3.0 (2024-12-27)
    60  #
    61  # ### Added
    62  #
    63  # - Debug output of the final command when DEBUG=true.
    64  #
    65  #   Useful to inspect if the script is correctly constructing the final command.
    66  #
    67  # ## 8.2.0 (2024-12-22)
    68  #
    69  # ### Removed
    70  #
    71  # - Special cases for Oracle and Datadog plugins now that they exist in the plugins monorepo.
    72  #
    73  # ## 8.1.0 (2024-08-22)
    74  #
    75  # ### Added
    76  #
    77  # - Additional website mounts for projects that use the website repository.
    78  #
    79  #   Mounts are required for `make docs` to work in the website repository or with the website project.
    80  #   The Makefile is also mounted for convenient development of the procedure in that repository.
    81  #
    82  # ## 8.0.1 (2024-07-01)
    83  #
    84  # ### Fixed
    85  #
    86  # - Update log suppression to catch new format of website /docs/ homepage REF_NOT_FOUND warnings.
    87  #
    88  #   These warnings are related to missing some pages during the build that are required for the /docs/ homepage.
    89  #   They were previously suppressed but the log format changed and without this change they reappear in the latest builds.
    90  #
    91  # ## 8.0.0 (2024-05-28)
    92  #
    93  # ### Changed
    94  #
    95  # - Add environment variable `OUTPUT_FORMAT` to control the output of commands.
    96  #
    97  #   The default value is `human` and means the output format is human readable.
    98  #   The value `json` is also supported and outputs JSON.
    99  #
   100  #   Note that the `json` format isn't supported by `make docs`, only `make doc-validator` and `make vale`.
   101  #
   102  # ## 7.0.0 (2024-05-03)
   103  #
   104  # ### Changed
   105  #
   106  # - Pull images for all recipes that use containers by default.
   107  #
   108  #   Use the `PULL=false` variable to disable this behavior.
   109  #
   110  # ### Removed
   111  #
   112  # - The `docs-no-pull` target as it's redundant with the new `PULL=false` variable.
   113  #
   114  # ## 6.1.0 (2024-04-22)
   115  #
   116  # ### Changed
   117  #
   118  # - Mount volumes with SELinux labels.
   119  #
   120  #   https://docs.docker.com/storage/bind-mounts/#configure-the-selinux-label
   121  #
   122  # ### Added
   123  #
   124  # - Pseudo project for including only website resources and no website content.
   125  #
   126  #   Facilitates testing shortcodes and layout changes with a small documentation set instead of Grafana Cloud or the entire website.
   127  #
   128  # ## 6.0.1 (2024-02-28)
   129  #
   130  # ### Added
   131  #
   132  # - Suppress new errors relating to absent content introduced in https://github.com/grafana/website/pull/17561.
   133  #
   134  # ## 6.0.0 (2024-02-16)
   135  #
   136  # ### Changed
   137  #
   138  # - Require `jq` for human readable `make doc-validator` output.
   139  #
   140  # ## 5.4.0 (2024-02-12)
   141  #
   142  # ### Changed
   143  #
   144  # - Set `WEBSITE_MOUNTS=true` when a user includes the `website` project.
   145  #
   146  #   Ensures consistent behavior across repositories.
   147  #   To disable website mounts, add `export WEBSITE_MOUNTS := false` to your `variables.mk` or `variables.mk.local` file.
   148  # - Use website mounts and container volumes also when a user includes the `grafana-cloud` project.
   149  #
   150  # ## 5.3.0 (2024-02-08)
   151  #
   152  # ### Changed
   153  #
   154  # - Updated support for plugins monorepo now that multiple projects have been moved into it.
   155  # - Use `printf` instead of `echo` for better portability of output.
   156  #
   157  #   https://www.in-ulm.de/~mascheck/various/echo+printf/
   158  #
   159  # ## 5.2.0 (2024-01-18)
   160  #
   161  # ### Changed
   162  #
   163  # - Updated `make vale` to use latest Vale style and configuration.
   164  # - Updated `make vale` to use platform appropriate image.
   165  #
   166  # ## 5.1.2 (2023-11-08)
   167  #
   168  # ### Added
   169  #
   170  # - Hide manual_mount warning messages from non-debug output.
   171  #   Set the DEBUG environment variable to see all hidden messages.
   172  #
   173  # ## 5.1.1 (2023-10-30)
   174  #
   175  # ### Added
   176  #
   177  # - Support for Datadog and Oracle data source plugins repositories.
   178  #
   179  # ## 5.1.0 (2023-10-20)
   180  #
   181  # ### Added
   182  #
   183  # - Support for the plugins monorepo.
   184  #
   185  # ## 5.0.0 (2023-10-18)
   186  #
   187  # ### Added
   188  #
   189  # - Improved support for website repository.
   190  #
   191  #   Mount more content and provide some feedback to users that the build can take time.
   192  #
   193  # - Ability to enter the `grafana/docs-base` container with a shell using the `ENTER` environment variable.
   194  #
   195  # ### Fixed
   196  #
   197  # - Correct key combination for interrupting the process.
   198  #
   199  #   Keyboards use capital letters so this more accurately reflects the exact key combination users are expected to press.
   200  #
   201  # ### Removed
   202  #
   203  # - Imperfect implementation of container name.
   204  #
   205  #   Facilitates running `make vale` and `make docs` at once.
   206  #   Container names are convenient for recognition in `docker ps` but the current implementation has more downsides than upsides.
   207  #
   208  # - Forced platform specification now that multiple architecture images exist.
   209  #
   210  #  Significantly speeds up build times on larger repositories.
   211  #
   212  # ## 4.2.2 (2023-10-05)
   213  
   214  # - Added support for Jira data source and MongoDB data source plugins repositories.
   215  #
   216  # ## 4.2.1 (2023-09-13)
   217  
   218  # ## Fixed
   219  #
   220  # - Improved consistency of the webserver request loop by polling the Hugo port rather than the proxy port.
   221  #
   222  # ## 4.2.0 (2023-09-01)
   223  #
   224  # ### Added
   225  #
   226  # - Retry the initial webserver request up to ten times to allow for the process to start.
   227  #   If it is still failing after ten seconds, an error message is logged.
   228  #
   229  # ## 4.1.1 (2023-07-20)
   230  #
   231  # ### Fixed
   232  #
   233  # - Replaced use of `realpath` with POSIX compatible alternative to determine default value for REPOS_PATH.
   234  #
   235  # ## 4.1.0 (2023-06-16)
   236  #
   237  # ### Added
   238  #
   239  # - Mounts of `layouts` and `config` directories for the `website` project.
   240  #   Ensures that local changes to mounts or shortcodes are reflected in the development server.
   241  #
   242  # ### Fixed
   243  #
   244  # - Version inference for versioned docs pages.
   245  #   Pages in versioned projects now have the `versioned: true` front matter set to ensure that "version" in $.Page.Scratch is set on builds.
   246  #
   247  # ## 4.0.0 (2023-06-06)
   248  #
   249  # ### Removed
   250  #
   251  # - `doc-validator/%` target.
   252  #   The behavior of the target was not as described.
   253  #   Instead, to limit `doc-validator` to only specific files, refer to https://grafana.com/docs/writers-toolkit/writing-guide/tooling-and-workflows/validate-technical-documentation/#run-on-specific-files.
   254  #
   255  # ## 3.0.0 (2023-05-18)
   256  #
   257  # ### Fixed
   258  #
   259  # - Compatibility with the updated Make targets in the `website` repository.
   260  #   `docs` now runs this script itself, `server-docs` builds the site with the `docs` Hugo environment.
   261  #
   262  # ## 2.0.0 (2023-05-18)
   263  #
   264  # ### Added
   265  #
   266  # - Support for the grafana-cloud/frontend-observability/faro-web-sdk project.
   267  # - Use of `doc-validator` v2.0.x which includes breaking changes to command line options.
   268  #
   269  # ### Fixed
   270  #
   271  # - Source grafana-cloud project from website repository.
   272  #
   273  # ### Added
   274  #
   275  # - Support for running the Vale linter with `make vale`.
   276  #
   277  # ## 1.2.1 (2023-05-05)
   278  #
   279  # ### Fixed
   280  #
   281  # - Use `latest` tag of `grafana/vale` image by default instead of hardcoded older version.
   282  # - Fix mounting multiple projects broken by the changes in 1.0.1
   283  #
   284  # ## 1.2.0 (2023-05-05)
   285  #
   286  # ### Added
   287  #
   288  # - Support for running the Vale linter with `make vale`.
   289  #
   290  # ### Fixed
   291  #
   292  # ## 1.1.0 (2023-05-05)
   293  #
   294  # ### Added
   295  #
   296  # - Rewrite error output so it can be followed by text editors.
   297  #
   298  # ### Fixed
   299  #
   300  # - Fix `docs-debug` container process port.
   301  #
   302  # ## 1.0.1 (2023-05-04)
   303  #
   304  # ### Fixed
   305  #
   306  # - Ensure complete section hierarchy so that all projects have a visible menu.
   307  #
   308  # ## 1.0.0 (2023-05-04)
   309  #
   310  # ### Added
   311  #
   312  # - Build multiple projects simultaneously if all projects are checked out locally.
   313  # - Run [`doc-validator`](https://github.com/grafana/technical-documentation/tree/main/tools/cmd/doc-validator) over projects.
   314  # - Redirect project root to mounted version.
   315  #   For example redirect `/docs/grafana/` to `/docs/grafana/latest/`.
   316  # - Support for Podman or Docker containers with `PODMAN` environment variable.
   317  # - Support for projects:
   318  #   - agent
   319  #   - enterprise-logs
   320  #   - enterprise-metrics
   321  #   - enterprise-traces
   322  #   - grafana
   323  #   - grafana-cloud
   324  #   - grafana-cloud/machine-learning
   325  #   - helm-charts/mimir-distributed
   326  #   - helm-charts/tempo-distributed
   327  #   - incident
   328  #   - loki
   329  #   - mimir
   330  #   - oncall
   331  #   - opentelemetry
   332  #   - phlare
   333  #   - plugins
   334  #   - slo
   335  #   - tempo
   336  #   - writers-toolkit
   337  
   338  
   339  set -ef
   340  
   341  readonly DOCS_HOST_PORT="${DOCS_HOST_PORT:-3002}"
   342  readonly DOCS_IMAGE="${DOCS_IMAGE:-grafana/docs-base:latest}"
   343  
   344  readonly DOC_VALIDATOR_INCLUDE="${DOC_VALIDATOR_INCLUDE:-.+\.md$}"
   345  readonly DOC_VALIDATOR_SKIP_CHECKS="${DOC_VALIDATOR_SKIP_CHECKS:-^image-}"
   346  
   347  readonly HUGO_REFLINKSERRORLEVEL="${HUGO_REFLINKSERRORLEVEL:-WARNING}"
   348  readonly VALE_MINALERTLEVEL="${VALE_MINALERTLEVEL:-error}"
   349  readonly WEBSITE_EXEC="${WEBSITE_EXEC:-make server-docs}"
   350  
   351  readonly OUTPUT_FORMAT="${OUTPUT_FORMAT:-human}"
   352  
   353  PODMAN="$(if command -v podman >/dev/null 2>&1; then echo podman; else echo docker; fi)"
   354  
   355  if ! command -v curl >/dev/null 2>&1; then
   356    if ! command -v wget >/dev/null 2>&1; then
   357      # shellcheck disable=SC2016
   358      errr 'either `curl` or `wget` must be installed for this script to work.'
   359  
   360      exit 1
   361    fi
   362  fi
   363  
   364  if ! command -v "${PODMAN}" >/dev/null 2>&1; then
   365    # shellcheck disable=SC2016
   366    errr 'either `podman` or `docker` must be installed for this script to work.'
   367  
   368    exit 1
   369  fi
   370  
   371  
   372  about() {
   373    cat <<EOF
   374  Test documentation locally with multiple source repositories.
   375  
   376  The REPOS_PATH environment variable is a colon (:) separated list of paths in which to look for project repositories.
   377  EOF
   378  }
   379  
   380  usage() {
   381    cat <<EOF
   382  Usage:
   383    REPOS_PATH=<PATH[:<PATH>...]> $0 [<PROJECT>[:<VERSION>[:<REPO>[:<DIR>]]]...]
   384  
   385  Examples:
   386    REPOS_PATH=~/ext/grafana/ $0 writers-toolkit tempo:latest helm-charts/mimir-distributed:latest:mimir:docs/sources/mimir-distributed
   387  EOF
   388  }
   389  
   390  if [ $# -lt 1 ]; then
   391    cat <<EOF >&2
   392  ERRR: arguments required but not supplied.
   393  
   394  $(about)
   395  
   396  $(usage)
   397  EOF
   398    exit 1
   399  fi
   400  
   401  readonly REPOS_PATH="${REPOS_PATH:-$(cd "$(git rev-parse --show-toplevel)/.." && echo "${PWD}")}"
   402  
   403  if [ -z "${REPOS_PATH}" ]; then
   404    cat <<EOF >&2
   405  ERRR: REPOS_PATH environment variable is required but has not been provided.
   406  
   407  $(usage)
   408  EOF
   409    exit 1
   410  fi
   411  
   412  # The following variables comprise a pseudo associative array of project names to source repositories.
   413  # You only need to set a SOURCES variable if the project name does not match the source repository name.
   414  # You can get a key identifier using the `identifier` function.
   415  # To look up the value of any pseudo associative array, use the `aget` function.
   416  SOURCES_as_code='as-code-docs'
   417  SOURCES_enterprise_metrics='backend-enterprise'
   418  SOURCES_enterprise_metrics_='backend-enterprise'
   419  SOURCES_grafana_cloud='website'
   420  SOURCES_grafana_cloud_alerting_and_irm_machine_learning='machine-learning'
   421  SOURCES_grafana_cloud_alerting_and_irm_slo='slo'
   422  SOURCES_grafana_cloud_k6='k6-docs'
   423  SOURCES_grafana_cloud_data_configuration_integrations='cloud-onboarding'
   424  SOURCES_grafana_cloud_frontend_observability_faro_web_sdk='faro-web-sdk'
   425  SOURCES_grafana_cloud_send_data_fleet_management='fleet-management'
   426  SOURCES_helm_charts_mimir_distributed='mimir'
   427  SOURCES_helm_charts_tempo_distributed='tempo'
   428  SOURCES_opentelemetry='opentelemetry-docs'
   429  SOURCES_resources='website'
   430  
   431  # The following variables comprise a pseudo associative array of project names to versions.
   432  # You only need to set a VERSIONS variable if it is not the default of 'latest'.
   433  # You can get a key identifier using the `identifier` function.
   434  # To look up the value of any pseudo associative array, use the `aget` function.
   435  VERSIONS_as_code='UNVERSIONED'
   436  VERSIONS_grafana_cloud='UNVERSIONED'
   437  VERSIONS_grafana_cloud_alerting_and_irm_machine_learning='UNVERSIONED'
   438  VERSIONS_grafana_cloud_alerting_and_irm_slo='UNVERSIONED'
   439  VERSIONS_grafana_cloud_k6='UNVERSIONED'
   440  VERSIONS_grafana_cloud_data_configuration_integrations='UNVERSIONED'
   441  VERSIONS_grafana_cloud_frontend_observability_faro_web_sdk='UNVERSIONED'
   442  VERSIONS_grafana_cloud_send_data_fleet_management='UNVERSIONED'
   443  VERSIONS_opentelemetry='UNVERSIONED'
   444  VERSIONS_resources='UNVERSIONED'
   445  VERSIONS_technical_documentation='UNVERSIONED'
   446  VERSIONS_website='UNVERSIONED'
   447  VERSIONS_writers_toolkit='UNVERSIONED'
   448  
   449  # The following variables comprise a pseudo associative array of project names to source repository paths.
   450  # You only need to set a PATHS variable if it is not the default of 'docs/sources'.
   451  # You can get a key identifier using the `identifier` function.
   452  # To look up the value of any pseudo associative array, use the `aget` function.
   453  PATHS_grafana_cloud='content/docs/grafana-cloud'
   454  PATHS_helm_charts_mimir_distributed='docs/sources/helm-charts/mimir-distributed'
   455  PATHS_helm_charts_tempo_distributed='docs/sources/helm-charts/tempo-distributed'
   456  PATHS_mimir='docs/sources/mimir'
   457  PATHS_resources='content'
   458  PATHS_tempo='docs/sources/tempo'
   459  PATHS_website='content'
   460  
   461  # identifier STR
   462  # Replace characters that are not valid in an identifier with underscores.
   463  identifier() {
   464    echo "$1" | tr -C '[:alnum:]_\n' '_'
   465  }
   466  
   467  # aget ARRAY KEY
   468  # Get the value of KEY from associative array ARRAY.
   469  # Characters that are not valid in an identifier are replaced with underscores.
   470  aget() {
   471    eval echo '$'"$(identifier "$1")_$(identifier "$2")"
   472  }
   473  
   474  # src returns the project source repository name for a project.
   475  src() {
   476    _project="$1"
   477  
   478    case "${_project}" in
   479      plugins/*)
   480        if [ -z "$(aget SOURCES "${_project}")" ]; then
   481          echo plugins-private
   482        else
   483          aget SOURCES "${_project}"
   484        fi
   485        ;;
   486      *)
   487        if [ -z "$(aget SOURCES "${_project}")" ]; then
   488          echo "${_project}"
   489        else
   490          aget SOURCES "${_project}"
   491        fi
   492        ;;
   493    esac
   494  
   495    unset _project
   496  }
   497  
   498  # path returns the relative path within the repository that contain the docs for a project.
   499  path() {
   500    _project="$1"
   501  
   502    case "${_project}" in
   503      plugins/*)
   504        if [ -z "$(aget PATHS "${_project}")" ]; then
   505          echo "${_project}/docs/sources"
   506        else
   507          aget PATHS "${_project}"
   508        fi
   509        ;;
   510      *)
   511        if [ -z "$(aget PATHS "${_project}")" ]; then
   512          echo "docs/sources"
   513        else
   514          aget PATHS "${_project}"
   515        fi
   516    esac
   517  
   518    unset _project
   519  }
   520  
   521  # version returns the version for a project. Unversioned projects return the special value 'UNVERSIONED'.
   522  version() {
   523    _project="$1"
   524  
   525    case "${_project}" in
   526      plugins/*)
   527        if [ -z "$(aget VERSIONS "${_project}")" ]; then
   528          echo "UNVERSIONED"
   529        else
   530          aget VERSIONS "${_project}"
   531        fi
   532        ;;
   533      *)
   534      if [ -z "$(aget VERSIONS "${_project}")" ]; then
   535        echo latest
   536      else
   537        aget VERSIONS "${_project}"
   538      fi
   539    esac
   540  
   541    unset _project
   542  }
   543  
   544  
   545  # new_proj populates a new project structure.
   546  new_proj() {
   547    _project="$1"
   548    _version="$2"
   549    _repo="$3"
   550    _path="$4"
   551  
   552    # If version is not set, use the script mapping of project to default versions if it exists.
   553    # Fallback to 'latest'.
   554    if [ -z "${_version}" ]; then
   555      _version="$(version "${_project}")"
   556    fi
   557  
   558    # If repo is not set, use the script mapping of project to repo name if it exists.
   559    # Fallback to using the project name.
   560    if [ -z "${_repo}" ]; then
   561      _repo="$(src "${_project}")"
   562    fi
   563  
   564    # If path is not set, use the script mapping of project to docs sources path if it exists.
   565    # Fallback to using 'docs/sources'.
   566    if [ -z "${_path}" ]; then
   567      _path="$(path "${_project}")"
   568    fi
   569  
   570    echo "${_project}:${_version}:${_repo}:${_path}"
   571    unset _project _version _repo _path
   572  }
   573  
   574  # proj_url returns the webserver URL for a project.
   575  # It expects a complete project structure as input.
   576  proj_url() {
   577    IFS=: read -r _project _version _ _ <<POSIX_HERESTRING
   578  $1
   579  POSIX_HERESTRING
   580  
   581    if [ "${_project}" = website ]; then
   582      echo "http://localhost:${DOCS_HOST_PORT}/docs/"
   583  
   584      unset _project _version
   585      return
   586    fi
   587  
   588    if [ -z "${_version}" ] || [ "${_version}" = 'UNVERSIONED' ]; then
   589      echo "http://localhost:${DOCS_HOST_PORT}/docs/${_project}/"
   590    else
   591      echo "http://localhost:${DOCS_HOST_PORT}/docs/${_project}/${_version}/"
   592    fi
   593  
   594    unset _project _version
   595  }
   596  
   597  # proj_ver returns the version for a project.
   598  # It expects a complete project structure as input.
   599  proj_ver() {
   600    IFS=: read -r _ _ver _ _ <<POSIX_HERESTRING
   601  $1
   602  POSIX_HERESTRING
   603  
   604    echo "${_ver}"
   605    unset _ver
   606  }
   607  
   608  # proj_dst returns the container path to content source for a project.
   609  # It expects a complete project structure as input.
   610  proj_dst() {
   611    IFS=: read -r _project _version _ _ <<POSIX_HERESTRING
   612  $1
   613  POSIX_HERESTRING
   614  
   615    if [ "${_project}" = website ]; then
   616      echo '/hugo/content'
   617  
   618      unset _project _version
   619      return
   620    fi
   621  
   622    if [ -z "${_version}" ] || [ "${_version}" = 'UNVERSIONED' ]; then
   623      echo "/hugo/content/docs/${_project}"
   624    else
   625      echo "/hugo/content/docs/${_project}/${_version}"
   626    fi
   627  
   628    unset _project _version
   629  }
   630  
   631  # repo_path returns the host path to the project repository.
   632  # It looks for the provided repository name in each of the paths specified in the REPOS_PATH environment variable.
   633  repo_path() {
   634    _repo="$1"
   635    IFS=:
   636    for lookup in ${REPOS_PATH}; do
   637      if [ -d "${lookup}/${_repo}" ]; then
   638        echo "${lookup}/${_repo}"
   639        unset _path _repo
   640        return
   641      fi
   642    done
   643    unset IFS
   644  
   645    errr "could not find project '${_repo}' in any of the paths in REPOS_PATH '${REPOS_PATH}'."
   646    note "you must have a checkout of the project '${_repo}' at '${REPOS_PATH##:*}/${_repo}'."
   647    note "if you have cloned the repository into a directory with a different name, consider changing it to ${_repo}."
   648  
   649    unset _repo
   650    exit 1
   651  }
   652  
   653  # proj_src returns the host path to content source for a project.
   654  # It expects a complete project structure as input.
   655  # It looks for the provided repository name in each of the paths specified in the REPOS_PATH environment variable.
   656  proj_src() {
   657    IFS=: read -r _ _ _repo _path <<POSIX_HERESTRING
   658  $1
   659  POSIX_HERESTRING
   660  
   661    _repo="$(repo_path "${_repo}")"
   662    echo "${_repo}/${_path}"
   663  
   664    unset _path _repo
   665  }
   666  
   667  # proj_canonical returns the canonical absolute path partial URI for a project.
   668  # It expects a complete project structure as input.
   669  proj_canonical() {
   670    IFS=: read -r _project _version _ _ <<POSIX_HERESTRING
   671  $1
   672  POSIX_HERESTRING
   673  
   674    if [ "${_project}" = website ]; then
   675      echo '/docs'
   676  
   677      unset _project _version
   678      return
   679    fi
   680  
   681    if [ -z "${_version}" ] || [ "${_version}" = 'UNVERSIONED' ]; then
   682      echo "/docs/${_project}"
   683    else
   684      echo "/docs/${_project}/${_version}"
   685    fi
   686  
   687    unset _project _version
   688  }
   689  
   690  proj_to_url_src_dst_ver() {
   691    _url="$(proj_url "$1")"
   692    _src="$(proj_src "$1")"
   693    _dst="$(proj_dst "$1")"
   694    _ver="$(proj_ver "$1")"
   695  
   696    echo "${_url}^${_src}^${_dst}^${_ver}"
   697    unset _url _src _dst _ver
   698  }
   699  
   700  url_src_dst_vers() {
   701    for arg in "$@"; do
   702      IFS=: read -r _project _version _repo _path <<POSIX_HERESTRING
   703  $arg
   704  POSIX_HERESTRING
   705  
   706      case "${_project}" in
   707        # Workaround for arbitrary mounts where the version field is expected to be the local directory
   708       # and the repo field is expected to be the container directory.
   709        arbitrary)
   710          echo "${_project}^${_version}^${_repo}^" # TODO
   711          ;;
   712        logs)
   713          proj_to_url_src_dst_ver "$(new_proj loki "${_version}")"
   714          proj_to_url_src_dst_ver "$(new_proj enterprise-logs "${_version}")"
   715          ;;
   716        metrics)
   717          proj_to_url_src_dst_ver "$(new_proj mimir "${_version}")"
   718          proj_to_url_src_dst_ver "$(new_proj helm-charts/mimir-distributed "${_version}")"
   719          proj_to_url_src_dst_ver "$(new_proj enterprise-metrics "${_version}")"
   720          ;;
   721        resources)
   722          _repo="$(repo_path website)"
   723          echo "arbitrary^${_repo}/config^/hugo/config" "arbitrary^${_repo}/layouts^/hugo/layouts" "arbitrary^${_repo}/scripts^/hugo/scripts"
   724          unset _repo
   725          ;;
   726        traces)
   727          proj_to_url_src_dst_ver "$(new_proj tempo "${_version}")"
   728          proj_to_url_src_dst_ver "$(new_proj enterprise-traces "${_version}")"
   729          ;;
   730        *)
   731          proj_to_url_src_dst_ver "$(new_proj "${_project}" "${_version}" "${_repo}" "${_path}")"
   732          ;;
   733      esac
   734    done
   735  
   736    unset _project _version _repo _path
   737  }
   738  
   739  await_build() {
   740    url="$1"
   741    req="$(if command -v curl >/dev/null 2>&1; then echo 'curl -s -o /dev/null'; else echo 'wget -q'; fi)"
   742  
   743    # Initial delay to allow container to start before beginning healthchecks
   744    sleep 3
   745  
   746    # Fast retries for initial startup (10 attempts, 1 second apart)
   747    i=1
   748    max=10
   749    while [ "${i}" -ne "${max}" ]
   750    do
   751      sleep 1
   752      debg "Retrying request to web server assuming the process is still starting up."
   753      i=$((i + 1))
   754  
   755      if ${req} "${url}"; then
   756        printf '\r\nView documentation locally:\r\n'
   757        for x in ${url_src_dst_vers}; do
   758          IFS='^' read -r url _ _ <<POSIX_HERESTRING
   759  $x
   760  POSIX_HERESTRING
   761  
   762          if [ -n "${url}" ]; then
   763            if [ "${url}" != arbitrary ]; then
   764              printf '\r  %s\r\n' "${url}"
   765            fi
   766          fi
   767        done
   768        printf '\r\nPress Ctrl+C to stop the server\r\n'
   769  
   770        unset i max req url
   771        return
   772      fi
   773    done
   774  
   775    # Continue checking with longer intervals for slower builds
   776    # This prevents false positives for large builds that take longer to start
   777    i=1
   778    max=20
   779    while [ "${i}" -ne "${max}" ]
   780    do
   781      sleep 2
   782      debg "Continuing to check web server (build may be taking longer than expected)."
   783      i=$((i + 1))
   784  
   785      if ${req} "${url}"; then
   786        printf '\r\nView documentation locally:\r\n'
   787        for x in ${url_src_dst_vers}; do
   788          IFS='^' read -r url _ _ <<POSIX_HERESTRING
   789  $x
   790  POSIX_HERESTRING
   791  
   792          if [ -n "${url}" ]; then
   793            if [ "${url}" != arbitrary ]; then
   794              printf '\r  %s\r\n' "${url}"
   795            fi
   796          fi
   797        done
   798        printf '\r\nPress Ctrl+C to stop the server\r\n'
   799  
   800        unset i max req url
   801        return
   802      fi
   803    done
   804  
   805    # Only log error after extended checking period (total ~60 seconds)
   806    printf '\r\n'
   807    errr 'The build was interrupted or a build error occurred, check the previous logs for possible causes.'
   808    note 'You might need to use Ctrl+C to end the process.'
   809  
   810    unset i max req url
   811  }
   812  
   813  debg() {
   814    if [ -n "${DEBUG}" ]; then
   815      printf 'DEBG: %s\r\n' "$1" >&2
   816    fi
   817  }
   818  
   819  errr() {
   820    printf 'ERRR: %s\r\n' "$1" >&2
   821  }
   822  
   823  note() {
   824    printf 'NOTE: %s\r\n' "$1" >&2
   825  }
   826  
   827  url_src_dst_vers="$(url_src_dst_vers "$@")"
   828  
   829  volumes=""
   830  redirects=""
   831  
   832  for arg in "$@"; do
   833    IFS=: read -r _project _ _repo _ <<POSIX_HERESTRING
   834  ${arg}
   835  POSIX_HERESTRING
   836    if [ "${_project}" = website ] || [ "${_project}" = grafana-cloud ]; then
   837      note "Please be patient, building the website can take some time."
   838  
   839        # If set, the docs-base image will run a prebuild script that sets up Hugo mounts.
   840      if [ "${WEBSITE_MOUNTS}" = false ]; then
   841        unset WEBSITE_MOUNTS
   842      else
   843        readonly WEBSITE_MOUNTS=true
   844      fi
   845  
   846      _repo="$(repo_path website)"
   847      volumes="--volume=${_repo}/config:/hugo/config:z"
   848      volumes="${volumes} --volume=${_repo}/content/guides:/hugo/content/guides:z"
   849      volumes="${volumes} --volume=${_repo}/content/whats-new:/hugo/content/whats-new:z"
   850      volumes="${volumes} --volume=${_repo}/Makefile:/hugo/Makefile:z"
   851      volumes="${volumes} --volume=${_repo}/layouts:/hugo/layouts:z"
   852      volumes="${volumes} --volume=${_repo}/scripts:/hugo/scripts:z"
   853    fi
   854    unset _project _repo
   855  done
   856  
   857  for x in ${url_src_dst_vers}; do
   858    IFS='^' read -r _url _src _dst _ver <<POSIX_HERESTRING
   859  $x
   860  POSIX_HERESTRING
   861  
   862    if [ "${_url}" != arbitrary ]; then
   863      if [ ! -f "${_src}/_index.md" ]; then
   864        errr "Index file '${_src}/_index.md' does not exist."
   865        note "Is '${_src}' the correct source directory?"
   866        exit 1
   867      fi
   868    fi
   869  
   870    debg "Mounting '${_src}' at container path '${_dst}'"
   871  
   872    if [ -z "${volumes}" ]; then
   873      volumes="--volume=${_src}:${_dst}:z"
   874    else
   875      volumes="${volumes} --volume=${_src}:${_dst}:z"
   876    fi
   877  
   878    if [ -n "${_ver}" ] && [ "${_ver}" != 'UNVERSIONED' ]; then
   879      if [ -z "${redirects}" ]; then
   880        redirects="${_dst}^${_ver}"
   881      else
   882        redirects="${redirects} ${_dst}^${_ver}"
   883      fi
   884    fi
   885    unset _url _src _dst _ver
   886  done
   887  
   888  IFS=':' read -r image _ <<POSIX_HERESTRING
   889  ${DOCS_IMAGE}
   890  POSIX_HERESTRING
   891  
   892  case "${image}" in
   893    'grafana/vale')
   894      proj="$(new_proj "$1")"
   895      printf '\r\n'
   896      IFS='' read -r cmd <<EOF
   897      ${PODMAN} run \
   898                  --init \
   899                  --interactive \
   900                  --rm \
   901                  --workdir /etc/vale \
   902                  --tty \
   903                  ${volumes} \
   904                  ${DOCS_IMAGE} \
   905                  --minAlertLevel=${VALE_MINALERTLEVEL} \
   906                  --glob=*.md \
   907                  /hugo/content/docs
   908  EOF
   909  
   910      if [ -n "${DEBUG}" ]; then
   911        debg "${cmd}"
   912      fi
   913  
   914      case "${OUTPUT_FORMAT}" in
   915        human)
   916          ${cmd} --output=line \
   917          | sed "s#$(proj_dst "${proj}")#sources#"
   918        ;;
   919        json)
   920          ${cmd} --output=/etc/vale/rdjsonl.tmpl \
   921          | sed "s#$(proj_dst "${proj}")#sources#"
   922        ;;
   923        *)
   924          errr "Invalid output format '${OUTPUT_FORMAT}'"
   925      esac
   926  
   927      ;;
   928    *)
   929      tempfile="$(mktemp -t make-docs.XXX)"
   930      cat <<EOF >"${tempfile}"
   931  #!/usr/bin/env bash
   932  
   933  tc() {
   934    set \${*,,}
   935    echo \${*^}
   936  }
   937  
   938  for redirect in ${redirects}; do
   939    IFS='^' read -r path ver <<<"\${redirect}"
   940    echo -e "---\\nredirectURL: \"\${path/\/hugo\/content/}\"\\ntype: redirect\\nversioned: true\\n---\\n" > "\${path/\${ver}/_index.md}"
   941  done
   942  
   943  for x in "${url_src_dst_vers}"; do
   944    IFS='^' read -r _ _ dst _ <<<"\${x}"
   945  
   946    title="\${dst%/*}"
   947    title="\$(tc \${title##*/})"
   948    while [[ -n "\${dst}" ]]; do
   949      if [[ ! -f "\${dst}/_index.md" ]]; then
   950          echo -e "---title: \${title}\\n---\\n\\n# \${title}\\n\\n{{< section >}}" > "\${dst}/_index.md"
   951      fi
   952      dst="\${dst%/*}"
   953    done
   954  done
   955  
   956  if [[ -n "${WEBSITE_MOUNTS}" ]]; then
   957    unset WEBSITE_SKIP_MOUNTS
   958  fi
   959  
   960  ${WEBSITE_EXEC}
   961  EOF
   962      chmod +x "${tempfile}"
   963      volumes="${volumes} --volume=${tempfile}:/entrypoint:z"
   964      readonly volumes
   965  
   966      IFS='' read -r cmd <<EOF
   967  ${PODMAN} run \
   968    --env=HUGO_REFLINKSERRORLEVEL=${HUGO_REFLINKSERRORLEVEL} \
   969    --init \
   970    --interactive \
   971    --publish=${DOCS_HOST_PORT}:3002 \
   972    --publish=3003:3003 \
   973    --rm \
   974    --tty \
   975    ${volumes} \
   976    ${DOCS_IMAGE}
   977  EOF
   978  
   979      if [ -n "${ENTER}" ]; then
   980        ${cmd} /bin/bash
   981      elif [ -n "${DEBUG}" ]; then
   982        await_build http://localhost:3003 &
   983  
   984        debg "${cmd} /entrypoint"
   985        ${cmd} /entrypoint
   986      else
   987        await_build http://localhost:3003 &
   988  
   989        ${cmd} /entrypoint  2>&1\
   990          | sed -u \
   991                -e '/Web Server is available at http:\/\/localhost:3003\/ (bind address 0.0.0.0)/ d' \
   992                -e '/^hugo server/ d' \
   993                -e '/fatal: not a git repository (or any parent up to mount point \/)/ d' \
   994                -e '/Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)./ d' \
   995                -e "/Makefile:[0-9]*: warning: overriding recipe for target 'docs'/ d" \
   996                -e "/docs.mk:[0-9]*: warning: ignoring old recipe for target 'docs'/ d" \
   997                -e '/\/usr\/bin\/make -j 2 proxy hserver-docs HUGO_PORT=3003/ d' \
   998                -e '/website-proxy/ d' \
   999                -e '/rm -rf dist*/ d' \
  1000                -e '/Press Ctrl+C to stop/ d' \
  1001                -e '/make/ d' \
  1002                -e '/WARNING: The manual_mount source directory/ d' \
  1003                -e '/"docs\/_index.md" not found/d'
  1004      fi
  1005      ;;
  1006  esac