Skip to content

Commit 3b46515

Browse files
subomiclaude
andauthored
feat: add openapi snip command to remove operations from specs (#1699)
Add new `speakeasy openapi snip` command that removes specified operations from OpenAPI specifications and cleans up unused components. Features: - Remove mode: removes specified operations (default) - Keep mode: keeps only specified operations, removes everything else - Supports operation selection by operationId or path:method format - Automatically cleans up unused components after operation removal - Outputs to stdout (for piping) or file Examples: - speakeasy openapi snip --schema spec.yaml --operationId deleteUser - speakeasy openapi snip --schema spec.yaml --operation /users/{id}:DELETE - speakeasy openapi snip --keep --schema spec.yaml --operation /users:GET 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 28ae0ea commit 3b46515

File tree

6 files changed

+508
-26
lines changed

6 files changed

+508
-26
lines changed

cmd/openapi/openapi.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ var OpenAPICmd = &model.CommandGroup{
2727
Short: "Utilities for working with OpenAPI documents",
2828
Long: utils.RenderMarkdown(openapiLong),
2929
InteractiveMsg: "What do you want to do?",
30-
Commands: []model.Command{openapiLintCmd, openapiDiffCmd, transformCmd},
30+
Commands: []model.Command{openapiLintCmd, openapiDiffCmd, transformCmd, snipCmd},
3131
}
3232

3333
var openapiLintCmd = &model.ExecutableCommand[lint.LintOpenapiFlags]{

cmd/openapi/snip.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package openapi
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
charm_internal "github.com/speakeasy-api/speakeasy/internal/charm"
8+
"github.com/speakeasy-api/speakeasy/internal/model"
9+
"github.com/speakeasy-api/speakeasy/internal/model/flag"
10+
"github.com/speakeasy-api/speakeasy/internal/utils"
11+
"github.com/speakeasy-api/speakeasy/pkg/transform"
12+
)
13+
14+
const snipLong = `Remove operations from an OpenAPI specification and clean up unused components.
15+
16+
This command can operate in two modes:
17+
18+
**Remove Mode (default):**
19+
Removes the specified operations from the document.
20+
21+
**Keep Mode (--keep flag):**
22+
Keeps only the specified operations and removes everything else.
23+
24+
## Specifying Operations
25+
26+
Operations can be specified in two ways:
27+
28+
1. **By Operation ID** (using --operationId):
29+
The operationId field from your OpenAPI spec
30+
31+
2. **By Path and Method** (using --operation):
32+
Format: path:METHOD
33+
Example: /users/{id}:DELETE
34+
35+
You can specify multiple operations by:
36+
- Using the flag multiple times: --operation /users:GET --operation /users:POST
37+
- Using comma-separated values: --operation /users:GET,/users:POST
38+
39+
## Examples
40+
41+
Remove specific operations by operation ID:
42+
` + "```" + `
43+
speakeasy openapi snip --schema ./spec.yaml --operationId deleteUser --operationId adminDebug
44+
` + "```" + `
45+
46+
Remove operations by path and method:
47+
` + "```" + `
48+
speakeasy openapi snip --schema ./spec.yaml --operation /users/{id}:DELETE --operation /admin:GET
49+
` + "```" + `
50+
51+
Keep only specified operations (remove everything else):
52+
` + "```" + `
53+
speakeasy openapi snip --schema ./spec.yaml --keep --operation /users:GET --operation /users:POST
54+
` + "```" + `
55+
56+
Write to a file instead of stdout:
57+
` + "```" + `
58+
speakeasy openapi snip --schema ./spec.yaml --out ./public-spec.yaml --operation /internal:GET
59+
` + "```" + `
60+
61+
Pipe to other commands:
62+
` + "```" + `
63+
speakeasy openapi snip --schema ./spec.yaml --operation /debug:GET | speakeasy openapi lint
64+
` + "```"
65+
66+
type snipFlags struct {
67+
Schema string `json:"schema"`
68+
Out string `json:"out"`
69+
OperationIDs []string `json:"operationId"`
70+
Operations []string `json:"operation"`
71+
Keep bool `json:"keep"`
72+
}
73+
74+
var snipCmd = &model.ExecutableCommand[snipFlags]{
75+
Usage: "snip",
76+
Short: "Remove operations from an OpenAPI specification",
77+
Long: utils.RenderMarkdown(snipLong),
78+
Run: runSnip,
79+
Flags: []flag.Flag{
80+
flag.StringFlag{
81+
Name: "schema",
82+
Shorthand: "s",
83+
Description: "the OpenAPI schema to process",
84+
Required: true,
85+
AutocompleteFileExtensions: charm_internal.OpenAPIFileExtensions,
86+
},
87+
flag.StringFlag{
88+
Name: "out",
89+
Shorthand: "o",
90+
Description: "write to a file instead of stdout",
91+
},
92+
flag.StringSliceFlag{
93+
Name: "operationId",
94+
Description: "operation ID to process (can be comma-separated or repeated)",
95+
},
96+
flag.StringSliceFlag{
97+
Name: "operation",
98+
Description: "operation as path:method to process (can be comma-separated or repeated)",
99+
},
100+
flag.BooleanFlag{
101+
Name: "keep",
102+
Shorthand: "k",
103+
Description: "keep mode: keep specified operations and remove all others",
104+
DefaultValue: false,
105+
},
106+
},
107+
}
108+
109+
func runSnip(ctx context.Context, flags snipFlags) error {
110+
// Validate that at least one operation is specified
111+
if len(flags.OperationIDs) == 0 && len(flags.Operations) == 0 {
112+
return fmt.Errorf("no operations specified; use --operationId or --operation flags")
113+
}
114+
115+
// Combine all operation specifications into a single list
116+
var allOperations []string
117+
allOperations = append(allOperations, flags.OperationIDs...)
118+
allOperations = append(allOperations, flags.Operations...)
119+
120+
// Setup output
121+
out, _, err := setupOutput(ctx, flags.Out)
122+
if err != nil {
123+
return err
124+
}
125+
defer out.Close()
126+
127+
// Run the snip transform
128+
return transform.Snip(ctx, flags.Schema, allOperations, flags.Keep, out)
129+
}

go.mod

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ go 1.25.0
55
replace github.com/pb33f/doctor => github.com/speakeasy-api/doctor v0.20.0-fixvacuum
66

77
replace github.com/pb33f/libopenapi => github.com/speakeasy-api/libopenapi v0.21.9-fixhiddencomps-fixed
8-
98
require (
109
github.com/AlekSi/pointer v1.2.0
1110
github.com/KimMachineGun/automemlimit v0.7.1
@@ -38,7 +37,7 @@ require (
3837
github.com/sethvargo/go-githubactions v1.1.0
3938
github.com/speakeasy-api/huh v1.1.2
4039
github.com/speakeasy-api/jq v0.1.0
41-
github.com/speakeasy-api/openapi v1.7.8
40+
github.com/speakeasy-api/openapi v1.11.0
4241
github.com/speakeasy-api/openapi-generation/v2 v2.745.2
4342
github.com/speakeasy-api/openapi-overlay v0.10.3
4443
github.com/speakeasy-api/sdk-gen-config v1.42.0
@@ -52,8 +51,8 @@ require (
5251
github.com/stretchr/testify v1.11.1
5352
go.uber.org/zap v1.27.0
5453
golang.org/x/sync v0.17.0
55-
golang.org/x/term v0.34.0
56-
golang.org/x/text v0.29.0
54+
golang.org/x/term v0.35.0
55+
golang.org/x/text v0.30.0
5756
gopkg.in/yaml.v3 v3.0.1
5857
oras.land/oras-go/v2 v2.5.0
5958
)
@@ -249,13 +248,13 @@ require (
249248
go.opentelemetry.io/otel/trace v1.35.0 // indirect
250249
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
251250
go.uber.org/multierr v1.11.0 // indirect
252-
golang.org/x/crypto v0.41.0 // indirect
251+
golang.org/x/crypto v0.42.0 // indirect
253252
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
254253
golang.org/x/image v0.30.0 // indirect
255-
golang.org/x/mod v0.27.0 // indirect
256-
golang.org/x/net v0.43.0 // indirect
257-
golang.org/x/sys v0.35.0 // indirect
258-
golang.org/x/tools v0.36.0 // indirect
254+
golang.org/x/mod v0.28.0 // indirect
255+
golang.org/x/net v0.44.0 // indirect
256+
golang.org/x/sys v0.36.0 // indirect
257+
golang.org/x/tools v0.37.0 // indirect
259258
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect
260259
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect
261260
google.golang.org/grpc v1.69.4 // indirect

go.sum

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -551,8 +551,8 @@ github.com/speakeasy-api/jsonpath v0.6.2 h1:Mys71yd6u8kuowNCR0gCVPlVAHCmKtoGXYoA
551551
github.com/speakeasy-api/jsonpath v0.6.2/go.mod h1:ymb2iSkyOycmzKwbEAYPJV/yi2rSmvBCLZJcyD+VVWw=
552552
github.com/speakeasy-api/libopenapi v0.21.9-fixhiddencomps-fixed h1:PL/kpBY5vkBmwLjg6l7J4amgSrPf3CNWReGNdwrRqJQ=
553553
github.com/speakeasy-api/libopenapi v0.21.9-fixhiddencomps-fixed/go.mod h1:Gc8oQkjr2InxwumK0zOBtKN9gIlv9L2VmSVIUk2YxcU=
554-
github.com/speakeasy-api/openapi v1.7.8 h1:GUASBB/AMewFEmONMsavY1xj5uZXCWeC31G3C6YmvZo=
555-
github.com/speakeasy-api/openapi v1.7.8/go.mod h1:fy+CvRcKj+HDU0QNdnyG6UkfJOEjhqCuNC7o4AtLPAk=
554+
github.com/speakeasy-api/openapi v1.11.0 h1:Hzr1zuXDtdJa+YLGw8ZMM6i19V4T3V5L8T5K8JUZ8gg=
555+
github.com/speakeasy-api/openapi v1.11.0/go.mod h1:JnmVyPKhAhwTup7/jKYpnv2B0O2Lm3C+cjpVZKieVvk=
556556
github.com/speakeasy-api/openapi-generation/v2 v2.745.2 h1:sxnS6HVpaT19UTzBphO322UhbCB/FjtKi3+Uy8Q6Iso=
557557
github.com/speakeasy-api/openapi-generation/v2 v2.745.2/go.mod h1:BFcupAsPG9V10q1DGDI+Uslajq2WcYtRFn4IP4vFYSk=
558558
github.com/speakeasy-api/openapi-overlay v0.10.3 h1:70een4vwHyslIp796vM+ox6VISClhtXsCjrQNhxwvWs=
@@ -690,8 +690,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
690690
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
691691
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
692692
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
693-
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
694-
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
693+
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
694+
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
695695
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
696696
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
697697
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@@ -702,8 +702,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
702702
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
703703
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
704704
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
705-
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
706-
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
705+
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
706+
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
707707
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
708708
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
709709
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -717,8 +717,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
717717
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
718718
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
719719
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
720-
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
721-
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
720+
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
721+
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
722722
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
723723
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
724724
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -763,25 +763,25 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
763763
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
764764
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
765765
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
766-
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
767-
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
766+
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
767+
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
768768
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
769769
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
770770
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
771771
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
772772
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
773773
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
774-
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
775-
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
774+
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
775+
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
776776
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
777777
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
778778
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
779779
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
780780
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
781781
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
782782
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
783-
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
784-
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
783+
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
784+
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
785785
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
786786
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
787787
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -791,8 +791,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
791791
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
792792
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
793793
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
794-
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
795-
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
794+
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
795+
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
796796
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
797797
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
798798
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

0 commit comments

Comments
 (0)