diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..5ee8dd5 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,64 @@ +name: Docs + +# The published reference must describe a version people can install, so it is +# built from release tags rather than from main. workflow_dispatch is here for +# fixing the site itself (theme, landing page) without cutting a release. +on: + push: + tags: ["v*"] + workflow_dispatch: + +permissions: + contents: read + +# One deployment at a time: GitHub Pages rejects concurrent deploys. +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + # configure-pages reads the site's base URL from the Pages API. It never + # creates the site: enablement defaults to false, so Pages has to be set + # to build from Actions by hand, and read is all this job needs. + pages: read + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version-file: go.mod + # Hextra needs the extended build for its SCSS. + - uses: peaceiris/actions-hugo@2752ce1d29631191ea3f27c23495fa06139a5b78 # v3.2.1 + with: + hugo-version: "0.165.0" + extended: true + - uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0 + id: pages + - name: Generate command reference + run: go run ./cmd/docgen -out website/content + - name: Build site + working-directory: website + # configure-pages reports the correct base URL, which differs between a + # project site (/flagsmith-cli/) and a custom domain. + run: hugo --minify --baseURL "${{ steps.pages.outputs.base_url }}/" + - uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 + with: + path: website/public + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + permissions: + pages: write # deploy to Pages + id-token: write # verify the deployment originates here + steps: + - uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0 + id: deployment diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 993af2b..0073db9 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -47,6 +47,20 @@ jobs: with: extra-args: --all-files --hook-stage pre-push renovate-config-validator + # The site itself is only built on release tags, but generating the reference + # is cheap and catches a broken generator here rather than mid-release. + docs: + name: docs generate + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 + with: + go-version-file: go.mod + - run: go run ./cmd/docgen -out "$RUNNER_TEMP/reference" + install-ps1: name: install.ps1 lint runs-on: windows-latest diff --git a/.gitignore b/.gitignore index a94b49e..13bb2a3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ /flagsmith /dist/ *.test + +# Generated command reference and Hugo build output (see website/README.md). +/website/content/ +/website/public/ +/website/resources/ +/website/.hugo_build.lock diff --git a/README.md b/README.md index 43335dc..5207790 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Flagsmith CLI -The next-generation Flagsmith command-line interface (work in progress). +The Flagsmith command-line interface helps you manage and roll out your feature flags without leaving your terminal. ## Install @@ -11,28 +11,38 @@ brew install Flagsmith/tap/flagsmith Or: ```sh -curl -fsSL https://raw.githubusercontent.com/Flagsmith/flagsmith-cli/main/install.sh | sh +curl -fsSL https://get.flagsmith.com | sh ``` Installs to `$HOME/.local/bin` and adds it to your `PATH`. Options: + + ```sh -curl -fsSL https://raw.githubusercontent.com/Flagsmith/flagsmith-cli/main/install.sh | sh -s -- --version v2.0.0 --bin-dir /usr/local/bin --no-modify-path -curl -fsSL https://raw.githubusercontent.com/Flagsmith/flagsmith-cli/main/install.sh | sh -s -- --help +curl -fsSL https://get.flagsmith.com | sh -s -- --version v2.0.0-beta.3 --bin-dir /usr/local/bin --no-modify-path +curl -fsSL https://get.flagsmith.com | sh -s -- --help ``` + + `FLAGSMITH_CLI_VERSION`, `FLAGSMITH_INSTALL_DIR` and `FLAGSMITH_NO_MODIFY_PATH` do the same if exported first. To pin the installer itself, fetch it at a commit you trust: `raw.githubusercontent.com/Flagsmith/flagsmith-cli//install.sh`. -Alternatively, `go install github.com/Flagsmith/flagsmith-cli/v2@v2.0.0-beta.3` (installs as `flagsmith-cli`), or grab an archive from [Releases](https://github.com/Flagsmith/flagsmith-cli/releases). - On Windows: ```powershell irm https://raw.githubusercontent.com/Flagsmith/flagsmith-cli/main/install.ps1 | iex ``` +Using npm: + +```sh +npm install -g @flagsmith/cli +``` + +Alternatively, `go install github.com/Flagsmith/flagsmith-cli/v2@v2.0.0-beta.3` (installs as `flagsmith-cli`), or grab an archive from [Releases](https://github.com/Flagsmith/flagsmith-cli/releases). + ## Build ```sh @@ -56,6 +66,8 @@ flagsmith flag list # list the flags in the current environment ## Commands +Full reference for every command and flag: . + Reading works against any Flagsmith instance. Changing flags — `flag update`, `flag enable`/`disable`, `flag reorder`, `flag delete` — needs Flagsmith 2.263.0 or newer, self-hosted or SaaS. - `flagsmith init` — bind the current directory to a project (writes `flagsmith.json`). diff --git a/cmd/docgen/main.go b/cmd/docgen/main.go new file mode 100644 index 0000000..2b1169c --- /dev/null +++ b/cmd/docgen/main.go @@ -0,0 +1,198 @@ +// Command docgen renders the CLI's own help into markdown for the website. +// +// Pages are cobra's own markdown output, so the published reference cannot +// drift from the binary: every page is the command's Short, Long, Example and +// flags as the terminal would print them. Hidden commands and hidden flags are +// skipped. +// +// The command tree is mirrored as a directory tree — `flagsmith flag update` +// becomes flag/update.md, served at /flag/update/ — so the site navigation +// nests the way the CLI does. cobra's own tree walker is not used because it +// flattens everything into one directory, which leaves a sidebar of six +// commands all called "create". +// +// The output is the whole site content: the root command's page is the home +// page, so there is nowhere for a hand-written description of the CLI to drift +// out of step with the CLI. +// +// Usage: +// +// go run ./cmd/docgen -out website/content +package main + +import ( + "bytes" + "flag" + "fmt" + "os" + "path" + "path/filepath" + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/cobra/doc" + + "github.com/Flagsmith/flagsmith-cli/v2/internal/cmd" +) + +func main() { + if err := run(os.Args); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func run(args []string) error { + flags := flag.NewFlagSet(filepath.Base(args[0]), flag.ContinueOnError) + out := flags.String("out", "", "directory to write markdown pages to (required)") + if err := flags.Parse(args[1:]); err != nil { + return err + } + if *out == "" { + flags.Usage() + return fmt.Errorf("-out not set") + } + + root := cmd.Root() + // Every page ends with cobra's "Auto generated ... on " footer + // otherwise, which makes the build non-reproducible. cobra only inherits + // this from a parent while rendering its "SEE ALSO" block, so set it on + // each command rather than trusting it to propagate from the root. + forEach(root, func(c *cobra.Command) { c.DisableAutoGenTag = true }) + + // The whole directory is generated, so clear it: a renamed or deleted + // command must not leave an orphan page behind. + if err := os.RemoveAll(*out); err != nil { + return err + } + return write(root, newSite(root), *out) +} + +// forEach applies fn to cmd and every command beneath it, skipping the ones +// cobra would not document. +func forEach(cmd *cobra.Command, fn func(*cobra.Command)) { + fn(cmd) + for _, sub := range cmd.Commands() { + if !sub.IsAvailableCommand() || sub.IsAdditionalHelpTopicCommand() { + continue + } + forEach(sub, fn) + } +} + +// site maps each documented command to where its page lives, and remembers the +// link names cobra will ask about while rendering cross-references. +type site struct { + // url is the served path of a command's page, e.g. "flag/update", relative + // to the site root. + url map[*cobra.Command]string + // byLink resolves the "flagsmith_flag_update.md" names cobra emits in its + // SEE ALSO block back to the command they point at. + byLink map[string]*cobra.Command +} + +func newSite(root *cobra.Command) *site { + s := &site{url: map[*cobra.Command]string{}, byLink: map[string]*cobra.Command{}} + forEach(root, func(c *cobra.Command) { + // path.Join returns "" for the root command; "." keeps it a valid path + // for filepath.Rel when links are computed. + s.url[c] = path.Join(".", strings.Join(segments(c), "/")) + s.byLink[strings.ReplaceAll(c.CommandPath(), " ", "_")+".md"] = c + }) + return s +} + +// segments is a command's path below the root, so `flagsmith flag update` +// yields [flag update] and the root itself yields nothing. +func segments(cmd *cobra.Command) []string { + return strings.Fields(cmd.CommandPath())[1:] +} + +// write renders cmd and its subcommands beneath dir. +func write(cmd *cobra.Command, s *site, dir string) error { + page, err := render(cmd, s) + if err != nil { + return err + } + + // A command with subcommands owns a directory, so its own page has to be + // that directory's index; a leaf is a plain page beside its siblings. + file := filepath.Join(dir, filepath.Join(segments(cmd)...)+".md") + if cmd.HasAvailableSubCommands() { + file = filepath.Join(dir, filepath.Join(segments(cmd)...), "_index.md") + } + if err := os.MkdirAll(filepath.Dir(file), 0o755); err != nil { + return err + } + if err := os.WriteFile(file, page, 0o644); err != nil { + return err + } + + for _, sub := range cmd.Commands() { + if !sub.IsAvailableCommand() || sub.IsAdditionalHelpTopicCommand() { + continue + } + if err := write(sub, s, dir); err != nil { + return err + } + } + return nil +} + +// render produces one page: Hugo front matter, then cobra's markdown. +func render(cmd *cobra.Command, s *site) ([]byte, error) { + var body bytes.Buffer + if err := doc.GenMarkdownCustom(cmd, &body, s.linker(cmd)); err != nil { + return nil, err + } + + var page bytes.Buffer + // Hextra renders the title as the page's heading, and cobra opens with the + // command path as a heading too. Keep the front matter one and drop + // cobra's, so the command name is not printed twice. + fmt.Fprintf(&page, "---\ntitle: %s\nlinkTitle: %s\ndescription: %s\n", + yamlString(cmd.CommandPath()), yamlString(cmd.Name()), yamlString(cmd.Short)) + if !cmd.HasParent() { + // Expand the top of the sidebar on arrival. + fmt.Fprint(&page, "sidebar:\n open: true\n") + } + fmt.Fprint(&page, "---\n\n") + page.Write(dropHeading(body.Bytes())) + return page.Bytes(), nil +} + +// linker resolves cobra's cross-reference filenames into links relative to the +// page being rendered, so they survive being served under a project-pages +// subpath such as /flagsmith-cli/. +func (s *site) linker(from *cobra.Command) func(string) string { + return func(link string) string { + to, ok := s.byLink[link] + if !ok { + return link + } + rel, err := filepath.Rel(s.url[from], s.url[to]) + if err != nil { + return link + } + return filepath.ToSlash(rel) + "/" + } +} + +// dropHeading removes the leading "## " line cobra writes, along +// with the blank line after it. +func dropHeading(md []byte) []byte { + if !bytes.HasPrefix(md, []byte("## ")) { + return md + } + _, rest, found := bytes.Cut(md, []byte("\n")) + if !found { + return md + } + return bytes.TrimLeft(rest, "\n") +} + +// yamlString quotes a value for a front matter scalar, since a Short can +// contain a colon or a quote. +func yamlString(s string) string { + return `"` + strings.NewReplacer(`\`, `\\`, `"`, `\"`).Replace(s) + `"` +} diff --git a/go.mod b/go.mod index 0ec789b..5c0398e 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,7 @@ require ( github.com/charmbracelet/x/term v0.2.1 // indirect github.com/clipperhouse/stringish v0.1.1 // indirect github.com/clipperhouse/uax29/v2 v2.3.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/danieljoos/wincred v1.2.3 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect @@ -49,7 +50,9 @@ require ( github.com/muesli/termenv v0.16.0 // indirect github.com/ohler55/ojg v1.28.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/net v0.56.0 // indirect golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.39.0 // indirect diff --git a/go.sum b/go.sum index e44eb7b..0c351cc 100644 --- a/go.sum +++ b/go.sum @@ -44,6 +44,7 @@ github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfa github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= @@ -97,6 +98,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= @@ -112,6 +114,7 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/zalando/go-keyring v0.2.8 h1:6sD/Ucpl7jNq10rM2pgqTs0sZ9V3qMrqfIIy5YPccHs= github.com/zalando/go-keyring v0.2.8/go.mod h1:tsMo+VpRq5NGyKfxoBVjCuMrG47yj8cmakZDO5QGii0= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= @@ -130,6 +133,7 @@ golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus= golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/cmd/root.go b/internal/cmd/root.go index 82b6e14..1b50b74 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -174,6 +174,14 @@ func nudgeInit(cmd *cobra.Command) bool { return true } +// Root returns the fully wired command tree, for callers that need to inspect +// it rather than run it — documentation generation, shell completions. It never +// executes a command, so no credential or network access is involved. +func Root() *cobra.Command { + prepare() + return rootCmd +} + func Execute() { prepare() // ExecuteC returns the command that actually ran (or failed to parse), so a diff --git a/release-please-config.json b/release-please-config.json index b652d56..511667a 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -9,7 +9,8 @@ "extra-files": [ "install.sh", "install.ps1", - "README.md" + "README.md", + "website/hugo.yaml" ] } }, diff --git a/website/README.md b/website/README.md new file mode 100644 index 0000000..9f0c5f0 --- /dev/null +++ b/website/README.md @@ -0,0 +1,55 @@ +# Command reference site + +The [Hugo](https://gohugo.io) site published to + by +[`.github/workflows/docs.yml`](../.github/workflows/docs.yml) on every release +tag. + +The site *is* the command reference: all of `content/` is generated from the +CLI's own help text by [`cmd/docgen`](../cmd/docgen) and is gitignored. Nothing +about a command is written here by hand — fix the `Short`, `Long` or `Example` +on the command itself in `internal/cmd/` and the page follows. There is +deliberately no landing page: installing and getting started are documented in +the [README](../README.md#install) and on +[docs.flagsmith.com](https://docs.flagsmith.com/integrating-with-flagsmith/CLI), +and a third copy here would be the one that goes stale. + +The command tree becomes a directory tree, so `flagsmith flag update` is served +at `/flag/update/`, the `flagsmith` root command is the home page, and the +sidebar nests the way the CLI does. + +## Preview locally + +Requires [Hugo extended](https://gohugo.io/installation/) (`brew install hugo`) +and Go. + +```sh +go run ./cmd/docgen -out website/content # from the repository root +cd website && hugo server +``` + +Then open . Re-run `docgen` after changing help text. + +## What is hand-written + +| Path | Purpose | +| --- | --- | +| `hugo.yaml` | Site config, navigation, theme | +| `layouts/_partials/navbar-title.html` | Theme override: version badge | +| `layouts/_partials/custom/footer.html` | Names the release being documented | +| `layouts/home.html` | Theme override, see below | + +Only the newest release is published: Pages serves a single artifact, so there +are no `/vX.Y.Z/` URLs. The version is therefore shown twice — as a badge beside +the navbar title, where it is seen without scrolling, and once in the footer with +the context that explains it. Both read `params.version` in `hugo.yaml`, which +release-please bumps in its release pull request (`website/hugo.yaml` is one of +its `extra-files`), so a plain `hugo server` shows exactly what a deploy would. + +Hextra's home layout hides the sidebar, which would leave the front page — the +root command's own page — with no navigation at all. `layouts/home.html` is the +theme's ordinary reference-page layout, copied so the home page gets the command +tree too. Re-check it against the theme when bumping Hextra. + +`go.mod` here is a Hugo module file and is intentionally separate from the CLI's +own `go.mod`, so the theme never becomes a dependency of the released binary. diff --git a/website/go.mod b/website/go.mod new file mode 100644 index 0000000..9ed82a6 --- /dev/null +++ b/website/go.mod @@ -0,0 +1,7 @@ +// Hugo module for the documentation site. Deliberately a separate module from +// the CLI: theme dependencies must not reach the released binary's go.mod. +module github.com/Flagsmith/flagsmith-cli/website + +go 1.26 + +require github.com/imfing/hextra v0.12.3 // indirect diff --git a/website/go.sum b/website/go.sum new file mode 100644 index 0000000..90d1e03 --- /dev/null +++ b/website/go.sum @@ -0,0 +1,4 @@ +github.com/imfing/hextra v0.12.0 h1:f6y35hW/WDJEcx9S0dOmbICOBxYE0PmP6IJFsTUgVyY= +github.com/imfing/hextra v0.12.0/go.mod h1:YAv8XRNSmcqjieFwI7fVQK1AoY2Do+45DO9HGqxSGu4= +github.com/imfing/hextra v0.12.3 h1:DZHY2rUWYteyzjlHi9r4n7Bb5e2Q+6LXe4C1Dqn0ZjM= +github.com/imfing/hextra v0.12.3/go.mod h1:vi+yhpq8YPp/aghvJlNKVnJKcPJ/VyAEcfC1BSV9ARo= diff --git a/website/hugo.yaml b/website/hugo.yaml new file mode 100644 index 0000000..7a432b1 --- /dev/null +++ b/website/hugo.yaml @@ -0,0 +1,68 @@ +# Hugo config for the generated command reference at +# https://flagsmith.github.io/flagsmith-cli/ +# +# The whole of content/ is generated by `go run ./cmd/docgen` and is not +# committed; see website/README.md. +baseURL: https://flagsmith.github.io/flagsmith-cli/ +title: Flagsmith CLI +locale: en-gb + +module: + imports: + - path: github.com/imfing/hextra + +enableRobotsTXT: true +# Generated pages have no git history of their own, so there is no +# "last modified" worth showing. +enableGitInfo: false + +markup: + highlight: + noClasses: false + goldmark: + renderer: + # Required by Hextra's shortcodes, which emit HTML. + unsafe: true + +menu: + main: + # Installing is documented in the README and on docs.flagsmith.com. Link to + # one of them rather than keeping a third copy in step. + - name: Install + url: https://github.com/Flagsmith/flagsmith-cli#install + weight: 1 + - name: Guides + url: https://docs.flagsmith.com/integrating-with-flagsmith/CLI + weight: 2 + - name: Search + weight: 3 + params: + type: search + - name: GitHub + weight: 4 + url: https://github.com/Flagsmith/flagsmith-cli + params: + icon: github + +params: + # The release this build documents, shown beside the navbar title and in the + # footer. Bumped by release-please, which is why website/hugo.yaml is one of + # its extra-files. + version: "v2.0.0-beta.3" # x-release-please-version + description: The Flagsmith command-line interface — manage flags, segments and environments from your terminal. + navbar: + displayTitle: true + displayLogo: false + footer: + displayCopyright: false + displayPoweredBy: false + # The reference is generated, so "edit this page" would point at a file that + # does not exist in the repository. + editURL: + enable: false + page: + width: normal + # Every page is a command, so the sidebar is the command tree and should be + # visible from the home page down. + sidebar: + displayTitle: false diff --git a/website/layouts/_partials/custom/footer.html b/website/layouts/_partials/custom/footer.html new file mode 100644 index 0000000..803c669 --- /dev/null +++ b/website/layouts/_partials/custom/footer.html @@ -0,0 +1,19 @@ +{{/* + Hextra's own extension point for footer content, so unlike layouts/home.html + this copies no theme markup and cannot drift from it. + + The site always describes the newest release, which is invisible to someone + who arrives from a search result while running an older CLI. Name the version, + so a stale page looks stale. HUGO_PARAMS_VERSION is set by the docs workflow; + it is empty for a local preview, which renders nothing. +*/}} +{{- with site.Params.version -}} +
+ Documenting + {{ . }}, the latest release. Run flagsmith --version to check yours. +
+{{- end -}} diff --git a/website/layouts/_partials/navbar-title.html b/website/layouts/_partials/navbar-title.html new file mode 100644 index 0000000..1040b3f --- /dev/null +++ b/website/layouts/_partials/navbar-title.html @@ -0,0 +1,39 @@ +{{/* + Hextra has no hook for navbar content, so this is the theme's own + navbar-title.html with a version badge added beside the title. The version + belongs where it is seen without scrolling: a reader arriving from a search + result on an older CLI should not have to reach the footer to find out which + release the page describes. + + The auto margin that pushes the menu to the right moves onto the badge when + there is one, so the badge sits beside the title rather than beside the menu. + + Copied from hextra@v0.12.3 layouts/_partials/navbar-title.html; re-check when + bumping the theme. +*/}} +{{- $logoPath := .Site.Params.navbar.logo.path | default "images/logo.svg" -}} +{{- $logoLink := .Site.Params.navbar.logo.link | default .Site.Home.RelPermalink -}} +{{- $logoWidth := .Site.Params.navbar.logo.width | default "20" -}} +{{- $logoHeight := .Site.Params.navbar.logo.height | default "20" -}} +{{- $logoDarkPath := .Site.Params.navbar.logo.dark | default $logoPath -}} +{{- $version := .Site.Params.version -}} +{{- $push := cond (eq $version "") "hx:ltr:mr-auto hx:rtl:ml-auto" "" -}} + + + {{- $displayTitle := (.Site.Params.navbar.displayTitle | default true) }} + {{- if (.Site.Params.navbar.displayLogo | default true) }} + {{ cond $displayTitle `Logo` .Site.Title }} + {{ cond $displayTitle `Dark Logo` .Site.Title }} + {{- end }} + {{- if $displayTitle }} + {{- .Site.Title -}} + {{- end }} + +{{- with $version }} + {{ . }} +{{- end }} diff --git a/website/layouts/home.html b/website/layouts/home.html new file mode 100644 index 0000000..589d3c4 --- /dev/null +++ b/website/layouts/home.html @@ -0,0 +1,34 @@ +{{/* + The site is the command reference, so the home page is the `flagsmith` root + command's own page and needs the command tree beside it. Hextra's home layout + passes disableSidebar, which would leave the front page with no navigation at + all, so use the layout the theme gives any other reference page. + + Copied from hextra@v0.12.3 layouts/docs/list.html; re-check when bumping the + theme. +*/}} +{{ define "main" }} +
+ {{ partial "sidebar.html" (dict "context" .) }} + {{ partial "toc.html" . }} +
+
+ {{ partial "breadcrumb.html" (dict "page" . "enable" true) }} +
+ {{ if .Title }} +
+

{{ .Title }}

+ {{ partial "components/page-context-menu" . }} +
+ {{ end }} + {{ .Content }} +
+ {{ partial "components/last-updated.html" . }} + {{- if (site.Params.page.displayPagination | default true) -}} + {{- partial "components/pager.html" . -}} + {{- end -}} + {{ partial "components/comments.html" . }} +
+
+
+{{ end }}