Skip to content

Commit 1914416

Browse files
committed
Implement automatic NAV generation
This change makes the navigation section of GW API website to be generated. The modification adds a new Go program that is able to transverse the GEP directory and generate a navigation section correctly from the existing GEPs. Additionally, some scripts and Makefiles are added to verify if the generated nav.yml file reflects the current state of GEPs, a Github Action that fails in case nav.yml is outdated
1 parent b9ef87e commit 1914416

File tree

10 files changed

+371
-127
lines changed

10 files changed

+371
-127
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Website Table of Content Validation
2+
3+
on:
4+
pull_request:
5+
types: [opened, edited, synchronize, reopened]
6+
7+
# Remove all permissions from GITHUB_TOKEN except metadata.
8+
permissions: {}
9+
10+
jobs:
11+
gep-validation:
12+
name: Verify if nav is updated
13+
runs-on: ubuntu-latest
14+
strategy:
15+
fail-fast: false
16+
steps:
17+
- name: Checkout code
18+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # tag=v4.2.2
19+
with:
20+
persist-credentials: false
21+
- name: Set up Go
22+
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # tag=v5.5.0
23+
- name: Run MKDocs nav Validation
24+
run: |
25+
make verify-mkdocs-nav

.yamllint.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ ignore: |
44
.github/
55
.golangci.yml
66
mkdocs.yml
7+
nav.yml
78
cloudbuild.yaml
89
config/crd
910

Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,14 @@ live-docs:
182182
docker build -t gw/mkdocs hack/mkdocs/image
183183
docker run --rm -it -p 3000:3000 -v ${PWD}:/docs gw/mkdocs
184184

185+
.PHONY: verify-mkdocs-nav
186+
verify-mkdocs-nav:
187+
hack/verify-mkdocs-nav.sh
188+
189+
.PHONY: update-mkdocs-nav
190+
update-mkdocs-nav:
191+
hack/update-mkdocs-nav.sh
192+
185193
.PHONY: api-ref-docs
186194
api-ref-docs:
187195
crd-ref-docs \

cmd/gepstoc/main.go

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import (
20+
"bytes"
21+
"flag"
22+
"fmt"
23+
"html/template"
24+
"io/fs"
25+
"log"
26+
"os"
27+
"path/filepath"
28+
"slices"
29+
"sort"
30+
"strconv"
31+
"strings"
32+
33+
"sigs.k8s.io/yaml"
34+
35+
gep "sigs.k8s.io/gateway-api/pkg/gep"
36+
)
37+
38+
var (
39+
GEPSDir string
40+
MKDocsTemplate string
41+
SkipGEPNumber string
42+
)
43+
44+
// Those are the GEPs that will be included in the final navigation bar
45+
// The order established below will be the order that the statuses will be shown
46+
var includeGEPStatus = []gep.GEPStatus{
47+
gep.GEPStatusImplementable,
48+
gep.GEPStatusExperimental,
49+
gep.GEPStatusStandard,
50+
gep.GEPStatusMemorandum,
51+
}
52+
53+
type GEPArray []GEPList
54+
55+
type GEPList struct {
56+
GepType string
57+
Geps []uint
58+
}
59+
60+
type TemplateData struct {
61+
GEPData GEPArray
62+
}
63+
64+
const kindDetails = "GEPDetails"
65+
66+
func main() {
67+
flag.StringVar(&GEPSDir, "g", "", "Defines the absolute path of the directory containing the GEPs")
68+
flag.StringVar(&MKDocsTemplate, "t", "", "Defines the absolute path of mkdocs.yaml file")
69+
flag.StringVar(&SkipGEPNumber, "s", "696", "Defines GEPs number to be skipped, should be comma-separated")
70+
flag.Parse()
71+
72+
if GEPSDir == "" || MKDocsTemplate == "" {
73+
log.Fatal("-g and -c are mandatory arguments")
74+
}
75+
76+
if strings.Contains(SkipGEPNumber, " ") {
77+
log.Fatal("-s flag should not contain spaces")
78+
}
79+
80+
skipGep := strings.Split(SkipGEPNumber, ",")
81+
82+
geps, err := walkGEPs(GEPSDir, skipGep)
83+
if err != nil {
84+
panic(err)
85+
}
86+
87+
tmpl, err := template.ParseFiles(MKDocsTemplate)
88+
if err != nil {
89+
log.Fatalf("error reading mkdocs template: %s", err)
90+
}
91+
92+
tmplData := TemplateData{
93+
GEPData: geps,
94+
}
95+
96+
buf := &bytes.Buffer{}
97+
98+
if err := tmpl.Execute(buf, tmplData); err != nil {
99+
panic(err)
100+
}
101+
fmt.Print(buf.String())
102+
}
103+
104+
func walkGEPs(dir string, skipGEPs []string) (GEPArray, error) {
105+
gepArray := make(GEPArray, 0)
106+
tmpMap := make(map[gep.GEPStatus]GEPList)
107+
108+
err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
109+
if err != nil {
110+
return fmt.Errorf("error accessing %s: %w", path, err)
111+
}
112+
if d.IsDir() || d.Name() != "metadata.yaml" {
113+
return nil
114+
}
115+
116+
content, err := os.ReadFile(path)
117+
if err != nil {
118+
return err
119+
}
120+
121+
gepDetail := &gep.GEPDetail{}
122+
log.Printf("checking %s", path)
123+
if err := yaml.Unmarshal(content, gepDetail); err != nil {
124+
return err
125+
}
126+
127+
if gepDetail.Kind != kindDetails {
128+
return nil
129+
}
130+
131+
// Skip the GEPs types we don't care
132+
if !slices.Contains(includeGEPStatus, gepDetail.Status) {
133+
return nil
134+
}
135+
136+
// Skip the GEPs numbers we don't care
137+
if slices.Contains(skipGEPs, strconv.FormatUint(uint64(gepDetail.Number), 10)) {
138+
return nil
139+
}
140+
141+
// Add the GEP to a map indexed by GEP types, so we can provide the sorted array
142+
// easily later
143+
_, ok := tmpMap[gepDetail.Status]
144+
if !ok {
145+
tmpMap[gepDetail.Status] = GEPList{
146+
GepType: string(gepDetail.Status),
147+
Geps: make([]uint, 0),
148+
}
149+
}
150+
151+
item := tmpMap[gepDetail.Status]
152+
item.Geps = append(item.Geps, gepDetail.Number)
153+
tmpMap[gepDetail.Status] = item
154+
return nil
155+
})
156+
if err != nil {
157+
return nil, err
158+
}
159+
160+
// Include the GEPs toc on the desired order
161+
for _, v := range includeGEPStatus {
162+
if geps, ok := tmpMap[v]; ok {
163+
gepArray = append(gepArray, geps)
164+
}
165+
}
166+
167+
for i := range gepArray {
168+
sort.SliceStable(gepArray[i].Geps, func(x, y int) bool {
169+
return gepArray[i].Geps[x] < gepArray[i].Geps[y]
170+
})
171+
}
172+
173+
return gepArray, nil
174+
}

hack/mkdocs/image/entrypoint.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ set -o errexit
1818

1919
CMD=$1
2020

21-
if [ "$CMD" == "build" ];
21+
if [ "$CMD" = "build" ];
2222
then
2323
mkdocs build
2424
exit 0;
2525
fi
2626

27-
mkdocs serve --dev-addr=0.0.0.0:3000 --livereload
27+
mkdocs serve --dev-addr=0.0.0.0:3000 --livereload

hack/update-mkdocs-nav.sh

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/bin/bash
2+
3+
# Copyright 2025 The Kubernetes Authors.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
set -o errexit
18+
set -o nounset
19+
set -o pipefail
20+
21+
SCRIPT_ROOT=$(dirname "${BASH_SOURCE}")/..
22+
23+
GEPS_TOC_SKIP="${GEPS_TOC_SKIP:-696}"
24+
NAV_CONF="${NAV_CONF:-${SCRIPT_ROOT}/nav.yml}"
25+
NAV_TEMPLATE="${NAV_TEMPLATE:-${NAV_CONF}.tmpl}"
26+
GEPS_TOC_DIR=${GEPS_TOC_DIR:-${SCRIPT_ROOT}/geps}
27+
28+
go run cmd/gepstoc/main.go -g "${GEPS_TOC_DIR}/" -t "${NAV_TEMPLATE}" -s "${GEPS_TOC_SKIP}" > "${NAV_CONF}"

hack/verify-all.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ if $SILENT ; then
5252
echo "Running in the silent mode, run with -v if you want to see script logs."
5353
fi
5454

55-
EXCLUDE="verify-all.sh"
55+
EXCLUDE="verify-all.sh verify-mkdocs-nav.sh"
5656

5757
SCRIPTS=$(find "${SCRIPT_ROOT}"/hack -name "verify-*.sh")
5858

hack/verify-mkdocs-nav.sh

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/bin/bash
2+
3+
# Copyright 2025 The Kubernetes Authors.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
set -o errexit
18+
set -o nounset
19+
set -o pipefail
20+
21+
SCRIPT_ROOT=$(dirname "${BASH_SOURCE}")/..
22+
23+
TMP_DIFFROOT="${SCRIPT_ROOT}/_tmp"
24+
TMP_FILE="${TMP_DIFFROOT}/nav.yml"
25+
26+
GEPS_TOC_SKIP="${GEPS_TOC_SKIP:-696}"
27+
NAV_CONF="${NAV_CONF:-${SCRIPT_ROOT}/nav.yml}"
28+
NAV_TEMPLATE="${NAV_TEMPLATE:-${NAV_CONF}.tmpl}"
29+
GEPS_TOC_DIR=${GEPS_TOC_DIR:-${SCRIPT_ROOT}/geps}
30+
31+
cleanup() {
32+
rm -rf "${TMP_DIFFROOT}"
33+
}
34+
trap "cleanup" EXIT SIGINT
35+
36+
cleanup
37+
38+
mkdir -p "${TMP_DIFFROOT}"
39+
40+
go run cmd/gepstoc/main.go -g "${GEPS_TOC_DIR}/" -t "${NAV_TEMPLATE}" -s "${GEPS_TOC_SKIP}" > "${TMP_FILE}"
41+
42+
echo "diffing ${NAV_CONF} against freshly generated configuration"
43+
ret=0
44+
diff -Naupr --no-dereference "${NAV_CONF}" "${TMP_FILE}" || ret=1
45+
46+
if [[ $ret -eq 0 ]]; then
47+
echo "${NAV_CONF} up to date."
48+
else
49+
echo "${NAV_CONF} is out of date. Please run hack/update-mkdocs-nav.sh"
50+
exit 1
51+
fi

0 commit comments

Comments
 (0)