diff --git a/.github/workflows/cli-docs.yml b/.github/workflows/cli-docs.yml new file mode 100644 index 000000000..a51abc5c8 --- /dev/null +++ b/.github/workflows/cli-docs.yml @@ -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() diff --git a/cmd/check_config.go b/cmd/check_config.go index 088ae2464..ef5f7f56e 100644 --- a/cmd/check_config.go +++ b/cmd/check_config.go @@ -12,8 +12,8 @@ var checkconfig = &cobra.Command{ Use: "checkconfig", Short: "Check config file for errors", Long: `Check the (specified or default) config file for errors. Note that - checkconfig only checks the config file for parsing errors and does - not check that individual device configurations are valid.`, +checkconfig only checks the config file for parsing errors and does +not check that individual device configurations are valid.`, Run: runConfigCheck, } diff --git a/cmd/gendock.go b/cmd/gendock.go index fb30e1873..b9f2e4c4d 100644 --- a/cmd/gendock.go +++ b/cmd/gendock.go @@ -1,6 +1,7 @@ package cmd import ( + "fmt" "os" "path/filepath" "regexp" @@ -31,41 +32,59 @@ func runGendoc(cmd *cobra.Command, args []string) { 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) } - // 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 { + if err != nil || info.IsDir() || !strings.HasSuffix(info.Name(), ".md") { + return err + } + + content, err := os.ReadFile(path) if err != nil { 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 - modifiedContent := strings.ReplaceAll(string(content), "## ", "# ") - // lowercase "see also" - modifiedContent = strings.ReplaceAll(modifiedContent, "SEE ALSO", "See also") - // convert single line indented code to backtick surrounded code - codeRe := regexp.MustCompile("\n\n\t(.*)\n") - modifiedContent = codeRe.ReplaceAllString(modifiedContent, "\n\n```\n$1\n```\n") - // remove auto generated date line - dateRe := regexp.MustCompile("##### Auto generated by spf13/cobra on.*\n") - modifiedContent = dateRe.ReplaceAllString(modifiedContent, "\n") + s := string(content) + // drop the title heading, the frontmatter already contains it + s = titleRe.ReplaceAllString(s, "") + // reduce header level by one + s = strings.ReplaceAll(s, "### ", "## ") + // lowercase "see also" + s = strings.ReplaceAll(s, "## SEE ALSO", "## See also") + // prettier-style list bullets + s = strings.ReplaceAll(s, "* [", "- [") + 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 err - } - } - return nil + return os.WriteFile(path, []byte(s), 0o644) }) if err != nil { 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) }