Skip to content

Commit a600e11

Browse files
committed
cli, docs: Add a docs command for doc generation
This took a lot longer than it looks to get right. It's not perfect, but it now reliably generates documentation which we can put into gohugo.
1 parent 7b45f94 commit a600e11

27 files changed

Lines changed: 1379 additions & 41 deletions

File tree

cli/cli.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ type Args struct {
123123

124124
FirstbootCmd *FirstbootArgs `arg:"subcommand:firstboot" help:"run some tasks on first boot"`
125125

126+
DocsCmd *DocsGenerateArgs `arg:"subcommand:docs" help:"generate documentation"`
127+
126128
// This never runs, it gets preempted in the real main() function.
127129
// XXX: Can we do it nicely with the new arg parser? can it ignore all args?
128130
EtcdCmd *EtcdArgs `arg:"subcommand:etcd" help:"run standalone etcd"`
@@ -167,6 +169,10 @@ func (obj *Args) Run(ctx context.Context, data *cliUtil.Data) (bool, error) {
167169
return cmd.Run(ctx, data)
168170
}
169171

172+
if cmd := obj.DocsCmd; cmd != nil {
173+
return cmd.Run(ctx, data)
174+
}
175+
170176
// NOTE: we could return true, fmt.Errorf("...") if more than one did
171177
return false, nil // nobody activated
172178
}

cli/docs.go

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// Mgmt
2+
// Copyright (C) 2013-2024+ James Shubin and the project contributors
3+
// Written by James Shubin <james@shubin.ca> and the project contributors
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
//
18+
// Additional permission under GNU GPL version 3 section 7
19+
//
20+
// If you modify this program, or any covered work, by linking or combining it
21+
// with embedded mcl code and modules (and that the embedded mcl code and
22+
// modules which link with this program, contain a copy of their source code in
23+
// the authoritative form) containing parts covered by the terms of any other
24+
// license, the licensors of this program grant you additional permission to
25+
// convey the resulting work. Furthermore, the licensors of this program grant
26+
// the original author, James Shubin, additional permission to update this
27+
// additional permission if he deems it necessary to achieve the goals of this
28+
// additional permission.
29+
30+
package cli
31+
32+
import (
33+
"context"
34+
"os"
35+
"os/signal"
36+
"sync"
37+
"syscall"
38+
39+
cliUtil "github.com/purpleidea/mgmt/cli/util"
40+
"github.com/purpleidea/mgmt/docs"
41+
)
42+
43+
// DocsGenerateArgs is the CLI parsing structure and type of the parsed result.
44+
// This particular one contains all the common flags for the `docs generate`
45+
// subcommand.
46+
type DocsGenerateArgs struct {
47+
docs.Config // embedded config (can't be a pointer) https://github.com/alexflint/go-arg/issues/240
48+
49+
DocsGenerate *cliUtil.DocsGenerateArgs `arg:"subcommand:generate" help:"generate documentation"`
50+
}
51+
52+
// Run executes the correct subcommand. It errors if there's ever an error. It
53+
// returns true if we did activate one of the subcommands. It returns false if
54+
// we did not. This information is used so that the top-level parser can return
55+
// usage or help information if no subcommand activates. This particular Run is
56+
// the run for the main `docs` subcommand.
57+
func (obj *DocsGenerateArgs) Run(ctx context.Context, data *cliUtil.Data) (bool, error) {
58+
ctx, cancel := context.WithCancel(ctx)
59+
defer cancel()
60+
61+
var name string
62+
var args interface{}
63+
if cmd := obj.DocsGenerate; cmd != nil {
64+
name = cliUtil.LookupSubcommand(obj, cmd) // "generate"
65+
args = cmd
66+
}
67+
_ = name
68+
69+
Logf := func(format string, v ...interface{}) {
70+
// Don't block this globally...
71+
//if !data.Flags.Debug {
72+
// return
73+
//}
74+
data.Flags.Logf("main: "+format, v...)
75+
}
76+
77+
var api docs.API
78+
79+
if cmd := obj.DocsGenerate; cmd != nil {
80+
api = &docs.Generate{
81+
DocsGenerateArgs: args.(*cliUtil.DocsGenerateArgs),
82+
Config: obj.Config,
83+
Program: data.Program,
84+
Version: data.Version,
85+
Debug: data.Flags.Debug,
86+
Logf: Logf,
87+
}
88+
}
89+
90+
if api == nil {
91+
return false, nil // nothing found (display help!)
92+
}
93+
94+
// We don't use these for the setup command in normal operation.
95+
if data.Flags.Debug {
96+
cliUtil.Hello(data.Program, data.Version, data.Flags) // say hello!
97+
defer Logf("goodbye!")
98+
}
99+
100+
// install the exit signal handler
101+
wg := &sync.WaitGroup{}
102+
defer wg.Wait()
103+
exit := make(chan struct{})
104+
defer close(exit)
105+
wg.Add(1)
106+
go func() {
107+
defer cancel()
108+
defer wg.Done()
109+
// must have buffer for max number of signals
110+
signals := make(chan os.Signal, 3+1) // 3 * ^C + 1 * SIGTERM
111+
signal.Notify(signals, os.Interrupt) // catch ^C
112+
//signal.Notify(signals, os.Kill) // catch signals
113+
signal.Notify(signals, syscall.SIGTERM)
114+
var count uint8
115+
for {
116+
select {
117+
case sig := <-signals: // any signal will do
118+
if sig != os.Interrupt {
119+
data.Flags.Logf("interrupted by signal")
120+
return
121+
}
122+
123+
switch count {
124+
case 0:
125+
data.Flags.Logf("interrupted by ^C")
126+
cancel()
127+
case 1:
128+
data.Flags.Logf("interrupted by ^C (fast pause)")
129+
cancel()
130+
case 2:
131+
data.Flags.Logf("interrupted by ^C (hard interrupt)")
132+
cancel()
133+
}
134+
count++
135+
136+
case <-exit:
137+
return
138+
}
139+
}
140+
}()
141+
142+
if err := api.Main(ctx); err != nil {
143+
if data.Flags.Debug {
144+
data.Flags.Logf("main: %+v", err)
145+
}
146+
return false, err
147+
}
148+
149+
return true, nil
150+
}

cli/util/args.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,3 +187,12 @@ type FirstbootStartArgs struct {
187187
DoneDir string `arg:"--done-dir" help:"dir to move done scripts to"`
188188
LoggingDir string `arg:"--logging-dir" help:"directory to store logs in"`
189189
}
190+
191+
// DocsGenerateArgs is the docgen utility CLI parsing structure and type of the
192+
// parsed result.
193+
type DocsGenerateArgs struct {
194+
Output string `arg:"--output" help:"output path to write to"`
195+
RootDir string `arg:"--root-dir" help:"path to mgmt source dir"`
196+
NoResources bool `arg:"--no-resources" help:"skip resource doc generation"`
197+
NoFunctions bool `arg:"--no-functions" help:"skip function doc generation"`
198+
}

docs/docs.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Mgmt
2+
// Copyright (C) 2013-2024+ James Shubin and the project contributors
3+
// Written by James Shubin <james@shubin.ca> and the project contributors
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
//
18+
// Additional permission under GNU GPL version 3 section 7
19+
//
20+
// If you modify this program, or any covered work, by linking or combining it
21+
// with embedded mcl code and modules (and that the embedded mcl code and
22+
// modules which link with this program, contain a copy of their source code in
23+
// the authoritative form) containing parts covered by the terms of any other
24+
// license, the licensors of this program grant you additional permission to
25+
// convey the resulting work. Furthermore, the licensors of this program grant
26+
// the original author, James Shubin, additional permission to update this
27+
// additional permission if he deems it necessary to achieve the goals of this
28+
// additional permission.
29+
30+
// Package docs provides a tool that generates documentation from the source.
31+
//
32+
// ./mgmt docs generate --output /tmp/docs.json && cat /tmp/docs.json | jq
33+
package docs
34+
35+
import (
36+
"context"
37+
)
38+
39+
// API is the simple interface we expect for any setup items.
40+
type API interface {
41+
// Main runs everything for this setup item.
42+
Main(context.Context) error
43+
}
44+
45+
// Config is a struct of all the configuration values which are shared by all of
46+
// the setup utilities. By including this as a separate struct, it can be used
47+
// as part of the API if we want.
48+
type Config struct {
49+
//Foo string `arg:"--foo,env:MGMT_DOCGEN_FOO" help:"Foo..."` // TODO: foo
50+
}

0 commit comments

Comments
 (0)