Refactor docs versions to major.minor variants#477
Conversation
✅ Deploy Preview for cozystack ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Important Review skippedToo many files! This PR contains 276 files, which is 126 over the limit of 150. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (24)
📒 Files selected for processing (276)
You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request implements a versioned documentation structure, organizing content into version-specific directories and updating the build system to support version derivation from release tags. Key changes include Makefile updates for automated versioning and OpenAPI spec management, a redesign of the sidebar version switcher into a dropdown menu, and a comprehensive update of internal documentation links. Feedback focuses on correcting link formatting in the new documentation files to ensure absolute paths and versioned references are used correctly.
| - `./packages/apps`: Helm charts for applications that can be installed from the dashboard. | ||
|
|
||
| Just like standard Cozystack applications, this external application package is using Helm and FluxCD. | ||
| To learn more about developing application packages, read Cozystack [Developer Guide](/docs/development/) |
There was a problem hiding this comment.
The link to the Developer Guide is a root-relative link (/docs/development/) which might not resolve correctly within the versioned documentation structure. To ensure it points to the correct versioned guide, please use the ref shortcode with an absolute path, similar to other links in the documentation.
| To learn more about developing application packages, read Cozystack [Developer Guide](/docs/development/) | |
| To learn more about developing application packages, read Cozystack [Developer Guide]({{% ref "/docs/v1.1/development" %}}) |
There was a problem hiding this comment.
Replaced the plain /docs/development/ link with a Hugo ref shortcode pointing to the versioned development page. Applied across v1.0, v1.1, and v1.2. Fixed in f8abb15.
| Read more: [Tenant System]({{% ref "docs/v1.1/guides/tenants" %}}). | ||
|
|
||
| ## Tenant Cluster | ||
|
|
||
| Users can deploy separate Kubernetes clusters in their own tenants. | ||
| These are not namespaces of the management cluster, but complete Kubernetes-in-Kubernetes clusters. | ||
|
|
||
| Tenant clusters are what many cloud providers call "managed Kubernetes". | ||
| They are used as development, testing, and production environments. | ||
|
|
||
| Read more: [tenant Kubernetes clusters]({{% ref "docs/v1.1/kubernetes" %}}). | ||
|
|
||
| ## Managed Applications | ||
|
|
||
| Cozystack comes with a catalog of **managed applications** (services) that can be deployed on the platform with minimal effort. | ||
| These include relational databases (PostgreSQL, MySQL/MariaDB), NoSQL/queues (Redis, NATS, Kafka, RabbitMQ), HTTP cache, load balancer, and others. | ||
|
|
||
| Tenants, tenant Kubernetes clusters, and VMs are also managed applications in terms of Cozystack. | ||
| They are created with the same user workflow and are managed with Helm and Flux, just as other applications. | ||
|
|
||
| Read more: [managed applications]({{% ref "/docs/v1.1/applications" %}}). | ||
|
|
||
| ## Cozystack API | ||
|
|
||
| Instead of a proprietary API or UI-only management, Cozystack exposes its functionality through | ||
| [Kubernetes Custom Resources](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) | ||
| and the standard Kubernetes API, accessible via REST API, `kubectl` client, and the Cozystack dashboard. | ||
|
|
||
| This approach combines well with role-based access control. | ||
| Non-administrative users can use `kubectl` to access the management cluster, | ||
| but their kubeconfig will authorize them only to create custom resources in their tenants. | ||
|
|
||
| Read more: [Cozystack API]({{% ref "/docs/v1.1/cozystack-api" %}}). | ||
|
|
||
| ## Variants | ||
|
|
||
| Variants are pre-defined configurations of Cozystack that determine which bundles and components are enabled. | ||
| Each variant is tested, versioned, and guaranteed to work as a unit. | ||
| They simplify installation, reduce the risk of misconfiguration, and make it easier to choose the right set of features for your deployment. | ||
|
|
||
| Read more: [Variants]({{% ref "/docs/v1.1/operations/configuration/variants" %}}). |
There was a problem hiding this comment.
Several links in this document are missing a leading slash / in the ref shortcode path. This will cause them to be treated as relative links and likely fail to resolve. Please add a leading slash to make them absolute paths from the content root.
This applies to the following links:
[Tenant System]({{% ref "docs/v1.1/guides/tenants" %}})on line 41[tenant Kubernetes clusters]({{% ref "docs/v1.1/kubernetes" %}})on line 51[managed applications]({{% ref "docs/v1.1/applications" %}})on line 61[Cozystack API]({{% ref "docs/v1.1/cozystack-api" %}})on line 73[Variants]({{% ref "docs/v1.1/operations/configuration/variants" %}})on line 81
For example, the link on line 41 should be:
[Tenant System]({{% ref "/docs/v1.1/guides/tenants" %}})
Read more: [Tenant System]({{% ref "/docs/v1.1/guides/tenants" %}}).
## Tenant Cluster
Users can deploy separate Kubernetes clusters in their own tenants.
These are not namespaces of the management cluster, but complete Kubernetes-in-Kubernetes clusters.
Tenant clusters are what many cloud providers call "managed Kubernetes".
They are used as development, testing, and production environments.
Read more: [tenant Kubernetes clusters]({{% ref "/docs/v1.1/kubernetes" %}}).
## Managed Applications
Cozystack comes with a catalog of **managed applications** (services) that can be deployed on the platform with minimal effort.
These include relational databases (PostgreSQL, MySQL/MariaDB), NoSQL/queues (Redis, NATS, Kafka, RabbitMQ), HTTP cache, load balancer, and others.
Tenants, tenant Kubernetes clusters, and VMs are also managed applications in terms of Cozystack.
They are created with the same user workflow and are managed with Helm and Flux, just as other applications.
Read more: [managed applications]({{% ref "/docs/v1.1/applications" %}}).
## Cozystack API
Instead of a proprietary API or UI-only management, Cozystack exposes its functionality through
[Kubernetes Custom Resources](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/)
and the standard Kubernetes API, accessible via REST API, `kubectl` client, and the Cozystack dashboard.
This approach combines well with role-based access control.
Non-administrative users can use `kubectl` to access the management cluster,
but their kubeconfig will authorize them only to create custom resources in their tenants.
Read more: [Cozystack API]({{% ref "/docs/v1.1/cozystack-api" %}}).
## Variants
Variants are pre-defined configurations of Cozystack that determine which bundles and components are enabled.
Each variant is tested, versioned, and guaranteed to work as a unit.
They simplify installation, reduce the risk of misconfiguration, and make it easier to choose the right set of features for your deployment.
Read more: [Variants]({{% ref "/docs/v1.1/operations/configuration/variants" %}}).There was a problem hiding this comment.
Added leading / to ref shortcode paths that were missing it. Also fixed the same issue in hetzner.md, monitoring/logs.md, monitoring/alerting.md, monitoring/dashboards.md, and operations/services/_index.md across all versions. Fixed in dc7476d.
lexfrei
left a comment
There was a problem hiding this comment.
Major refactor of docs versioning into per-minor-version directories. The UI, redirects, and build-time OpenAPI download all work on the deploy preview. However, the sync automation has three blocking issues that will ship incorrect content to production.
Blocker 1: v1.1 api.json is a byte-for-byte copy of the v1.0 spec
content/en/docs/v1.1/cozystack-api/api.json has md5 6476e14adf822b931aa52dd481b1a3e6 and size 1818459 bytes — identical to content/en/docs/v1.0/cozystack-api/api.json. The v1.1 swagger UI will silently serve the v1.0 schema. Per v1.1.0 release notes, v1.1 introduces OpenBAO, tiered object storage user model, RabbitMQ version selection, and MongoDB dashboards — none of which appear in the v1.0 spec.
Since v1.1.x releases don't ship an openapi.json asset and hack/download_openapi.sh:38 skips versions with api.json already in content/, this will never be auto-corrected.
Fix: extract the real v1.1 spec from a running v1.1.x cluster (kubectl get --raw '/openapi/v3/apis/apps.cozystack.io/v1alpha1') and commit it.
(GitHub rejected an inline comment on api.json because the diff is too large; see Makefile:13 and the workflow comment for the other two blockers.)
Blocker 2: daily/manual sync without RELEASE_TAG overwrites stable v1.2 docs with main
See inline on Makefile:13.
Blocker 3: shell injection in release_tag
See inline on .github/workflows/update-managed-apps.yaml:36.
Extra observation: .github/workflows/hugo.yaml skips the OpenAPI download
Production cozystack.io (Netlify) is fine — netlify.toml calls ./hack/download_openapi.sh before hugo. But this workflow (contributor forks deploying to personal GitHub Pages) does not, so the v1.2 swagger UI will 404 on fork previews. Either add make download-openapi-all before Build with Hugo, or delete the workflow if unused. (Not commented inline because the file isn't modified in this PR.)
Non-blocking observations and suggestions are inline.
| override BRANCH := release-$(_major).$(_minor) | ||
| else | ||
| DOC_VERSION ?= v1.2 | ||
| BRANCH ?= main |
There was a problem hiding this comment.
Blocker: BRANCH fallback silently overwrites v1.2 docs with main.
When RELEASE_TAG is absent, BRANCH defaults to main. The scheduled daily run and manual dispatch without a tag enter this branch: the workflow reads LATEST=v1.2 from hugo.yaml, then calls make update-all DOC_VERSION=v1.2. hack/update_apps.sh then fetches READMEs from raw.githubusercontent.com/cozystack/cozystack/main/... and stamps source: links pointing to main, overwriting the curated v1.2 docs with unreleased content.
Fix: derive BRANCH from DOC_VERSION when no release tag is given, e.g. BRANCH ?= release-$(DOC_VERSION:v%=%) (with a v0→main special case), or pass BRANCH explicitly from the workflow's "latest" path.
There was a problem hiding this comment.
This will be addressed by cozystack/cozystack#2214, which moves the website docs pipeline from pull-based (daily schedule) to push-based (triggered on release). The daily schedule and the fallback path will be removed entirely once that lands.
| id: version | ||
| run: | | ||
| # Get release tag from dispatch payload, workflow input, or default to empty | ||
| RELEASE_TAG="${{ github.event.client_payload.release_tag || github.event.inputs.release_tag || '' }}" |
There was a problem hiding this comment.
Blocker: shell injection in release_tag.
${{ ... }} expands before the shell runs. A payload like v1.2.1"; touch /tmp/pwn; # becomes RELEASE_TAG="v1.2.1"; touch /tmp/pwn; #" on the runner. repository_dispatch requires a PAT, but this is still the standard hardening pattern called out in GitHub's Actions security guide.
Fix: pass via env:, then validate format before use:
env:
RELEASE_TAG_INPUT: ${{ github.event.client_payload.release_tag || github.event.inputs.release_tag || '' }}
run: |
if [[ -n "$RELEASE_TAG_INPUT" && ! "$RELEASE_TAG_INPUT" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Invalid release tag format" >&2; exit 1
fi
RELEASE_TAG="$RELEASE_TAG_INPUT"There was a problem hiding this comment.
Fixed — release_tag is now passed via env: and validated against ^v[0-9]+\.[0-9]+\.[0-9]+$ before use. All other ${{ steps.* }} references in run: blocks were also moved to env:. See ef4648f.
| _major := $(word 1,$(subst ., ,$(_ver))) | ||
| _minor := $(word 2,$(subst ., ,$(_ver))) | ||
| DOC_VERSION := $(if $(filter 0,$(_major)),v0,v$(_major).$(_minor)) | ||
| override BRANCH := release-$(_major).$(_minor) |
There was a problem hiding this comment.
Observation (non-blocking): RELEASE_TAG does not pin to the tag.
override BRANCH := release-$(_major).$(_minor) fetches from the rolling release-1.2 branch, not from the tag. After v1.2.1 is tagged, later patches can land on release-1.2 before v1.2.2, so re-running make update-all RELEASE_TAG=v1.2.1 produces different output over time. raw.githubusercontent.com accepts tags as refs, so BRANCH := $(RELEASE_TAG) would give reproducible builds.
There was a problem hiding this comment.
Good catch — changed override BRANCH from release-$(_major).$(_minor) to $(RELEASE_TAG) so builds are pinned to the exact tag. See ef4648f.
| find "$TARGET_DIR" -name 'api.json' -delete | ||
|
|
||
| # Update internal doc references (both /docs/vX.Y/ URLs and "docs/vX.Y/" Hugo refs) | ||
| find "$TARGET_DIR" -name '*.md' -exec sed -i \ |
There was a problem hiding this comment.
Observation (non-blocking): GNU sed -i is not portable.
sed -i without a backup suffix works on GNU sed (Linux CI) but fails on BSD sed (macOS). Maintainers running make init-version locally on Mac get an error. Either use sed -i.bak ... && find -name '*.bak' -delete, branch on uname, or switch to Perl/Python.
There was a problem hiding this comment.
Switched to sed -i.bak with subsequent cleanup across all sed invocations in the script. See ef4648f.
| } | ||
|
|
||
| # Fetch all releases once (up to 100) | ||
| RELEASES_JSON=$(curl_gh "${API_BASE}/releases?per_page=100") |
There was a problem hiding this comment.
Observation (non-blocking): unauthenticated GitHub API per build.
curl_gh "${API_BASE}/releases?per_page=100" runs on every Netlify build, deploy preview, and branch deploy without a token by default (60 req/hr/IP). Preview-heavy periods from the shared Netlify egress pool can exhaust the quota and make builds flaky. Provision a GITHUB_TOKEN env in Netlify, or pin release tags in repo metadata and skip the API entirely.
There was a problem hiding this comment.
The script already supports GITHUB_TOKEN when set (lines 16–19) — it adds the Authorization header to all curl_gh calls. The remaining piece is provisioning the token in Netlify site settings, which is an ops configuration change outside this PR.
| update-api: | ||
| kubectl get --raw '/openapi/v3/apis/apps.cozystack.io/v1alpha1' > content/en/docs/cozystack-api/api.json | ||
| # Download openapi.json for a specific version from GitHub release | ||
| download-openapi: |
There was a problem hiding this comment.
Observation (non-blocking): silent no-op without RELEASE_TAG.
Without RELEASE_TAG, this target produces no output and exits 0 — looks successful. Add $(error RELEASE_TAG is required) in an else branch, or only expose download-openapi-all publicly.
There was a problem hiding this comment.
Added $(error RELEASE_TAG is required ...) via ifndef RELEASE_TAG so the target fails loudly instead of silently succeeding. See ef4648f.
| {{- with site.GetPage $trimmed -}} | ||
| {{- $targetPath = .RelPermalink -}} | ||
| <div class="version-switcher dropdown"> | ||
| <button class="version-switcher__toggle dropdown-toggle" type="button" |
There was a problem hiding this comment.
Observation (non-blocking): accessibility and single-version rendering.
The button has no id, so the menu cannot use aria-labelledby (compare layouts/partials/navbar-version-selector.html:9-19). The dropdown also renders even when only one version exists. Short-circuit to plain text when len .Site.Params.versions <= 1 and add the id/aria-labelledby pair.
There was a problem hiding this comment.
Added id="versionSwitcherButton" and aria-labelledby="versionSwitcherButton" to match navbar-version-selector.html. The dropdown now short-circuits to plain text when only one version exists. See ef4648f.
| {{- /* Use index in versions list (ordered newest-first) to compare */ -}} | ||
| {{- $currentIdx := -1 -}} | ||
| {{- $latestIdx := -1 -}} | ||
| {{- range $i, $v := .Site.Params.versions -}} |
There was a problem hiding this comment.
Observation (non-blocking): index-based comparison is order-dependent.
The new logic computes "newer/older" from position in .Site.Params.versions. It only works because that list happens to be newest-first in hugo.yaml. A future reorder or non-chronological insertion would silently flip the "beta" and "older release" banners. Either compare the version string as semver or add an explicit order field.
There was a problem hiding this comment.
Added an explicit order field to each entry in hugo.yaml versions list. The banner template now compares order values instead of array indices — safe against reordering. See ef4648f.
| @@ -1,5 +1,5 @@ | |||
| { | |||
| "name": "cozystack.io", | |||
| "name": "website", | |||
| "lockfileVersion": 2, | |||
There was a problem hiding this comment.
Observation (non-blocking): unrelated change.
"name" changed from "cozystack.io" to "website", unrelated to the versioning refactor. Revert or split into a separate PR.
There was a problem hiding this comment.
Reverted — name is back to "cozystack.io". See ef4648f.
| **Additional Resources:** | ||
| - [Release notes](https://github.com/cozystack/cozystack/releases) | ||
| - [v0 to v1 upgrade guide](/docs/v1/operations/upgrades/) | ||
| - [v0 to v1 upgrade guide](/docs/v1.2/operations/upgrades/) |
There was a problem hiding this comment.
Observation (non-blocking): link text says "v1", points to v1.2.
- [v0 to v1 upgrade guide](/docs/v1.2/operations/upgrades/)Rename to "v0 to v1.x upgrade guide" for consistency.
There was a problem hiding this comment.
Changed to "v0 to v1.x upgrade guide". See ef4648f.
ba18305 to
dc7476d
Compare
Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
- Fix shell injection: pass release_tag via env + validate format - Pin BRANCH to exact tag for reproducible builds - Add explicit order field to versions config for banner comparison - Add error for download-openapi without RELEASE_TAG - Make sed portable (BSD/macOS) in init_version.sh - Add id/aria-labelledby to version-switcher, skip dropdown for single version - Revert unrelated package-lock.json name change - Fix "v1" → "v1.x" in upgrade guide link text - Fix stale /docs/v1/ refs from rebase (new upstream content) - Distribute application-definitions.md to v1.0, v1.1, v1.2 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Address review feedback from gemini-code-assist on content/en/docs/v1.1/applications/external.md:26: replace plain /docs/development/ link (404s in versioned docs) with Hugo ref shortcode pointing to the versioned development page. Applied across v1.0, v1.1, v1.2. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
Address review feedback from gemini-code-assist on content/en/docs/v1.1/guides/concepts.md:41: ref shortcodes without leading / are relative paths; add / to make them absolute from the content root. Applied across all affected files in v1.0, v1.1, v1.2. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
dc7476d to
332bf33
Compare
lexfrei
left a comment
There was a problem hiding this comment.
Fixes in 00720fb, 475a8a9, and 332bf33 address the shell injection blocker and all non-blocking observations from the previous review — nice work.
My previous blocker (v1.1 api.json is a byte-for-byte copy of v1.0) remains unresolved.
Observation (non-blocking): content/en/docs/v0/_index.md:8 has weight: 20, same as v1.1. Hugo resolves ties alphabetically by title, which places "v0" before "v1.1" in the sidebar. Expected order: v1.2 → v1.1 → v1.0 → v0 (newest first). Setting v0’s weight to 40 would fix the ordering.
|
Re: Blocker 1 — v1.1 We're going to leave this as-is; it's not a regression introduced by this PR.
|
Summary
Refactors the docs versioning structure from
v0/v1to per-minor-version directories (v0,v1.0,v1.1,v1.2), making the site compatible with the push-based docs update pipeline from cozystack/cozystack#2214.What changed
Docs structure:
content/en/docs/v1/→v1.0/v1.1andv1.2fromv1.0, then pulled actual content fromrelease-1.1andrelease-1.2branches/docs/v1/*→/docs/v1.0/:splatinnetlify.tomlfor backward compatibilityVersion-aware Makefile:
RELEASE_TAG=v1.2.1automatically derivesDOC_VERSION=v1.2,BRANCH=release-1.2override BRANCHfixes the mismatch where cozystack passesBRANCH=release-1.2.1but the actual branch isrelease-1.2init-version(bootstrap new version dir),download-openapi,download-openapi-allOpenAPI spec out of git:
v0andv1.0keepapi.jsonin-repo (no release asset available for those versions)v1.1also keeps a copy in-repo (v1.1.x releases predate the openapi.json artifact)v1.2+downloadsopenapi.jsonfrom GitHub releases at Netlify build time viahack/download_openapi.shstatic/docs/*/cozystack-api/added to.gitignoreVersion switcher UI:
version-banner.html— replacedint()cast (breaks onv1.0) with index-based comparisonGitHub workflow:
update-managed-apps.yamlnow acceptsRELEASE_TAGvia dispatch payload or workflow inputhugo.yaml)update-managed-apps-v1.2New scripts
hack/init_version.sh— copies content from previous version, updates all internal refs, removesapi.jsonhack/download_openapi.sh— downloads openapi.json from GitHub releases at build time (works withoutghCLI)Note for cozystack/cozystack#2214
The
update-website-docsjob passesBRANCH=release-X.Y.Z(full version) but cozystack release branches arerelease-X.Y(major.minor). This PR handles the mismatch viaoverride BRANCHin the Makefile, but ideally the cozystack workflow should also be fixed to passBRANCH=release-X.Y.🤖 Generated with Claude Code