From 63f99483e03fb57ddfb6901ee975fd54913111f5 Mon Sep 17 00:00:00 2001 From: Lukas Wagner Date: Sun, 10 May 2026 16:23:15 +0200 Subject: [PATCH 1/4] Rename project to lldap-operator and add documentation foundation Renames all crates and Helm chart from lldap-resources-operator to lldap-operator, and adds mdBook documentation site with CRD references, architecture overview, and development guide. --- .../changesets/financially-central-pipit.md | 9 ++++ .github/workflows/ci.yml | 16 ++++++- .github/workflows/docs.yml | 47 +++++++++++++++++++ .github/workflows/e2e.yml | 2 +- .github/workflows/release.yml | 2 +- .gitignore | 3 ++ CONTRIBUTING.md | 32 +++++++++++++ Cargo.lock | 14 +++--- Cargo.toml | 24 +++++----- README.md | 22 ++++----- .../Chart.yaml | 2 +- .../crds/lldap-group.yaml | 4 +- .../crds/lldap-user.yaml | 4 +- .../templates/NOTES.txt | 4 +- .../templates/_helpers.tpl | 20 ++++---- .../templates/clusterrole.yaml | 8 ++-- .../templates/clusterrolebinding.yaml | 14 ++++++ .../templates/deployment.yaml | 10 ++-- .../templates/serviceaccount.yaml | 4 +- .../values.yaml | 2 +- .../templates/clusterrolebinding.yaml | 14 ------ .../Cargo.toml | 2 +- .../src/lib.rs | 0 .../Cargo.toml | 2 +- .../src/lib.rs | 0 .../Cargo.toml | 2 +- .../src/lib.rs | 0 .../Cargo.toml | 2 +- .../src/lib.rs | 0 .../Cargo.toml | 2 +- .../src/main.rs | 0 docs/book.toml | 15 ++++++ docs/examples/lldap-attribute-schema.yaml | 12 +++++ docs/examples/lldap-group.yaml | 6 +++ docs/examples/lldap-membership.yaml | 9 ++++ docs/examples/lldap-user.yaml | 23 +++++++++ docs/src/SUMMARY.md | 25 ++++++++++ docs/src/architecture.md | 19 ++++++++ docs/src/configuration.md | 11 +++++ docs/src/crds/README.md | 13 +++++ docs/src/crds/lldap-attribute-schema.md | 8 ++++ docs/src/crds/lldap-group.md | 26 ++++++++++ docs/src/crds/lldap-membership.md | 8 ++++ docs/src/crds/lldap-user.md | 33 +++++++++++++ docs/src/development.md | 45 ++++++++++++++++++ docs/src/installation.md | 23 +++++++++ docs/src/introduction.md | 16 +++++++ docs/src/multi-instance.md | 4 ++ docs/src/password-management.md | 4 ++ docs/src/troubleshooting.md | 3 ++ scripts/local-dev-setup.sh | 4 +- tests/e2e/fixtures/test-group.yaml | 2 +- tests/e2e/fixtures/test-user.yaml | 2 +- 53 files changed, 493 insertions(+), 85 deletions(-) create mode 100644 .changeset/changesets/financially-central-pipit.md create mode 100644 .github/workflows/docs.yml create mode 100644 CONTRIBUTING.md rename charts/{lldap-resources-operator => lldap-operator}/Chart.yaml (81%) rename charts/{lldap-resources-operator => lldap-operator}/crds/lldap-group.yaml (94%) rename charts/{lldap-resources-operator => lldap-operator}/crds/lldap-user.yaml (95%) rename charts/{lldap-resources-operator => lldap-operator}/templates/NOTES.txt (54%) rename charts/{lldap-resources-operator => lldap-operator}/templates/_helpers.tpl (66%) rename charts/{lldap-resources-operator => lldap-operator}/templates/clusterrole.yaml (71%) create mode 100644 charts/lldap-operator/templates/clusterrolebinding.yaml rename charts/{lldap-resources-operator => lldap-operator}/templates/deployment.yaml (79%) rename charts/{lldap-resources-operator => lldap-operator}/templates/serviceaccount.yaml (67%) rename charts/{lldap-resources-operator => lldap-operator}/values.yaml (85%) delete mode 100644 charts/lldap-resources-operator/templates/clusterrolebinding.yaml rename crates/{lldap-resources-operator-client => lldap-operator-client}/Cargo.toml (87%) rename crates/{lldap-resources-operator-client => lldap-operator-client}/src/lib.rs (100%) rename crates/{lldap-resources-operator-crds => lldap-operator-crds}/Cargo.toml (88%) rename crates/{lldap-resources-operator-crds => lldap-operator-crds}/src/lib.rs (100%) rename crates/{lldap-resources-operator-reconciler => lldap-operator-reconciler}/Cargo.toml (87%) rename crates/{lldap-resources-operator-reconciler => lldap-operator-reconciler}/src/lib.rs (100%) rename crates/{lldap-resources-operator-traits => lldap-operator-traits}/Cargo.toml (87%) rename crates/{lldap-resources-operator-traits => lldap-operator-traits}/src/lib.rs (100%) rename crates/{lldap-resources-operator => lldap-operator}/Cargo.toml (93%) rename crates/{lldap-resources-operator => lldap-operator}/src/main.rs (100%) create mode 100644 docs/book.toml create mode 100644 docs/examples/lldap-attribute-schema.yaml create mode 100644 docs/examples/lldap-group.yaml create mode 100644 docs/examples/lldap-membership.yaml create mode 100644 docs/examples/lldap-user.yaml create mode 100644 docs/src/SUMMARY.md create mode 100644 docs/src/architecture.md create mode 100644 docs/src/configuration.md create mode 100644 docs/src/crds/README.md create mode 100644 docs/src/crds/lldap-attribute-schema.md create mode 100644 docs/src/crds/lldap-group.md create mode 100644 docs/src/crds/lldap-membership.md create mode 100644 docs/src/crds/lldap-user.md create mode 100644 docs/src/development.md create mode 100644 docs/src/installation.md create mode 100644 docs/src/introduction.md create mode 100644 docs/src/multi-instance.md create mode 100644 docs/src/password-management.md create mode 100644 docs/src/troubleshooting.md diff --git a/.changeset/changesets/financially-central-pipit.md b/.changeset/changesets/financially-central-pipit.md new file mode 100644 index 0000000..acc9cf7 --- /dev/null +++ b/.changeset/changesets/financially-central-pipit.md @@ -0,0 +1,9 @@ +--- +lldap-operator-helm-chart: none +lldap-operator-reconciler: none +lldap-operator: none +lldap-operator-traits: none +lldap-operator-client: none +lldap-operator-crds: none +--- +Rename project from lldap-resources-operator to lldap-operator diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ac2127..a4269f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -90,4 +90,18 @@ jobs: - uses: azure/setup-helm@v4 - name: Lint Helm chart - run: helm lint charts/lldap-resources-operator + run: helm lint charts/lldap-operator + + docs: + name: Documentation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Install mdBook + uses: peaceiris/actions-mdbook@v2 + with: + mdbook-version: '0.5.2' + + - name: Build book + run: mdbook build docs diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..5588f53 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,47 @@ +--- +name: Deploy Docs + +on: + push: + branches: [main] + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + name: Build Documentation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Install mdBook + uses: peaceiris/actions-mdbook@v2 + with: + mdbook-version: '0.5.2' + + - name: Build book + run: mdbook build docs + + - name: Upload artifact + uses: actions/upload-pages-artifact@v4 + with: + path: docs/book + + deploy: + name: Deploy to GitHub Pages + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 39358f2..978ca37 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -39,7 +39,7 @@ jobs: - name: Install operator via Helm run: | - helm install lldap-operator charts/lldap-resources-operator \ + helm install lldap-operator charts/lldap-operator \ --set image.repository=lldap-operator \ --set image.tag=test \ --set image.pullPolicy=Never \ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2ed9e54..a511015 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -123,7 +123,7 @@ jobs: id: version run: | VERSION=$(cargo metadata --format-version=1 --no-deps \ - | jq -r '.packages[] | select(.name=="lldap-resources-operator") | .version') + | jq -r '.packages[] | select(.name=="lldap-operator") | .version') echo "version=$VERSION" >> "$GITHUB_OUTPUT" - uses: docker/build-push-action@v6 diff --git a/.gitignore b/.gitignore index 492ceaf..45682f5 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,6 @@ $RECYCLE.BIN/ # Added by cargo /target + +# mdBook build output +docs/book/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..3f5d4c2 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,32 @@ +# Contributing to lldap-operator + +Thank you for your interest in contributing! + +## Documentation + +The project documentation is built with [mdBook](https://rust-lang.github.io/mdBook/). +The published version is available at +. + +To build and preview locally: + +```bash +cargo install mdbook +cd docs +mdbook serve +``` + +See the [Architecture](https://lukidoescode.github.io/lldap-operator/architecture.html) +chapter for an overview of the codebase structure and the +[Development Guide](https://lukidoescode.github.io/lldap-operator/development.html) +for build instructions and local setup. + +## Reporting Issues + +Please file issues on the +[GitHub issue tracker](https://github.com/lukidoescode/lldap-operator/issues). + +## License + +By contributing, you agree that your contributions will be licensed under the +[MIT License](LICENSE). diff --git a/Cargo.lock b/Cargo.lock index 11dc043..c9803ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,9 +54,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", ] @@ -92,7 +92,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] -name = "lldap-resources-operator" +name = "lldap-operator" version = "0.0.0" dependencies = [ "clap", @@ -100,19 +100,19 @@ dependencies = [ ] [[package]] -name = "lldap-resources-operator-client" +name = "lldap-operator-client" version = "0.0.0" [[package]] -name = "lldap-resources-operator-crds" +name = "lldap-operator-crds" version = "0.0.0" [[package]] -name = "lldap-resources-operator-reconciler" +name = "lldap-operator-reconciler" version = "0.0.0" [[package]] -name = "lldap-resources-operator-traits" +name = "lldap-operator-traits" version = "0.0.0" [[package]] diff --git a/Cargo.toml b/Cargo.toml index fd02577..c5da709 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,8 +6,8 @@ resolver = "3" edition = "2024" rust-version = "1.85.0" license = "MIT" -repository = "https://github.com/lukidoescode/lldap-resources-operator" -homepage = "https://github.com/lukidoescode/lldap-resources-operator" +repository = "https://github.com/lukidoescode/lldap-operator" +homepage = "https://github.com/lukidoescode/lldap-operator" authors = ["Lukas Wagner "] [workspace.lints.clippy] @@ -31,29 +31,29 @@ commit-title-template = "Release {new-version}" changes-in-body = true [[workspace.metadata.changeset.additional-packages]] -name = "lldap-resources-operator-helm-chart" -path = "charts/lldap-resources-operator" -influence = ["charts/lldap-resources-operator/**"] +name = "lldap-operator-helm-chart" +path = "charts/lldap-operator" +influence = ["charts/lldap-operator/**"] [workspace.metadata.changeset.additional-packages.manifest] -file-path = "charts/lldap-resources-operator/Chart.yaml" +file-path = "charts/lldap-operator/Chart.yaml" format = "yaml" version-field-path = "version" [[workspace.metadata.changeset.additional-packages.dependencies]] -dependency-name = "lldap-resources-operator" +dependency-name = "lldap-operator" [workspace.metadata.changeset.additional-packages.dependencies.version-tracking-manifest] -file-path = "charts/lldap-resources-operator/Chart.yaml" +file-path = "charts/lldap-operator/Chart.yaml" format = "yaml" version-field-path = "appVersion" [workspace.dependencies] # Internal dependencies -lldap-resources-operator-crds = { path = "crates/lldap-resources-operator-crds", version = "0.0.0" } -lldap-resources-operator-traits = { path = "crates/lldap-resources-operator-traits", version = "0.0.0" } -lldap-resources-operator-client = { path = "crates/lldap-resources-operator-client", version = "0.0.0" } -lldap-resources-operator-reconciler = { path = "crates/lldap-resources-operator-reconciler", version = "0.0.0" } +lldap-operator-crds = { path = "crates/lldap-operator-crds", version = "0.0.0" } +lldap-operator-traits = { path = "crates/lldap-operator-traits", version = "0.0.0" } +lldap-operator-client = { path = "crates/lldap-operator-client", version = "0.0.0" } +lldap-operator-reconciler = { path = "crates/lldap-operator-reconciler", version = "0.0.0" } # External dependencies thiserror = "2.0.18" diff --git a/README.md b/README.md index e5166d1..258dd8c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# lldap-resources-operator +# lldap-operator -[![CI](https://github.com/lukidoescode/lldap-resources-operator/actions/workflows/ci.yml/badge.svg)](https://github.com/lukidoescode/lldap-resources-operator/actions/workflows/ci.yml) +[![CI](https://github.com/lukidoescode/lldap-operator/actions/workflows/ci.yml/badge.svg)](https://github.com/lukidoescode/lldap-operator/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) A Kubernetes operator that manages [lldap](https://github.com/lldap/lldap) @@ -21,8 +21,8 @@ users and groups as native Kubernetes resources via Custom Resource Definitions Install using Helm: ```bash -helm repo add lldap-resources-operator https://lukidoescode.github.io/lldap-resources-operator -helm install lldap-operator lldap-resources-operator/lldap-resources-operator +helm repo add lldap-operator https://lukidoescode.github.io/lldap-operator +helm install lldap-operator lldap-operator/lldap-operator ``` ## Usage @@ -30,7 +30,7 @@ helm install lldap-operator lldap-resources-operator/lldap-resources-operator ### Creating a group ```yaml -apiVersion: lldap-resources-operator.lukidoescode.com/v1alpha1 +apiVersion: lldap-operator.lukidoescode.com/v1alpha1 kind: LldapGroup metadata: name: my-group @@ -41,7 +41,7 @@ spec: ### Creating a user ```yaml -apiVersion: lldap-resources-operator.lukidoescode.com/v1alpha1 +apiVersion: lldap-operator.lukidoescode.com/v1alpha1 kind: LldapUser metadata: name: my-user @@ -65,11 +65,11 @@ and organized as a Cargo workspace: | Crate | Purpose | |-------|---------| -| `lldap-resources-operator` | Operator binary | -| `lldap-resources-operator-crds` | CRD type definitions | -| `lldap-resources-operator-traits` | Trait definitions for lldap operations | -| `lldap-resources-operator-client` | GraphQL client for lldap | -| `lldap-resources-operator-reconciler` | Reconciliation business logic | +| `lldap-operator` | Operator binary | +| `lldap-operator-crds` | CRD type definitions | +| `lldap-operator-traits` | Trait definitions for lldap operations | +| `lldap-operator-client` | GraphQL client for lldap | +| `lldap-operator-reconciler` | Reconciliation business logic | ## Development diff --git a/charts/lldap-resources-operator/Chart.yaml b/charts/lldap-operator/Chart.yaml similarity index 81% rename from charts/lldap-resources-operator/Chart.yaml rename to charts/lldap-operator/Chart.yaml index 981deb7..e701a95 100644 --- a/charts/lldap-resources-operator/Chart.yaml +++ b/charts/lldap-operator/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -name: lldap-resources-operator +name: lldap-operator description: A Helm chart for the lldap resources Kubernetes operator type: application version: 0.0.0 diff --git a/charts/lldap-resources-operator/crds/lldap-group.yaml b/charts/lldap-operator/crds/lldap-group.yaml similarity index 94% rename from charts/lldap-resources-operator/crds/lldap-group.yaml rename to charts/lldap-operator/crds/lldap-group.yaml index efe195e..86d7d92 100644 --- a/charts/lldap-resources-operator/crds/lldap-group.yaml +++ b/charts/lldap-operator/crds/lldap-group.yaml @@ -1,9 +1,9 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: - name: lldapgroups.lldap-resources-operator.lukidoescode.com + name: lldapgroups.lldap-operator.lukidoescode.com spec: - group: lldap-resources-operator.lukidoescode.com + group: lldap-operator.lukidoescode.com names: kind: LldapGroup listKind: LldapGroupList diff --git a/charts/lldap-resources-operator/crds/lldap-user.yaml b/charts/lldap-operator/crds/lldap-user.yaml similarity index 95% rename from charts/lldap-resources-operator/crds/lldap-user.yaml rename to charts/lldap-operator/crds/lldap-user.yaml index 2860468..c535bd3 100644 --- a/charts/lldap-resources-operator/crds/lldap-user.yaml +++ b/charts/lldap-operator/crds/lldap-user.yaml @@ -1,9 +1,9 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: - name: lldapusers.lldap-resources-operator.lukidoescode.com + name: lldapusers.lldap-operator.lukidoescode.com spec: - group: lldap-resources-operator.lukidoescode.com + group: lldap-operator.lukidoescode.com names: kind: LldapUser listKind: LldapUserList diff --git a/charts/lldap-resources-operator/templates/NOTES.txt b/charts/lldap-operator/templates/NOTES.txt similarity index 54% rename from charts/lldap-resources-operator/templates/NOTES.txt rename to charts/lldap-operator/templates/NOTES.txt index 4910282..8fe0571 100644 --- a/charts/lldap-resources-operator/templates/NOTES.txt +++ b/charts/lldap-operator/templates/NOTES.txt @@ -1,8 +1,8 @@ -The lldap-resources-operator has been deployed. +The lldap-operator has been deployed. It will watch for LldapUser and LldapGroup custom resources and reconcile them with the lldap instance at {{ .Values.lldap.url }}. To verify the operator is running: - kubectl get pods -l app.kubernetes.io/name={{ include "lldap-resources-operator.name" . }} + kubectl get pods -l app.kubernetes.io/name={{ include "lldap-operator.name" . }} diff --git a/charts/lldap-resources-operator/templates/_helpers.tpl b/charts/lldap-operator/templates/_helpers.tpl similarity index 66% rename from charts/lldap-resources-operator/templates/_helpers.tpl rename to charts/lldap-operator/templates/_helpers.tpl index 8c02711..4b8d9d3 100644 --- a/charts/lldap-resources-operator/templates/_helpers.tpl +++ b/charts/lldap-operator/templates/_helpers.tpl @@ -1,14 +1,14 @@ {{/* Expand the name of the chart. */}} -{{- define "lldap-resources-operator.name" -}} +{{- define "lldap-operator.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Create a default fully qualified app name. */}} -{{- define "lldap-resources-operator.fullname" -}} +{{- define "lldap-operator.fullname" -}} {{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} @@ -24,16 +24,16 @@ Create a default fully qualified app name. {{/* Create chart name and version as used by the chart label. */}} -{{- define "lldap-resources-operator.chart" -}} +{{- define "lldap-operator.chart" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Common labels */}} -{{- define "lldap-resources-operator.labels" -}} -helm.sh/chart: {{ include "lldap-resources-operator.chart" . }} -{{ include "lldap-resources-operator.selectorLabels" . }} +{{- define "lldap-operator.labels" -}} +helm.sh/chart: {{ include "lldap-operator.chart" . }} +{{ include "lldap-operator.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} @@ -43,17 +43,17 @@ app.kubernetes.io/managed-by: {{ .Release.Service }} {{/* Selector labels */}} -{{- define "lldap-resources-operator.selectorLabels" -}} -app.kubernetes.io/name: {{ include "lldap-resources-operator.name" . }} +{{- define "lldap-operator.selectorLabels" -}} +app.kubernetes.io/name: {{ include "lldap-operator.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} {{/* Create the name of the service account to use */}} -{{- define "lldap-resources-operator.serviceAccountName" -}} +{{- define "lldap-operator.serviceAccountName" -}} {{- if .Values.serviceAccount.create }} -{{- default (include "lldap-resources-operator.fullname" .) .Values.serviceAccount.name }} +{{- default (include "lldap-operator.fullname" .) .Values.serviceAccount.name }} {{- else }} {{- default "default" .Values.serviceAccount.name }} {{- end }} diff --git a/charts/lldap-resources-operator/templates/clusterrole.yaml b/charts/lldap-operator/templates/clusterrole.yaml similarity index 71% rename from charts/lldap-resources-operator/templates/clusterrole.yaml rename to charts/lldap-operator/templates/clusterrole.yaml index 2495d07..07dfc3c 100644 --- a/charts/lldap-resources-operator/templates/clusterrole.yaml +++ b/charts/lldap-operator/templates/clusterrole.yaml @@ -1,12 +1,12 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: {{ include "lldap-resources-operator.fullname" . }} + name: {{ include "lldap-operator.fullname" . }} labels: - {{- include "lldap-resources-operator.labels" . | nindent 4 }} + {{- include "lldap-operator.labels" . | nindent 4 }} rules: - apiGroups: - - lldap-resources-operator.lukidoescode.com + - lldap-operator.lukidoescode.com resources: - lldapusers - lldapgroups @@ -17,7 +17,7 @@ rules: - patch - update - apiGroups: - - lldap-resources-operator.lukidoescode.com + - lldap-operator.lukidoescode.com resources: - lldapusers/status - lldapgroups/status diff --git a/charts/lldap-operator/templates/clusterrolebinding.yaml b/charts/lldap-operator/templates/clusterrolebinding.yaml new file mode 100644 index 0000000..7c96919 --- /dev/null +++ b/charts/lldap-operator/templates/clusterrolebinding.yaml @@ -0,0 +1,14 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "lldap-operator.fullname" . }} + labels: + {{- include "lldap-operator.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "lldap-operator.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ include "lldap-operator.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} diff --git a/charts/lldap-resources-operator/templates/deployment.yaml b/charts/lldap-operator/templates/deployment.yaml similarity index 79% rename from charts/lldap-resources-operator/templates/deployment.yaml rename to charts/lldap-operator/templates/deployment.yaml index 32c2429..3547d70 100644 --- a/charts/lldap-resources-operator/templates/deployment.yaml +++ b/charts/lldap-operator/templates/deployment.yaml @@ -1,24 +1,24 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ include "lldap-resources-operator.fullname" . }} + name: {{ include "lldap-operator.fullname" . }} labels: - {{- include "lldap-resources-operator.labels" . | nindent 4 }} + {{- include "lldap-operator.labels" . | nindent 4 }} spec: replicas: {{ .Values.replicaCount }} selector: matchLabels: - {{- include "lldap-resources-operator.selectorLabels" . | nindent 6 }} + {{- include "lldap-operator.selectorLabels" . | nindent 6 }} template: metadata: labels: - {{- include "lldap-resources-operator.labels" . | nindent 8 }} + {{- include "lldap-operator.labels" . | nindent 8 }} spec: {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} - serviceAccountName: {{ include "lldap-resources-operator.serviceAccountName" . }} + serviceAccountName: {{ include "lldap-operator.serviceAccountName" . }} containers: - name: operator image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" diff --git a/charts/lldap-resources-operator/templates/serviceaccount.yaml b/charts/lldap-operator/templates/serviceaccount.yaml similarity index 67% rename from charts/lldap-resources-operator/templates/serviceaccount.yaml rename to charts/lldap-operator/templates/serviceaccount.yaml index 97c77e8..8a71b83 100644 --- a/charts/lldap-resources-operator/templates/serviceaccount.yaml +++ b/charts/lldap-operator/templates/serviceaccount.yaml @@ -2,9 +2,9 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: {{ include "lldap-resources-operator.serviceAccountName" . }} + name: {{ include "lldap-operator.serviceAccountName" . }} labels: - {{- include "lldap-resources-operator.labels" . | nindent 4 }} + {{- include "lldap-operator.labels" . | nindent 4 }} {{- with .Values.serviceAccount.annotations }} annotations: {{- toYaml . | nindent 4 }} diff --git a/charts/lldap-resources-operator/values.yaml b/charts/lldap-operator/values.yaml similarity index 85% rename from charts/lldap-resources-operator/values.yaml rename to charts/lldap-operator/values.yaml index 5382f72..74ab7fb 100644 --- a/charts/lldap-resources-operator/values.yaml +++ b/charts/lldap-operator/values.yaml @@ -1,7 +1,7 @@ replicaCount: 1 image: - repository: ghcr.io/lukidoescode/lldap-resources-operator + repository: ghcr.io/lukidoescode/lldap-operator pullPolicy: IfNotPresent tag: "" diff --git a/charts/lldap-resources-operator/templates/clusterrolebinding.yaml b/charts/lldap-resources-operator/templates/clusterrolebinding.yaml deleted file mode 100644 index 867511f..0000000 --- a/charts/lldap-resources-operator/templates/clusterrolebinding.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: {{ include "lldap-resources-operator.fullname" . }} - labels: - {{- include "lldap-resources-operator.labels" . | nindent 4 }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ include "lldap-resources-operator.fullname" . }} -subjects: - - kind: ServiceAccount - name: {{ include "lldap-resources-operator.serviceAccountName" . }} - namespace: {{ .Release.Namespace }} diff --git a/crates/lldap-resources-operator-client/Cargo.toml b/crates/lldap-operator-client/Cargo.toml similarity index 87% rename from crates/lldap-resources-operator-client/Cargo.toml rename to crates/lldap-operator-client/Cargo.toml index 0cfa53d..7dc5658 100644 --- a/crates/lldap-resources-operator-client/Cargo.toml +++ b/crates/lldap-operator-client/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "lldap-resources-operator-client" +name = "lldap-operator-client" version = "0.0.0" edition.workspace = true rust-version.workspace = true diff --git a/crates/lldap-resources-operator-client/src/lib.rs b/crates/lldap-operator-client/src/lib.rs similarity index 100% rename from crates/lldap-resources-operator-client/src/lib.rs rename to crates/lldap-operator-client/src/lib.rs diff --git a/crates/lldap-resources-operator-crds/Cargo.toml b/crates/lldap-operator-crds/Cargo.toml similarity index 88% rename from crates/lldap-resources-operator-crds/Cargo.toml rename to crates/lldap-operator-crds/Cargo.toml index 54f5f96..f0a398a 100644 --- a/crates/lldap-resources-operator-crds/Cargo.toml +++ b/crates/lldap-operator-crds/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "lldap-resources-operator-crds" +name = "lldap-operator-crds" version = "0.0.0" edition.workspace = true rust-version.workspace = true diff --git a/crates/lldap-resources-operator-crds/src/lib.rs b/crates/lldap-operator-crds/src/lib.rs similarity index 100% rename from crates/lldap-resources-operator-crds/src/lib.rs rename to crates/lldap-operator-crds/src/lib.rs diff --git a/crates/lldap-resources-operator-reconciler/Cargo.toml b/crates/lldap-operator-reconciler/Cargo.toml similarity index 87% rename from crates/lldap-resources-operator-reconciler/Cargo.toml rename to crates/lldap-operator-reconciler/Cargo.toml index 4d4980e..d1521ba 100644 --- a/crates/lldap-resources-operator-reconciler/Cargo.toml +++ b/crates/lldap-operator-reconciler/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "lldap-resources-operator-reconciler" +name = "lldap-operator-reconciler" version = "0.0.0" edition.workspace = true rust-version.workspace = true diff --git a/crates/lldap-resources-operator-reconciler/src/lib.rs b/crates/lldap-operator-reconciler/src/lib.rs similarity index 100% rename from crates/lldap-resources-operator-reconciler/src/lib.rs rename to crates/lldap-operator-reconciler/src/lib.rs diff --git a/crates/lldap-resources-operator-traits/Cargo.toml b/crates/lldap-operator-traits/Cargo.toml similarity index 87% rename from crates/lldap-resources-operator-traits/Cargo.toml rename to crates/lldap-operator-traits/Cargo.toml index e9dd7ac..52a271c 100644 --- a/crates/lldap-resources-operator-traits/Cargo.toml +++ b/crates/lldap-operator-traits/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "lldap-resources-operator-traits" +name = "lldap-operator-traits" version = "0.0.0" edition.workspace = true rust-version.workspace = true diff --git a/crates/lldap-resources-operator-traits/src/lib.rs b/crates/lldap-operator-traits/src/lib.rs similarity index 100% rename from crates/lldap-resources-operator-traits/src/lib.rs rename to crates/lldap-operator-traits/src/lib.rs diff --git a/crates/lldap-resources-operator/Cargo.toml b/crates/lldap-operator/Cargo.toml similarity index 93% rename from crates/lldap-resources-operator/Cargo.toml rename to crates/lldap-operator/Cargo.toml index 5918501..5370eb4 100644 --- a/crates/lldap-resources-operator/Cargo.toml +++ b/crates/lldap-operator/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "lldap-resources-operator" +name = "lldap-operator" version = "0.0.0" edition.workspace = true rust-version.workspace = true diff --git a/crates/lldap-resources-operator/src/main.rs b/crates/lldap-operator/src/main.rs similarity index 100% rename from crates/lldap-resources-operator/src/main.rs rename to crates/lldap-operator/src/main.rs diff --git a/docs/book.toml b/docs/book.toml new file mode 100644 index 0000000..b8b31d0 --- /dev/null +++ b/docs/book.toml @@ -0,0 +1,15 @@ +[book] +title = "lldap-operator" +authors = ["Lukas Wagner"] +description = "Documentation for the lldap-operator Kubernetes operator" +language = "en" +src = "src" + +[output.html] +git-repository-url = "https://github.com/lukidoescode/lldap-operator" +edit-url-template = "https://github.com/lukidoescode/lldap-operator/edit/main/docs/{path}" +default-theme = "navy" +preferred-dark-theme = "navy" + +[output.html.search] +enable = true diff --git a/docs/examples/lldap-attribute-schema.yaml b/docs/examples/lldap-attribute-schema.yaml new file mode 100644 index 0000000..0174aa9 --- /dev/null +++ b/docs/examples/lldap-attribute-schema.yaml @@ -0,0 +1,12 @@ +# NOTE: LldapAttributeSchema is a planned CRD and is not yet available. +# This example shows the anticipated resource structure. +apiVersion: lldap-operator.lukidoescode.com/v1alpha1 +kind: LldapAttributeSchema +metadata: + name: department +spec: + attributeName: "department" + attributeType: "String" + isList: false + isVisible: true + isEditable: true diff --git a/docs/examples/lldap-group.yaml b/docs/examples/lldap-group.yaml new file mode 100644 index 0000000..8e8dd1a --- /dev/null +++ b/docs/examples/lldap-group.yaml @@ -0,0 +1,6 @@ +apiVersion: lldap-operator.lukidoescode.com/v1alpha1 +kind: LldapGroup +metadata: + name: engineering +spec: + displayName: "Engineering" diff --git a/docs/examples/lldap-membership.yaml b/docs/examples/lldap-membership.yaml new file mode 100644 index 0000000..d7879a3 --- /dev/null +++ b/docs/examples/lldap-membership.yaml @@ -0,0 +1,9 @@ +# NOTE: LldapMembership is a planned CRD and is not yet available. +# This example shows the anticipated resource structure. +apiVersion: lldap-operator.lukidoescode.com/v1alpha1 +kind: LldapMembership +metadata: + name: alice-engineering +spec: + userRef: "alice" + groupRef: "engineering" diff --git a/docs/examples/lldap-user.yaml b/docs/examples/lldap-user.yaml new file mode 100644 index 0000000..0e58556 --- /dev/null +++ b/docs/examples/lldap-user.yaml @@ -0,0 +1,23 @@ +apiVersion: lldap-operator.lukidoescode.com/v1alpha1 +kind: LldapUser +metadata: + name: alice +spec: + username: "alice.smith" + email: "alice.smith@example.com" + displayName: "Alice Smith" + firstName: "Alice" + lastName: "Smith" + groups: + - "engineering" + passwordSecretRef: + name: alice-password + key: password +--- +apiVersion: v1 +kind: Secret +metadata: + name: alice-password +type: Opaque +stringData: + password: "change-me-on-first-login" diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md new file mode 100644 index 0000000..6b90030 --- /dev/null +++ b/docs/src/SUMMARY.md @@ -0,0 +1,25 @@ +# Summary + +[Introduction](introduction.md) + +--- + +# User Guide + +- [Installation](installation.md) +- [Configuration Reference](configuration.md) +- [CRD Reference](crds/README.md) + - [LldapUser](crds/lldap-user.md) + - [LldapGroup](crds/lldap-group.md) + - [LldapMembership](crds/lldap-membership.md) + - [LldapAttributeSchema](crds/lldap-attribute-schema.md) +- [Multi-Instance Deployment](multi-instance.md) +- [Password Management](password-management.md) +- [Troubleshooting](troubleshooting.md) + +--- + +# Contributor Guide + +- [Architecture](architecture.md) +- [Development Guide](development.md) diff --git a/docs/src/architecture.md b/docs/src/architecture.md new file mode 100644 index 0000000..231e53b --- /dev/null +++ b/docs/src/architecture.md @@ -0,0 +1,19 @@ +# Architecture + +This chapter is for contributors who want to understand how the operator is +structured internally. + +## Workspace Structure + +The operator is organized as a Cargo workspace with focused crates: + +| Crate | Purpose | +|-------|---------| +| `lldap-operator` | Operator binary (entry point, CLI, controller setup) | +| `lldap-operator-crds` | CRD type definitions (`CustomResource` derives) | +| `lldap-operator-traits` | Trait definitions for lldap operations | +| `lldap-operator-client` | GraphQL client for the lldap API | +| `lldap-operator-reconciler` | Reconciliation business logic | + + + diff --git a/docs/src/configuration.md b/docs/src/configuration.md new file mode 100644 index 0000000..41cd4d6 --- /dev/null +++ b/docs/src/configuration.md @@ -0,0 +1,11 @@ +# Configuration Reference + + + +## Helm Values + + + +## Operator Environment Variables + + diff --git a/docs/src/crds/README.md b/docs/src/crds/README.md new file mode 100644 index 0000000..180b440 --- /dev/null +++ b/docs/src/crds/README.md @@ -0,0 +1,13 @@ +# CRD Reference + +The lldap-operator manages the following Custom Resource Definitions: + +| Kind | Description | Status | +|------|-------------|--------| +| [LldapUser](lldap-user.md) | Manages lldap user accounts | Available | +| [LldapGroup](lldap-group.md) | Manages lldap groups | Available | +| [LldapMembership](lldap-membership.md) | Manages group membership relationships | Planned | +| [LldapAttributeSchema](lldap-attribute-schema.md) | Manages custom attribute schemas | Planned | + +All resources use the API group +`lldap-operator.lukidoescode.com/v1alpha1`. diff --git a/docs/src/crds/lldap-attribute-schema.md b/docs/src/crds/lldap-attribute-schema.md new file mode 100644 index 0000000..c6eedc8 --- /dev/null +++ b/docs/src/crds/lldap-attribute-schema.md @@ -0,0 +1,8 @@ +# LldapAttributeSchema + +> **Planned:** This CRD is not yet implemented. + +An `LldapAttributeSchema` resource will manage custom attribute schemas in +lldap, allowing operators to define additional fields for users and groups. + + diff --git a/docs/src/crds/lldap-group.md b/docs/src/crds/lldap-group.md new file mode 100644 index 0000000..ebc5b98 --- /dev/null +++ b/docs/src/crds/lldap-group.md @@ -0,0 +1,26 @@ +# LldapGroup + +An `LldapGroup` resource declares an lldap group managed by the operator. + +## Example + +```yaml +{{#include ../../examples/lldap-group.yaml}} +``` + + + +## Spec Fields + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `displayName` | string | yes | Group display name | + +## Status + +| Field | Type | Description | +|-------|------|-------------| +| `groupId` | integer | Group ID assigned by lldap | +| `uuid` | string | Group UUID assigned by lldap | +| `observedGeneration` | int64 | Last observed resource generation | +| `conditions` | Condition[] | Standard Kubernetes conditions | diff --git a/docs/src/crds/lldap-membership.md b/docs/src/crds/lldap-membership.md new file mode 100644 index 0000000..3cb1f4c --- /dev/null +++ b/docs/src/crds/lldap-membership.md @@ -0,0 +1,8 @@ +# LldapMembership + +> **Planned:** This CRD is not yet implemented. + +An `LldapMembership` resource will manage the relationship between lldap users +and groups independently of the `LldapUser` resource. + + diff --git a/docs/src/crds/lldap-user.md b/docs/src/crds/lldap-user.md new file mode 100644 index 0000000..3223897 --- /dev/null +++ b/docs/src/crds/lldap-user.md @@ -0,0 +1,33 @@ +# LldapUser + +An `LldapUser` resource declares an lldap user account managed by the operator. + +## Example + +```yaml +{{#include ../../examples/lldap-user.yaml}} +``` + + + +## Spec Fields + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `username` | string | yes | LDAP username | +| `email` | string | yes | Email address | +| `displayName` | string | no | Display name | +| `firstName` | string | no | First name | +| `lastName` | string | no | Last name | +| `groups` | string[] | no | Group memberships by name | +| `passwordSecretRef` | object | yes | Reference to a Secret containing the password | +| `passwordSecretRef.name` | string | yes | Name of the Secret | +| `passwordSecretRef.key` | string | yes | Key within the Secret containing the password | + +## Status + +| Field | Type | Description | +|-------|------|-------------| +| `uuid` | string | User UUID assigned by lldap | +| `observedGeneration` | int64 | Last observed resource generation | +| `conditions` | Condition[] | Standard Kubernetes conditions | diff --git a/docs/src/development.md b/docs/src/development.md new file mode 100644 index 0000000..0af76bd --- /dev/null +++ b/docs/src/development.md @@ -0,0 +1,45 @@ +# Development Guide + +This chapter is for contributors who want to build and test the operator +locally. + +## Prerequisites + +- Rust 1.85+ +- Docker +- [kind](https://kind.sigs.k8s.io/) or [k3d](https://k3d.io/) +- [Helm](https://helm.sh/) v3 +- kubectl + +## Local Setup + +```bash +./scripts/local-dev-setup.sh +``` + +This creates a kind cluster, deploys an lldap test instance, and installs the +CRDs. + +## Building + +```bash +cargo build --workspace +``` + +## Running Tests + +```bash +cargo test --workspace +``` + +## Code Quality + +Before submitting changes, ensure the following pass: + +```bash +cargo fmt --all -- --check +cargo clippy --workspace --all-targets -- -D warnings +cargo test --workspace +``` + + diff --git a/docs/src/installation.md b/docs/src/installation.md new file mode 100644 index 0000000..5476bd9 --- /dev/null +++ b/docs/src/installation.md @@ -0,0 +1,23 @@ +# Installation + + + +## Prerequisites + +- A running Kubernetes cluster (v1.26+) +- [Helm](https://helm.sh/) v3 +- An accessible [lldap](https://github.com/lldap/lldap) instance + +## Quick Start + +```bash +helm repo add lldap-operator \ + https://lukidoescode.github.io/lldap-operator +helm install lldap-operator \ + lldap-operator/lldap-operator +``` + +## Configuration + +See the [Configuration Reference](configuration.md) for all available Helm +values. diff --git a/docs/src/introduction.md b/docs/src/introduction.md new file mode 100644 index 0000000..8ab54f7 --- /dev/null +++ b/docs/src/introduction.md @@ -0,0 +1,16 @@ +# Introduction + +**lldap-operator** is a Kubernetes operator that manages +[lldap](https://github.com/lldap/lldap) users and groups as native Kubernetes +resources via Custom Resource Definitions (CRDs). + +> **Note:** This project is in early development. APIs are not yet stable. + +## Features + +- Declare lldap users and groups as Kubernetes CRDs +- Reconcile desired state with lldap via its GraphQL API +- Password management through Kubernetes Secret references +- Standard operator patterns: status conditions, finalizers, observed generation + + diff --git a/docs/src/multi-instance.md b/docs/src/multi-instance.md new file mode 100644 index 0000000..89b5e1c --- /dev/null +++ b/docs/src/multi-instance.md @@ -0,0 +1,4 @@ +# Multi-Instance Deployment + + + diff --git a/docs/src/password-management.md b/docs/src/password-management.md new file mode 100644 index 0000000..2fc95da --- /dev/null +++ b/docs/src/password-management.md @@ -0,0 +1,4 @@ +# Password Management + + + diff --git a/docs/src/troubleshooting.md b/docs/src/troubleshooting.md new file mode 100644 index 0000000..5a63ea2 --- /dev/null +++ b/docs/src/troubleshooting.md @@ -0,0 +1,3 @@ +# Troubleshooting + + diff --git a/scripts/local-dev-setup.sh b/scripts/local-dev-setup.sh index 9be7bb0..ea91a0b 100755 --- a/scripts/local-dev-setup.sh +++ b/scripts/local-dev-setup.sh @@ -46,13 +46,13 @@ echo "Waiting for lldap to be ready..." kubectl wait --for=condition=available deployment/lldap --timeout=120s echo "Installing CRDs..." -kubectl apply -f "$PROJECT_DIR/charts/lldap-resources-operator/crds/" +kubectl apply -f "$PROJECT_DIR/charts/lldap-operator/crds/" echo "" echo "Local development environment is ready." echo "" echo "To install the operator via Helm:" -echo " helm install lldap-operator $PROJECT_DIR/charts/lldap-resources-operator \\" +echo " helm install lldap-operator $PROJECT_DIR/charts/lldap-operator \\" echo " --set image.repository=lldap-operator \\" echo " --set image.tag=dev \\" echo " --set image.pullPolicy=Never \\" diff --git a/tests/e2e/fixtures/test-group.yaml b/tests/e2e/fixtures/test-group.yaml index a94ae04..15de9fd 100644 --- a/tests/e2e/fixtures/test-group.yaml +++ b/tests/e2e/fixtures/test-group.yaml @@ -1,4 +1,4 @@ -apiVersion: lldap-resources-operator.lukidoescode.com/v1alpha1 +apiVersion: lldap-operator.lukidoescode.com/v1alpha1 kind: LldapGroup metadata: name: test-group diff --git a/tests/e2e/fixtures/test-user.yaml b/tests/e2e/fixtures/test-user.yaml index d18efa4..94796fc 100644 --- a/tests/e2e/fixtures/test-user.yaml +++ b/tests/e2e/fixtures/test-user.yaml @@ -1,4 +1,4 @@ -apiVersion: lldap-resources-operator.lukidoescode.com/v1alpha1 +apiVersion: lldap-operator.lukidoescode.com/v1alpha1 kind: LldapUser metadata: name: test-user From 8c6f6be27d19d2495b497e348717df9db350d0ed Mon Sep 17 00:00:00 2001 From: Lukas Wagner Date: Sun, 10 May 2026 16:29:49 +0200 Subject: [PATCH 2/4] Bump pages actions to v5 Update actions/upload-pages-artifact and actions/deploy-pages from v4 to v5 in the docs deployment workflow. --- .github/workflows/docs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 5588f53..4f8d9c3 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -30,7 +30,7 @@ jobs: run: mdbook build docs - name: Upload artifact - uses: actions/upload-pages-artifact@v4 + uses: actions/upload-pages-artifact@v5 with: path: docs/book @@ -44,4 +44,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5 From c00d293acff99103d5ece2df9e505df608ae1df8 Mon Sep 17 00:00:00 2001 From: Lukas Wagner Date: Sun, 10 May 2026 16:45:45 +0200 Subject: [PATCH 3/4] Disable E2E workflow until operator is implemented The operator binary is a stub with no reconciliation logic, so E2E tests always time out. Switch to manual-only trigger until the operator is functional. --- .github/workflows/e2e.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 978ca37..35ddf64 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -2,8 +2,7 @@ name: E2E Tests on: - pull_request: - branches: [main] + workflow_dispatch: permissions: contents: read From 9f2d5b8618c913e03725545f342caa71dbb3558a Mon Sep 17 00:00:00 2001 From: Lukas Wagner Date: Sun, 10 May 2026 22:39:10 +0200 Subject: [PATCH 4/4] Add versioned documentation deployment with version selector Replace the simple GitHub Pages deployment with a versioned docs system: - Add deploy-docs.yml workflow triggered by CI (main-branch) and releases - Add helper scripts for generating versions.json and root redirect - Add version selector dropdown (JS/CSS) to mdBook output - Wire CI and release workflows to trigger docs deployment --- .github/scripts/generate-redirect.sh | 21 +++++ .github/scripts/generate-versions-json.sh | 42 ++++++++++ .github/workflows/ci.yml | 22 ++++++ .github/workflows/deploy-docs.yml | 96 +++++++++++++++++++++++ .github/workflows/docs.yml | 47 ----------- .github/workflows/release.yml | 43 ++++++++++ docs/book.toml | 2 + docs/src/version-selector.css | 26 ++++++ docs/src/version-selector.js | 46 +++++++++++ 9 files changed, 298 insertions(+), 47 deletions(-) create mode 100755 .github/scripts/generate-redirect.sh create mode 100755 .github/scripts/generate-versions-json.sh create mode 100644 .github/workflows/deploy-docs.yml delete mode 100644 .github/workflows/docs.yml create mode 100644 docs/src/version-selector.css create mode 100644 docs/src/version-selector.js diff --git a/.github/scripts/generate-redirect.sh b/.github/scripts/generate-redirect.sh new file mode 100755 index 0000000..d193a63 --- /dev/null +++ b/.github/scripts/generate-redirect.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -euo pipefail + +LATEST="${1}" +OUTPUT="${2:-index.html}" + +cat > "$OUTPUT" << HTMLEOF + + + + + + Redirecting to latest docs + + +

Redirecting to latest documentation (${LATEST})

+ + +HTMLEOF + +echo "Generated root redirect -> /${LATEST}/" diff --git a/.github/scripts/generate-versions-json.sh b/.github/scripts/generate-versions-json.sh new file mode 100755 index 0000000..386bc0a --- /dev/null +++ b/.github/scripts/generate-versions-json.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +set -euo pipefail + +GH_PAGES_DIR="${1:-.}" +OUTPUT="${2:-versions.json}" + +VERSIONS=() +for dir in "$GH_PAGES_DIR"/v*/; do + [ -d "$dir" ] || continue + VERSIONS+=("$(basename "$dir")") +done + +if [[ ${#VERSIONS[@]} -eq 0 ]]; then + SORTED=() +else + IFS=$'\n' + SORTED=($(printf '%s\n' "${VERSIONS[@]}" | sed 's/^v//' | sort -t. -k1,1rn -k2,2rn -k3,3rn | sed 's/^/v/')) + unset IFS +fi + +LATEST="${SORTED[0]:-}" + +HAS_MAIN=false +if [ -d "$GH_PAGES_DIR/main-branch" ]; then + HAS_MAIN=true +fi + +{ + echo '{' + echo " \"latest\": \"${LATEST}\"," + echo " \"has_main\": ${HAS_MAIN}," + echo ' "versions": [' + for i in "${!SORTED[@]}"; do + COMMA="," + if [ "$i" -eq $(( ${#SORTED[@]} - 1 )) ]; then COMMA=""; fi + echo " \"${SORTED[$i]}\"${COMMA}" + done + echo ' ]' + echo '}' +} > "$OUTPUT" + +echo "Generated $OUTPUT with ${#SORTED[@]} versions (latest: $LATEST)" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a4269f3..fa9e82c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -105,3 +105,25 @@ jobs: - name: Build book run: mdbook build docs + + deploy-docs: + name: Deploy Main Branch Docs + needs: docs + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + permissions: + actions: write + steps: + - name: Trigger docs deployment + uses: actions/github-script@v7 + with: + script: | + await github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'deploy-docs.yml', + ref: 'main', + inputs: { + version: 'main-branch' + } + }); diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml new file mode 100644 index 0000000..770656b --- /dev/null +++ b/.github/workflows/deploy-docs.yml @@ -0,0 +1,96 @@ +--- +name: Deploy Docs + +# Triggered explicitly: by ci.yml (main-branch) or release.yml (versioned). +# The workflow YAML is always read from the dispatching ref (usually main), +# but docs content is checked out from the target ref (tag or main). If the +# mdBook configuration changes incompatibly between versions, older docs may +# not build correctly with the current workflow. +on: + workflow_dispatch: + inputs: + version: + description: >- + Version to deploy (e.g. v0.3.0) or "main-branch" for dev docs + required: true + type: string + +permissions: + contents: write + +concurrency: + group: deploy-docs + cancel-in-progress: false + +jobs: + deploy: + name: Build and Deploy Documentation + runs-on: ubuntu-latest + steps: + - name: Determine deployment target + id: target + run: | + VERSION="${{ inputs.version }}" + if [[ "$VERSION" == "main-branch" ]]; then + echo "destination=main-branch" >> "$GITHUB_OUTPUT" + echo "is_release=false" >> "$GITHUB_OUTPUT" + echo "ref=${{ github.sha }}" >> "$GITHUB_OUTPUT" + echo "Deploying main-branch docs to /main-branch/" + elif [[ "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]]; then + echo "destination=${VERSION}" >> "$GITHUB_OUTPUT" + echo "is_release=true" >> "$GITHUB_OUTPUT" + echo "ref=lldap-operator@${VERSION}" >> "$GITHUB_OUTPUT" + echo "Deploying release docs to /${VERSION}/" + else + echo "::error::Invalid version format '${VERSION}'. Expected 'vX.Y.Z' (e.g. v0.3.0) or 'main-branch'." + exit 1 + fi + + - name: Checkout source + uses: actions/checkout@v6 + with: + ref: ${{ steps.target.outputs.ref }} + + - name: Install mdBook + uses: peaceiris/actions-mdbook@v2 + with: + mdbook-version: '0.5.2' + + - name: Build book + run: mdbook build docs + + - name: Deploy versioned docs + uses: peaceiris/actions-gh-pages@v4 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./docs/book + destination_dir: ${{ steps.target.outputs.destination }} + keep_files: true + + - name: Update versions.json and root redirect + if: steps.target.outputs.is_release == 'true' + run: | + git fetch origin gh-pages + git worktree add /tmp/gh-pages gh-pages + + chmod +x .github/scripts/generate-versions-json.sh + .github/scripts/generate-versions-json.sh /tmp/gh-pages /tmp/versions.json + + LATEST=$(python3 -c "import json; print(json.load(open('/tmp/versions.json'))['latest'])") + + chmod +x .github/scripts/generate-redirect.sh + .github/scripts/generate-redirect.sh "$LATEST" /tmp/root-index.html + + cp /tmp/versions.json /tmp/gh-pages/versions.json + cp /tmp/root-index.html /tmp/gh-pages/index.html + + cd /tmp/gh-pages + git add versions.json index.html + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git commit -m "Update versions.json and root redirect for ${{ inputs.version }}" \ + || echo "No changes to commit" + git push origin gh-pages + + cd - + git worktree remove /tmp/gh-pages diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index 4f8d9c3..0000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,47 +0,0 @@ ---- -name: Deploy Docs - -on: - push: - branches: [main] - -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: pages - cancel-in-progress: false - -jobs: - build: - name: Build Documentation - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - name: Install mdBook - uses: peaceiris/actions-mdbook@v2 - with: - mdbook-version: '0.5.2' - - - name: Build book - run: mdbook build docs - - - name: Upload artifact - uses: actions/upload-pages-artifact@v5 - with: - path: docs/book - - deploy: - name: Deploy to GitHub Pages - needs: build - runs-on: ubuntu-latest - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v5 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a511015..2568404 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,9 +35,17 @@ jobs: runs-on: ubuntu-latest environment: release steps: + - name: Generate app token + id: app-token + uses: actions/create-github-app-token@v3 + with: + client-id: ${{ secrets.RELEASE_APP_CLIENT_ID }} + private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} + - uses: actions/checkout@v6 with: fetch-depth: 0 + token: ${{ steps.app-token.outputs.token }} - name: Configure git identity run: | @@ -99,6 +107,41 @@ jobs: if: ${{ inputs.dry_run == false }} run: git push --follow-tags + docs: + name: Deploy Release Docs + needs: release + if: ${{ inputs.dry_run == false }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + ref: main + fetch-depth: 0 + + - name: Extract version from latest tag + id: version + run: | + VERSION=$(git tag -l 'lldap-operator@v*' \ + | sed 's/lldap-operator@//' \ + | sort -t. -k1,1rn -k2,2rn -k3,3rn \ + | head -1) + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "Detected version: $VERSION" + + - name: Trigger docs deployment + uses: actions/github-script@v7 + with: + script: | + await github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: 'deploy-docs.yml', + ref: 'main', + inputs: { + version: '${{ steps.version.outputs.version }}' + } + }); + docker: name: Build and Push Docker Image needs: release diff --git a/docs/book.toml b/docs/book.toml index b8b31d0..48f1588 100644 --- a/docs/book.toml +++ b/docs/book.toml @@ -10,6 +10,8 @@ git-repository-url = "https://github.com/lukidoescode/lldap-operator" edit-url-template = "https://github.com/lukidoescode/lldap-operator/edit/main/docs/{path}" default-theme = "navy" preferred-dark-theme = "navy" +additional-js = ["src/version-selector.js"] +additional-css = ["src/version-selector.css"] [output.html.search] enable = true diff --git a/docs/src/version-selector.css b/docs/src/version-selector.css new file mode 100644 index 0000000..22842f8 --- /dev/null +++ b/docs/src/version-selector.css @@ -0,0 +1,26 @@ +#version-selector { + appearance: none; + -webkit-appearance: none; + background-color: transparent; + border: 1px solid var(--sidebar-bg); + border-radius: 4px; + color: var(--icons); + cursor: pointer; + font-size: 0.85em; + margin-right: 8px; + padding: 4px 24px 4px 8px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 8px center; +} + +#version-selector:hover, +#version-selector:focus { + border-color: var(--icons-hover); + color: var(--icons-hover); +} + +#version-selector option { + background-color: var(--bg); + color: var(--fg); +} diff --git a/docs/src/version-selector.js b/docs/src/version-selector.js new file mode 100644 index 0000000..0306289 --- /dev/null +++ b/docs/src/version-selector.js @@ -0,0 +1,46 @@ +(function () { + 'use strict'; + + var pathSegments = window.location.pathname.split('/'); + var currentVersion = pathSegments[1] || ''; + var currentPage = pathSegments.slice(2).join('/'); + + fetch('/versions.json') + .then(function (res) { + if (!res.ok) throw new Error(res.status); + return res.json(); + }) + .then(function (data) { + var select = document.createElement('select'); + select.id = 'version-selector'; + select.setAttribute('aria-label', 'Select documentation version'); + + if (data.has_main) { + var mainOpt = document.createElement('option'); + mainOpt.value = 'main-branch'; + mainOpt.textContent = 'main (dev)'; + if (currentVersion === 'main-branch') mainOpt.selected = true; + select.appendChild(mainOpt); + } + + data.versions.forEach(function (v) { + var opt = document.createElement('option'); + opt.value = v; + opt.textContent = v + (v === data.latest ? ' (latest)' : ''); + if (currentVersion === v) opt.selected = true; + select.appendChild(opt); + }); + + select.addEventListener('change', function () { + window.location.href = '/' + this.value + '/' + currentPage; + }); + + var container = document.querySelector('.right-buttons'); + if (container) { + container.insertBefore(select, container.firstChild); + } + }) + .catch(function () { + // versions.json not available yet — no selector rendered + }); +})();