docs: auto-update evcc CLI reference on release (#31846)

This commit is contained in:
Michael Geers 2026-07-16 17:10:43 +02:00 • committed by GitHub
parent 09b0d9cb25
commit bd200460f7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 89 additions and 25 deletions

45
.github/workflows/cli-docs.yml vendored Normal file
View file

@ -0,0 +1,45 @@
name: Deploy CLI docs
on:
release:
types: [created]
workflow_dispatch:
inputs:
target_branch:
description: Target branch in the docs repository
default: main
jobs:
clidocs:
name: Deploy CLI docs
runs-on: depot-ubuntu-24.04-arm
permissions:
contents: read
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-go@v6
with:
go-version-file: go.mod
- name: Generate CLI docs
run: go run main.go gendoc ./cli-docs
- name: Format
run: npx prettier@3 --write "./cli-docs/**/*.md"
- name: Deploy CLI docs to docs repo
uses: peaceiris/actions-gh-pages@84c30a85c19949d7eee79c4ff27748b70285e453 # v4
with:
personal_token: ${{ secrets.DOCS_DEPLOY_TOKEN }}
publish_dir: ./cli-docs
external_repository: evcc-io/docs
publish_branch: ${{ github.event.inputs.target_branch || 'main' }}
destination_dir: src/content/docs/en/reference/cli
allow_empty_commit: false
commit_message: CLI docs update
if: success()

View file

@ -12,8 +12,8 @@ var checkconfig = &cobra.Command{
Use: "checkconfig", Use: "checkconfig",
Short: "Check config file for errors", Short: "Check config file for errors",
Long: `Check the (specified or default) config file for errors. Note that Long: `Check the (specified or default) config file for errors. Note that
checkconfig only checks the config file for parsing errors and does checkconfig only checks the config file for parsing errors and does
not check that individual device configurations are valid.`, not check that individual device configurations are valid.`,
Run: runConfigCheck, Run: runConfigCheck,
} }

View file

@ -1,6 +1,7 @@
package cmd package cmd
import ( import (
"fmt"
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
@ -31,41 +32,59 @@ func runGendoc(cmd *cobra.Command, args []string) {
log.FATAL.Fatalf("Failed to create output directory: %v", err) log.FATAL.Fatalf("Failed to create output directory: %v", err)
} }
if err := doc.GenMarkdownTree(rootCmd, outputDir); err != nil { rootCmd.DisableAutoGenTag = true
// frontmatter title from filename: evcc_password_reset.md -> "evcc password reset"
filePrepender := func(filename string) string {
title := strings.ReplaceAll(strings.TrimSuffix(filepath.Base(filename), ".md"), "_", " ")
return fmt.Sprintf("---\ntitle: \"%s\"\n---\n\n", title)
}
// absolute site links without .md extension
linkHandler := func(name string) string {
return "/en/reference/cli/" + strings.TrimSuffix(name, ".md")
}
if err := doc.GenMarkdownTreeCustom(rootCmd, outputDir, filePrepender, linkHandler); err != nil {
log.FATAL.Fatalf("Failed to generate documentation: %v", err) log.FATAL.Fatalf("Failed to generate documentation: %v", err)
} }
// make some modifications to the generated files titleRe := regexp.MustCompile(`(?m)^## evcc.*\n\n`)
codeRe := regexp.MustCompile(`\n\n\t(.*)\n`)
blankRe := regexp.MustCompile(`\n{3,}`)
// make the generated files ready to commit in the docs repo
err := filepath.Walk(outputDir, func(path string, info os.FileInfo, err error) error { err := filepath.Walk(outputDir, func(path string, info os.FileInfo, err error) error {
if err != nil || info.IsDir() || !strings.HasSuffix(info.Name(), ".md") {
return err
}
content, err := os.ReadFile(path)
if err != nil { if err != nil {
return err return err
} }
if !info.IsDir() && strings.HasSuffix(info.Name(), ".md") {
content, err := os.ReadFile(path)
if err != nil {
return err
}
// reduce header level by one s := string(content)
modifiedContent := strings.ReplaceAll(string(content), "## ", "# ") // drop the title heading, the frontmatter already contains it
// lowercase "see also" s = titleRe.ReplaceAllString(s, "")
modifiedContent = strings.ReplaceAll(modifiedContent, "SEE ALSO", "See also") // reduce header level by one
// convert single line indented code to backtick surrounded code s = strings.ReplaceAll(s, "### ", "## ")
codeRe := regexp.MustCompile("\n\n\t(.*)\n") // lowercase "see also"
modifiedContent = codeRe.ReplaceAllString(modifiedContent, "\n\n```\n$1\n```\n") s = strings.ReplaceAll(s, "## SEE ALSO", "## See also")
// remove auto generated date line // prettier-style list bullets
dateRe := regexp.MustCompile("##### Auto generated by spf13/cobra on.*\n") s = strings.ReplaceAll(s, "* [", "- [")
modifiedContent = dateRe.ReplaceAllString(modifiedContent, "\n") s = strings.ReplaceAll(s, ")\t - ", ") - ")
// convert single line indented code to backtick surrounded code
s = codeRe.ReplaceAllString(s, "\n\n```\n$1\n```\n")
// collapse multiple blank lines and trailing newlines (prettier style)
s = blankRe.ReplaceAllString(s, "\n\n")
s = strings.TrimRight(s, "\n") + "\n"
if err := os.WriteFile(path, []byte(modifiedContent), 0o644); err != nil { return os.WriteFile(path, []byte(s), 0o644)
return err
}
}
return nil
}) })
if err != nil { if err != nil {
log.FATAL.Fatalf("Failed to modify documentation: %v", err) log.FATAL.Fatalf("Failed to modify documentation: %v", err)
} }
log.INFO.Printf("Documentation generated and modified in %s", outputDir) log.INFO.Printf("Documentation generated in %s", outputDir)
} }