From 486c362ca6ea0ed874ac8bbd07b6c2b6a791186f Mon Sep 17 00:00:00 2001 From: Nick Date: Fri, 21 Feb 2025 19:55:33 -0500 Subject: [PATCH] nickcernera/sitemap-cleanup (#411) * chore: add proper sitemap generation, add lastmod to sitemap, prepare for algolia crawl * chore: add generate-pages script to yarn dev * test: testing script to detect file moves via git * enhancement: auto-detect reversions of redirects * chore: cleaning deprecated redirects * chore: lastmod changed from test --- docsearch.json | 48 +++ index-pages.mjs | 68 ++- next.config.js | 194 +-------- package.json | 8 +- pages/deployments/operator/agent-api.md | 526 ------------------------ pages/sitemap.xml.ts | 59 +-- public/robots.txt | 14 + scripts/track-moves.ts | 153 +++++++ src/generated/pages.json | 165 +++++--- src/routes/docs.generated.ts | 6 - src/routing/navigation.ts | 17 - 11 files changed, 402 insertions(+), 856 deletions(-) create mode 100644 docsearch.json delete mode 100644 pages/deployments/operator/agent-api.md create mode 100644 scripts/track-moves.ts diff --git a/docsearch.json b/docsearch.json new file mode 100644 index 00000000..63240d41 --- /dev/null +++ b/docsearch.json @@ -0,0 +1,48 @@ +{ + "index_name": "plural_docs", + "start_urls": [ + "https://docs.plural.sh/" + ], + "stop_urls": [ + "/pages-archive/", + "/__components/" + ], + "selectors": { + "lvl0": { + "selector": "h1", + "global": true, + "default_value": "Documentation" + }, + "lvl1": "article h2", + "lvl2": "article h3", + "lvl3": "article h4", + "lvl4": "article h5", + "lvl5": "article h6", + "text": "article p, article li, article td", + "language": { + "selector": "/html/@lang", + "type": "xpath", + "global": true + } + }, + "strip_chars": "0123456789-", + "custom_settings": { + "separatorsToIndex": "_", + "attributesForFaceting": ["language", "version"], + "attributesToRetrieve": [ + "hierarchy", + "content", + "anchor", + "url", + "url_without_anchor", + "type" + ] + }, + "js_render": false, + "use_anchors": true, + "selectors_exclude": [ + ".DocSearch-content" + ], + "scrape_start_urls": true, + "min_indexed_level": 0 +} \ No newline at end of file diff --git a/index-pages.mjs b/index-pages.mjs index cb7dba17..b569972a 100644 --- a/index-pages.mjs +++ b/index-pages.mjs @@ -1,4 +1,4 @@ -import { Dirent } from 'fs' +import { execSync } from 'child_process' import { readdir, writeFile } from 'fs/promises' import path from 'path' import { fileURLToPath } from 'url' @@ -7,23 +7,6 @@ const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) -function writeUrl({ location, lastMod, priority = '0.5' }) { - return ` - ${process.env.NEXT_PUBLIC_ROOT_URL}/${location} - ${lastMod} - weekly - ${priority || '0.5'} - ` -} - -function wrapSiteMap(content) { - return ` - -${content} - -` -} - const ignore = [ // sitemaps /^sitemap\.xml.*/, @@ -32,7 +15,7 @@ const ignore = [ // [nextjs_dynamic_pages] // 404 and 500 error pages /^([_.[]|404|500).*$/, - /^__components\/.*/ + /^__components\/.*/, ] const pageFilter = (file) => { @@ -41,10 +24,41 @@ const pageFilter = (file) => { return false } } + if (file?.isDirectory()) { return true } - return file.name.match(/\.(ts|tsx|js|jsx|md|mdoc)$/) + + // Only include markdown files + return file.name.match(/\.(md|mdoc)$/) +} + +// Function to strip numeric prefixes from path segments +function stripNumericPrefixes(pathStr) { + return pathStr + .split('/') + .map((segment) => segment.replace(/^\d+-/, '')) + .join('/') +} + +// Function to get the last git modification date of a file +function getGitLastModified(filePath) { + try { + // Get the last commit date that modified this file in UTC + const date = execSync(`git log -1 --format="%cI" -- "${filePath}"`, { + encoding: 'utf-8', + }).trim() + + if (!date) { + return new Date().toISOString() + } + + // Convert to UTC and format as ISO string + return new Date(date).toISOString() + } catch (error) { + // Fallback to current date if git command fails + return new Date().toISOString() + } } const rootDir = __dirname @@ -58,13 +72,25 @@ async function crawlPages(filePath = '/') { const filteredFiles = files.filter(pageFilter) const pages = [] + for (const file of filteredFiles) { if (file.isDirectory()) { pages.push(...(await crawlPages(path.join(filePath, file.name)))) } else { let pathname = file.name.split('.').slice(0, -1).join('.') + pathname = path.join(filePath, pathname.replace(/(^|\/)index$/g, '')) - pages.push({ path: pathname }) + // Strip numeric prefixes from the entire path + pathname = stripNumericPrefixes(pathname) + + // Get git modification time + const fullFilePath = path.join(fullPath, file.name) + const lastmod = getGitLastModified(fullFilePath) + + pages.push({ + path: pathname, + lastmod, + }) } } diff --git a/next.config.js b/next.config.js index e6babea2..e1b9a873 100644 --- a/next.config.js +++ b/next.config.js @@ -27,6 +27,11 @@ const nextConfig = { }, async redirects() { return [ + { + source: '/getting-started/agent-api-reference', + destination: '/overview/agent-api-reference', + permanent: true, + }, { source: '/getting-started/readme', destination: '/', @@ -43,55 +48,8 @@ const nextConfig = { permanent: true, }, { - source: '/reference/architecture-1', - destination: '/reference/architecture', - permanent: true, - }, - { - source: '/reference/workspaces/workspace-structure', - destination: '/reference/workspaces', - permanent: true, - }, - { - source: '/getting-started/getting-started-with-runbooks/runbook-yaml', - destination: - '/adding-new-application/getting-started-with-runbooks/runbook-yaml', - permanent: true, - }, - { - source: '/basic-setup-and-deployment/setting-up-gitops', - destination: - '/getting-started/managing-git-repository/setting-up-gitops', - permanent: true, - }, - { - source: '/basic-setup-and-deployment/openid-connect', - destination: '/getting-started/openid-connect', - permanent: true, - }, - { - source: '/basic-setup-and-deployment/admin-console', - destination: '/getting-started/admin-console', - permanent: true, - }, - { - source: '/basic-setup-and-deployment/cloud-shell-quickstart', - destination: '/getting-started/cloud-shell-quickstart', - permanent: true, - }, - { - source: '/basic-setup-and-deployment/uninstall', - destination: '/operations/uninstall', - permanent: true, - }, - { - source: '/reference/operator-guides', - destination: '/repositories', - permanent: true, - }, - { - source: '/reference/operator-guides/adding-kubecost-for-cost-analysis', - destination: '/repositories/kubecost', + source: '/deployments/architecture', + destination: '/overview/architecture', permanent: true, }, { @@ -99,144 +57,6 @@ const nextConfig = { destination: '/', permanent: true, }, - { - source: '/repositories', - destination: '/applications', - permanent: true, - }, - { - source: '/repositories/airbyte', - destination: '/applications/airbyte', - permanent: true, - }, - { - source: '/repositories/airflow', - destination: '/applications/airflow', - permanent: true, - }, - { - source: '/repositories/console', - destination: '/applications/console', - permanent: true, - }, - { - source: '/advanced-topics', - destination: '/operations', - permanent: true, - }, - { - source: '/advanced-topics/network-configuration', - destination: '/operations/network-configuration', - permanent: true, - }, - { - source: '/advanced-topics/dns-setup', - destination: '/operations/dns-setup', - permanent: true, - }, - { - source: - '/advanced-topics/dns-setup/creating-dns-zone-in-your-cloud-provider-console', - destination: - '/operations/dns-setup/creating-dns-zone-in-your-cloud-provider-console', - permanent: true, - }, - { - source: '/advanced-topics/security', - destination: '/operations/security', - permanent: true, - }, - { - source: '/advanced-topics/security/secret-management', - destination: - '/getting-started/manage-git-repositories/sharing-git-repositories', - permanent: true, - }, - { - source: '/advanced-topics/debugging', - destination: '/debugging', - permanent: true, - }, - { - source: '/advanced-topics/debugging/health-checks', - destination: '/debugging/health-checks', - permanent: true, - }, - { - source: '/advanced-topics/debugging/logs', - destination: '/debugging/logs', - permanent: true, - }, - { - source: '/advanced-topics/debugging/proxies', - destination: '/debugging/proxies', - permanent: true, - }, - { - source: '/advanced-topics/identity-and-access-management', - destination: '/operations/auth-access-control', - permanent: true, - }, - { - source: '/advanced-topics/identity-and-access-management/introduction', - destination: '/operations/auth-access-control', - permanent: true, - }, - { - source: - '/advanced-topics/identity-and-access-management/openid-connect', - destination: '/operations/auth-access-control/openid-connect', - permanent: true, - }, - { - source: '/advanced-topics/identity-and-access-management/api-tokens', - destination: '/operations/auth-access-control/api-tokens', - permanent: true, - }, - { - source: - '/advanced-topics/identity-and-access-management/identity-and-installations', - destination: - '/operations/auth-access-control/identity-and-installations', - permanent: true, - }, - { - source: - '/advanced-topics/identity-and-access-management/identity-and-installations/audit-logging', - destination: - '/operations/auth-access-control/identity-and-installations/audit-logging', - permanent: true, - }, - { - source: - '/advanced-topics/identity-and-access-management/identity-and-installations/service-accounts', - destination: - '/operations/auth-access-control/identity-and-installations/service-accounts', - permanent: true, - }, - { - source: - '/advanced-topics/identity-and-access-management/identity-and-installations/sharing-existing-repos', - destination: - '/getting-started/manage-git-repositories/sharing-git-repository', - permanent: true, - }, - { - source: '/reference/workspaces', - destination: - '/getting-started/manage-git-repositories/your-plural-workspace', - permanent: true, - }, - { - source: '/introduction', - destination: '/overview/introduction', - permanent: true, - }, - { - source: '/deployments/architecture', - destination: '/overview/architecture', - permanent: true, - }, { source: '/deployments/operator/api', destination: '/overview/api-reference', diff --git a/package.json b/package.json index de755df4..fa902adf 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,9 @@ "node": "22.12.0" }, "scripts": { - "dev": "run-s generate:routes && concurrently \"next dev\" \"yarn graphql:codegen:watch\"", + "dev": "run-s generate:routes generate:pages track:redirects && concurrently \"next dev\" \"yarn graphql:codegen:watch\"", "dev:watch": "concurrently \"next dev\" \"yarn graphql:codegen:watch\"", - "build": "run-s generate:routes build:next", + "build": "run-s generate:routes generate:pages build:next", "build:next": "next build", "start": "next start", "lint": "run-p lint:format lint:js lint:css", @@ -19,7 +19,9 @@ "fix:css": "stylelint src/**/*.css --fix", "graphql:codegen": "graphql-codegen --config codegen.yml", "graphql:codegen:watch": "graphql-codegen --watch --config codegen.yml", - "generate:routes": "ts-node scripts/generate-unified-routes.ts" + "generate:routes": "ts-node scripts/generate-unified-routes.ts", + "generate:pages": "node index-pages.mjs", + "track:redirects": "ts-node scripts/track-moves.ts" }, "dependencies": { "@apollo/client": "3.7.15", diff --git a/pages/deployments/operator/agent-api.md b/pages/deployments/operator/agent-api.md deleted file mode 100644 index 2ec07dec..00000000 --- a/pages/deployments/operator/agent-api.md +++ /dev/null @@ -1,526 +0,0 @@ -# API Reference - -## Packages -- [deployments.plural.sh/v1alpha1](#deploymentspluralshv1alpha1) - - -## deployments.plural.sh/v1alpha1 - -Package v1alpha1 contains API Schema definitions for the deployments v1alpha1 API group - -### Resource Types -- [CustomHealth](#customhealth) -- [IngressReplica](#ingressreplica) -- [KubecostExtractor](#kubecostextractor) -- [MetricsAggregate](#metricsaggregate) -- [PipelineGate](#pipelinegate) -- [UpgradeInsights](#upgradeinsights) -- [VirtualCluster](#virtualcluster) - - - -#### AWSProviderCredentials - - - - - - - -_Appears in:_ -- [ProviderCredentials](#providercredentials) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `region` _string_ | Region is the name of the AWS region cluster lives in. | | Required: \{\}
| -| `accessKeyID` _string_ | AccessKeyID is your access key ID used to authenticate against AWS API. | | Optional: \{\}
| -| `secretAccessKeyRef` _[SecretReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#secretreference-v1-core)_ | SecretAccessKeyRef is a reference to the secret that contains secret access key.
Since UpgradeInsights is a cluster-scoped resource we can't use local reference.
SecretAccessKey must be stored in a key named "secretAccessKey".
An example secret can look like this:
apiVersion: v1
kind: Secret
metadata:
name: eks-credentials
namespace: upgrade-insights-test
stringData:
secretAccessKey: "changeme"
Then it can be referenced like this:
...
secretAccessKeyRef:
name: eks-credentials
namespace: upgrade-insights-test | | Optional: \{\}
| - - -#### AgentHelmConfiguration - - - - - - - -_Appears in:_ -- [HelmSpec](#helmspec) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `chartName` _string_ | ChartName is a helm chart name. | | | -| `repoUrl` _string_ | RepoUrl is a url that points to this helm chart. | | Optional: \{\}
Type: string
| -| `values` _[RawExtension](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#rawextension-runtime-pkg)_ | Values allows defining arbitrary YAML values to pass to the helm as values.yaml file.
Use only one of:
- Values
- ValuesSecretRef
- ValuesConfigMapRef | | Optional: \{\}
| -| `valuesSecretRef` _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#secretkeyselector-v1-core)_ | ValuesSecretRef fetches helm values from a secret in this cluster.
Use only one of:
- Values
- ValuesSecretRef
- ValuesConfigMapRef | | Optional: \{\}
| -| `valuesConfigMapRef` _[ConfigMapKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#configmapkeyselector-v1-core)_ | ValuesConfigMapRef fetches helm values from a config map in this cluster.
Use only one of:
- Values
- ValuesSecretRef
- ValuesConfigMapRef | | Optional: \{\}
| - - -#### Binding - - - -Binding ... - - - -_Appears in:_ -- [Bindings](#bindings) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `id` _string_ | | | Optional: \{\}
| -| `UserID` _string_ | | | Optional: \{\}
| -| `userEmail` _string_ | | | Optional: \{\}
| -| `groupID` _string_ | | | Optional: \{\}
| -| `groupName` _string_ | | | Optional: \{\}
| - - -#### Bindings - - - -Bindings represents a policy bindings that -can be used to define read/write permissions -to this resource for users/groups in the system. - - - -_Appears in:_ -- [ClusterSpec](#clusterspec) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `read` _[Binding](#binding) array_ | Read bindings. | | Optional: \{\}
| -| `write` _[Binding](#binding) array_ | Write bindings. | | Optional: \{\}
| - - -#### ClusterSpec - - - - - - - -_Appears in:_ -- [VirtualClusterSpec](#virtualclusterspec) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `handle` _string_ | Handle is a short, unique human-readable name used to identify this cluster.
Does not necessarily map to the cloud resource name. | | Optional: \{\}
| -| `tags` _object (keys:string, values:string)_ | Tags used to filter clusters. | | Optional: \{\}
| -| `metadata` _[RawExtension](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#rawextension-runtime-pkg)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | Optional: \{\}
| -| `bindings` _[Bindings](#bindings)_ | Bindings contain read and write policies of this cluster | | Optional: \{\}
| - - - - - - - - -#### CustomHealth - - - -CustomHealth is the Schema for the HealthConverts API - - - - - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `apiVersion` _string_ | `deployments.plural.sh/v1alpha1` | | | -| `kind` _string_ | `CustomHealth` | | | -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | -| `spec` _[CustomHealthSpec](#customhealthspec)_ | | | | - - -#### CustomHealthSpec - - - -CustomHealthSpec defines the desired state of CustomHealth - - - -_Appears in:_ -- [CustomHealth](#customhealth) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `script` _string_ | | | | - - - - -#### GateSpec - - - -GateSpec defines the detailed gate specifications - - - -_Appears in:_ -- [PipelineGateSpec](#pipelinegatespec) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `job` _[JobSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#jobspec-v1-batch)_ | resuse JobSpec type from the kubernetes api | | | - - -#### GateState - -_Underlying type:_ _GateState_ - -GateState represents the state of a gate, reused from console client - -_Validation:_ -- Enum: [PENDING OPEN CLOSED RUNNING] - -_Appears in:_ -- [PipelineGateStatus](#pipelinegatestatus) - - - -#### GateType - -_Underlying type:_ _GateType_ - -GateType represents the type of a gate, reused from console client - -_Validation:_ -- Enum: [APPROVAL WINDOW JOB] - -_Appears in:_ -- [PipelineGateSpec](#pipelinegatespec) - - - - - -#### HelmConfiguration - - - - - - - -_Appears in:_ -- [AgentHelmConfiguration](#agenthelmconfiguration) -- [VClusterHelmConfiguration](#vclusterhelmconfiguration) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `chartName` _string_ | ChartName is a helm chart name. | | | -| `repoUrl` _string_ | RepoUrl is a url that points to this helm chart. | | Optional: \{\}
Type: string
| -| `values` _[RawExtension](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#rawextension-runtime-pkg)_ | Values allows defining arbitrary YAML values to pass to the helm as values.yaml file.
Use only one of:
- Values
- ValuesSecretRef
- ValuesConfigMapRef | | Optional: \{\}
| -| `valuesSecretRef` _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#secretkeyselector-v1-core)_ | ValuesSecretRef fetches helm values from a secret in this cluster.
Use only one of:
- Values
- ValuesSecretRef
- ValuesConfigMapRef | | Optional: \{\}
| -| `valuesConfigMapRef` _[ConfigMapKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#configmapkeyselector-v1-core)_ | ValuesConfigMapRef fetches helm values from a config map in this cluster.
Use only one of:
- Values
- ValuesSecretRef
- ValuesConfigMapRef | | Optional: \{\}
| - - -#### HelmSpec - - - - - - - -_Appears in:_ -- [VirtualClusterSpec](#virtualclusterspec) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `agent` _[AgentHelmConfiguration](#agenthelmconfiguration)_ | Agent allows configuring agent specific helm chart options. | | Optional: \{\}
| -| `vcluster` _[VClusterHelmConfiguration](#vclusterhelmconfiguration)_ | VCluster allows configuring vcluster specific helm chart options. | | Optional: \{\}
| - - -#### IngressReplica - - - -IngressReplica is the Schema for the console ingress replica - - - - - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `apiVersion` _string_ | `deployments.plural.sh/v1alpha1` | | | -| `kind` _string_ | `IngressReplica` | | | -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | -| `spec` _[IngressReplicaSpec](#ingressreplicaspec)_ | Spec of the IngressReplica | | Required: \{\}
| - - -#### IngressReplicaSpec - - - - - - - -_Appears in:_ -- [IngressReplica](#ingressreplica) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `ingressRef` _[ObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#objectreference-v1-core)_ | | | Required: \{\}
| -| `ingressClassName` _string_ | | | Optional: \{\}
| -| `tls` _[IngressTLS](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#ingresstls-v1-networking) array_ | | | Optional: \{\}
| -| `hostMappings` _object (keys:string, values:string)_ | | | Required: \{\}
| - - -#### KubecostExtractor - - - -KubecostExtractor - - - - - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `apiVersion` _string_ | `deployments.plural.sh/v1alpha1` | | | -| `kind` _string_ | `KubecostExtractor` | | | -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | -| `spec` _[KubecostExtractorSpec](#kubecostextractorspec)_ | | | | - - -#### KubecostExtractorSpec - - - - - - - -_Appears in:_ -- [KubecostExtractor](#kubecostextractor) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `interval` _string_ | | 1h | Optional: \{\}
| -| `kubecostServiceRef` _[ObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#objectreference-v1-core)_ | | | | -| `kubecostPort` _integer_ | | | Optional: \{\}
| -| `recommendationThreshold` _string_ | RecommendationThreshold float value for example: `1.2 or 0.001` | | | -| `recommendationsSettings` _[RecommendationsSettings](#recommendationssettings)_ | | | Optional: \{\}
| - - -#### MetricsAggregate - - - -MetricsAggregate - - - - - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `apiVersion` _string_ | `deployments.plural.sh/v1alpha1` | | | -| `kind` _string_ | `MetricsAggregate` | | | -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | - - - - -#### PipelineGate - - - -PipelineGate represents a gate blocking promotion along a release pipeline - - - - - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `apiVersion` _string_ | `deployments.plural.sh/v1alpha1` | | | -| `kind` _string_ | `PipelineGate` | | | -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | -| `spec` _[PipelineGateSpec](#pipelinegatespec)_ | | | | - - -#### PipelineGateSpec - - - -PipelineGateSpec defines the detailed gate specifications - - - -_Appears in:_ -- [PipelineGate](#pipelinegate) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `id` _string_ | | | | -| `name` _string_ | | | | -| `type` _[GateType](#gatetype)_ | | | Enum: [APPROVAL WINDOW JOB]
| -| `gateSpec` _[GateSpec](#gatespec)_ | | | | - - - - -#### ProviderCredentials - - - - - - - -_Appears in:_ -- [UpgradeInsightsSpec](#upgradeinsightsspec) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `aws` _[AWSProviderCredentials](#awsprovidercredentials)_ | AWS defines attributes required to auth with AWS API. | | Optional: \{\}
| - - -#### RecommendationsSettings - - - - - - - -_Appears in:_ -- [KubecostExtractorSpec](#kubecostextractorspec) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `excludeNamespaces` _string array_ | | | | -| `requireAnnotations` _object (keys:string, values:string)_ | | | | - - -#### Status - - - - - - - -_Appears in:_ -- [VirtualClusterStatus](#virtualclusterstatus) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `id` _string_ | ID of the resource in the Console API. | | Optional: \{\}
Type: string
| -| `sha` _string_ | SHA of last applied configuration. | | Optional: \{\}
Type: string
| -| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#condition-v1-meta) array_ | Represents the observations of a PrAutomation's current state. | | | - - -#### UpgradeInsights - - - -UpgradeInsights is the Schema for the UpgradeInsights API - - - - - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `apiVersion` _string_ | `deployments.plural.sh/v1alpha1` | | | -| `kind` _string_ | `UpgradeInsights` | | | -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | -| `spec` _[UpgradeInsightsSpec](#upgradeinsightsspec)_ | | | | - - - - -#### UpgradeInsightsSpec - - - - - - - -_Appears in:_ -- [UpgradeInsights](#upgradeinsights) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `distro` _[ClusterDistro](#clusterdistro)_ | Distro defines which provider API should be used to fetch latest upgrade insights.
If not provided, we get the distro from the Plural API cluster tied to this operator deploy token. | | Enum: [EKS]
Optional: \{\}
| -| `clusterName` _string_ | ClusterName is your cloud provider cluster identifier (usually name) that is used
to fetch latest upgrade insights information from the cloud provider API.
If not provided, we get the cluster name from the Plural API cluster tied to this
operator deploy token and assume that it is the same as the cluster name in your cloud provider. | | Optional: \{\}
| -| `interval` _string_ | Interval defines how often should the upgrade insights information be fetched. | 10m | Optional: \{\}
| -| `credentials` _[ProviderCredentials](#providercredentials)_ | Credentials allow overriding default provider credentials bound to the operator. | | Optional: \{\}
| - - -#### VClusterHelmConfiguration - - - - - - - -_Appears in:_ -- [HelmSpec](#helmspec) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `chartName` _string_ | ChartName is a helm chart name. | | | -| `repoUrl` _string_ | RepoUrl is a url that points to this helm chart. | | Optional: \{\}
Type: string
| -| `values` _[RawExtension](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#rawextension-runtime-pkg)_ | Values allows defining arbitrary YAML values to pass to the helm as values.yaml file.
Use only one of:
- Values
- ValuesSecretRef
- ValuesConfigMapRef | | Optional: \{\}
| -| `valuesSecretRef` _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#secretkeyselector-v1-core)_ | ValuesSecretRef fetches helm values from a secret in this cluster.
Use only one of:
- Values
- ValuesSecretRef
- ValuesConfigMapRef | | Optional: \{\}
| -| `valuesConfigMapRef` _[ConfigMapKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#configmapkeyselector-v1-core)_ | ValuesConfigMapRef fetches helm values from a config map in this cluster.
Use only one of:
- Values
- ValuesSecretRef
- ValuesConfigMapRef | | Optional: \{\}
| - - -#### VirtualCluster - - - -VirtualCluster is the Schema for the virtual cluster API - - - - - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `apiVersion` _string_ | `deployments.plural.sh/v1alpha1` | | | -| `kind` _string_ | `VirtualCluster` | | | -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | -| `spec` _[VirtualClusterSpec](#virtualclusterspec)_ | Spec ... | | Required: \{\}
| - - -#### VirtualClusterSpec - - - - - - - -_Appears in:_ -- [VirtualCluster](#virtualcluster) - -| Field | Description | Default | Validation | -| --- | --- | --- | --- | -| `kubeconfigRef` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#localobjectreference-v1-core)_ | KubeconfigRef is a reference to the secret created by the
vcluster helm chart. It contains kubeconfig with information
on how to access created virtual cluster. | | Required: \{\}
| -| `credentialsRef` _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#secretkeyselector-v1-core)_ | CredentialsRef is a reference to the secret pointing to the
key that holds Console API access token. It allows to communicate
with the standard Console API. | | Required: \{\}
| -| `cluster` _[ClusterSpec](#clusterspec)_ | Cluster is a simplified representation of the Console API cluster
object. See [ClusterSpec] for more information. | | Optional: \{\}
| -| `external` _boolean_ | External marks this virtual cluster as external one, meaning
that the vcluster deployment will not be automatically created.
User has to pre-provision vcluster and provide a valid KubeconfigRef
pointing to an existing vcluster installation. | | Optional: \{\}
| -| `helm` _[HelmSpec](#helmspec)_ | Helm allows configuring helm chart options of both agent and vcluster.
It is then deployed by the [VirtualCluster] CRD controller. | | Optional: \{\}
| - - - - diff --git a/pages/sitemap.xml.ts b/pages/sitemap.xml.ts index 0122ef45..6cb4f1ec 100644 --- a/pages/sitemap.xml.ts +++ b/pages/sitemap.xml.ts @@ -1,26 +1,16 @@ -import { until } from '@open-draft/until' - -import { getRepos } from '@src/data/getRepos' - import pages from '../src/generated/pages.json' const S_MAXAGE = 1 * 60 * 60 // 1s * 60s/m * 60m/h = 1 hour const STALE_WHILE_REVALIDATE = S_MAXAGE * 2 -function urlTag({ - location, - lastMod, - priority = '0.5', -}: { - location: string - lastMod: string - priority?: string | number -}) { +function urlTag({ location, lastmod }: { location: string; lastmod?: string }) { return ` - ${process.env.NEXT_PUBLIC_ROOT_URL}${location} - ${lastMod} - weekly - ${priority || '0.5'} + ${process.env.NEXT_PUBLIC_ROOT_URL}${location}${ + lastmod + ? ` + ${lastmod}` + : '' + } ` } @@ -36,36 +26,23 @@ export default function SiteMap() { // getServerSideProps will do the heavy lifting } -function generateSiteMap({ - repos, -}: { - repos?: Awaited> -} = {}) { - const lastMod = new Date().toISOString() +interface PageInfo { + path: string + lastmod: string +} - // We generate the XML sitemap with the posts data - const sitemap = wrapSiteMap(`${pages - ?.map((page) => urlTag({ location: `${page.path}`, lastMod })) - .join('\n')} - ${repos - ?.map((repo) => urlTag({ location: `/applications/${repo.name}`, lastMod })) - .join('\n')}`) +function generateSiteMap() { + const sitemap = wrapSiteMap( + (pages as PageInfo[]) + ?.map((page) => urlTag({ location: page.path, lastmod: page.lastmod })) + .join('\n') + ) return sitemap } -let cachedSiteMap: string = generateSiteMap() - export async function getServerSideProps({ res }) { - // We make an API call to gather the URLs for our site - const { data: repos, error: reposError } = await until(() => getRepos()) - - let sitemap: string = cachedSiteMap - - if (!reposError) { - sitemap = await generateSiteMap({ repos }) - cachedSiteMap = sitemap - } + const sitemap = generateSiteMap() res.setHeader('Content-Type', 'text/xml') res.setHeader( diff --git a/public/robots.txt b/public/robots.txt index 68c311b6..86bc94b1 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,2 +1,16 @@ User-agent: * +Allow: / + +# Exclude archive and component directories +Disallow: /pages-archive/ +Disallow: /__components/ Disallow: /test + +# Allow Algolia crawler +User-agent: Algolia Crawler +Allow: / +Disallow: /pages-archive/ +Disallow: /__components/ + +# Sitemap +Sitemap: https://docs.plural.sh/sitemap.xml diff --git a/scripts/track-moves.ts b/scripts/track-moves.ts new file mode 100644 index 00000000..74b2b22f --- /dev/null +++ b/scripts/track-moves.ts @@ -0,0 +1,153 @@ +import { execSync } from 'child_process' +import fs from 'fs' + +const CONFIG_FILE = 'next.config.js' + +// Strips numbered prefixes like "01-", "02-" from path segments +function stripNumberedPrefixes(path: string): string { + return path + .split('/') + .map((segment) => segment.replace(/^\d+-/, '')) + .join('/') +} + +function removeRedirectFromConfig(source: string) { + let content = fs.readFileSync(CONFIG_FILE, 'utf-8') + + // Find the redirect entry + const redirectRegex = new RegExp( + `\\s*\\{\\s*source:\\s*'${source}',[^}]+\\},?\\n?`, + 'g' + ) + + // Remove the redirect + content = content.replace(redirectRegex, '') + + // Clean up any double newlines created by the removal + content = content.replace(/\n\n\n+/g, '\n\n') + + // Write back to the file + fs.writeFileSync(CONFIG_FILE, content) + console.log(`Removed redirect for: ${source}`) +} + +function addRedirectToConfig(oldPath: string, newPath: string) { + // Read the current next.config.js + let content = fs.readFileSync(CONFIG_FILE, 'utf-8') + + // Convert file paths to URL paths and strip numbered prefixes + const oldUrl = stripNumberedPrefixes( + oldPath + .replace(/^pages\//, '/') + .replace(/\.md$/, '') + .replace(/\/index$/, '') + ) + + const newUrl = stripNumberedPrefixes( + newPath + .replace(/^pages\//, '/') + .replace(/\.md$/, '') + .replace(/\/index$/, '') + ) + + // Check if this is a file returning to its original location + // by looking for a redirect where this file's new location was the source + const returningFileRegex = new RegExp( + `source:\\s*'${newUrl}',[^}]+destination:\\s*'${oldUrl}'` + ) + + if (content.match(returningFileRegex)) { + console.log(`File returning to original location: ${newUrl} -> ${oldUrl}`) + removeRedirectFromConfig(newUrl) + + return + } + + // Check if redirect already exists + if (content.includes(`source: '${oldUrl}'`)) { + console.log(`Redirect already exists for: ${oldUrl}`) + + return + } + + // Find the redirects array + const redirectsStart = content.indexOf('return [') + + if (redirectsStart === -1) { + console.error('Could not find redirects array in next.config.js') + + return + } + + // Insert the new redirect at the start of the array + const newRedirect = ` { + source: '${oldUrl}', + destination: '${newUrl}', + permanent: true, + },\n` + + content = + content.slice(0, redirectsStart + 8) + + newRedirect + + content.slice(redirectsStart + 8) + + // Write back to the file + fs.writeFileSync(CONFIG_FILE, content) + console.log(`Added redirect: ${oldUrl} -> ${newUrl}`) +} + +// Get all renamed/moved markdown files in the pages directory +function getMovedFiles(): Array<[string, string]> { + try { + // Get renamed files from Git status + const gitStatus = execSync('git status -s', { encoding: 'utf8' }) + const movedFiles: Array<[string, string]> = [] + + gitStatus.split('\n').forEach((line) => { + // R = renamed/moved file + if (line.startsWith('R ') || line.startsWith(' R')) { + // Git status format for renames is: R old-path -> new-path + const [_, paths] = line.trim().split(/\s+(.+)/) + const [oldPath, newPath] = paths.split(' -> ') + + if ( + oldPath.startsWith('pages/') && + newPath.startsWith('pages/') && + oldPath.endsWith('.md') && + newPath.endsWith('.md') + ) { + console.log('Found moved file:', oldPath, '->', newPath) + movedFiles.push([oldPath, newPath]) + } + } + }) + + return movedFiles + } catch (error) { + console.error('Error getting moved files:', error) + + return [] + } +} + +// Process all moved files +function processMovedFiles() { + const movedFiles = getMovedFiles() + + if (movedFiles.length === 0) { + console.log('No moved markdown files detected in the pages directory') + + return + } + + console.log('Processing moved files...') + movedFiles.forEach(([oldPath, newPath]) => { + console.log(`\nProcessing move: ${oldPath} -> ${newPath}`) + addRedirectToConfig(oldPath, newPath) + }) +} + +// If run directly, process all moved files +if (require.main === module) { + processMovedFiles() +} diff --git a/src/generated/pages.json b/src/generated/pages.json index 03a627f1..2377616a 100644 --- a/src/generated/pages.json +++ b/src/generated/pages.json @@ -1,167 +1,222 @@ [ { - "path": "/01-introduction" + "path": "/overview/introduction", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/ai/architecture" + "path": "/overview/architecture", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/ai/cost" + "path": "/overview/management-api-reference", + "lastmod": "2025-02-21T20:28:52.000Z" }, { - "path": "/ai/overview" + "path": "/overview/agent-api-reference", + "lastmod": "2025-02-21T22:12:08.000Z" }, { - "path": "/ai/setup" + "path": "/getting-started/first-steps/cli-quickstart", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/advanced-configuration" + "path": "/getting-started/first-steps/existing-cluster", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/architecture" + "path": "/getting-started/first-steps", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/cli-quickstart" + "path": "/getting-started/how-to-use/mgmt-cluster", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/dashboard" + "path": "/getting-started/how-to-use/rbac", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/deprecations" + "path": "/getting-started/how-to-use/scm-connection", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/existing-cluster" + "path": "/getting-started/how-to-use/workload-cluster", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/multi-tenancy" + "path": "/getting-started/how-to-use/controllers", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/network-configuration" + "path": "/getting-started/how-to-use/pr-automation", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/notifications" + "path": "/getting-started/how-to-use/microservice", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/operator/api" + "path": "/getting-started/how-to-use/pipelines", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/operator/architecture" + "path": "/getting-started/how-to-use", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/operator/git-service" + "path": "/getting-started/advanced-config/sandboxing", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/operator/global-service" + "path": "/getting-started/advanced-config/network-configuration", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/operator/helm-service" + "path": "/getting-started/advanced-config/private-ca", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/pr/crds" + "path": "/getting-started/advanced-config", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/pr/pipelines" + "path": "/plural-features/continuous-deployment/deployment-operator", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/pr/testing" + "path": "/plural-features/continuous-deployment/git-service", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/pr-automation" + "path": "/plural-features/continuous-deployment/helm-service", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/private-ca" + "path": "/plural-features/continuous-deployment/global-service", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/sandboxing" + "path": "/plural-features/continuous-deployment", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/stacks" + "path": "/plural-features/k8s-upgrade-assistant", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/templating-filters" + "path": "/plural-features/stacks-iac-management/customize-runners", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/templating" + "path": "/plural-features/stacks-iac-management/pr-workflow", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/terraform-interop" + "path": "/plural-features/stacks-iac-management/manual-runs", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/deployments/using-operator" + "path": "/plural-features/stacks-iac-management/local-execution", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/faq/01-security" + "path": "/plural-features/stacks-iac-management/custom-stacks", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/faq/02-plural-oidc" + "path": "/plural-features/stacks-iac-management/auto-cancellation", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/faq/03-certifications" + "path": "/plural-features/stacks-iac-management/service-contexts", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/faq/04-paid-tiers" + "path": "/plural-features/stacks-iac-management", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/getting-started/deployments" + "path": "/plural-features/service-catalog/creation", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/how-to/deploy/microservice" + "path": "/plural-features/service-catalog/contribution-program", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/how-to/deploy/pipelines" + "path": "/plural-features/service-catalog", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/how-to/deploy/pr-automation" + "path": "/plural-features/kubernetes-dashboard", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/how-to" + "path": "/plural-features/plural-ai/setup", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/how-to/set-up/controllers" + "path": "/plural-features/plural-ai/architecture", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/how-to/set-up/mgmt-cluster" + "path": "/plural-features/plural-ai/cost", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/how-to/set-up/rbac" + "path": "/plural-features/plural-ai", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/how-to/set-up/scm-connection" + "path": "/plural-features/pr-automation/crds", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/how-to/set-up/workload-cluster" + "path": "/plural-features/pr-automation/testing", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/" + "path": "/plural-features/pr-automation/pipelines", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/reference/release-notes" + "path": "/plural-features/pr-automation", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/service-catalog/contributing" + "path": "/plural-features/service-templating/supporting-liquid-filters", + "lastmod": "2025-02-21T20:53:12.000Z" }, { - "path": "/service-catalog/creation" + "path": "/plural-features/service-templating", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/service-catalog/overview" + "path": "/plural-features/projects-and-multi-tenancy", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/stacks/auto-cancellation" + "path": "/plural-features/notifications", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/stacks/custom-stacks" + "path": "/faq/security", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/stacks/customize-runners" + "path": "/faq/plural-oidc", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/stacks/local-execution" + "path": "/faq/certifications", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/stacks/manual-runs" + "path": "/faq/paid-tiers", + "lastmod": "2025-02-20T19:05:59.000Z" }, { - "path": "/stacks/pr-workflow" + "path": "/resources/release-notes", + "lastmod": "2025-02-20T19:05:59.000Z" } ] \ No newline at end of file diff --git a/src/routes/docs.generated.ts b/src/routes/docs.generated.ts index 920161b5..97c6fd0e 100644 --- a/src/routes/docs.generated.ts +++ b/src/routes/docs.generated.ts @@ -335,12 +335,6 @@ export const docRoutes: DocRouteMap = { title: 'Release Notes', id: 'resources_release_notes', }, - - deployments_operator_agent_api: { - path: '/deployments/operator/agent-api', - title: 'Agent API', - id: 'deployments_operator_agent_api', - }, } as const // Type-safe route getter diff --git a/src/routing/navigation.ts b/src/routing/navigation.ts index 70685d19..ba65ba79 100644 --- a/src/routing/navigation.ts +++ b/src/routing/navigation.ts @@ -318,22 +318,5 @@ export const docNavigation: NavMenu = [ "sortPath": "/05-resources/01-release-notes" } ] - }, - { - "title": "Deployments", - "href": "/deployments", - "sections": [ - { - "title": "Operator", - "href": "/deployments/operator", - "sections": [ - { - "title": "Agent API", - "href": "/deployments/operator/agent-api", - "sortPath": "/deployments/operator/agent-api" - } - ] - } - ] } ] \ No newline at end of file