Skip to content

Commit 113f716

Browse files
authored
Merge pull request #1013 from starius/urfave-cli-updates
cmd/loop: update urfave/cli to v3, auto-generate loop CLI reference manual and man file
2 parents 3323bea + 9eddee7 commit 113f716

24 files changed

+2056
-549
lines changed

.github/workflows/main.yml

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,25 @@ jobs:
4343

4444
- name: run check
4545
run: make rpc-check
46-
46+
47+
########################
48+
# SQL compile and check
49+
########################
50+
sqlc-check:
51+
name: SQL compilation check
52+
runs-on: ubuntu-latest
53+
steps:
54+
- name: git checkout
55+
uses: actions/checkout@v2
56+
57+
- name: setup go ${{ env.GO_VERSION }}
58+
uses: actions/setup-go@v2
59+
with:
60+
go-version: '~${{ env.GO_VERSION }}'
61+
62+
- name: run check
63+
run: make sqlc-check
64+
4765
########################
4866
# go mod check
4967
########################
@@ -85,6 +103,26 @@ jobs:
85103
- name: lint
86104
run: make lint
87105

106+
########################
107+
# Verify documentation
108+
########################
109+
docs-check:
110+
name: verify that auto-generated documentation is up-to-date
111+
runs-on: ubuntu-latest
112+
steps:
113+
- name: git checkout
114+
uses: actions/checkout@v2
115+
with:
116+
fetch-depth: 0
117+
118+
- name: setup go ${{ env.GO_VERSION }}
119+
uses: actions/setup-go@v5
120+
with:
121+
go-version: '~${{ env.GO_VERSION }}'
122+
123+
- name: check
124+
run: make docs-check
125+
88126
########################
89127
# run unit tests
90128
########################

Makefile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,15 @@ sqlc-check: sqlc
170170
@$(call print, "Verifying sql code generation.")
171171
if test -n "$$(git status --porcelain '*.go')"; then echo "SQL models not properly generated!"; git status --porcelain '*.go'; exit 1; fi
172172

173+
docs: build
174+
@$(call print, "Building man and markdown files in docs/")
175+
./loop-debug man > docs/loop.1
176+
./loop-debug markdown > docs/loop.md
177+
178+
docs-check: docs
179+
@$(call print, "Verifying man and markdown files in docs/")
180+
if test -n "$$(git status --porcelain 'docs/loop.*')"; then echo "Man and markdown files not properly generated!"; git diff; exit 1; fi
181+
173182
fsm:
174183
@$(call print, "Generating state machine docs")
175184
./scripts/fsm-generate.sh;

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ loop in <amt_in_satoshis>
6565
```
6666

6767
### More info
68-
For more information about using Loop checkout our [Loop FAQs](./docs/faqs.md).
68+
69+
- [Loop FAQs](./docs/faqs.md)
70+
- [Loop CLI manual](./docs/loop.md)
6971

7072
## Development
7173

cmd/loop/debug.go

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,32 @@ import (
77
"context"
88

99
"github.com/lightninglabs/loop/looprpc"
10-
"github.com/urfave/cli"
10+
"github.com/urfave/cli/v3"
1111
)
1212

1313
func init() {
1414
// Register the debug command.
1515
commands = append(commands, forceAutoloopCmd)
1616
}
1717

18-
var forceAutoloopCmd = cli.Command{
18+
var forceAutoloopCmd = &cli.Command{
1919
Name: "forceautoloop",
2020
Usage: `
2121
Forces to trigger an autoloop step, regardless of the current internal
2222
autoloop timer. THIS MUST NOT BE USED IN A PROD ENVIRONMENT.
2323
`,
2424
Action: forceAutoloop,
25+
Hidden: true,
2526
}
2627

27-
func forceAutoloop(ctx *cli.Context) error {
28-
client, cleanup, err := getDebugClient(ctx)
28+
func forceAutoloop(ctx context.Context, cmd *cli.Command) error {
29+
client, cleanup, err := getDebugClient(ctx, cmd)
2930
if err != nil {
3031
return err
3132
}
3233
defer cleanup()
3334

34-
cfg, err := client.ForceAutoLoop(
35-
context.Background(), &looprpc.ForceAutoLoopRequest{},
36-
)
35+
cfg, err := client.ForceAutoLoop(ctx, &looprpc.ForceAutoLoopRequest{})
3736
if err != nil {
3837
return err
3938
}
@@ -43,13 +42,13 @@ func forceAutoloop(ctx *cli.Context) error {
4342
return nil
4443
}
4544

46-
func getDebugClient(ctx *cli.Context) (looprpc.DebugClient, func(), error) {
47-
rpcServer := ctx.GlobalString("rpcserver")
48-
tlsCertPath, macaroonPath, err := extractPathArgs(ctx)
45+
func getDebugClient(ctx context.Context, cmd *cli.Command) (looprpc.DebugClient, func(), error) {
46+
rpcServer := cmd.String("rpcserver")
47+
tlsCertPath, macaroonPath, err := extractPathArgs(cmd)
4948
if err != nil {
5049
return nil, nil, err
5150
}
52-
conn, err := getClientConn(rpcServer, tlsCertPath, macaroonPath)
51+
conn, err := getClientConn(ctx, rpcServer, tlsCertPath, macaroonPath)
5352
if err != nil {
5453
return nil, nil, err
5554
}

cmd/loop/docs.go

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package main
2+
3+
import (
4+
"context"
5+
_ "embed"
6+
"fmt"
7+
8+
docs "github.com/urfave/cli-docs/v3"
9+
"github.com/urfave/cli/v3"
10+
)
11+
12+
//go:embed markdown_tabular.md.gotmpl
13+
var markdownTabularDocTemplate string
14+
15+
// We have a copy of this template taken from
16+
// https://github.com/urfave/cli-docs where we remove column
17+
// "Environment variables" if it has no values.
18+
// TODO: remove this when https://github.com/urfave/cli-docs/pull/15
19+
// is merged.
20+
func init() {
21+
docs.MarkdownTabularDocTemplate = markdownTabularDocTemplate
22+
}
23+
24+
var printManCommand = &cli.Command{
25+
Name: "man",
26+
Usage: "prints man file",
27+
Description: "Prints documentation of loop CLI in man format",
28+
Action: printMan,
29+
Hidden: true,
30+
}
31+
32+
func printMan(_ context.Context, cmd *cli.Command) error {
33+
root := filterNestedHelpCommands(cmd.Root())
34+
35+
const userCommandsSection = 1
36+
man, err := docs.ToManWithSection(root, userCommandsSection)
37+
if err != nil {
38+
return fmt.Errorf("failed to produce man: %w", err)
39+
}
40+
41+
fmt.Println(man)
42+
43+
return nil
44+
}
45+
46+
var printMarkdownCommand = &cli.Command{
47+
Name: "markdown",
48+
Usage: "prints markdown file",
49+
Description: "Prints documentation of loop CLI in markdown format",
50+
Action: printMarkdown,
51+
Hidden: true,
52+
}
53+
54+
func printMarkdown(_ context.Context, cmd *cli.Command) error {
55+
root := filterNestedHelpCommands(cmd.Root())
56+
57+
md, err := docs.ToTabularMarkdown(root, "loop")
58+
if err != nil {
59+
return fmt.Errorf("failed to produce man: %w", err)
60+
}
61+
62+
fmt.Println(md)
63+
64+
return nil
65+
}
66+
67+
// filterNestedHelpCommands clones cmd, drops nested help commands, and normalises
68+
// flag defaults so generated documentation avoids absolute paths.
69+
func filterNestedHelpCommands(cmd *cli.Command) *cli.Command {
70+
cloned := cloneCommand(cmd, 0)
71+
overrideDocFlags(cloned)
72+
return cloned
73+
}
74+
75+
// cloneCommand clones the command, filtering out nested "help" subcommands.
76+
func cloneCommand(cmd *cli.Command, depth int) *cli.Command {
77+
if cmd == nil {
78+
return nil
79+
}
80+
81+
cloned := *cmd
82+
if len(cmd.Commands) == 0 {
83+
return &cloned
84+
}
85+
86+
filtered := make([]*cli.Command, 0, len(cmd.Commands))
87+
for _, sub := range cmd.Commands {
88+
if sub == nil {
89+
continue
90+
}
91+
childDepth := depth + 1
92+
93+
// TODO: remove when https://github.com/urfave/cli-docs/pull/16
94+
if childDepth > 0 && sub.Name == "help" {
95+
continue
96+
}
97+
98+
filtered = append(filtered, cloneCommand(sub, childDepth))
99+
}
100+
101+
cloned.Commands = filtered
102+
return &cloned
103+
}
104+
105+
// overrideDocFlags walks the command tree and replaces string flag defaults
106+
// that leak user-specific filesystem paths, keeping generated docs stable.
107+
func overrideDocFlags(cmd *cli.Command) {
108+
if cmd == nil {
109+
return
110+
}
111+
112+
if len(cmd.Flags) > 0 {
113+
clonedFlags := make([]cli.Flag, len(cmd.Flags))
114+
for i, fl := range cmd.Flags {
115+
clonedFlags[i] = cloneFlagWithOverrides(fl)
116+
}
117+
cmd.Flags = clonedFlags
118+
}
119+
120+
for _, sub := range cmd.Commands {
121+
overrideDocFlags(sub)
122+
}
123+
}
124+
125+
// docFlagOverrides maps global flag names to the canonical values we want to
126+
// show in documentation instead of user-specific absolute paths.
127+
var docFlagOverrides = map[string]string{
128+
loopDirFlag.Name: "~/.loop",
129+
tlsCertFlag.Name: "~/.loop/mainnet/tls.cert",
130+
macaroonPathFlag.Name: "~/.loop/mainnet/loop.macaroon",
131+
}
132+
133+
// cloneFlagWithOverrides returns a copy of flag with overridden default values
134+
// when the flag participates in docFlagOverrides. Non-string flags are reused
135+
// unchanged to minimise allocations.
136+
func cloneFlagWithOverrides(flag cli.Flag) cli.Flag {
137+
sf, ok := flag.(*cli.StringFlag)
138+
if !ok {
139+
return flag
140+
}
141+
142+
value, ok := docFlagOverrides[sf.Name]
143+
if !ok {
144+
return flag
145+
}
146+
147+
cloned := *sf
148+
cloned.Value = value
149+
cloned.DefaultText = value
150+
151+
return &cloned
152+
}

cmd/loop/info.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import (
44
"context"
55

66
"github.com/lightninglabs/loop/looprpc"
7-
"github.com/urfave/cli"
7+
"github.com/urfave/cli/v3"
88
)
99

10-
var getInfoCommand = cli.Command{
10+
var getInfoCommand = &cli.Command{
1111
Name: "getinfo",
1212
Usage: "show general information about the loop daemon",
1313
Description: "Displays general information about the daemon like " +
@@ -16,16 +16,14 @@ var getInfoCommand = cli.Command{
1616
Action: getInfo,
1717
}
1818

19-
func getInfo(ctx *cli.Context) error {
20-
client, cleanup, err := getClient(ctx)
19+
func getInfo(ctx context.Context, cmd *cli.Command) error {
20+
client, cleanup, err := getClient(ctx, cmd)
2121
if err != nil {
2222
return err
2323
}
2424
defer cleanup()
2525

26-
cfg, err := client.GetInfo(
27-
context.Background(), &looprpc.GetInfoRequest{},
28-
)
26+
cfg, err := client.GetInfo(ctx, &looprpc.GetInfoRequest{})
2927
if err != nil {
3028
return err
3129
}

0 commit comments

Comments
 (0)