From 62891b3eb7242524f696cb05650905078ba4d54d Mon Sep 17 00:00:00 2001 From: Safwan Date: Wed, 1 Jul 2026 11:29:57 +0500 Subject: [PATCH 1/5] added a workflow to generate api docs --- .../generate-crd-api-reference/README.md | 62 ++++++++++++++ .../generate-crd-api-reference/action.yml | 70 ++++++++++++++++ .../crd-ref-templates/gv_details.tpl | 68 +++++++++++++++ .../crd-ref-templates/gv_list.tpl | 15 ++++ .../crd-ref-templates/type.tpl | 49 +++++++++++ .../crd-ref-templates/type_members.tpl | 8 ++ .../generate-api-reference.sh | 60 +++++++++++++ .github/workflows/generate-api-reference.yaml | 84 +++++++++++++++++++ 8 files changed, 416 insertions(+) create mode 100644 .github/actions/generate-crd-api-reference/README.md create mode 100644 .github/actions/generate-crd-api-reference/action.yml create mode 100644 .github/actions/generate-crd-api-reference/crd-ref-templates/gv_details.tpl create mode 100644 .github/actions/generate-crd-api-reference/crd-ref-templates/gv_list.tpl create mode 100644 .github/actions/generate-crd-api-reference/crd-ref-templates/type.tpl create mode 100644 .github/actions/generate-crd-api-reference/crd-ref-templates/type_members.tpl create mode 100755 .github/actions/generate-crd-api-reference/generate-api-reference.sh create mode 100644 .github/workflows/generate-api-reference.yaml diff --git a/.github/actions/generate-crd-api-reference/README.md b/.github/actions/generate-crd-api-reference/README.md new file mode 100644 index 0000000..6fb5c7c --- /dev/null +++ b/.github/actions/generate-crd-api-reference/README.md @@ -0,0 +1,62 @@ +# generate-crd-api-reference + +Generate a CRD API-reference markdown page from an operator's Go types using +[`crd-ref-docs`](https://github.com/elastic/crd-ref-docs). Repo-agnostic; bundles +its own rendering templates and post-processing fixups. + +## Turnkey usage (reusable workflow → PR) + +In a docs repo, add `crd-ref-docs.yaml` (your operator's ignore rules) and a thin +caller workflow: + +```yaml +name: Generate API Reference +on: + workflow_dispatch: + inputs: + operator_ref: + description: "Operator git ref to generate from" + required: false + default: "main" +jobs: + generate: + uses: stakater/.github/.github/workflows/generate-api-reference.yaml@main + with: + operator_repo: https://github.com/stakater-ab/.git + operator_ref: ${{ github.event.inputs.operator_ref }} + api_path: api// + config_file: crd-ref-docs.yaml + output_file: content/reference/api.md + secrets: + PR_TOKEN: ${{ secrets.PUBLISH_TOKEN }} +``` + +## Direct action usage (custom triggers / PR policy) + +```yaml +- uses: actions/checkout@v5 +- uses: stakater/.github/.github/actions/generate-crd-api-reference@main + with: + operator_repo: https://github.com/stakater-ab/.git + api_path: api// + config_file: crd-ref-docs.yaml + output_file: content/reference/api.md +``` + +## Private operator repos + +The action clones `operator_repo` over HTTPS. For **private** operators, pass a +token-authenticated URL or pre-configure a git credential, e.g.: + +```yaml +operator_repo: https://x-access-token:${{ secrets.OPERATOR_TOKEN }}@github.com/stakater-ab/.git +``` + +## Notes + +- `api_path` may point at a single group (`api/group/v1alpha1`) or a parent + (`api/`) — `crd-ref-docs` recurses, so multi-group operators work unchanged. +- Two generic post-processing fixups keep output `mkdocs build --strict`-clean: + broken `map[string]Type` anchors are stripped to plain text, and a + `` line is prepended. +- Scratch (`.work/` locally, `RUNNER_TEMP` in CI) is never committed. diff --git a/.github/actions/generate-crd-api-reference/action.yml b/.github/actions/generate-crd-api-reference/action.yml new file mode 100644 index 0000000..5dd9b2e --- /dev/null +++ b/.github/actions/generate-crd-api-reference/action.yml @@ -0,0 +1,70 @@ +name: Generate CRD API Reference +description: > + Generate a CRD API-reference markdown page from an operator's Go types using + crd-ref-docs. Repo-agnostic; bundles its own rendering templates. + +inputs: + operator_repo: + description: "Git URL of the operator repository to read Go types from." + required: true + operator_ref: + description: "Operator git ref (branch/tag) to generate from." + required: false + default: "main" + api_path: + description: "Path within the operator repo to the API types (e.g. api/group/v1alpha1, or api/ for all groups)." + required: true + config_file: + description: "Path (in the caller checkout) to crd-ref-docs.yaml." + required: false + default: "crd-ref-docs.yaml" + output_file: + description: "Path (in the caller checkout) to write the generated markdown." + required: false + default: "content/reference/api.md" + crd_ref_docs_version: + description: "crd-ref-docs version to install." + required: false + default: "v0.3.0" + go_version: + description: "Go version used to build crd-ref-docs." + required: false + default: "1.26" + working_directory: + description: "Directory of the caller checkout that config_file/output_file are relative to." + required: false + default: "." + +outputs: + output_file: + description: "Absolute path to the generated markdown file." + value: ${{ steps.generate.outputs.output_file }} + +runs: + using: composite + steps: + - name: Setup Go + uses: actions/setup-go@v6 + with: + go-version: ${{ inputs.go_version }} + + - name: Generate API reference + id: generate + shell: bash + working-directory: ${{ inputs.working_directory }} + env: + OPERATOR_REPO: ${{ inputs.operator_repo }} + OPERATOR_REF: ${{ inputs.operator_ref }} + API_PATH: ${{ inputs.api_path }} + CRD_REF_DOCS_VERSION: ${{ inputs.crd_ref_docs_version }} + CONFIG_FILE_REL: ${{ inputs.config_file }} + OUTPUT_FILE_REL: ${{ inputs.output_file }} + ACTION_PATH: ${{ github.action_path }} + run: | + set -euo pipefail + export CONFIG_FILE="$(pwd)/$CONFIG_FILE_REL" + export OUTPUT_FILE="$(pwd)/$OUTPUT_FILE_REL" + export WORK_DIR="${RUNNER_TEMP:-$(pwd)/.work}/crd-ref" + # Script + templates live beside the action. + "$ACTION_PATH/generate-api-reference.sh" + echo "output_file=$OUTPUT_FILE" >> "$GITHUB_OUTPUT" diff --git a/.github/actions/generate-crd-api-reference/crd-ref-templates/gv_details.tpl b/.github/actions/generate-crd-api-reference/crd-ref-templates/gv_details.tpl new file mode 100644 index 0000000..0e39c44 --- /dev/null +++ b/.github/actions/generate-crd-api-reference/crd-ref-templates/gv_details.tpl @@ -0,0 +1,68 @@ +{{- /* + Render types in hierarchical order: each container followed by the types it + contains. Root kinds first in declared order; any leftover types not + reached by traversal at the end, alphabetical. +*/ -}} + +{{- define "renderHierarchy" -}} +{{- $args := . -}} +{{- $type := index $args 0 -}} +{{- $visited := index $args 1 -}} +{{- $typeNames := index $args 2 -}} +{{- $allTypes := index $args 3 -}} +{{- if not (hasKey $visited $type.Name) }} +{{- $_ := set $visited $type.Name true }} + +{{ template "type" $type }} +{{- range $type.Members }} +{{- if .Type }} + {{- $memberType := .Type }} + {{- if not (hasKey $typeNames $memberType.Name) }} + {{- if $memberType.UnderlyingType }}{{- $memberType = $memberType.UnderlyingType }}{{- end }} + {{- if and $memberType $memberType.UnderlyingType }} + {{- if not (hasKey $typeNames $memberType.Name) }} + {{- $memberType = $memberType.UnderlyingType }} + {{- end }} + {{- end }} + {{- end }} + {{- if and $memberType (hasKey $typeNames $memberType.Name) }} + {{- $resolved := index $allTypes $memberType.Name }} + {{- template "renderHierarchy" (list $resolved $visited $typeNames $allTypes) }} + {{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end -}} + +{{- define "gvDetails" -}} +{{- $gv := . -}} + +## {{ $gv.GroupVersionString }} + +{{ $gv.Doc }} + +{{- if $gv.Kinds }} +### Resource Types +{{- range $gv.SortedKinds }} +- {{ $gv.TypeForKind . | markdownRenderTypeLink }} +{{- end }} +{{ end }} + +{{- $typeNames := dict -}} +{{- range $gv.SortedTypes -}} +{{- $_ := set $typeNames .Name true -}} +{{- end -}} + +{{- $visited := dict -}} + +{{ range $gv.SortedKinds }} +{{- template "renderHierarchy" (list ($gv.TypeForKind .) $visited $typeNames $gv.Types) }} +{{- end }} + +{{- range $gv.SortedTypes }} +{{- if not (hasKey $visited .Name) }} +{{- template "renderHierarchy" (list . $visited $typeNames $gv.Types) }} +{{- end }} +{{- end }} + +{{- end -}} diff --git a/.github/actions/generate-crd-api-reference/crd-ref-templates/gv_list.tpl b/.github/actions/generate-crd-api-reference/crd-ref-templates/gv_list.tpl new file mode 100644 index 0000000..a4d3dad --- /dev/null +++ b/.github/actions/generate-crd-api-reference/crd-ref-templates/gv_list.tpl @@ -0,0 +1,15 @@ +{{- define "gvList" -}} +{{- $groupVersions := . -}} + +# API Reference + +## Packages +{{- range $groupVersions }} +- {{ markdownRenderGVLink . }} +{{- end }} + +{{ range $groupVersions }} +{{ template "gvDetails" . }} +{{ end }} + +{{- end -}} diff --git a/.github/actions/generate-crd-api-reference/crd-ref-templates/type.tpl b/.github/actions/generate-crd-api-reference/crd-ref-templates/type.tpl new file mode 100644 index 0000000..7d89d04 --- /dev/null +++ b/.github/actions/generate-crd-api-reference/crd-ref-templates/type.tpl @@ -0,0 +1,49 @@ +{{- define "type" -}} +{{- $type := . -}} +{{- if markdownShouldRenderType $type -}} + +#### {{ $type.Name }} + +{{ if $type.IsAlias }}_Underlying type:_ _{{ markdownRenderTypeLink $type.UnderlyingType }}_{{ end }} + +{{ $type.Doc }} + +{{ if $type.Validation -}} +_Validation:_ +{{- range $type.Validation }} +- {{ . }} +{{- end }} +{{- end }} + +{{ if $type.References -}} +_Appears in:_ +{{- range $type.SortedReferences }} +- {{ markdownRenderTypeLink . }} +{{- end }} +{{- end }} + +{{ if $type.Members -}} +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +{{ if $type.GVK -}} +| `apiVersion` _string_ | `{{ $type.GVK.Group }}/{{ $type.GVK.Version }}` | | | +| `kind` _string_ | `{{ $type.GVK.Kind }}` | | | +{{ end -}} + +{{ range $type.Members -}} +| `{{ .Name }}` _{{ markdownRenderType .Type }}_ | {{ template "type_members" . }} | {{ markdownRenderDefault .Default }} | {{ range .Validation -}} {{ markdownRenderFieldDoc . }}
{{ end }} | +{{ end -}} + +{{ end -}} + +{{ if $type.EnumValues -}} +| Field | Description | +| --- | --- | +{{ range $type.EnumValues -}} +| `{{ .Name }}` | {{ markdownRenderFieldDoc .Doc }} | +{{ end -}} +{{ end -}} + + +{{- end -}} +{{- end -}} diff --git a/.github/actions/generate-crd-api-reference/crd-ref-templates/type_members.tpl b/.github/actions/generate-crd-api-reference/crd-ref-templates/type_members.tpl new file mode 100644 index 0000000..041758a --- /dev/null +++ b/.github/actions/generate-crd-api-reference/crd-ref-templates/type_members.tpl @@ -0,0 +1,8 @@ +{{- define "type_members" -}} +{{- $field := . -}} +{{- if eq $field.Name "metadata" -}} +Refer to Kubernetes API documentation for fields of `metadata`. +{{- else -}} +{{ markdownRenderFieldDoc $field.Doc }} +{{- end -}} +{{- end -}} diff --git a/.github/actions/generate-crd-api-reference/generate-api-reference.sh b/.github/actions/generate-crd-api-reference/generate-api-reference.sh new file mode 100755 index 0000000..e7e9ef7 --- /dev/null +++ b/.github/actions/generate-crd-api-reference/generate-api-reference.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Generic CRD API-reference generator. Driven entirely by env vars so it can be +# invoked from a composite action, a reusable workflow, or locally. No +# operator-specific defaults — required values must be set by the caller. + +# --- Required --- +: "${OPERATOR_REPO:?OPERATOR_REPO is required (e.g. https://github.com/org/operator.git)}" +: "${API_PATH:?API_PATH is required (e.g. api/group.example.com/v1alpha1, or api/ for all groups)}" +: "${OUTPUT_FILE:?OUTPUT_FILE is required (absolute path to the markdown to write)}" +: "${CONFIG_FILE:?CONFIG_FILE is required (absolute path to crd-ref-docs.yaml)}" + +# --- Optional (with defaults) --- +OPERATOR_REF="${OPERATOR_REF:-main}" +CRD_REF_DOCS_VERSION="${CRD_REF_DOCS_VERSION:-v0.3.0}" + +# Templates ALWAYS resolved relative to this script so they travel with it. +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +TEMPLATES_DIR="${TEMPLATES_DIR:-$SCRIPT_DIR/crd-ref-templates}" + +# Scratch area (tool binary + operator clone); never committed. +WORK_DIR="${WORK_DIR:-$PWD/.work}" +LOCALBIN="$WORK_DIR/bin" +CRD_REF_DOCS="$LOCALBIN/crd-ref-docs" + +mkdir -p "$LOCALBIN" "$(dirname "$OUTPUT_FILE")" + +# 1. Install crd-ref-docs (pinned) +if [ ! -x "$CRD_REF_DOCS" ]; then + echo "Installing crd-ref-docs $CRD_REF_DOCS_VERSION..." + GOBIN="$LOCALBIN" go install "github.com/elastic/crd-ref-docs@$CRD_REF_DOCS_VERSION" +fi + +# 2. Clone / update the operator repo at the requested ref +if [ -d "$WORK_DIR/operator/.git" ]; then + git -C "$WORK_DIR/operator" fetch origin "$OPERATOR_REF" + git -C "$WORK_DIR/operator" checkout "$OPERATOR_REF" + git -C "$WORK_DIR/operator" reset --hard "origin/$OPERATOR_REF" +else + git clone --depth=20 --branch "$OPERATOR_REF" "$OPERATOR_REPO" "$WORK_DIR/operator" +fi + +# 3. Generate the reference +"$CRD_REF_DOCS" \ + --source-path="$WORK_DIR/operator/$API_PATH" \ + --config="$CONFIG_FILE" \ + --templates-dir="$TEMPLATES_DIR" \ + --renderer=markdown \ + --output-path="$OUTPUT_FILE" + +# 4. Post-process (generic fixups for mkdocs strict builds) +# 4a. crd-ref-docs renders nested `map[string]Type` values as a link whose +# anchor contains brackets (`[map[string]X](#map[string]x)`) — an invalid +# anchor that fails `mkdocs build --strict`. Strip the link, keep the text. +perl -i -pe 's/\[(map\[string\][A-Za-z0-9]+)\]\(#map\[string\][a-z0-9]+\)/$1/g' "$OUTPUT_FILE" +# 4b. Silence markdownlint on the generated file. +sed -i '1i ' "$OUTPUT_FILE" + +echo "Wrote $OUTPUT_FILE (OPERATOR_REPO=$OPERATOR_REPO, OPERATOR_REF=$OPERATOR_REF, API_PATH=$API_PATH)" diff --git a/.github/workflows/generate-api-reference.yaml b/.github/workflows/generate-api-reference.yaml new file mode 100644 index 0000000..fca24d5 --- /dev/null +++ b/.github/workflows/generate-api-reference.yaml @@ -0,0 +1,84 @@ +name: Generate API Reference + +on: + workflow_call: + inputs: + operator_repo: + type: string + required: true + operator_ref: + type: string + required: false + default: "main" + api_path: + type: string + required: true + config_file: + type: string + required: false + default: "crd-ref-docs.yaml" + output_file: + type: string + required: false + default: "content/reference/api.md" + crd_ref_docs_version: + type: string + required: false + default: "v0.3.0" + go_version: + type: string + required: false + default: "1.26" + pr_branch: + type: string + required: false + default: "chore/regenerate-api-reference" + secrets: + PR_TOKEN: + required: true + +jobs: + generate: + runs-on: ubuntu-latest + steps: + - name: Checkout caller repo + uses: actions/checkout@v5 + + - name: Generate CRD API reference + uses: stakater/.github/.github/actions/generate-crd-api-reference@main + with: + operator_repo: ${{ inputs.operator_repo }} + operator_ref: ${{ inputs.operator_ref }} + api_path: ${{ inputs.api_path }} + config_file: ${{ inputs.config_file }} + output_file: ${{ inputs.output_file }} + crd_ref_docs_version: ${{ inputs.crd_ref_docs_version }} + go_version: ${{ inputs.go_version }} + + - name: Create Pull Request + env: + GH_TOKEN: ${{ secrets.PR_TOKEN }} + OUTPUT_FILE: ${{ inputs.output_file }} + OPERATOR_REPO: ${{ inputs.operator_repo }} + OPERATOR_REF: ${{ inputs.operator_ref }} + PR_BRANCH: ${{ inputs.pr_branch }} + run: | + set -euo pipefail + # No diff -> nothing to PR. + if git diff --quiet -- "$OUTPUT_FILE"; then + echo "No changes in $OUTPUT_FILE; skipping PR." + exit 0 + fi + git config user.name "stakater-github-bot" + git config user.email "github-bot@stakater.com" + git checkout -B "$PR_BRANCH" + git add "$OUTPUT_FILE" + git commit -m "docs: regenerate API reference ($OPERATOR_REF)" + git push --force-with-lease origin "$PR_BRANCH" + # Create the PR only if one isn't already open for this branch. + if [ -z "$(gh pr list --head "$PR_BRANCH" --state open --json number -q '.[].number')" ]; then + gh pr create \ + --title "docs: regenerate API reference" \ + --body "Regenerated \`$OUTPUT_FILE\` from \`$OPERATOR_REPO@$OPERATOR_REF\`." \ + --head "$PR_BRANCH" + fi From 4031391e535e8c299b09d10abd269f06ba387f96 Mon Sep 17 00:00:00 2001 From: Safwan Date: Wed, 1 Jul 2026 11:42:16 +0500 Subject: [PATCH 2/5] handling for private repos --- .../generate-crd-api-reference/README.md | 28 +++++++++++++++++-- .github/workflows/generate-api-reference.yaml | 13 +++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/.github/actions/generate-crd-api-reference/README.md b/.github/actions/generate-crd-api-reference/README.md index 6fb5c7c..3f31ec7 100644 --- a/.github/actions/generate-crd-api-reference/README.md +++ b/.github/actions/generate-crd-api-reference/README.md @@ -45,11 +45,33 @@ jobs: ## Private operator repos -The action clones `operator_repo` over HTTPS. For **private** operators, pass a -token-authenticated URL or pre-configure a git credential, e.g.: +The action clones `operator_repo` over HTTPS. For **private** `stakater-ab` +operators, set `ENABLE_STAKATER_AB_GIT_AUTH: true` and pass the +`STAKATER_AB_REPOS` token secret — the reusable workflow then configures git +(`url.insteadOf`) so the plain `https://github.com/stakater-ab/.git` +URL clones with auth. This matches the pattern used by the operator +build/push workflows. ```yaml -operator_repo: https://x-access-token:${{ secrets.OPERATOR_TOKEN }}@github.com/stakater-ab/.git +jobs: + generate: + uses: stakater/.github/.github/workflows/generate-api-reference.yaml@main + with: + operator_repo: https://github.com/stakater-ab/.git + api_path: api// + ENABLE_STAKATER_AB_GIT_AUTH: true + secrets: + PR_TOKEN: ${{ secrets.PUBLISH_TOKEN }} + STAKATER_AB_REPOS: ${{ secrets.STAKATER_AB_REPOS }} +``` + +When calling the **action** directly (not via the reusable workflow), add the +equivalent git-config step before the action in your own workflow: + +```yaml +- name: Configure private repo + run: | + git config --global url."https://${{ secrets.STAKATER_AB_REPOS }}:x-oauth-basic@github.com/stakater-ab".insteadOf "https://github.com/stakater-ab" ``` ## Notes diff --git a/.github/workflows/generate-api-reference.yaml b/.github/workflows/generate-api-reference.yaml index fca24d5..7e821ea 100644 --- a/.github/workflows/generate-api-reference.yaml +++ b/.github/workflows/generate-api-reference.yaml @@ -33,9 +33,17 @@ on: type: string required: false default: "chore/regenerate-api-reference" + ENABLE_STAKATER_AB_GIT_AUTH: + description: "Configure git to clone stakater-ab private repos" + required: false + default: false + type: boolean secrets: PR_TOKEN: required: true + STAKATER_AB_REPOS: + description: "GitHub token with access to stakater-ab repos" + required: false jobs: generate: @@ -44,6 +52,11 @@ jobs: - name: Checkout caller repo uses: actions/checkout@v5 + - name: Configure private repo + if: ${{ inputs.ENABLE_STAKATER_AB_GIT_AUTH }} + run: | + git config --global url."https://${{ secrets.STAKATER_AB_REPOS }}:x-oauth-basic@github.com/stakater-ab".insteadOf "https://github.com/stakater-ab" + - name: Generate CRD API reference uses: stakater/.github/.github/actions/generate-crd-api-reference@main with: From c3767ff1e488fad3e3dd3d96556749d671ef8d8c Mon Sep 17 00:00:00 2001 From: Safwan Date: Wed, 1 Jul 2026 13:00:45 +0500 Subject: [PATCH 3/5] correct branch name --- .github/workflows/generate-api-reference.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generate-api-reference.yaml b/.github/workflows/generate-api-reference.yaml index 7e821ea..c13bc3d 100644 --- a/.github/workflows/generate-api-reference.yaml +++ b/.github/workflows/generate-api-reference.yaml @@ -58,7 +58,7 @@ jobs: git config --global url."https://${{ secrets.STAKATER_AB_REPOS }}:x-oauth-basic@github.com/stakater-ab".insteadOf "https://github.com/stakater-ab" - name: Generate CRD API reference - uses: stakater/.github/.github/actions/generate-crd-api-reference@main + uses: stakater/.github/.github/actions/generate-crd-api-reference@feat/api-gen-workflow with: operator_repo: ${{ inputs.operator_repo }} operator_ref: ${{ inputs.operator_ref }} From 0e9de9bf114d77ff71dd5cb1bcf3c25d880dfe07 Mon Sep 17 00:00:00 2001 From: Safwan Date: Wed, 1 Jul 2026 17:33:46 +0500 Subject: [PATCH 4/5] invert the strategy --- .../generate-crd-api-reference/README.md | 86 +++++++++---------- .../generate-crd-api-reference/action.yml | 28 +++--- .../generate-api-reference.sh | 35 +++----- .github/workflows/generate-api-reference.yaml | 55 ++++++------ 4 files changed, 94 insertions(+), 110 deletions(-) diff --git a/.github/actions/generate-crd-api-reference/README.md b/.github/actions/generate-crd-api-reference/README.md index 3f31ec7..99509ee 100644 --- a/.github/actions/generate-crd-api-reference/README.md +++ b/.github/actions/generate-crd-api-reference/README.md @@ -1,82 +1,76 @@ # generate-crd-api-reference -Generate a CRD API-reference markdown page from an operator's Go types using -[`crd-ref-docs`](https://github.com/elastic/crd-ref-docs). Repo-agnostic; bundles -its own rendering templates and post-processing fixups. +Render a CRD API-reference markdown page from a **local** path of an operator's +Go types using [`crd-ref-docs`](https://github.com/elastic/crd-ref-docs). +Repo-agnostic; bundles its own rendering templates and post-processing fixups. +It does **not** clone — checking out sources is the caller's job. -## Turnkey usage (reusable workflow → PR) +## Model -In a docs repo, add `crd-ref-docs.yaml` (your operator's ignore rules) and a thin -caller workflow: +The generator runs in the **operator** repo's CI: it reads the operator's own +Go types locally (no cross-repo read of private operator code), renders the +reference, and opens a PR in the **docs** repo. The only cross-repo access +needed is **write** to the docs repo, via `DOCS_TOKEN` (the operator's existing +broad-access token, e.g. `STAKATER_AB_REPOS`). + +## Turnkey usage (reusable workflow → PR in the docs repo) + +Add `crd-ref-docs.yaml` (your operator's ignore rules) to the **operator** repo +and a caller workflow: ```yaml name: Generate API Reference on: + release: + types: [published] workflow_dispatch: - inputs: - operator_ref: - description: "Operator git ref to generate from" - required: false - default: "main" jobs: generate: uses: stakater/.github/.github/workflows/generate-api-reference.yaml@main with: - operator_repo: https://github.com/stakater-ab/.git - operator_ref: ${{ github.event.inputs.operator_ref }} - api_path: api// + docs_repo: stakater/-docs + source_path: api// config_file: crd-ref-docs.yaml output_file: content/reference/api.md secrets: - PR_TOKEN: ${{ secrets.PUBLISH_TOKEN }} + DOCS_TOKEN: ${{ secrets.STAKATER_AB_REPOS }} ``` -## Direct action usage (custom triggers / PR policy) +## Direct action usage (render only, custom delivery) + +Check out whatever you need, then point the action at a local `source_path`: ```yaml -- uses: actions/checkout@v5 +- uses: actions/checkout@v5 # the operator repo — has the Go types + config - uses: stakater/.github/.github/actions/generate-crd-api-reference@main with: - operator_repo: https://github.com/stakater-ab/.git - api_path: api// + source_path: api// config_file: crd-ref-docs.yaml output_file: content/reference/api.md ``` -## Private operator repos +The action renders `output_file` from the local `source_path`; committing, +pushing, and PR policy are up to your workflow. -The action clones `operator_repo` over HTTPS. For **private** `stakater-ab` -operators, set `ENABLE_STAKATER_AB_GIT_AUTH: true` and pass the -`STAKATER_AB_REPOS` token secret — the reusable workflow then configures git -(`url.insteadOf`) so the plain `https://github.com/stakater-ab/.git` -URL clones with auth. This matches the pattern used by the operator -build/push workflows. +## Run locally -```yaml -jobs: - generate: - uses: stakater/.github/.github/workflows/generate-api-reference.yaml@main - with: - operator_repo: https://github.com/stakater-ab/.git - api_path: api// - ENABLE_STAKATER_AB_GIT_AUTH: true - secrets: - PR_TOKEN: ${{ secrets.PUBLISH_TOKEN }} - STAKATER_AB_REPOS: ${{ secrets.STAKATER_AB_REPOS }} -``` +```bash +git clone --depth 1 https://github.com/stakater/.github /tmp/stakater-github +ACTION=/tmp/stakater-github/.github/actions/generate-crd-api-reference -When calling the **action** directly (not via the reusable workflow), add the -equivalent git-config step before the action in your own workflow: - -```yaml -- name: Configure private repo - run: | - git config --global url."https://${{ secrets.STAKATER_AB_REPOS }}:x-oauth-basic@github.com/stakater-ab".insteadOf "https://github.com/stakater-ab" +# from your operator repo root +SOURCE_PATH="$PWD/api//" \ +CONFIG_FILE="$PWD/crd-ref-docs.yaml" \ +OUTPUT_FILE="$PWD/api.md" \ +bash "$ACTION/generate-api-reference.sh" ``` +Needs `go` and `perl` on PATH. Templates resolve automatically (sibling of the +script). + ## Notes -- `api_path` may point at a single group (`api/group/v1alpha1`) or a parent +- `source_path` may point at a single group (`api/group/v1alpha1`) or a parent (`api/`) — `crd-ref-docs` recurses, so multi-group operators work unchanged. - Two generic post-processing fixups keep output `mkdocs build --strict`-clean: broken `map[string]Type` anchors are stripped to plain text, and a diff --git a/.github/actions/generate-crd-api-reference/action.yml b/.github/actions/generate-crd-api-reference/action.yml index 5dd9b2e..8b08cbc 100644 --- a/.github/actions/generate-crd-api-reference/action.yml +++ b/.github/actions/generate-crd-api-reference/action.yml @@ -1,25 +1,20 @@ name: Generate CRD API Reference description: > - Generate a CRD API-reference markdown page from an operator's Go types using - crd-ref-docs. Repo-agnostic; bundles its own rendering templates. + Render a CRD API-reference markdown page from a LOCAL path of an operator's Go + types using crd-ref-docs. Repo-agnostic; bundles its own rendering templates. + Checkout of source repos is the caller's responsibility — this action does not + clone. inputs: - operator_repo: - description: "Git URL of the operator repository to read Go types from." - required: true - operator_ref: - description: "Operator git ref (branch/tag) to generate from." - required: false - default: "main" - api_path: - description: "Path within the operator repo to the API types (e.g. api/group/v1alpha1, or api/ for all groups)." + source_path: + description: "Path (in the checkout) to the API types (e.g. api/group/v1alpha1, or api/ for all groups)." required: true config_file: - description: "Path (in the caller checkout) to crd-ref-docs.yaml." + description: "Path (in the checkout) to crd-ref-docs.yaml." required: false default: "crd-ref-docs.yaml" output_file: - description: "Path (in the caller checkout) to write the generated markdown." + description: "Path (in the checkout) to write the generated markdown." required: false default: "content/reference/api.md" crd_ref_docs_version: @@ -31,7 +26,7 @@ inputs: required: false default: "1.26" working_directory: - description: "Directory of the caller checkout that config_file/output_file are relative to." + description: "Directory that source_path/config_file/output_file are relative to." required: false default: "." @@ -53,15 +48,14 @@ runs: shell: bash working-directory: ${{ inputs.working_directory }} env: - OPERATOR_REPO: ${{ inputs.operator_repo }} - OPERATOR_REF: ${{ inputs.operator_ref }} - API_PATH: ${{ inputs.api_path }} + SOURCE_PATH_REL: ${{ inputs.source_path }} CRD_REF_DOCS_VERSION: ${{ inputs.crd_ref_docs_version }} CONFIG_FILE_REL: ${{ inputs.config_file }} OUTPUT_FILE_REL: ${{ inputs.output_file }} ACTION_PATH: ${{ github.action_path }} run: | set -euo pipefail + export SOURCE_PATH="$(pwd)/$SOURCE_PATH_REL" export CONFIG_FILE="$(pwd)/$CONFIG_FILE_REL" export OUTPUT_FILE="$(pwd)/$OUTPUT_FILE_REL" export WORK_DIR="${RUNNER_TEMP:-$(pwd)/.work}/crd-ref" diff --git a/.github/actions/generate-crd-api-reference/generate-api-reference.sh b/.github/actions/generate-crd-api-reference/generate-api-reference.sh index e7e9ef7..7752b88 100755 --- a/.github/actions/generate-crd-api-reference/generate-api-reference.sh +++ b/.github/actions/generate-crd-api-reference/generate-api-reference.sh @@ -1,25 +1,25 @@ #!/usr/bin/env bash set -euo pipefail -# Generic CRD API-reference generator. Driven entirely by env vars so it can be -# invoked from a composite action, a reusable workflow, or locally. No -# operator-specific defaults — required values must be set by the caller. +# Generic CRD API-reference renderer. Renders markdown from a LOCAL path of Go +# API types using crd-ref-docs, then applies generic post-processing. Checkout +# of any source repos is the caller's responsibility — this script never clones. +# Driven entirely by env vars so it can run from a composite action, a reusable +# workflow, or locally. # --- Required --- -: "${OPERATOR_REPO:?OPERATOR_REPO is required (e.g. https://github.com/org/operator.git)}" -: "${API_PATH:?API_PATH is required (e.g. api/group.example.com/v1alpha1, or api/ for all groups)}" +: "${SOURCE_PATH:?SOURCE_PATH is required (local path to the API types, e.g. api/group.example.com/v1alpha1, or api/ for all groups)}" : "${OUTPUT_FILE:?OUTPUT_FILE is required (absolute path to the markdown to write)}" : "${CONFIG_FILE:?CONFIG_FILE is required (absolute path to crd-ref-docs.yaml)}" # --- Optional (with defaults) --- -OPERATOR_REF="${OPERATOR_REF:-main}" CRD_REF_DOCS_VERSION="${CRD_REF_DOCS_VERSION:-v0.3.0}" # Templates ALWAYS resolved relative to this script so they travel with it. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" TEMPLATES_DIR="${TEMPLATES_DIR:-$SCRIPT_DIR/crd-ref-templates}" -# Scratch area (tool binary + operator clone); never committed. +# Scratch area (tool binary only); never committed. WORK_DIR="${WORK_DIR:-$PWD/.work}" LOCALBIN="$WORK_DIR/bin" CRD_REF_DOCS="$LOCALBIN/crd-ref-docs" @@ -32,29 +32,20 @@ if [ ! -x "$CRD_REF_DOCS" ]; then GOBIN="$LOCALBIN" go install "github.com/elastic/crd-ref-docs@$CRD_REF_DOCS_VERSION" fi -# 2. Clone / update the operator repo at the requested ref -if [ -d "$WORK_DIR/operator/.git" ]; then - git -C "$WORK_DIR/operator" fetch origin "$OPERATOR_REF" - git -C "$WORK_DIR/operator" checkout "$OPERATOR_REF" - git -C "$WORK_DIR/operator" reset --hard "origin/$OPERATOR_REF" -else - git clone --depth=20 --branch "$OPERATOR_REF" "$OPERATOR_REPO" "$WORK_DIR/operator" -fi - -# 3. Generate the reference +# 2. Generate the reference from the local source path "$CRD_REF_DOCS" \ - --source-path="$WORK_DIR/operator/$API_PATH" \ + --source-path="$SOURCE_PATH" \ --config="$CONFIG_FILE" \ --templates-dir="$TEMPLATES_DIR" \ --renderer=markdown \ --output-path="$OUTPUT_FILE" -# 4. Post-process (generic fixups for mkdocs strict builds) -# 4a. crd-ref-docs renders nested `map[string]Type` values as a link whose +# 3. Post-process (generic fixups for mkdocs strict builds) +# 3a. crd-ref-docs renders nested `map[string]Type` values as a link whose # anchor contains brackets (`[map[string]X](#map[string]x)`) — an invalid # anchor that fails `mkdocs build --strict`. Strip the link, keep the text. perl -i -pe 's/\[(map\[string\][A-Za-z0-9]+)\]\(#map\[string\][a-z0-9]+\)/$1/g' "$OUTPUT_FILE" -# 4b. Silence markdownlint on the generated file. +# 3b. Silence markdownlint on the generated file. sed -i '1i ' "$OUTPUT_FILE" -echo "Wrote $OUTPUT_FILE (OPERATOR_REPO=$OPERATOR_REPO, OPERATOR_REF=$OPERATOR_REF, API_PATH=$API_PATH)" +echo "Wrote $OUTPUT_FILE (SOURCE_PATH=$SOURCE_PATH)" diff --git a/.github/workflows/generate-api-reference.yaml b/.github/workflows/generate-api-reference.yaml index c13bc3d..99f8af5 100644 --- a/.github/workflows/generate-api-reference.yaml +++ b/.github/workflows/generate-api-reference.yaml @@ -1,23 +1,33 @@ name: Generate API Reference +# Runs in the OPERATOR repo's context: reads the operator's own Go types locally +# (no cross-repo clone of private operator code), renders the API reference, and +# opens a PR in the docs repo. The only cross-repo access needed is WRITE to the +# docs repo, via DOCS_TOKEN. + on: workflow_call: inputs: - operator_repo: + docs_repo: + description: "Docs repository to write the reference into (owner/name)." type: string required: true - operator_ref: + docs_ref: + description: "Docs repo ref to base the PR on." type: string required: false default: "main" - api_path: + source_path: + description: "Path in THIS (operator) repo to the API types (e.g. api/group/v1alpha1)." type: string required: true config_file: + description: "Path in THIS (operator) repo to crd-ref-docs.yaml." type: string required: false default: "crd-ref-docs.yaml" output_file: + description: "Path in the DOCS repo to write the generated markdown." type: string required: false default: "content/reference/api.md" @@ -33,47 +43,42 @@ on: type: string required: false default: "chore/regenerate-api-reference" - ENABLE_STAKATER_AB_GIT_AUTH: - description: "Configure git to clone stakater-ab private repos" - required: false - default: false - type: boolean secrets: - PR_TOKEN: + DOCS_TOKEN: + description: "Token with write access to the docs repo (checkout + PR)." required: true - STAKATER_AB_REPOS: - description: "GitHub token with access to stakater-ab repos" - required: false jobs: generate: runs-on: ubuntu-latest steps: - - name: Checkout caller repo + - name: Checkout operator repo uses: actions/checkout@v5 - - name: Configure private repo - if: ${{ inputs.ENABLE_STAKATER_AB_GIT_AUTH }} - run: | - git config --global url."https://${{ secrets.STAKATER_AB_REPOS }}:x-oauth-basic@github.com/stakater-ab".insteadOf "https://github.com/stakater-ab" + - name: Checkout docs repo + uses: actions/checkout@v5 + with: + repository: ${{ inputs.docs_repo }} + ref: ${{ inputs.docs_ref }} + token: ${{ secrets.DOCS_TOKEN }} + path: docs-repo - name: Generate CRD API reference uses: stakater/.github/.github/actions/generate-crd-api-reference@feat/api-gen-workflow with: - operator_repo: ${{ inputs.operator_repo }} - operator_ref: ${{ inputs.operator_ref }} - api_path: ${{ inputs.api_path }} + source_path: ${{ inputs.source_path }} config_file: ${{ inputs.config_file }} - output_file: ${{ inputs.output_file }} + output_file: docs-repo/${{ inputs.output_file }} crd_ref_docs_version: ${{ inputs.crd_ref_docs_version }} go_version: ${{ inputs.go_version }} - name: Create Pull Request + working-directory: docs-repo env: - GH_TOKEN: ${{ secrets.PR_TOKEN }} + GH_TOKEN: ${{ secrets.DOCS_TOKEN }} OUTPUT_FILE: ${{ inputs.output_file }} - OPERATOR_REPO: ${{ inputs.operator_repo }} - OPERATOR_REF: ${{ inputs.operator_ref }} + OPERATOR_REPO: ${{ github.repository }} + OPERATOR_REF: ${{ github.ref_name }} PR_BRANCH: ${{ inputs.pr_branch }} run: | set -euo pipefail @@ -86,7 +91,7 @@ jobs: git config user.email "github-bot@stakater.com" git checkout -B "$PR_BRANCH" git add "$OUTPUT_FILE" - git commit -m "docs: regenerate API reference ($OPERATOR_REF)" + git commit -m "docs: regenerate API reference from $OPERATOR_REPO@$OPERATOR_REF" git push --force-with-lease origin "$PR_BRANCH" # Create the PR only if one isn't already open for this branch. if [ -z "$(gh pr list --head "$PR_BRANCH" --state open --json number -q '.[].number')" ]; then From 221995255cd20aa1056c169df95b2bb46172674f Mon Sep 17 00:00:00 2001 From: Safwan Date: Wed, 1 Jul 2026 20:46:14 +0500 Subject: [PATCH 5/5] fix for non existent file --- .github/workflows/generate-api-reference.yaml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/generate-api-reference.yaml b/.github/workflows/generate-api-reference.yaml index 99f8af5..6fe73ba 100644 --- a/.github/workflows/generate-api-reference.yaml +++ b/.github/workflows/generate-api-reference.yaml @@ -82,17 +82,20 @@ jobs: PR_BRANCH: ${{ inputs.pr_branch }} run: | set -euo pipefail - # No diff -> nothing to PR. - if git diff --quiet -- "$OUTPUT_FILE"; then - echo "No changes in $OUTPUT_FILE; skipping PR." - exit 0 - fi git config user.name "stakater-github-bot" git config user.email "github-bot@stakater.com" git checkout -B "$PR_BRANCH" + # Stage first so a brand-new (untracked) file counts as a change too; + # `git diff --cached` then covers add/modify/delete uniformly. git add "$OUTPUT_FILE" + if git diff --cached --quiet -- "$OUTPUT_FILE"; then + echo "No changes in $OUTPUT_FILE; skipping PR." + exit 0 + fi git commit -m "docs: regenerate API reference from $OPERATOR_REPO@$OPERATOR_REF" - git push --force-with-lease origin "$PR_BRANCH" + # Dedicated bot branch: force-push (shallow single-branch checkout has + # no remote-tracking ref for a lease, so --force-with-lease is unsafe here). + git push --force origin "$PR_BRANCH" # Create the PR only if one isn't already open for this branch. if [ -z "$(gh pr list --head "$PR_BRANCH" --state open --json number -q '.[].number')" ]; then gh pr create \