mirror of
https://github.com/coder/coder.git
synced 2026-09-23 05:43:53 +08:00
## What `clidocgen` and the new `configdocgen` (coder/coder#26824) both carried a byte-identical `prepareEnv()` that unsets `CODER_*` and pins `CLIDOCGEN_*` / `TMPDIR` so generated docs don't embed the generating host's home directory. This extracts it to `scripts/docgenenv.Prepare()` and migrates `clidocgen`. ## Why Duplication flagged during review of #26824. `configdocgen` adopts the shared helper in that PR, removing its copy. ## Risk Behavior-preserving: regenerating the CLI reference (`make docs/reference/cli/index.md`) yields no diff, and `make pre-commit` passes (`lint/go`, `lint/ts`, `build`). A focused unit test pins the `Prepare()` contract, and `_test.go` files are excluded from `CLIDOCGEN_INPUTS` so test edits don't mark the generated docs stale. <details> <summary>CI status — blocked by an unrelated <code>main</code> breakage (#24993)</summary> All red checks on this PR are inherited from `main`, not caused by these changes. This PR touches only `Makefile` and `scripts/{clidocgen,docgenenv}`; it does not touch Helm. `main` went red at `d0f68cb9b0` ("feat: add listenerset", #24993, merged ~18:26 UTC). The committed `helm/coder/tests/testdata/listenerset*.golden` files don't match what `helm template` renders, so: - **`gen`** regenerates those goldens, and the unstaged-files check fails. - **`test-go-pg` (ubuntu-latest, pg-17) and `test-go-race-pg`** fail only on `TestRenderChart/{coder,default}/listenerset[_redirect]` (golden mismatch; the test prints "Run with -update to update golden files"). The same `test-go-pg` job passes on macOS and Windows, where the Helm render test is skipped, and `scripts/docgenenv` reports `ok` on the failing runners. Base commit `14a61041d9` was green; `main` is red from `d0f68cb9b0` onward. These checks clear once `main` is fixed and this branch is updated. `fmt`, `lint`, `Storybook`, `check-build`, and `test-e2e` are green. </details> --- 🤖 Opened by Coder Agents on behalf of @nickvigilante. --------- Co-authored-by: Cian Johnston <cian@coder.com>
176 lines
3.8 KiB
Go
176 lines
3.8 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
|
|
"github.com/coder/coder/v2/enterprise/cli"
|
|
"github.com/coder/coder/v2/scripts/atomicwrite"
|
|
"github.com/coder/coder/v2/scripts/docgenenv"
|
|
"github.com/coder/flog"
|
|
"github.com/coder/serpent"
|
|
)
|
|
|
|
// route is an individual page object in the docs manifest.json.
|
|
type route struct {
|
|
Title string `json:"title,omitempty"`
|
|
Description string `json:"description,omitempty"`
|
|
Path string `json:"path,omitempty"`
|
|
IconPath string `json:"icon_path,omitempty"`
|
|
State []string `json:"state,omitempty"`
|
|
Children []route `json:"children,omitempty"`
|
|
}
|
|
|
|
// manifest describes the entire documentation index.
|
|
type manifest struct {
|
|
Versions []string `json:"versions,omitempty"`
|
|
Routes []route `json:"routes,omitempty"`
|
|
}
|
|
|
|
func deleteEmptyDirs(dir string) error {
|
|
return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !info.IsDir() {
|
|
return nil
|
|
}
|
|
ents, err := os.ReadDir(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(ents) == 0 {
|
|
flog.Infof("deleting empty dir\t %v", path)
|
|
err = os.Remove(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func main() {
|
|
docgenenv.Prepare()
|
|
|
|
workdir, err := os.Getwd()
|
|
if err != nil {
|
|
flog.Fatalf("getwd: %v", err)
|
|
}
|
|
root := (&cli.RootCmd{})
|
|
|
|
// wroteMap indexes file paths to commands.
|
|
wroteMap := make(map[string]*serpent.Command)
|
|
|
|
var (
|
|
docsDir = filepath.Join(workdir, "docs")
|
|
cliMarkdownDir = filepath.Join(docsDir, "reference/cli")
|
|
)
|
|
|
|
if d := os.Getenv("DOCS_DIR"); d != "" {
|
|
docsDir = d
|
|
cliMarkdownDir = filepath.Join(docsDir, "reference/cli")
|
|
}
|
|
|
|
cmd, err := root.Command(root.EnterpriseSubcommands())
|
|
if err != nil {
|
|
flog.Fatalf("creating command: %v", err)
|
|
}
|
|
err = genTree(
|
|
cliMarkdownDir,
|
|
cmd,
|
|
wroteMap,
|
|
)
|
|
if err != nil {
|
|
flog.Fatalf("generating markdowns: %v", err)
|
|
}
|
|
|
|
// Delete old files
|
|
err = filepath.Walk(cliMarkdownDir, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if info.IsDir() {
|
|
return nil
|
|
}
|
|
_, ok := wroteMap[path]
|
|
if !ok {
|
|
flog.Infof("deleting old doc\t %v", path)
|
|
if err := os.Remove(path); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
flog.Fatalf("deleting old docs: %v", err)
|
|
}
|
|
|
|
err = deleteEmptyDirs(cliMarkdownDir)
|
|
if err != nil {
|
|
flog.Fatalf("deleting empty dirs: %v", err)
|
|
}
|
|
|
|
// Update manifest
|
|
manifestPath := filepath.Join(docsDir, "manifest.json")
|
|
|
|
manifestByt, err := os.ReadFile(manifestPath)
|
|
if err != nil {
|
|
flog.Fatalf("reading manifest: %v", err)
|
|
}
|
|
|
|
var manifest manifest
|
|
err = json.Unmarshal(manifestByt, &manifest)
|
|
if err != nil {
|
|
flog.Fatalf("unmarshalling manifest: %v", err)
|
|
}
|
|
|
|
var found bool
|
|
for i := range manifest.Routes {
|
|
rt := &manifest.Routes[i]
|
|
if rt.Title != "Reference" {
|
|
continue
|
|
}
|
|
for j := range rt.Children {
|
|
child := &rt.Children[j]
|
|
if child.Title != "Command Line" {
|
|
continue
|
|
}
|
|
child.Children = nil
|
|
found = true
|
|
for path, cmd := range wroteMap {
|
|
relPath, err := filepath.Rel(docsDir, path)
|
|
if err != nil {
|
|
flog.Fatalf("getting relative path: %v", err)
|
|
}
|
|
child.Children = append(child.Children, route{
|
|
Title: fullName(cmd),
|
|
Description: cmd.Short,
|
|
Path: relPath,
|
|
})
|
|
}
|
|
// Sort children by title because wroteMap iteration is
|
|
// non-deterministic.
|
|
sort.Slice(child.Children, func(i, j int) bool {
|
|
return child.Children[i].Title < child.Children[j].Title
|
|
})
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
flog.Fatalf("could not find Command Line route in manifest")
|
|
}
|
|
|
|
manifestByt, err = json.MarshalIndent(manifest, "", " ")
|
|
if err != nil {
|
|
flog.Fatalf("marshaling manifest: %v", err)
|
|
}
|
|
|
|
err = atomicwrite.File(manifestPath, manifestByt)
|
|
if err != nil {
|
|
flog.Fatalf("writing manifest: %v", err)
|
|
}
|
|
}
|