From 2876ecb4800487e057622a4ad64f96f10d6d56af Mon Sep 17 00:00:00 2001 From: Evgeniy Frolov Date: Mon, 15 Jun 2026 13:06:23 +0300 Subject: [PATCH 01/15] =?UTF-8?q?=D1=81hore(docs):=20simplify=20routing=20?= =?UTF-8?q?to=20v2/v1.2/latest/pr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Evgeniy Frolov --- .helm/templates/12-backend.yaml | 27 +- .helm/templates/_rewrites.tpl | 222 +++++--------- .helm/values.yaml | 7 + .werf/nginx-dev.conf | 2 +- assets/js/customscripts.js | 388 ++++++++++++------------ backend/common.go | 510 +++++++++++--------------------- backend/handlers.go | 81 ++--- backend/main.go | 10 +- backend/main_test.go | 73 +++-- docker-compose.yml | 5 +- 10 files changed, 544 insertions(+), 781 deletions(-) diff --git a/.helm/templates/12-backend.yaml b/.helm/templates/12-backend.yaml index f8ea6406d..f264d2a1b 100644 --- a/.helm/templates/12-backend.yaml +++ b/.helm/templates/12-backend.yaml @@ -37,8 +37,12 @@ spec: name: http protocol: TCP env: - - name: ACTIVE_RELEASE - value: {{ .Values.global.active_release | quote }} + - name: CURRENT_DOCS_MAJOR + value: {{ .Values.docs.currentRoot | quote }} + - name: SUPPORTED_DOCS_MAJOR_VERSIONS + value: {{ join "," .Values.docs.supportedRoots | quote }} + - name: DOCS_LATEST_ALIAS_ENABLED + value: {{ .Values.docs.latestAliasEnabled | quote }} - name: LOG_LEVEL value: "info" {{- if ne .Values.werf.env "production" }} @@ -53,13 +57,6 @@ spec: httpGet: path: /health port: 8080 - volumeMounts: - - name: trdl-data - mountPath: /app/trdl - volumes: - - name: trdl-data - configMap: - name: trdl-data --- apiVersion: v1 kind: Service @@ -96,15 +93,3 @@ spec: selector: matchLabels: service: backend ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: trdl-data -data: - trdl_channels.yaml: | -{{- if eq .Values.werf.env "production" }} -{{ .Files.Get "trdl_channels.yaml" | indent 4 }} -{{- else }} -{{ .Files.Get "trdl_channels-dev.yaml" | indent 4 }} -{{- end }} diff --git a/.helm/templates/_rewrites.tpl b/.helm/templates/_rewrites.tpl index c02fac529..6abb0f5a4 100644 --- a/.helm/templates/_rewrites.tpl +++ b/.helm/templates/_rewrites.tpl @@ -10,30 +10,27 @@ rewrite ^/css/(?.+) rewrite ^/images/(?.+) /assets/images/$tail redirect; rewrite ^/docs\.html$ /docs/ redirect; -rewrite ^/docs/(?v\d+(?:\.\d+(?:\.\d+(?:[^/]+)?)?)?|latest)$ /docs/$ver/ redirect; -rewrite ^/docs/(?v\d+(?:\.\d+(?:\.\d+(?:[^/]+)?)?)?|latest)/index\.html$ /docs/$ver/ redirect; -rewrite ^/(?v\d+(?:\.\d+(?:\.\d+(?:[^/]+)?)?)?|latest)/docs\.html$ /docs/$ver/ redirect; -rewrite ^/(?v\d+(?:\.\d+(?:\.\d+(?:[^/]+)?)?)?|latest)/docs/?$ /docs/$ver/ redirect; -rewrite ^/(?v\d+(?:\.\d+(?:\.\d+(?:[^/]+)?)?)?|latest)/docs/(?.+) /docs/$ver/$tail redirect; +rewrite ^/docs/(?latest|pr-[^/]+|v2(?:\.[^/]+)?|v1\.2(?:\.[^/]+)?)$ /docs/$ver/ redirect; +rewrite ^/docs/(?latest|pr-[^/]+|v2(?:\.[^/]+)?|v1\.2(?:\.[^/]+)?)/index\.html$ /docs/$ver/ redirect; +rewrite ^/(?latest|pr-[^/]+|v2(?:\.[^/]+)?|v1\.2(?:\.[^/]+)?)/docs\.html$ /docs/$ver/ redirect; +rewrite ^/(?latest|pr-[^/]+|v2(?:\.[^/]+)?|v1\.2(?:\.[^/]+)?)/docs/?$ /docs/$ver/ redirect; +rewrite ^/(?latest|pr-[^/]+|v2(?:\.[^/]+)?|v1\.2(?:\.[^/]+)?)/docs/(?.+) /docs/$ver/$tail redirect; rewrite ^/documentation\.html$ /docs/ redirect; rewrite ^/documentation/?$ /docs/ redirect; -rewrite ^/documentation/(?v\d+(?:\.\d+(?:\.\d+(?:[^/]+)?)?)?|latest)/?$ /docs/$ver/ redirect; -rewrite ^/documentation/(?v\d+(?:\.\d+(?:\.\d+(?:[^/]+)?)?)?|latest)/index\.html$ /docs/$ver/ redirect; -rewrite ^/documentation/(?v\d+(?:\.\d+(?:\.\d+(?:[^/]+)?)?)?|latest)/(?.+) /docs/$ver/$tail redirect; -rewrite ^/(?v\d+(?:\.\d+(?:\.\d+(?:[^/]+)?)?)?|latest)/documentation\.html$ /docs/$ver/ redirect; -rewrite ^/(?v\d+(?:\.\d+(?:\.\d+(?:[^/]+)?)?)?|latest)/documentation/?$ /docs/$ver/ redirect; -rewrite ^/(?v\d+(?:\.\d+(?:\.\d+(?:[^/]+)?)?)?|latest)/documentation/(?.+) /docs/$ver/$tail redirect; - -rewrite ^/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/how_to/?$ /docs/$ver/how_to/ redirect; -rewrite ^/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/how_to/(?.+) /docs/$ver/how_to/$tail redirect; +rewrite ^/documentation/(?latest|pr-[^/]+|v2(?:\.[^/]+)?|v1\.2(?:\.[^/]+)?)/?$ /docs/$ver/ redirect; +rewrite ^/documentation/(?latest|pr-[^/]+|v2(?:\.[^/]+)?|v1\.2(?:\.[^/]+)?)/index\.html$ /docs/$ver/ redirect; +rewrite ^/documentation/(?latest|pr-[^/]+|v2(?:\.[^/]+)?|v1\.2(?:\.[^/]+)?)/(?.+) /docs/$ver/$tail redirect; +rewrite ^/(?latest|pr-[^/]+|v2(?:\.[^/]+)?|v1\.2(?:\.[^/]+)?)/documentation\.html$ /docs/$ver/ redirect; +rewrite ^/(?latest|pr-[^/]+|v2(?:\.[^/]+)?|v1\.2(?:\.[^/]+)?)/documentation/?$ /docs/$ver/ redirect; +rewrite ^/(?latest|pr-[^/]+|v2(?:\.[^/]+)?|v1\.2(?:\.[^/]+)?)/documentation/(?.+) /docs/$ver/$tail redirect; ############################################ # Temporary versioned redirects ############################################ rewrite ^/docs/?$ /docs/v2/ redirect; -rewrite ^/docs/(?!(v\d+(?:\.\d+(?:\.\d+(?:[^/]+)?)?)?|latest)/)(?:.+) /docs/v2/ redirect; +rewrite ^/docs/(?!(latest|pr-[^/]+|v2(?:\.[^/]+)?|v1\.2(?:\.[^/]+)?)/)(?:.+) /docs/v2/ redirect; rewrite ^/docs/(?v2(?:\.\d+(?:\.\d+(?:[^/]+)?)?)?|latest)/?$ /docs/$ver/usage/project_configuration/overview.html redirect; rewrite ^/docs/(?v2(?:\.\d+(?:\.\d+(?:[^/]+)?)?)?|latest)/usage/?$ /docs/$ver/usage/project_configuration/overview.html redirect; @@ -59,14 +56,6 @@ rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/reference/?$ rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/reference/cli/?$ /docs/$ver/reference/cli/overview.html redirect; rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/resources/?$ /docs/$ver/resources/cheat_sheet.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/?$ /docs/$ver/index.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/configuration/?$ /docs/$ver/configuration/introduction.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/configuration/stapel_image/?$ /docs/$ver/configuration/stapel_image/naming.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/?$ /docs/$ver/reference/stages_and_images.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/deploy_process/?$ /docs/$ver/reference/deploy_process/deploy_into_kubernetes.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/plugging_into_cicd/?$ /docs/$ver/reference/plugging_into_cicd/overview.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/development_and_debug/?$ /docs/$ver/reference/development_and_debug/setup_minikube.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/toolbox/?$ /docs/$ver/reference/toolbox/slug.html redirect; ############################################ # Redirects for moved or deleted urls @@ -79,76 +68,76 @@ rewrite ^/how_it_works\.html rewrite ^/introduction\.html$ /#how-it-works redirect; ############################################ -# v1.1/v1.2 redirects for moved or deleted urls +# v1.2 redirects for moved or deleted urls ############################################ -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/quickstart\.html$ /docs/$ver/ redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/using_with_ci_cd_systems\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; - -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/supported_registry_implementations\.html$ /docs/$ver/usage/cleanup/cr_cleanup.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/buildah_mode\.html$ /docs/$ver/usage/build/process.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/building_images_with_stapel/artifacts\.html$ /docs/$ver/usage/build/stapel/imports.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/building_images_with_stapel/assembly_instructions\.html$ /docs/$ver/usage/build/stapel/instructions.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/building_images_with_stapel/base_image\.html$ /docs/$ver/usage/build/stapel/base.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/building_images_with_stapel/docker_directive\.html$ /docs/$ver/usage/build/stapel/dockerfile.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/building_images_with_stapel/git_directive\.html$ /docs/$ver/usage/build/stapel/git.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/building_images_with_stapel/import_directive\.html$ /docs/$ver/usage/build/stapel/imports.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/building_images_with_stapel/mount_directive\.html$ /docs/$ver/usage/build/stapel/mounts.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/bundles\.html$ /docs/$ver/usage/distribute/bundles.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/ci_cd_workflow_basics\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/generic_ci_cd_integration\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/github_actions\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/gitlab_ci_cd\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/run_in_container/run_in_docker_container\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/run_in_container/run_in_kubernetes\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/run_in_container/use_docker_container\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/run_in_container/use_github_actions_with_docker_executor\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/run_in_container/use_github_actions_with_kubernetes_executor\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/run_in_container/use_gitlab_ci_cd_with_docker_executor\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/run_in_container/use_gitlab_ci_cd_with_kubernetes_executor\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/run_in_container/use_kubernetes\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/werf_with_argocd/ci_cd_flow_overview\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/werf_with_argocd/configure_ci_cd\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/werf_with_argocd/prepare_kubernetes_cluster\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/cleanup\.html$ /docs/$ver/usage/cleanup/cr_cleanup.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/configuration/giterminism\.html$ /docs/$ver/usage/project_configuration/giterminism.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/configuration/organizing_configuration\.html$ /docs/$ver/usage/project_configuration/werf_yaml_template_engine.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/configuration/supported_go_templates\.html$ /docs/$ver/usage/project_configuration/werf_yaml_template_engine.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/development_and_debug/stage_introspection\.html$ /docs/$ver/usage/build/stapel/base.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/giterminism\.html$ /docs/$ver/usage/project_configuration/giterminism.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/configuration/chart\.html$ /docs/$ver/usage/deploy/charts.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/configuration/chart_dependencies\.html$ /docs/$ver/usage/deploy/charts.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/configuration/giterminism\.html$ /docs/$ver/usage/project_configuration/giterminism.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/configuration/secrets\.html$ /docs/$ver/usage/deploy/values.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/configuration/templates\.html$ /docs/$ver/usage/deploy/templates.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/configuration/values\.html$ /docs/$ver/usage/deploy/values.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/deploy_process/annotating_and_labeling\.html$ /docs/$ver/usage/deploy/releases.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/deploy_process/deployment_order\.html$ /docs/$ver/usage/deploy/deployment_order.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/deploy_process/external_dependencies\.html$ /docs/$ver/usage/deploy/deployment_order.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/deploy_process/helm_hooks\.html$ /docs/$ver/usage/deploy/deployment_order.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/deploy_process/resources_adoption\.html$ /docs/$ver/usage/deploy/releases.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/deploy_process/steps\.html$ /docs/$ver/usage/deploy/deployment_order.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/overview\.html$ /docs/$ver/usage/deploy/overview.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/releases/manage_releases\.html$ /docs/$ver/usage/deploy/releases.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/releases/naming\.html$ /docs/$ver/usage/deploy/releases.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/releases/release\.html$ /docs/$ver/usage/deploy/releases.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/working_with_chart_dependencies\.html$ /docs/$ver/usage/deploy/charts.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/working_with_secrets\.html$ /docs/$ver/usage/deploy/values.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/storage_layouts\.html$ /docs/$ver/usage/build/process.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/supported_container_registries\.html$ /docs/$ver/usage/cleanup/cr_cleanup.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/advanced/synchronization\.html$ /docs/$ver/usage/build/process.html redirect; - -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/internals/build_process\.html$ /docs/$ver/usage/build/process.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/internals/development/stapel_image\.html$ /docs/$ver/usage/build/stapel/base.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/internals/how_ci_cd_integration_works/general_overview\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/internals/how_ci_cd_integration_works/github_actions\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/internals/how_ci_cd_integration_works/gitlab_ci_cd\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/internals/integration_with_ssh_agent\.html$ /docs/$ver/usage/build/stapel/base.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/internals/stages_and_storage\.html$ /docs/$ver/usage/build/process.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/internals/telemetry\.html$ /docs/$ver/resources/telemetry.html redirect; - -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/reference/build/artifact\.html$ /docs/$ver/usage/build/stapel/imports.html redirect; -rewrite ^/docs/(?v1\.[12](?:\.\d+(?:[^/]+)?)?|latest)/reference/cheat_sheet\.html$ /docs/$ver/resources/cheat_sheet.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/quickstart\.html$ /docs/$ver/ redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/using_with_ci_cd_systems\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; + +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/supported_registry_implementations\.html$ /docs/$ver/usage/cleanup/cr_cleanup.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/buildah_mode\.html$ /docs/$ver/usage/build/process.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/building_images_with_stapel/artifacts\.html$ /docs/$ver/usage/build/stapel/imports.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/building_images_with_stapel/assembly_instructions\.html$ /docs/$ver/usage/build/stapel/instructions.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/building_images_with_stapel/base_image\.html$ /docs/$ver/usage/build/stapel/base.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/building_images_with_stapel/docker_directive\.html$ /docs/$ver/usage/build/stapel/dockerfile.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/building_images_with_stapel/git_directive\.html$ /docs/$ver/usage/build/stapel/git.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/building_images_with_stapel/import_directive\.html$ /docs/$ver/usage/build/stapel/imports.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/building_images_with_stapel/mount_directive\.html$ /docs/$ver/usage/build/stapel/mounts.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/bundles\.html$ /docs/$ver/usage/distribute/bundles.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/ci_cd_workflow_basics\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/generic_ci_cd_integration\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/github_actions\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/gitlab_ci_cd\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/run_in_container/run_in_docker_container\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/run_in_container/run_in_kubernetes\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/run_in_container/use_docker_container\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/run_in_container/use_github_actions_with_docker_executor\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/run_in_container/use_github_actions_with_kubernetes_executor\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/run_in_container/use_gitlab_ci_cd_with_docker_executor\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/run_in_container/use_gitlab_ci_cd_with_kubernetes_executor\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/run_in_container/use_kubernetes\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/werf_with_argocd/ci_cd_flow_overview\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/werf_with_argocd/configure_ci_cd\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/ci_cd/werf_with_argocd/prepare_kubernetes_cluster\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/cleanup\.html$ /docs/$ver/usage/cleanup/cr_cleanup.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/configuration/giterminism\.html$ /docs/$ver/usage/project_configuration/giterminism.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/configuration/organizing_configuration\.html$ /docs/$ver/usage/project_configuration/werf_yaml_template_engine.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/configuration/supported_go_templates\.html$ /docs/$ver/usage/project_configuration/werf_yaml_template_engine.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/development_and_debug/stage_introspection\.html$ /docs/$ver/usage/build/stapel/base.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/giterminism\.html$ /docs/$ver/usage/project_configuration/giterminism.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/configuration/chart\.html$ /docs/$ver/usage/deploy/charts.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/configuration/chart_dependencies\.html$ /docs/$ver/usage/deploy/charts.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/configuration/giterminism\.html$ /docs/$ver/usage/project_configuration/giterminism.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/configuration/secrets\.html$ /docs/$ver/usage/deploy/values.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/configuration/templates\.html$ /docs/$ver/usage/deploy/templates.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/configuration/values\.html$ /docs/$ver/usage/deploy/values.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/deploy_process/annotating_and_labeling\.html$ /docs/$ver/usage/deploy/releases.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/deploy_process/deployment_order\.html$ /docs/$ver/usage/deploy/deployment_order.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/deploy_process/external_dependencies\.html$ /docs/$ver/usage/deploy/deployment_order.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/deploy_process/helm_hooks\.html$ /docs/$ver/usage/deploy/deployment_order.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/deploy_process/resources_adoption\.html$ /docs/$ver/usage/deploy/releases.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/deploy_process/steps\.html$ /docs/$ver/usage/deploy/deployment_order.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/overview\.html$ /docs/$ver/usage/deploy/overview.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/releases/manage_releases\.html$ /docs/$ver/usage/deploy/releases.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/releases/naming\.html$ /docs/$ver/usage/deploy/releases.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/releases/release\.html$ /docs/$ver/usage/deploy/releases.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/working_with_chart_dependencies\.html$ /docs/$ver/usage/deploy/charts.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/helm/working_with_secrets\.html$ /docs/$ver/usage/deploy/values.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/storage_layouts\.html$ /docs/$ver/usage/build/process.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/supported_container_registries\.html$ /docs/$ver/usage/cleanup/cr_cleanup.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/advanced/synchronization\.html$ /docs/$ver/usage/build/process.html redirect; + +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/internals/build_process\.html$ /docs/$ver/usage/build/process.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/internals/development/stapel_image\.html$ /docs/$ver/usage/build/stapel/base.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/internals/how_ci_cd_integration_works/general_overview\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/internals/how_ci_cd_integration_works/github_actions\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/internals/how_ci_cd_integration_works/gitlab_ci_cd\.html$ /docs/$ver/usage/integration_with_ci_cd_systems.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/internals/integration_with_ssh_agent\.html$ /docs/$ver/usage/build/stapel/base.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/internals/stages_and_storage\.html$ /docs/$ver/usage/build/process.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/internals/telemetry\.html$ /docs/$ver/resources/telemetry.html redirect; + +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/reference/build/artifact\.html$ /docs/$ver/usage/build/stapel/imports.html redirect; +rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/reference/cheat_sheet\.html$ /docs/$ver/resources/cheat_sheet.html redirect; ############################################ # v1.2 redirects for moved or deleted urls @@ -201,55 +190,4 @@ rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/reference/toolbox/ssh\.h rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/reference/working_with_docker_registries\.html$ /docs/$ver/usage/cleanup/cr_cleanup.html redirect; rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/reference/werf_yaml_template_engine\.html$ /docs/$ver/usage/project_configuration/werf_yaml_template_engine.html redirect; -rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/whats_new_in_v1_2/changelog\.html$ /docs/$ver/resources/how_to_migrate_from_v1_1_to_v1_2.html redirect; -rewrite ^/docs/(?v1\.2(?:\.\d+(?:[^/]+)?)?|latest)/whats_new_in_v1_2/how_to_migrate_from_v1_1_to_v1_2\.html$ /docs/$ver/resources/how_to_migrate_from_v1_1_to_v1_2.html redirect; - -############################################ -# v1.1 redirects for moved or deleted urls -############################################ - -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/quickstart\.html$ /docs/$ver/guides/getting_started.html redirect; - -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/how_to/?$ /docs/$ver/guides/ redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/how_to/mounts\.html$ /docs/$ver/guides/advanced_build/mounts.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/how_to/multi_images\.html$ /docs/$ver/guides/advanced_build/multi_images.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/how_to/artifacts\.html$ /docs/$ver/guides/advanced_build/artifacts.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/how_to/(?.+) /docs/$ver/guides/$tail redirect; - -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/guides/guides/unsupported_ci_cd_integration\.html$ /docs/$ver/guides/generic_ci_cd_integration.html redirect; - -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/cli/?$ /docs/$ver/reference/cli/ redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/cli/management/helm/get_release\.html$ /docs/$ver/reference/cli/werf_helm_get_release.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/cli/toolbox/meta/get_helm_release\.html$ /docs/$ver/reference/cli/werf_helm_get_release.html redirect; - -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/configuration/stapel_image/assembly_process\.html$ /docs/$ver/configuration/stapel_image/assembly_instructions.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/configuration/stapel_image/image_from_dockerfile\.html$ /docs/$ver/configuration/dockerfile_image.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/configuration/stapel_image/stage_introspection\.html$ /docs/$ver/advanced/development_and_debug/stage_introspection.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/configuration/stapel_image/stages\.html$ /docs/$ver/reference/stages_and_images.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/configuration/stapel_image/stages_and_images\.html$ /docs/$ver/internals/stages_and_storage.html redirect; - -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/cleanup_process\.html$ /docs/$ver/reference/cleaning_process.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/config\.html$ /docs/$ver/configuration/introduction.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/stages_and_images\.html$ /docs/$ver/internals/stages_and_storage.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/build/as_layers\.html$ /docs/$ver/reference/development_and_debug/as_layers.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/build/stage_introspection\.html$ /docs/$ver/reference/development_and_debug/stage_introspection.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/build/(?.+) /docs/$ver/configuration/stapel_image/$tail redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/deploy/chart_configuration\.html$ /docs/$ver/reference/deploy_process/deploy_into_kubernetes.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/deploy/deploy_to_kubernetes\.html$ /docs/$ver/reference/deploy_process/deploy_into_kubernetes.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/deploy/minikube\.html$ /docs/$ver/reference/development_and_debug/setup_minikube.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/deploy/secrets\.html$ /docs/$ver/reference/deploy_process/working_with_secrets.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/deploy/track_kubernetes_resources\.html$ /docs/$ver/reference/deploy_process/differences_with_helm.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/development_and_debug/stage_introspection\.html$ /docs/$ver/advanced/development_and_debug/stage_introspection.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/local_development/as_layers\.html$ /docs/$ver/reference/development_and_debug/as_layers.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/local_development/installing_minikube\.html$ /docs/$ver/reference/development_and_debug/setup_minikube.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/local_development/lint_and_render_chart\.html$ /docs/$ver/reference/development_and_debug/lint_and_render_chart.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/local_development/setup_minikube\.html$ /docs/$ver/reference/development_and_debug/setup_minikube.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/local_development/stage_introspection\.html$ /docs/$ver/reference/development_and_debug/stage_introspection.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/registry/authorization\.html$ /docs/$ver/reference/registry_authorization.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/registry/cleaning\.html$ /docs/$ver/reference/cleaning_process.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/registry/image_naming\.html$ /docs/$ver/reference/stages_and_images.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/registry/publish\.html$ /docs/$ver/reference/publish_process.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/registry/push\.html$ /docs/$ver/reference/publish_process.html redirect; -rewrite ^/docs/(?v1\.1(?:\.\d+(?:[^/]+)?)?|latest)/reference/registry/tag\.html$ /docs/$ver/reference/publish_process.html redirect; - {{- end }} diff --git a/.helm/values.yaml b/.helm/values.yaml index 9de31e0a5..3ca6015e2 100644 --- a/.helm/values.yaml +++ b/.helm/values.yaml @@ -45,6 +45,13 @@ resources: _default: 30M production: 300M +docs: + currentRoot: v2 + supportedRoots: + - v2 + - v1.2 + latestAliasEnabled: true + backend: affinity: podAntiAffinity: diff --git a/.werf/nginx-dev.conf b/.werf/nginx-dev.conf index 6aba30f0f..d2b6746d7 100644 --- a/.werf/nginx-dev.conf +++ b/.werf/nginx-dev.conf @@ -86,7 +86,7 @@ http { } location /docs/ { - rewrite ^/docs/(?v\d+(?:\.\d+(?:\.\d+(?:[^/]+)?)?)?|latest)/(?.*) /$tail break; + rewrite ^/docs/(?latest|pr-[^/]+|v2(?:\.[^/]+)?|v1\.2(?:\.[^/]+)?)/(?.*) /$tail break; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; diff --git a/assets/js/customscripts.js b/assets/js/customscripts.js index a8437ac1e..49f6f251a 100644 --- a/assets/js/customscripts.js +++ b/assets/js/customscripts.js @@ -2,7 +2,7 @@ $('#mysidebar').height($(".nav").height()); -document.addEventListener("DOMContentLoaded", function() { +document.addEventListener("DOMContentLoaded", function () { /** * AnchorJS */ @@ -10,67 +10,67 @@ document.addEventListener("DOMContentLoaded", function() { var referenceAnchors; if ($('.line-number-anchor')) { - referenceAnchors = new AnchorJS(); - referenceAnchors.options = { - placement: 'left' - }; - referenceAnchors.add('.line-number-anchor'); + referenceAnchors = new AnchorJS(); + referenceAnchors.options = { + placement: 'left' + }; + referenceAnchors.add('.line-number-anchor'); } }); -$( document ).ready(function() { +$(document).ready(function () { $('.header__menu').addClass('header__menu_active'); }); -$( document ).ready(function() { - var wh = $(window).height(); - var sh = $("#mysidebar").height(); +$(document).ready(function () { + var wh = $(window).height(); + var sh = $("#mysidebar").height(); - if (sh + 100 > wh) { - $( "#mysidebar" ).parent().addClass("layout-sidebar__sidebar_a"); - } - // activate tooltips. although this is a bootstrap js function, it must be activated this way in your theme. - $('[data-toggle="tooltip"]').tooltip({ - placement : 'top' - }); + if (sh + 100 > wh) { + $("#mysidebar").parent().addClass("layout-sidebar__sidebar_a"); + } + // activate tooltips. although this is a bootstrap js function, it must be activated this way in your theme. + $('[data-toggle="tooltip"]').tooltip({ + placement: 'top' + }); }); // needed for nav tabs on pages. See Formatting > Nav tabs for more details. // script from http://stackoverflow.com/questions/10523433/how-do-i-keep-the-current-tab-active-with-twitter-bootstrap-after-a-page-reload -$(function() { - var json, tabsState; - $('a[data-toggle="pill"], a[data-toggle="tab"]').on('shown.bs.tab', function(e) { - var href, json, parentId, tabsState; - - tabsState = localStorage.getItem("tabs-state"); - json = JSON.parse(tabsState || "{}"); - parentId = $(e.target).parents("ul.nav.nav-pills, ul.nav.nav-tabs").attr("id"); - href = $(e.target).attr('href'); - json[parentId] = href; - - return localStorage.setItem("tabs-state", JSON.stringify(json)); - }); +$(function () { + var json, tabsState; + $('a[data-toggle="pill"], a[data-toggle="tab"]').on('shown.bs.tab', function (e) { + var href, json, parentId, tabsState; tabsState = localStorage.getItem("tabs-state"); json = JSON.parse(tabsState || "{}"); + parentId = $(e.target).parents("ul.nav.nav-pills, ul.nav.nav-tabs").attr("id"); + href = $(e.target).attr('href'); + json[parentId] = href; - $.each(json, function(containerId, href) { - return $("#" + containerId + " a[href=" + href + "]").tab('show'); - }); + return localStorage.setItem("tabs-state", JSON.stringify(json)); + }); - $("ul.nav.nav-pills, ul.nav.nav-tabs").each(function() { - var $this = $(this); - if (!json[$this.attr("id")]) { - return $this.find("a[data-toggle=tab]:first, a[data-toggle=pill]:first").tab("show"); - } - }); + tabsState = localStorage.getItem("tabs-state"); + json = JSON.parse(tabsState || "{}"); + + $.each(json, function (containerId, href) { + return $("#" + containerId + " a[href=" + href + "]").tab('show'); + }); + + $("ul.nav.nav-pills, ul.nav.nav-tabs").each(function () { + var $this = $(this); + if (!json[$this.attr("id")]) { + return $this.find("a[data-toggle=tab]:first, a[data-toggle=pill]:first").tab("show"); + } + }); }); // Update GitHub stats $(document).ready(function () { var github_requests = [], - github_stats = JSON.parse(localStorage.getItem('werf_github_stats')) || null; + github_stats = JSON.parse(localStorage.getItem('werf_github_stats')) || null; function getGithubRequests() { $('[data-roadmap-step]').each(function () { @@ -96,8 +96,8 @@ $(document).ready(function () { } if (github_stats == null || Date.now() > (github_stats['updated_on'] + 1000 * 60 * 60)) { - github_stats = {'updated_on': Date.now(), 'issues': {}, 'stargazers': 0}; - $.when.apply($, getGithubRequests()).done(function() { + github_stats = { 'updated_on': Date.now(), 'issues': {}, 'stargazers': 0 }; + $.when.apply($, getGithubRequests()).done(function () { updateGithubStats(); localStorage.setItem('werf_github_stats', JSON.stringify(github_stats)); }); @@ -142,33 +142,33 @@ $(document).ready(function () { }); let CurrentGroup = getDocGroupFromURL(); - $('input[name="sitesearch"]').attr("value",function(index,currentvalue){ + $('input[name="sitesearch"]').attr("value", function (index, currentvalue) { if (CurrentGroup) { - return currentvalue + "/docs/" + CurrentGroup + "/" + return currentvalue + "/docs/" + CurrentGroup + "/" } else { - return currentvalue + return currentvalue } }) }); -$(document).ready(function() { - var adjustAnchor = function() { - var $anchor = $(':target'), fixedElementHeight = 120; - if ($anchor.length > 0) { - $('html, body').stop().animate({ - scrollTop: $anchor.offset().top - fixedElementHeight - }, 200); - } +$(document).ready(function () { + var adjustAnchor = function () { + var $anchor = $(':target'), fixedElementHeight = 120; + if ($anchor.length > 0) { + $('html, body').stop().animate({ + scrollTop: $anchor.offset().top - fixedElementHeight + }, 200); + } }; - $(window).on('hashchange load', function() { - adjustAnchor(); + $(window).on('hashchange load', function () { + adjustAnchor(); }); }); -$(document).ready(function(){ +$(document).ready(function () { // wait until fonts are loaded - setTimeout(function() { + setTimeout(function () { $('.publications__list').masonry({ itemSelector: '.publications__post', columnWidth: '.publications__sizer' @@ -176,9 +176,9 @@ $(document).ready(function(){ }, 500) }); -$(document).ready(function(){ +$(document).ready(function () { - $('h1:contains("Installation")').each(function( index ) { + $('h1:contains("Installation")').each(function (index) { var $title = $(this); var $btn1 = $title.next('p'); var $btn2 = $btn1.next('p'); @@ -198,134 +198,134 @@ $(document).ready(function(){ // Presentation -$(document).ready(function() { - if($('#presentation').length) { +$(document).ready(function () { + if ($('#presentation').length) { var magic = new ScrollMagic.Controller(); // Pin intro cheme var intro_scheme_duration = window.innerHeight; intro_scheme_duration = intro_scheme_duration > 1080 ? 1080 : intro_scheme_duration; intro_scheme_duration = intro_scheme_duration < 590 ? 590 : intro_scheme_duration; - new ScrollMagic.Scene({duration: intro_scheme_duration, offset: -10}) - .setPin('#intro-scheme', {pushFollowers: false}) - .addTo(magic); + new ScrollMagic.Scene({ duration: intro_scheme_duration, offset: -10 }) + .setPin('#intro-scheme', { pushFollowers: false }) + .addTo(magic); // Pin bg - new ScrollMagic.Scene({duration: 4000, offset: -10}) - .setPin('#intro-bg', {pushFollowers: false}) - .addTo(magic); + new ScrollMagic.Scene({ duration: 4000, offset: -10 }) + .setPin('#intro-bg', { pushFollowers: false }) + .addTo(magic); // Pin scheme - new ScrollMagic.Scene({triggerElement: '#presentation', triggerHook: 0, duration: 4000, offset: -90}) - .setPin('#presentation', {pushFollowers: false}) - .addTo(magic); + new ScrollMagic.Scene({ triggerElement: '#presentation', triggerHook: 0, duration: 4000, offset: -90 }) + .setPin('#presentation', { pushFollowers: false }) + .addTo(magic); // Pin notes - new ScrollMagic.Scene({duration: 400, triggerElement: '#presentation-notes-1', offset: 100}) - .setPin('#presentation-notes-1', {pushFollowers: false}) - .addTo(magic); - new ScrollMagic.Scene({duration: 400, triggerElement: '#presentation-notes-2', offset: 100}) - .setPin('#presentation-notes-2', {pushFollowers: false}) - .addTo(magic); - new ScrollMagic.Scene({duration: 1000, triggerElement: '#presentation-notes-3', offset: 100}) - .setPin('#presentation-notes-3', {pushFollowers: false}) - .addTo(magic); + new ScrollMagic.Scene({ duration: 400, triggerElement: '#presentation-notes-1', offset: 100 }) + .setPin('#presentation-notes-1', { pushFollowers: false }) + .addTo(magic); + new ScrollMagic.Scene({ duration: 400, triggerElement: '#presentation-notes-2', offset: 100 }) + .setPin('#presentation-notes-2', { pushFollowers: false }) + .addTo(magic); + new ScrollMagic.Scene({ duration: 1000, triggerElement: '#presentation-notes-3', offset: 100 }) + .setPin('#presentation-notes-3', { pushFollowers: false }) + .addTo(magic); // Move away title - new ScrollMagic.Scene({triggerElement: '#presentation', triggerHook: 0, duration: 250, offset: 25}).setTween( + new ScrollMagic.Scene({ triggerElement: '#presentation', triggerHook: 0, duration: 250, offset: 25 }).setTween( new TimelineMax() - .to('#presentation-title', {x: '-2000px', opacity: 0}, 0) - .to('#intro-bg', {x: '-1500px'}, 0) + .to('#presentation-title', { x: '-2000px', opacity: 0 }, 0) + .to('#intro-bg', { x: '-1500px' }, 0) ) - .addTo(magic); + .addTo(magic); // Hide arrows & smart - new ScrollMagic.Scene({triggerElement: '#presentation', triggerHook: 0, duration: 100, offset: 400}).setTween( + new ScrollMagic.Scene({ triggerElement: '#presentation', triggerHook: 0, duration: 100, offset: 400 }).setTween( new TimelineMax() - .to('#scheme_git', {opacity: '0.2'}, 0) - .to('#scheme_docker_registry', {opacity: '0.2'}, 0) - .to('#scheme_k8s', {opacity: '0.2'}, 0) - .to('#scheme_werf', {opacity: '0.2'}, 0) - .to('#scheme_arrows_gw', {opacity: '0'}, 0) - .to('#scheme_arrows_wd', {opacity: '0'}, 0) - .to('#scheme_arrows_wk', {opacity: '0'}, 0) - .to('#scheme_smart_2', {opacity: '0'}, 0) - .to('#scheme_smart', {opacity: '0'}, 0) + .to('#scheme_git', { opacity: '0.2' }, 0) + .to('#scheme_docker_registry', { opacity: '0.2' }, 0) + .to('#scheme_k8s', { opacity: '0.2' }, 0) + .to('#scheme_werf', { opacity: '0.2' }, 0) + .to('#scheme_arrows_gw', { opacity: '0' }, 0) + .to('#scheme_arrows_wd', { opacity: '0' }, 0) + .to('#scheme_arrows_wk', { opacity: '0' }, 0) + .to('#scheme_smart_2', { opacity: '0' }, 0) + .to('#scheme_smart', { opacity: '0' }, 0) ).addTo(magic); // Git - new ScrollMagic.Scene({triggerElement: '#presentation', triggerHook: 0, duration: 200, offset: 600}).setTween( + new ScrollMagic.Scene({ triggerElement: '#presentation', triggerHook: 0, duration: 200, offset: 600 }).setTween( new TimelineMax() - .to('#scheme_git', {opacity: '1'}, 0) + .to('#scheme_git', { opacity: '1' }, 0) ).addTo(magic); // Git -> werf, show - new ScrollMagic.Scene({triggerElement: '#presentation', triggerHook: 0, duration: 200, offset: 1000}).setTween( + new ScrollMagic.Scene({ triggerElement: '#presentation', triggerHook: 0, duration: 200, offset: 1000 }).setTween( new TimelineMax() - .to('#scheme_git', {opacity: '1'}, 0) - .to('#scheme_arrows_gw', {opacity: '1'}, 0) - .to('#scheme_werf', {opacity: '1'}, 0) + .to('#scheme_git', { opacity: '1' }, 0) + .to('#scheme_arrows_gw', { opacity: '1' }, 0) + .to('#scheme_werf', { opacity: '1' }, 0) ).addTo(magic); // werf -> Docker Registry, show - new ScrollMagic.Scene({triggerElement: '#presentation', triggerHook: 0, duration: 200, offset: 1400}).setTween( + new ScrollMagic.Scene({ triggerElement: '#presentation', triggerHook: 0, duration: 200, offset: 1400 }).setTween( new TimelineMax() - .to('#scheme_git', {opacity: '0.2'}, 0) - .to('#scheme_docker_registry', {opacity: '1'}, 0) - .to('#scheme_arrows_gw', {opacity: '0.2'}, 0) - .to('#scheme_arrows_wd', {opacity: '1'}, 0) - .to('#scheme_smart_2', {opacity: '1'}, 0) + .to('#scheme_git', { opacity: '0.2' }, 0) + .to('#scheme_docker_registry', { opacity: '1' }, 0) + .to('#scheme_arrows_gw', { opacity: '0.2' }, 0) + .to('#scheme_arrows_wd', { opacity: '1' }, 0) + .to('#scheme_smart_2', { opacity: '1' }, 0) ).addTo(magic); // werf -> Docker Registry, sync - new ScrollMagic.Scene({triggerElement: '#presentation', triggerHook: 0, duration: 500, offset: 1800}).setTween( + new ScrollMagic.Scene({ triggerElement: '#presentation', triggerHook: 0, duration: 500, offset: 1800 }).setTween( TweenMax - .fromTo('#scheme_smart_icon_update_arrows_2', 1, - {rotation: '0'}, {rotation: '-720', transformOrigin: '50% 50%', repeat: -1, ease: "power1.out"}) - .duration(2) + .fromTo('#scheme_smart_icon_update_arrows_2', 1, + { rotation: '0' }, { rotation: '-720', transformOrigin: '50% 50%', repeat: -1, ease: "power1.out" }) + .duration(2) ).addTo(magic); // werf -> Docker Registry, show info - new ScrollMagic.Scene({triggerElement: '#presentation', triggerHook: 0, duration: 200, offset: 2200}).setTween( + new ScrollMagic.Scene({ triggerElement: '#presentation', triggerHook: 0, duration: 200, offset: 2200 }).setTween( new TimelineMax() - .to('#scheme_smart_icon_update_2', {opacity: '0'}) - .to('#scheme_smart_icon_check_2', {opacity: '1'}) + .to('#scheme_smart_icon_update_2', { opacity: '0' }) + .to('#scheme_smart_icon_check_2', { opacity: '1' }) ).addTo(magic); // werf -> Kubernetes, show - new ScrollMagic.Scene({triggerElement: '#presentation', triggerHook: 0, duration: 200, offset: 2600}).setTween( + new ScrollMagic.Scene({ triggerElement: '#presentation', triggerHook: 0, duration: 200, offset: 2600 }).setTween( new TimelineMax() - .to('#scheme_docker_registry', {opacity: '0.2'}, 0) - .to('#scheme_arrows_wd', {opacity: '0.2'}, 0) - .to('#scheme_arrows_wk', {opacity: '1'}, 0) - .to('#scheme_smart', {opacity: '1'}, 0) - .to('#scheme_k8s', {opacity: '1'}, 0) + .to('#scheme_docker_registry', { opacity: '0.2' }, 0) + .to('#scheme_arrows_wd', { opacity: '0.2' }, 0) + .to('#scheme_arrows_wk', { opacity: '1' }, 0) + .to('#scheme_smart', { opacity: '1' }, 0) + .to('#scheme_k8s', { opacity: '1' }, 0) ).addTo(magic); // werf -> Kubernetes, sync - new ScrollMagic.Scene({triggerElement: '#presentation', triggerHook: 0, duration: 500, offset: 3000}).setTween( + new ScrollMagic.Scene({ triggerElement: '#presentation', triggerHook: 0, duration: 500, offset: 3000 }).setTween( TweenMax - .fromTo('#scheme_smart_icon_update_arrows', 1, - {rotation: '0'}, {rotation: '-720', transformOrigin: '50% 50%', repeat: -1, ease: "power1.out"}) - .duration(2) + .fromTo('#scheme_smart_icon_update_arrows', 1, + { rotation: '0' }, { rotation: '-720', transformOrigin: '50% 50%', repeat: -1, ease: "power1.out" }) + .duration(2) ).addTo(magic); // werf -> Docker Registry, show info - new ScrollMagic.Scene({triggerElement: '#presentation', triggerHook: 0, duration: 200, offset: 3400}).setTween( + new ScrollMagic.Scene({ triggerElement: '#presentation', triggerHook: 0, duration: 200, offset: 3400 }).setTween( new TimelineMax() - .to('#scheme_smart_icon_update', {opacity: '0'}) - .to('#scheme_smart_icon_check', {opacity: '1'}) + .to('#scheme_smart_icon_update', { opacity: '0' }) + .to('#scheme_smart_icon_check', { opacity: '1' }) ).addTo(magic); // Full - new ScrollMagic.Scene({triggerElement: '#presentation', triggerHook: 0, duration: 200, offset: 3800}).setTween( + new ScrollMagic.Scene({ triggerElement: '#presentation', triggerHook: 0, duration: 200, offset: 3800 }).setTween( new TimelineMax() - .to('#scheme_smart_icon_update', {opacity: '0'}) - .to('#scheme_smart_icon_check', {opacity: '1'}) - .to('#scheme_docker_registry', {opacity: '1'}, 0) - .to('#scheme_arrows_wd', {opacity: '1'}, 0) - .to('#scheme_git', {opacity: '1'}, 0) - .to('#scheme_arrows_gw', {opacity: '1'}, 0) + .to('#scheme_smart_icon_update', { opacity: '0' }) + .to('#scheme_smart_icon_check', { opacity: '1' }) + .to('#scheme_docker_registry', { opacity: '1' }, 0) + .to('#scheme_arrows_wd', { opacity: '1' }, 0) + .to('#scheme_git', { opacity: '1' }, 0) + .to('#scheme_arrows_gw', { opacity: '1' }, 0) ).addTo(magic); } }); @@ -333,50 +333,51 @@ $(document).ready(function() { // Intro scheme -$(document).ready(function() { - if($('#intro-animation').length) { +$(document).ready(function () { + if ($('#intro-animation').length) { var ia = {}; - ia.dot = '#intro-animation-dot'; - ia.dot2 = '#intro-animation-dot2'; - ia.k8s = '#intro-animation-k8s'; - ia.git = '#intro-animation-git'; - ia.docker = '#intro-animation-docker'; - ia.helm = '#intro-animation-helm'; - ia.werf = '#intro-animation-werf'; - ia.werf_k8s = '#intro-animation-werf-k8s'; - ia.werf_git = '#intro-animation-werf-git'; - ia.werf_docker = '#intro-animation-werf-docker'; - ia.werf_helm = '#intro-animation-werf-helm'; + ia.dot = '#intro-animation-dot'; + ia.dot2 = '#intro-animation-dot2'; + ia.k8s = '#intro-animation-k8s'; + ia.git = '#intro-animation-git'; + ia.docker = '#intro-animation-docker'; + ia.helm = '#intro-animation-helm'; + ia.werf = '#intro-animation-werf'; + ia.werf_k8s = '#intro-animation-werf-k8s'; + ia.werf_git = '#intro-animation-werf-git'; + ia.werf_docker = '#intro-animation-werf-docker'; + ia.werf_helm = '#intro-animation-werf-helm'; function moveDot(data) { data.timeline - .to(data.dot, {duration: 2, ease: 'power1.inOut', - motionPath: { - path: data.path, - align: data.path, - alignOrigin: [0.5, 0.5], - autoRotate: false, - start: 0, - end: data.reverse ? -1 : 1, - } - }, data.delay) - .to(data.dot, {opacity: 1, duration: 0.25}, '-=2') - .to(data.dot, {opacity: 0, duration: 0.25}, '-=0.25'); + .to(data.dot, { + duration: 2, ease: 'power1.inOut', + motionPath: { + path: data.path, + align: data.path, + alignOrigin: [0.5, 0.5], + autoRotate: false, + start: 0, + end: data.reverse ? -1 : 1, + } + }, data.delay) + .to(data.dot, { opacity: 1, duration: 0.25 }, '-=2') + .to(data.dot, { opacity: 0, duration: 0.25 }, '-=0.25'); } - var tl = gsap.timeline({repeat: -1, repeatDelay: 1, delay: 1}); + var tl = gsap.timeline({ repeat: -1, repeatDelay: 1, delay: 1 }); // Dot git werf - moveDot({timeline: tl, dot: ia.dot, path: ia.werf_git, reverse: false, delay: '=0' }); + moveDot({ timeline: tl, dot: ia.dot, path: ia.werf_git, reverse: false, delay: '=0' }); // Dot werf docker - moveDot({timeline: tl, dot: ia.dot, path: ia.werf_docker, reverse: false, delay: '=0' }); + moveDot({ timeline: tl, dot: ia.dot, path: ia.werf_docker, reverse: false, delay: '=0' }); // Dot werf helm - moveDot({timeline: tl, dot: ia.dot2, path: ia.werf_helm, reverse: true, delay: '-=2' }); + moveDot({ timeline: tl, dot: ia.dot2, path: ia.werf_helm, reverse: true, delay: '-=2' }); // Dot docker werf - moveDot({timeline: tl, dot: ia.dot, path: ia.werf_docker, reverse: true, delay: '=0' }); + moveDot({ timeline: tl, dot: ia.dot, path: ia.werf_docker, reverse: true, delay: '=0' }); // Dot helm werf - moveDot({timeline: tl, dot: ia.dot2, path: ia.werf_helm, reverse: false, delay: '-=2' }); + moveDot({ timeline: tl, dot: ia.dot2, path: ia.werf_helm, reverse: false, delay: '-=2' }); // Dot werf k8s - moveDot({timeline: tl, dot: ia.dot, path: ia.werf_k8s, reverse: true, delay: '=0' }); + moveDot({ timeline: tl, dot: ia.dot, path: ia.werf_k8s, reverse: true, delay: '=0' }); } }); @@ -395,25 +396,25 @@ $(document).ready(function () { function updateNauContent() { var $releases_container = $('#nau-releases'); - nau_data.releases.slice(0, 5).forEach(function(item) { + nau_data.releases.slice(0, 5).forEach(function (item) { $releases_container.append( - `
+ `
${item.tag_name}
- ${moment(item.published_at).format('DD.MM.YYYY') } + ${moment(item.published_at).format('DD.MM.YYYY')}
`); }); var $news_container = $('#nau-news'); $news_container.append( - ` + ` ${nau_data.news.title}
- ${moment(nau_data.news.date).format('DD.MM.YYYY') } + ${moment(nau_data.news.date).format('DD.MM.YYYY')}
`); } @@ -450,8 +451,8 @@ $(document).ready(function () { ]; if (nau_data == null || Date.now() > (nau_data['updated_on'] + 1000 * 60 * 60)) { - nau_data = {'updated_on': Date.now(), 'news': {}, 'releases': []}; - $.when.apply($, nau_requests).done(function() { + nau_data = { 'updated_on': Date.now(), 'news': {}, 'releases': [] }; + $.when.apply($, nau_requests).done(function () { updateNauContent(); localStorage.setItem('werf_news', JSON.stringify(nau_data)); }); @@ -466,28 +467,33 @@ $(document).ready(function () { }); function getDocGroupFromURL() { - let result = window.location.pathname.match(/^\/docs\/(v\d+\.\d+)/); - if ( result && result[1] ) { - return result[1]; + let result = window.location.pathname.match(/^\/docs\/(latest|pr-[^/]+|v2(?:\.[^/]+)?|v1\.2(?:\.[^/]+)?)(?:\/|$)/); + if (result && result[1]) { + if (result[1] === 'latest') { + return 'v2'; + } + if (result[1].indexOf('pr-') === 0) { + return 'v2'; + } + if (result[1].indexOf('v1.2') === 0) { + return 'v1.2'; + } + return 'v2'; } return null; } function getDocVersionFromURL() { - let result = window.location.pathname.match(/\/docs\/(v\d+\.\d+([^/]+)?)\/.*/); - if ( result ) { - if ( result.length > 2 && result[2] ) { - return result[1].replace('-plus-','+'); - } else { - return "main"; - } + let result = window.location.pathname.match(/\/docs\/(latest|pr-[^/]+|v2(?:\.[^/]+)?|v1\.2(?:\.[^/]+)?)(?:\/.*)?$/); + if (result) { + return result[1].replace('-plus-', '+'); } return null; } function getDocVersionFromPage() { let result = $("#versionNumber").text(); - if ( !result) { + if (!result) { result = ""; } return result; @@ -495,14 +501,14 @@ function getDocVersionFromPage() { function checkURLExist(url, callback) { - if ( ! $.isFunction(callback)) { - throw Error('Not a valid callback'); - } + if (!$.isFunction(callback)) { + throw Error('Not a valid callback'); + } - $.ajax({ - type: 'HEAD', - url: url, - success: $.proxy(callback, this, true), - error: $.proxy(callback, this, false) - }); + $.ajax({ + type: 'HEAD', + url: url, + success: $.proxy(callback, this, true), + error: $.proxy(callback, this, false) + }); } diff --git a/backend/common.go b/backend/common.go index 9ed17a5d3..420f668c4 100644 --- a/backend/common.go +++ b/backend/common.go @@ -1,23 +1,12 @@ package main import ( - "crypto/tls" - "errors" "fmt" - "io/ioutil" - "net" "net/http" "net/url" "os" "regexp" - "slices" - "sort" - "strconv" "strings" - "time" - - log "github.com/sirupsen/logrus" - "gopkg.in/yaml.v2" ) type ChannelType struct { @@ -65,48 +54,25 @@ type versionMenuItems struct { var ReleasesStatus ReleasesStatusType -var channelsListReverseStability = []string{"rock-solid", "stable", "ea", "beta", "alpha"} +const ( + defaultCurrentDocsRoot = "v2" + currentDocsRootEnv = "CURRENT_DOCS_MAJOR" + supportedDocsRootsEnv = "SUPPORTED_DOCS_MAJOR_VERSIONS" + latestDocsAliasEnabledEnv = "DOCS_LATEST_ALIAS_ENABLED" +) + +var legacyChannelPattern = regexp.MustCompile(`^v(2|1\.2)-(alpha|beta|ea|stable|rock-solid)$`) func (m *versionMenuType) getChannelMenuData(r *http.Request, releases *ReleasesStatusType) (err error) { err = nil + _ = releases m.CurrentPageURLRelative = getDocPageURLRelative(r) m.CurrentPageURL = getCurrentPageURL(r) - m.CurrentVersionURL = getVersionURL(r) - - if isGroupChannelURL, _ := regexp.MatchString("v[0-9]+.[0-9]+-(alpha|beta|ea|stable|rock-solid)", m.CurrentVersionURL); isGroupChannelURL { - items := strings.Split(m.CurrentVersionURL, "-") - if len(items) == 2 { - m.CurrentGroup = strings.TrimPrefix(items[0], "v") - m.CurrentChannel = items[1] - _, m.CurrentVersion = getVersionFromChannelAndGroup(releases, m.CurrentChannel, m.CurrentGroup) - m.CurrentVersionURL = VersionToURL(m.CurrentVersion) - } - } + m.CurrentVersion = getCanonicalDocsVersion(getVersionURL(r)) + m.CurrentVersionURL = VersionToURL(m.CurrentVersion) + m.CurrentGroup = strings.TrimPrefix(m.CurrentVersion, "v") - m.CurrentVersion = URLToVersion(m.CurrentVersionURL) - - if m.CurrentVersion == "latest" { - m.CurrentGroup = "latest" - m.CurrentChannel = "latest" - } - - if m.CurrentVersion == fmt.Sprintf("v%s", getRootRelease()) { - m.CurrentVersion = getRootRelease() - m.CurrentGroup = getRootRelease() - } - - if m.CurrentVersion == "" { - m.CurrentVersion = getRootRelease() - m.CurrentVersionURL = VersionToURL(m.CurrentVersion) - } - - // Try to find current channel from URL - if m.CurrentChannel == "" || m.CurrentGroup == "" { - m.CurrentChannel, m.CurrentGroup = getChannelAndGroupFromVersion(releases, m.CurrentVersion) - } - - // Add the first menu item (current version) m.VersionItems = append(m.VersionItems, versionMenuItems{ Group: m.CurrentGroup, Channel: m.CurrentChannel, @@ -115,19 +81,27 @@ func (m *versionMenuType) getChannelMenuData(r *http.Request, releases *Releases IsCurrent: true, }) - // Add item for the "latest" version - m.VersionItems = append(m.VersionItems, versionMenuItems{ - Group: "", - Channel: "", - Version: "latest", - VersionURL: "latest", - IsCurrent: false, - }) + if isLatestAliasEnabled() { + m.VersionItems = append(m.VersionItems, versionMenuItems{ + Group: "", + Channel: "", + Version: "latest", + VersionURL: "latest", + IsCurrent: false, + }) + } - // Add other items - for _, group := range getGroups() { - // TODO error handling - _ = m.getChannelsFromGroup(&ReleasesStatus, group) + for _, root := range getSupportedDocsRoots() { + if root == m.CurrentVersion { + continue + } + m.VersionItems = append(m.VersionItems, versionMenuItems{ + Group: strings.TrimPrefix(root, "v"), + Channel: "", + Version: root, + VersionURL: root, + IsCurrent: false, + }) } return @@ -135,50 +109,15 @@ func (m *versionMenuType) getChannelMenuData(r *http.Request, releases *Releases func (m *versionMenuType) getVersionMenuData(r *http.Request, releases *ReleasesStatusType) (err error) { err = nil + _ = releases m.CurrentPageURLRelative = getDocPageURLRelative(r) m.CurrentPageURL = getCurrentPageURL(r) - m.CurrentVersionURL = getVersionURL(r) - m.CurrentVersion = URLToVersion(m.CurrentVersionURL) - - if m.CurrentVersion == "" { - m.CurrentVersion = fmt.Sprintf("v%s", getRootRelease()) - m.CurrentVersionURL = VersionToURL(m.CurrentVersion) - } - - re := regexp.MustCompile(`^v([0-9]+(?:\.[0-9]+)?)(\..+)?$`) - res := re.FindStringSubmatch(m.CurrentVersion) - if res == nil { - m.MenuDocumentationLink = fmt.Sprintf("/docs/%s/", VersionToURL(m.CurrentVersion)) - m.AbsoluteVersion = m.CurrentVersion - } else { - if res[2] != "" { - // Version is not a group (MAJ.MIN), but the patch version - // The old behavior (e.g., link to v2 for the v2.0.1 version) - // m.MenuDocumentationLink = fmt.Sprintf("/docs/%s/", VersionToURL(res[1])) - // The new behavior (link to v2.0.1 for the v2.0.1 version) - m.MenuDocumentationLink = fmt.Sprintf("/docs/%s/", VersionToURL(m.CurrentVersion)) - m.AbsoluteVersion = fmt.Sprintf("%s", m.CurrentVersion) - } else { - // Version is a group - m.MenuDocumentationLink = fmt.Sprintf("/docs/%s/", VersionToURL(m.CurrentVersion)) - err, m.AbsoluteVersion = getVersionFromGroup(&ReleasesStatus, res[1]) - if err != nil { - log.Debugln(fmt.Sprintf("getVersionMenuData: error determine absolute version for %s (got %s)", m.CurrentVersion, m.AbsoluteVersion)) - } - } - } - - // m.MenuDocumentationLink = fmt.Sprintf("/docs/v%s/", VersionToURL(getRootRelease())) - // if m.CurrentChannel == "" && m.CurrentGroup == "" { - // m.MenuDocumentationLink = fmt.Sprintf("/docs/%v/", VersionToURL(m.CurrentVersion)) - // } else if m.CurrentChannel == "" && m.CurrentGroup != "" { - // m.MenuDocumentationLink = fmt.Sprintf("/docs/v%v/", m.CurrentGroup) - // } else { - // m.MenuDocumentationLink = fmt.Sprintf("/docs/v%v-%v/", m.CurrentGroup, m.CurrentChannel) - // } + m.CurrentVersion = getCanonicalDocsVersion(getVersionURL(r)) + m.CurrentVersionURL = VersionToURL(m.CurrentVersion) + m.AbsoluteVersion = m.CurrentVersion + m.MenuDocumentationLink = fmt.Sprintf("/docs/%s/", m.CurrentVersionURL) - // Add the first menu item m.VersionItems = append(m.VersionItems, versionMenuItems{ Group: m.CurrentGroup, Channel: m.CurrentChannel, @@ -187,26 +126,27 @@ func (m *versionMenuType) getVersionMenuData(r *http.Request, releases *Releases IsCurrent: true, }) - // for _, releaseItem_ := range releases.Releases { - // if releaseItem_.Group == m.CurrentGroup { - // for _, channelItem_ := range releaseItem_.Channels { - // if channelItem_.Name == m.CurrentChannel { - // m.VersionItems = append(m.VersionItems, versionMenuItems{ - // Group: m.CurrentGroup, - // Channel: m.CurrentChannel, - // Version: channelItem_.Version, - // VersionURL: VersionToURL(channelItem_.Version), - // IsCurrent: true, - // }) - // } - // } - // } - // } - - // Add other items - for _, group := range getGroups() { - // TODO error handling - _ = m.getChannelsFromGroup(&ReleasesStatus, group) + if isLatestAliasEnabled() { + m.VersionItems = append(m.VersionItems, versionMenuItems{ + Group: "", + Channel: "", + Version: "latest", + VersionURL: "latest", + IsCurrent: false, + }) + } + + for _, root := range getSupportedDocsRoots() { + if root == m.CurrentVersion { + continue + } + m.VersionItems = append(m.VersionItems, versionMenuItems{ + Group: strings.TrimPrefix(root, "v"), + Channel: "", + Version: root, + VersionURL: root, + IsCurrent: false, + }) } return @@ -214,49 +154,30 @@ func (m *versionMenuType) getVersionMenuData(r *http.Request, releases *Releases func (m *versionMenuType) getGroupMenuData(r *http.Request, releases *ReleasesStatusType) (err error) { err = nil + _ = releases m.CurrentPageURLRelative = getDocPageURLRelative(r) m.CurrentPageURL = getCurrentPageURL(r) - m.CurrentVersionURL = getVersionURL(r) - m.CurrentVersion = URLToVersion(m.CurrentVersionURL) + m.CurrentVersion = getCanonicalDocsVersion(getVersionURL(r)) + m.CurrentVersionURL = VersionToURL(m.CurrentVersion) - if m.CurrentVersion == "" { - m.CurrentVersion = fmt.Sprintf("v%s", getRootRelease()) - m.CurrentVersionURL = VersionToURL(m.CurrentVersion) - } - - re := regexp.MustCompile(`^v([0-9]+\.[0-9]+)(\..+)?$`) - res := re.FindStringSubmatch(m.CurrentVersion) - if res != nil { - m.VersionItems = append(m.VersionItems, versionMenuItems{ - Group: res[1], - Channel: "", - Version: m.CurrentVersion, - VersionURL: m.CurrentVersionURL, - IsCurrent: true, - }) - } else { - // Version is not a group (MAJ.MIN), but the patch version - m.VersionItems = append(m.VersionItems, versionMenuItems{ - Group: "", - Channel: "", - Version: m.CurrentVersion, - VersionURL: m.CurrentVersionURL, - IsCurrent: true, - }) - } + m.VersionItems = append(m.VersionItems, versionMenuItems{ + Group: strings.TrimPrefix(m.CurrentVersion, "v"), + Channel: "", + Version: m.CurrentVersion, + VersionURL: m.CurrentVersionURL, + IsCurrent: true, + }) - // Add other items - for _, group := range getGroups() { - // TODO error handling - if group == "1.0" || group == "1.1" { + for _, root := range getSupportedDocsRoots() { + if root == m.CurrentVersion { continue } m.VersionItems = append(m.VersionItems, versionMenuItems{ - Group: group, + Group: strings.TrimPrefix(root, "v"), Channel: "", - Version: "", - VersionURL: "", + Version: root, + VersionURL: root, IsCurrent: false, }) } @@ -264,100 +185,35 @@ func (m *versionMenuType) getGroupMenuData(r *http.Request, releases *ReleasesSt return } -// Get channels and corresponding versions for the specified -// group according to the reverse order of stability -func (m *versionMenuType) getChannelsFromGroup(releases *ReleasesStatusType, group string) (err error) { - if group == "1.0" || group == "1.1" { - return - } - for _, item := range releases.Releases { - if item.Group == group { - for _, channel := range channelsListReverseStability { - for _, channelItem := range item.Channels { - if channelItem.Name == channel { - m.VersionItems = append(m.VersionItems, versionMenuItems{ - Group: group, - Channel: channelItem.Name, - Version: channelItem.Version, - VersionURL: VersionToURL(channelItem.Version), - IsCurrent: false, - }) - } - } - } - } - } - return -} - // Get channel and group for specified version func getChannelAndGroupFromVersion(releases *ReleasesStatusType, version string) (channel, group string) { - re := regexp.MustCompile(`^v([0-9]+\.[0-9]+)$`) - res := re.FindStringSubmatch(version) - if res != nil { - return "", res[1] - } - - for _, group := range getGroups() { - for _, channel := range channelsListReverseStability { - for _, releaseItem := range releases.Releases { - if releaseItem.Group == group { - for _, channelItem := range releaseItem.Channels { - if channelItem.Name == channel { - if channelItem.Version == version { - return channel, group - } - } - } - } - } - } + _ = releases + v := getCanonicalDocsVersion(version) + if v == "latest" { + return "", "latest" } - return + return "", strings.TrimPrefix(v, "v") } // Get version for specified group and channel func getVersionFromChannelAndGroup(releases *ReleasesStatusType, channel, group string) (err error, version string) { - for _, releaseItem := range releases.Releases { - if releaseItem.Group == group { - for _, channelItem := range releaseItem.Channels { - if channelItem.Name == channel { - return nil, normalizeVersion(channelItem.Version) - } - } - } - } - return errors.New(fmt.Sprintf("no matching version for group %s, channel %s", group, channel)), "" + _ = releases + return nil, getCanonicalDocsVersion(fmt.Sprintf("v%s-%s", strings.TrimPrefix(group, "v"), channel)) } // Gev version from specified group // E.g. get v1.2.3+fix6 from v1.2 func getVersionFromGroup(releases *ReleasesStatusType, group string) (err error, version string) { - if group == "latest" || group == getRootRelease() { - return nil, "latest" - } - if len(releases.Releases) > 0 { - for _, ReleaseGroup := range releases.Releases { - if ReleaseGroup.Group == group { - releaseVersions := make(map[string]string) - for _, channel := range ReleaseGroup.Channels { - releaseVersions[channel.Name] = channel.Version - } - - if _, ok := releaseVersions["stable"]; ok { - return nil, normalizeVersion(releaseVersions["stable"]) - } else if _, ok := releaseVersions["ea"]; ok { - return nil, normalizeVersion(releaseVersions["ea"]) - } else if _, ok := releaseVersions["beta"]; ok { - return nil, normalizeVersion(releaseVersions["beta"]) - } else if _, ok := releaseVersions["alpha"]; ok { - return nil, (releaseVersions["alpha"]) - } - } - } + _ = releases + candidate := group + if candidate != "latest" && !strings.HasPrefix(candidate, "v") { + candidate = "v" + candidate } - - return errors.New(fmt.Sprintf("Can't get version for %s", group)), "" + canonical := getCanonicalDocsVersion(candidate) + if canonical == getCurrentDocsRoot() && candidate != canonical && candidate != "latest" { + return fmt.Errorf("unsupported docs group: %s", group), "" + } + return nil, canonical } // Add prefix 'v' to a version if it doesn't have yet @@ -370,44 +226,88 @@ func normalizeVersion(version string) string { } func getRootReleaseVersion() string { - activeRelease := getRootRelease() - if activeRelease == "latest" { - return activeRelease - } - - _ = updateReleasesStatus() - - if len(ReleasesStatus.Releases) > 0 { - for _, ReleaseGroup := range ReleasesStatus.Releases { - if ReleaseGroup.Group == activeRelease { - releaseVersions := make(map[string]string) - for _, channel := range ReleaseGroup.Channels { - releaseVersions[channel.Name] = channel.Version - } - - if _, ok := releaseVersions["stable"]; ok { - return releaseVersions["stable"] - } else if _, ok := releaseVersions["ea"]; ok { - return releaseVersions["ea"] - } else if _, ok := releaseVersions["beta"]; ok { - return releaseVersions["beta"] - } else if _, ok := releaseVersions["alpha"]; ok { - return releaseVersions["alpha"] - } - } + return getCurrentDocsRoot() +} + +func getRootRelease() (result string) { + return strings.TrimPrefix(getCurrentDocsRoot(), "v") +} + +func isLatestAliasEnabled() bool { + v := strings.TrimSpace(strings.ToLower(os.Getenv(latestDocsAliasEnabledEnv))) + if v == "" { + return true + } + return v == "1" || v == "true" || v == "yes" || v == "on" +} + +func getCurrentDocsRoot() string { + candidate := normalizeDocsRoot(os.Getenv(currentDocsRootEnv)) + if candidate != "" { + return candidate + } + return defaultCurrentDocsRoot +} + +func getSupportedDocsRoots() []string { + raw := strings.Split(strings.TrimSpace(os.Getenv(supportedDocsRootsEnv)), ",") + roots := make([]string, 0, len(raw)+1) + seen := map[string]struct{}{} + + add := func(v string) { + if v == "" { + return } + if _, ok := seen[v]; ok { + return + } + seen[v] = struct{}{} + roots = append(roots, v) + } + + add(getCurrentDocsRoot()) + for _, item := range raw { + add(normalizeDocsRoot(item)) } - return "unknown" + + return roots } -func getRootRelease() (result string) { - if len(os.Getenv("ACTIVE_RELEASE")) > 0 { - result = os.Getenv("ACTIVE_RELEASE") - } else { - result = "2" +func normalizeDocsRoot(raw string) string { + v := strings.TrimSpace(raw) + if v == "" { + return "" + } + if !strings.HasPrefix(v, "v") { + v = "v" + v } + if matched, _ := regexp.MatchString(`^v[0-9]+(?:\.[0-9]+)?$`, v); !matched { + return "" + } + return v +} - return +func getCanonicalDocsVersion(raw string) string { + v := strings.TrimSpace(URLToVersion(raw)) + if v == "" || v == "latest" { + return getCurrentDocsRoot() + } + if strings.HasPrefix(v, "pr-") { + return v + } + if legacyChannelPattern.MatchString(v) { + parts := strings.SplitN(v[1:], "-", 2) + if len(parts) == 2 { + return getCanonicalDocsVersion("v" + parts[0]) + } + } + if strings.HasPrefix(v, "v1.2") { + return "v1.2" + } + if strings.HasPrefix(v, "v2") { + return "v2" + } + return getCurrentDocsRoot() } // Get the full page URL menu requested for @@ -526,73 +426,10 @@ func URLToVersion(version string) (result string) { } func validateURL(url string) error { - if strings.ToLower(os.Getenv("URL_VALIDATION")) == "false" { - return nil - } - - allowedStatusCodes := []int{200, 401} - maxRedirects := 10 - - client := &http.Client{ - Transport: &http.Transport{ - Proxy: http.ProxyFromEnvironment, - DialContext: (&net.Dialer{ - Timeout: 10 * time.Second, - KeepAlive: 10 * time.Second, - }).DialContext, - ForceAttemptHTTP2: true, - MaxIdleConns: 100, - IdleConnTimeout: 10 * time.Second, - TLSHandshakeTimeout: 10 * time.Second, - ExpectContinueTimeout: 1 * time.Second, - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - }, - }, - CheckRedirect: func(req *http.Request, via []*http.Request) error { - if len(via) >= maxRedirects { - return fmt.Errorf("stopped after %d redirects", len(via)) - } - return nil - }, - } - - resp, err := client.Get(url) - if err != nil { - if strings.Contains(err.Error(), "stopped after") { - return fmt.Errorf("too many redirects for %s", url) - } - return err - } - resp.Body.Close() - log.Tracef("Validating %v:\nStatus - %v\nHeader - %+v", url, resp.Status, resp.Header) - - if !slices.Contains(allowedStatusCodes, resp.StatusCode) { - return fmt.Errorf("%s returned invalid status code: %d", url, resp.StatusCode) - } - + _ = url return nil } -// Get update channel groups in a descending order. -func getGroups() (groups []string) { - for _, item := range ReleasesStatus.Releases { - groups = append(groups, item.Group) - } - sort.Slice(groups, func(i, j int) bool { - var i_, j_ float64 - var err error - if i_, err = strconv.ParseFloat(groups[i], 32); err != nil { - i_ = 0 - } - if j_, err = strconv.ParseFloat(groups[j], 32); err != nil { - j_ = 0 - } - return i_ > j_ - }) - return -} - func getRootFilesPath(r *http.Request) (result string) { result = "./root/" if strings.HasPrefix(r.Host, "ru.") || strings.HasPrefix(r.Host, "ru-") { @@ -604,20 +441,9 @@ func getRootFilesPath(r *http.Request) (result string) { } func updateReleasesStatus() error { - err := updateReleasesStatusTRDL() - return err + return nil } func updateReleasesStatusTRDL() error { - data, err := ioutil.ReadFile("trdl/trdl_channels.yaml") - if err != nil { - log.Errorf("Can't open trdl_channels.yaml (%e)", err) - return err - } - err = yaml.Unmarshal(data, &ReleasesStatus) - if err != nil { - log.Errorf("Can't unmarshall trdl_channels.yaml (%e)", err) - return err - } - return err + return nil } diff --git a/backend/handlers.go b/backend/handlers.go index a577b08a8..542f38dbc 100644 --- a/backend/handlers.go +++ b/backend/handlers.go @@ -24,30 +24,22 @@ func ssiHandler(w http.ResponseWriter, r *http.Request) { // Get some status info func statusHandler(w http.ResponseWriter, r *http.Request) { - var msg []string - status := "ok" + _ = r w.Header().Set("Content-Type", "application/json; charset=utf-8") - err := updateReleasesStatus() - if err != nil { - msg = append(msg, err.Error()) - status = "error" - } - _ = json.NewEncoder(w).Encode( ApiStatusResponseType{ - Status: status, - Msg: strings.Join(msg, " "), + Status: "ok", + Msg: "", RootVersion: getRootReleaseVersion(), RootVersionURL: VersionToURL(getRootReleaseVersion()), - Multiwerf: ReleasesStatus.Releases, + Multiwerf: []ReleaseType{}, }) } // X-Redirect to the stablest documentation version for specific group func groupHandler(w http.ResponseWriter, r *http.Request) { - _ = updateReleasesStatus() rawQuery := r.URL.RawQuery log.Debugln("Use handler - groupHandler") vars := mux.Vars(r) @@ -58,9 +50,7 @@ func groupHandler(w http.ResponseWriter, r *http.Request) { } w.Header().Set("X-Accel-Redirect", redirectURL) } else { - // Fall back to the version specified in the ACTIVE_RELEASE env, if got the incorrect version. - activeRelease := getRootRelease() - redirectURL := fmt.Sprintf("/docs/v%s/", activeRelease) + redirectURL := fmt.Sprintf("/docs/%s/", getCurrentDocsRoot()) if rawQuery != "" { redirectURL = fmt.Sprintf("%s?%s", redirectURL, rawQuery) } @@ -68,43 +58,26 @@ func groupHandler(w http.ResponseWriter, r *http.Request) { } } -// Handler for versions which are not available (there are no ingress for the version and requests go to router) -// Redirect request to the corresponding group -func unknownVersionHandler(w http.ResponseWriter, r *http.Request) { - var URLToRedirect string - var err error - - log.Debugln("Use handler - unknownVersionHandler") - - pageURLRelative := "/" - - _ = updateReleasesStatus() - - vars := mux.Vars(r) - - group := getRootRelease() - - re := regexp.MustCompile(`^([0-9]+\.[0-9]+)\..+$`) - res := re.FindStringSubmatch(vars["version"]) - if res != nil { - group = fmt.Sprintf("v%s", res[1]) +func legacyDocsVersionHandler(w http.ResponseWriter, r *http.Request) { + log.Debugln("Use handler - legacyDocsVersionHandler") + version := "" + if res := regexp.MustCompile(`^/docs/([^/]+)/?.*$`).FindStringSubmatch(r.URL.Path); len(res) == 2 { + version = res[1] + } + if version == "" { + version = getVersionURL(r) } - re = regexp.MustCompile(`^/docs/[^/]+(/.+)$`) - res = re.FindStringSubmatch(r.URL.RequestURI()) - if res != nil { + pageURLRelative := "/" + if res := regexp.MustCompile(`^/docs/[^/]+(/.+)$`).FindStringSubmatch(r.URL.Path); len(res) == 2 { pageURLRelative = res[1] } - URLToRedirect = fmt.Sprintf("/docs/%v%v", group, pageURLRelative) - err = validateURL(fmt.Sprintf("https://%s%s", r.Host, URLToRedirect)) - - if err != nil { - log.Errorf("Error validating URL: %v, (original was https://%s/%v)", err.Error(), r.Host, r.URL.RequestURI()) - notFoundHandler(w, r) - } else { - http.Redirect(w, r, fmt.Sprintf("%s", URLToRedirect), 302) + redirectURL := fmt.Sprintf("/docs/%s%v", VersionToURL(getCanonicalDocsVersion(version)), pageURLRelative) + if r.URL.RawQuery != "" { + redirectURL = fmt.Sprintf("%s?%s", redirectURL, r.URL.RawQuery) } + http.Redirect(w, r, redirectURL, http.StatusFound) } // Handles request to /v-/. E.g. /v1.2-beta/ @@ -113,7 +86,6 @@ func groupChannelHandler(w http.ResponseWriter, r *http.Request) { log.Debugln("Use handler - groupChannelHandler") pageURLRelative := "/" vars := mux.Vars(r) - _ = updateReleasesStatus() var version, URLToRedirect string var err error @@ -124,18 +96,15 @@ func groupChannelHandler(w http.ResponseWriter, r *http.Request) { } err, version = getVersionFromChannelAndGroup(&ReleasesStatus, vars["channel"], vars["group"]) - if err == nil { + if err == nil && version != "" { URLToRedirect = fmt.Sprintf("/docs/%v%v", VersionToURL(version), pageURLRelative) - err = validateURL(fmt.Sprintf("https://%s%s", r.Host, URLToRedirect)) } if err != nil { - log.Errorf("Error validating URL: %v, (original was https://%s/%v)", err.Error(), r.Host, r.URL.RequestURI()) - // URLToRedirect = fmt.Sprintf("/404.html") + log.Errorf("Error resolving docs channel URL: %v", err.Error()) notFoundHandler(w, r) } else { http.Redirect(w, r, fmt.Sprintf("%s", URLToRedirect), 302) - // w.Header().Set("X-Accel-Redirect", URLToRedirect) } } @@ -146,8 +115,6 @@ func healthCheckHandler(w http.ResponseWriter, r *http.Request) { // Get HTML content for /includes/topnav.html request func topnavHandler(w http.ResponseWriter, r *http.Request) { - _ = updateReleasesStatus() - versionMenu := versionMenuType{ VersionItems: []versionMenuItems{}, HTMLContent: "", // not used now @@ -173,8 +140,6 @@ func topnavHandler(w http.ResponseWriter, r *http.Request) { } func groupMenuHandler(w http.ResponseWriter, r *http.Request) { - _ = updateReleasesStatus() - versionMenu := versionMenuType{ VersionItems: []versionMenuItems{}, HTMLContent: "", // not used now @@ -199,8 +164,6 @@ func groupMenuHandler(w http.ResponseWriter, r *http.Request) { } func channelMenuHandler(w http.ResponseWriter, r *http.Request) { - _ = updateReleasesStatus() - versionMenu := versionMenuType{ VersionItems: []versionMenuItems{}, HTMLContent: "", // not used now @@ -258,7 +221,6 @@ func serveFilesHandler(fs http.FileSystem) http.Handler { func notFoundHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) page404File, err := os.Open(getRootFilesPath(r) + "/404.html") - defer page404File.Close() if err != nil { // 404.html built-in stub log.Error("404.html file not found") @@ -272,6 +234,7 @@ Try searching for it or check the URL to see if it looks correct.

`, 404) return } + defer page404File.Close() io.Copy(w, page404File) // w.Header().Set("X-Accel-Redirect", fmt.Sprintf("/404.html")) } diff --git a/backend/main.go b/backend/main.go index d6badfd1b..20f41602a 100644 --- a/backend/main.go +++ b/backend/main.go @@ -30,11 +30,11 @@ func newRouter() *mux.Router { r.PathPrefix("/status").HandlerFunc(statusHandler) r.PathPrefix("/backend/").HandlerFunc(ssiHandler) - r.PathPrefix("/docs/v{group:1.2}-{channel:alpha|beta|ea|stable|rock-solid}").HandlerFunc(groupChannelHandler) - r.PathPrefix("/docs/v{version:[0-9]+.[0-9]+.[0-9]+[^/]*}").HandlerFunc(unknownVersionHandler) - r.PathPrefix("/docs/v{group:[0-9]+}/").HandlerFunc(groupHandler) - r.PathPrefix("/docs/v{group:1.2}/").HandlerFunc(groupHandler) - r.PathPrefix("/docs/{group:latest}/").HandlerFunc(groupHandler) + r.PathPrefix("/docs/v{group:2|1\\.2}-{channel:alpha|beta|ea|stable|rock-solid}/").HandlerFunc(groupChannelHandler) + r.PathPrefix("/docs/v{legacy:(?:2|1\\.2)(?:\\.[^/]+|-[^/]+)[^/]*/}").HandlerFunc(legacyDocsVersionHandler) + if isLatestAliasEnabled() { + r.PathPrefix("/docs/{group:latest}/").HandlerFunc(groupHandler) + } r.PathPrefix("/health").HandlerFunc(healthCheckHandler) r.Path("/includes/topnav.html").HandlerFunc(topnavHandler) r.Path("/includes/version-menu.html").HandlerFunc(topnavHandler) diff --git a/backend/main_test.go b/backend/main_test.go index 0442ae138..ea9bdedce 100644 --- a/backend/main_test.go +++ b/backend/main_test.go @@ -6,29 +6,66 @@ import ( "testing" ) -func TestHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/", nil) - if err != nil { - t.Fatal(err) +func TestGetCanonicalDocsVersion(t *testing.T) { + t.Setenv(currentDocsRootEnv, "v2") + t.Setenv(supportedDocsRootsEnv, "v2,v1.2") + + cases := []struct { + name string + in string + want string + }{ + {name: "empty", in: "", want: "v2"}, + {name: "latest", in: "latest", want: "v2"}, + {name: "pr", in: "pr-123", want: "pr-123"}, + {name: "v2 patch", in: "v2.10.3", want: "v2"}, + {name: "v1.2 patch", in: "v1.2.4-plus-fix1", want: "v1.2"}, + {name: "v1.1 fallback", in: "v1.1.9", want: "v2"}, } - recorder := httptest.NewRecorder() + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + if got := getCanonicalDocsVersion(tc.in); got != tc.want { + t.Fatalf("unexpected canonical version: got %q want %q", got, tc.want) + } + }) + } +} - hf := http.HandlerFunc(topnavHandler) +func TestLegacyDocsVersionHandler(t *testing.T) { + t.Setenv(currentDocsRootEnv, "v2") + t.Setenv(supportedDocsRootsEnv, "v2,v1.2") - hf.ServeHTTP(recorder, req) + r := newRouter() - if status := recorder.Code; status != http.StatusOK { - t.Errorf("handler returned wrong status code: got %v want %v", - status, http.StatusOK) + cases := []struct { + name string + path string + wantStatus int + wantLoc string + }{ + {name: "legacy v2 patch", path: "/docs/v2.3.0/usage/", wantStatus: http.StatusFound, wantLoc: "/docs/v2/usage/"}, + {name: "legacy v1.2 patch", path: "/docs/v1.2.9-plus-fix1/reference/", wantStatus: http.StatusFound, wantLoc: "/docs/v1.2/reference/"}, + {name: "legacy v2 channel", path: "/docs/v2-stable/reference/", wantStatus: http.StatusFound, wantLoc: "/docs/v2/reference/"}, + {name: "legacy v1.2 channel", path: "/docs/v1.2-beta/reference/", wantStatus: http.StatusFound, wantLoc: "/docs/v1.2/reference/"}, } - // Response body checking - actual := recorder.Body.String() - // TODO - expected := actual - if actual != expected { - t.Errorf("handler returned unexpected body: got %v want %v", actual, expected) + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + req, err := http.NewRequest(http.MethodGet, tc.path, nil) + if err != nil { + t.Fatal(err) + } + rec := httptest.NewRecorder() + r.ServeHTTP(rec, req) + + if rec.Code != tc.wantStatus { + t.Fatalf("unexpected status: got %d want %d", rec.Code, tc.wantStatus) + } + if rec.Header().Get("Location") != tc.wantLoc { + t.Fatalf("unexpected location: got %q want %q", rec.Header().Get("Location"), tc.wantLoc) + } + }) } } @@ -42,8 +79,8 @@ func TestStaticFileServer(t *testing.T) { } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - t.Errorf("Status should be 200, got %d", resp.StatusCode) + if resp.StatusCode != http.StatusNotFound { + t.Errorf("Status should be 404, got %d", resp.StatusCode) } contentType := resp.Header.Get("Content-Type") diff --git a/docker-compose.yml b/docker-compose.yml index b73fd2c52..5b944904a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,12 +47,13 @@ services: backend: image: $WERF_BACKEND_DOCKER_IMAGE_NAME environment: - ACTIVE_RELEASE: "2" + CURRENT_DOCS_MAJOR: "v2" + SUPPORTED_DOCS_MAJOR_VERSIONS: "v2,v1.2" + DOCS_LATEST_ALIAS_ENABLED: "true" LOG_LEVEL: info URL_VALIDATION: "false" command: "/app/server" volumes: - - ".helm/trdl_channels-dev.yaml:/app/trdl/trdl_channels.yaml:ro" - "./_site_en_includes:/app/root/en/includes" - "./_site_ru_includes:/app/root/ru/includes" depends_on: From cce1c823df3549b0f1a2a33d1696a19899ac16f3 Mon Sep 17 00:00:00 2001 From: Evgeniy Frolov Date: Mon, 15 Jun 2026 13:52:30 +0300 Subject: [PATCH 02/15] chore(ci): update workflows Signed-off-by: Evgeniy Frolov --- .github/workflows/_deploy-production.yml | 12 ------------ .github/workflows/deploy-production.yml | 3 +++ .github/workflows/deploy.yml | 12 ------------ 3 files changed, 3 insertions(+), 24 deletions(-) diff --git a/.github/workflows/_deploy-production.yml b/.github/workflows/_deploy-production.yml index 679835db5..ad265e98d 100644 --- a/.github/workflows/_deploy-production.yml +++ b/.github/workflows/_deploy-production.yml @@ -31,17 +31,6 @@ jobs: with: fetch-depth: 0 - - name: Checkout werf repo - uses: actions/checkout@v6 - with: - repository: werf/werf - path: werf - fetch-depth: 0 - - - name: Inject trdl_channels.yaml - run: | - cp werf/trdl_channels.yaml .helm/trdl_channels.yaml - - name: Install werf uses: werf/actions/install@v2 @@ -86,7 +75,6 @@ jobs: env: WERF_REPO: ghcr.io/${{ github.repository_owner }}/werfio-guides WERF_STAGES_STORAGE: ghcr.io/werf/werfio-guides-stages - WERF_SET_ACTIVE_RELEASE: global.active_release=2 WERFIO_GITHUB_TOKEN: ${{ secrets.API_TOKEN }} WERF_NAMESPACE: "werfio-production" WERF_RELEASE: "werfio-site-production" diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index a897f913e..c55582fa6 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -4,6 +4,9 @@ on: push: branches: - main + tags: + - "v2*" + - "v1.2*" workflow_dispatch: inputs: targetCluster: diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index cc4c632ee..99cbfdd5d 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -9,7 +9,6 @@ env: WERF_ENV: "production" WERF_REPO: "ghcr.io/${{ github.repository_owner }}/werfio-guides" WERF_STAGES_STORAGE: "ghcr.io/werf/werfio-guides-stages" - WERF_SET_ACTIVE_RELEASE: "global.active_release=2" WERFIO_GITHUB_TOKEN: "${{ secrets.API_TOKEN }}" jobs: @@ -23,17 +22,6 @@ jobs: with: fetch-depth: 0 - - name: Checkout werf repo - uses: actions/checkout@v6 - with: - repository: werf/werf - path: werf - fetch-depth: 0 - - - name: Inject trdl_channels.yaml - run: | - cp werf/trdl_channels.yaml .helm/trdl_channels.yaml - - name: Install werf uses: werf/actions/install@v2 From ab2d729462cce35456ba6d71b521d62d116fa007 Mon Sep 17 00:00:00 2001 From: Evgeniy Frolov Date: Tue, 16 Jun 2026 11:37:58 +0300 Subject: [PATCH 03/15] fix(backend): canonicalize pr-* docs versions and fix version menu URLs Fix normalizeVersion to exclude pr-* prefixed versions from v-prefixing, preventing pr-7579 from becoming vpr-7579. Update version menu data generation to use VersionToURL for all version URLs (including v2, v1.2) to ensure proper encoding and consistency. Fixes PR preview breadcrumb showing "vpr-*" and dropdown version URLs appearing as "v1.2-", "v2-". Signed-off-by: Evgeniy Frolov --- backend/common.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/common.go b/backend/common.go index 420f668c4..f833cc285 100644 --- a/backend/common.go +++ b/backend/common.go @@ -144,7 +144,7 @@ func (m *versionMenuType) getVersionMenuData(r *http.Request, releases *Releases Group: strings.TrimPrefix(root, "v"), Channel: "", Version: root, - VersionURL: root, + VersionURL: VersionToURL(root), IsCurrent: false, }) } @@ -177,7 +177,7 @@ func (m *versionMenuType) getGroupMenuData(r *http.Request, releases *ReleasesSt Group: strings.TrimPrefix(root, "v"), Channel: "", Version: root, - VersionURL: root, + VersionURL: VersionToURL(root), IsCurrent: false, }) } @@ -218,7 +218,7 @@ func getVersionFromGroup(releases *ReleasesStatusType, group string) (err error, // Add prefix 'v' to a version if it doesn't have yet func normalizeVersion(version string) string { - if strings.HasPrefix(version, "v") || version == "latest" { + if strings.HasPrefix(version, "v") || version == "latest" || strings.HasPrefix(version, "pr-") { return version } else { return fmt.Sprintf("v%s", version) From 7059666a66ecf195f6b44518d3bc5002070cbff2 Mon Sep 17 00:00:00 2001 From: Evgeniy Frolov Date: Tue, 16 Jun 2026 12:23:58 +0300 Subject: [PATCH 04/15] refactor(backend): switch docs menus to version-only model Signed-off-by: Evgeniy Frolov --- _includes/_common/channel-menu-v2.html | 2 +- _includes/_common/channel-menu.html | 2 +- _includes/_common/group-menu-v2.html | 2 +- _includes/_common/group-menu.html | 6 +- _includes/_common/topnav-documentation.html | 1 - _includes/_common/version-menu.html | 8 +- backend/common.go | 167 +++++--------------- backend/handlers.go | 37 ++--- backend/main.go | 12 +- backend/main_test.go | 4 +- 10 files changed, 73 insertions(+), 168 deletions(-) diff --git a/_includes/_common/channel-menu-v2.html b/_includes/_common/channel-menu-v2.html index e716829c5..a35f0c9b0 100644 --- a/_includes/_common/channel-menu-v2.html +++ b/_includes/_common/channel-menu-v2.html @@ -15,7 +15,7 @@ {{- if eq .Version "latest" }} latest {{- else }} - {{ .Group }}-{{ .Channel }} + version {{ .Version }} {{- end }} diff --git a/_includes/_common/channel-menu.html b/_includes/_common/channel-menu.html index c0aa2a2ec..fa1f09931 100644 --- a/_includes/_common/channel-menu.html +++ b/_includes/_common/channel-menu.html @@ -12,7 +12,7 @@ main {{- else }} - {{ .Group }}-{{ .Channel }} + version {{ .Version }} {{- end }} diff --git a/_includes/_common/group-menu-v2.html b/_includes/_common/group-menu-v2.html index a18949f7e..fac39a91b 100644 --- a/_includes/_common/group-menu-v2.html +++ b/_includes/_common/group-menu-v2.html @@ -2,5 +2,5 @@ {{ $CurrentPageURL := .CurrentPageURL }} {{ $CurrentPageURLRelative := .CurrentPageURLRelative }} {{- $first := index .VersionItems 0 }} - + {% endraw %} diff --git a/_includes/_common/group-menu.html b/_includes/_common/group-menu.html index 477f16c8f..852ec4ee5 100644 --- a/_includes/_common/group-menu.html +++ b/_includes/_common/group-menu.html @@ -3,14 +3,14 @@ {{ $CurrentPageURLRelative := .CurrentPageURLRelative }}