Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
367e9949d4 | ||
|
|
726027490b | ||
|
|
4545cb8290 | ||
|
|
1aad18306d | ||
|
|
d97e3e5cc8 | ||
|
|
636fa9d62f | ||
|
|
610b122fca | ||
|
|
ce5a6b2af6 | ||
|
|
1481dfc871 | ||
|
|
f77d2717ca | ||
|
|
df2279267b | ||
|
|
2a96e13444 | ||
|
|
775e41d329 | ||
|
|
8b2dd2453d | ||
|
|
b142f804d8 | ||
|
|
371003d4c7 | ||
|
|
23fecd7e14 | ||
|
|
738c2bfde1 |
@@ -42,7 +42,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -67,4 +67,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
|
||||
@@ -14,11 +14,11 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
uses: actions/setup-go@v5
|
||||
- name: Set CURRENT_TAG
|
||||
run: echo "GORELEASER_CURRENT_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@14707cd26fbb4b6c8abf03fb8ea4eb6c59711a62
|
||||
uses: goreleaser/goreleaser-action@d50b0c42eeed7b45ad20ecc8046a958047efe1d5
|
||||
with:
|
||||
version: latest
|
||||
args: release --clean
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: github.com/spf13/cobra
|
||||
version: v1.4.0
|
||||
version: v1.8.0
|
||||
type: go
|
||||
summary: Package cobra is a commander providing a simple interface to create powerful
|
||||
modern CLI interfaces.
|
||||
@@ -184,5 +184,5 @@ licenses:
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
- sources: README.md
|
||||
text: Cobra is released under the Apache 2.0 license. See [LICENSE.txt](https://github.com/spf13/cobra/blob/master/LICENSE.txt)
|
||||
text: Cobra is released under the Apache 2.0 license. See [LICENSE.txt](https://github.com/spf13/cobra/blob/main/LICENSE.txt)
|
||||
notices: []
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/crypto/argon2
|
||||
version: v0.17.0
|
||||
version: v0.19.0
|
||||
type: go
|
||||
summary: Package argon2 implements the key derivation function Argon2.
|
||||
homepage: https://pkg.go.dev/golang.org/x/crypto/argon2
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/crypto/blake2b
|
||||
version: v0.17.0
|
||||
version: v0.19.0
|
||||
type: go
|
||||
summary: Package blake2b implements the BLAKE2b hash algorithm defined by RFC 7693
|
||||
and the extendable output function (XOF) BLAKE2Xb.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/crypto/blowfish
|
||||
version: v0.17.0
|
||||
version: v0.19.0
|
||||
type: go
|
||||
summary: Package blowfish implements Bruce Schneier's Blowfish encryption algorithm.
|
||||
homepage: https://pkg.go.dev/golang.org/x/crypto/blowfish
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/crypto/cast5
|
||||
version: v0.17.0
|
||||
version: v0.19.0
|
||||
type: go
|
||||
summary: Package cast5 implements CAST5, as defined in RFC 2144.
|
||||
homepage: https://pkg.go.dev/golang.org/x/crypto/cast5
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/crypto/curve25519
|
||||
version: v0.17.0
|
||||
version: v0.19.0
|
||||
type: go
|
||||
summary: Package curve25519 provides an implementation of the X25519 function, which
|
||||
performs scalar multiplication on the elliptic curve known as Curve25519.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/crypto/openpgp
|
||||
version: v0.17.0
|
||||
version: v0.19.0
|
||||
type: go
|
||||
summary: Package openpgp implements high level operations on OpenPGP messages.
|
||||
homepage: https://pkg.go.dev/golang.org/x/crypto/openpgp
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/crypto/openpgp/armor
|
||||
version: v0.17.0
|
||||
version: v0.19.0
|
||||
type: go
|
||||
summary: Package armor implements OpenPGP ASCII Armor, see RFC 4880.
|
||||
homepage: https://pkg.go.dev/golang.org/x/crypto/openpgp/armor
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/crypto/openpgp/elgamal
|
||||
version: v0.17.0
|
||||
version: v0.19.0
|
||||
type: go
|
||||
summary: Package elgamal implements ElGamal encryption, suitable for OpenPGP, as specified
|
||||
in "A Public-Key Cryptosystem and a Signature Scheme Based on Discrete Logarithms,"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/crypto/openpgp/errors
|
||||
version: v0.17.0
|
||||
version: v0.19.0
|
||||
type: go
|
||||
summary: Package errors contains common error types for the OpenPGP packages.
|
||||
homepage: https://pkg.go.dev/golang.org/x/crypto/openpgp/errors
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/crypto/openpgp/packet
|
||||
version: v0.17.0
|
||||
version: v0.19.0
|
||||
type: go
|
||||
summary: Package packet implements parsing and serialization of OpenPGP packets, as
|
||||
specified in RFC 4880.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/crypto/openpgp/s2k
|
||||
version: v0.17.0
|
||||
version: v0.19.0
|
||||
type: go
|
||||
summary: Package s2k implements the various OpenPGP string-to-key transforms as specified
|
||||
in RFC 4800 section 3.7.1.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/crypto/sha3
|
||||
version: v0.17.0
|
||||
version: v0.19.0
|
||||
type: go
|
||||
summary: Package sha3 implements the SHA-3 fixed-output-length hash functions and
|
||||
the SHAKE variable-output-length hash functions defined by FIPS-202.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/crypto/ssh
|
||||
version: v0.17.0
|
||||
version: v0.19.0
|
||||
type: go
|
||||
summary: Package ssh implements an SSH client and server.
|
||||
homepage: https://pkg.go.dev/golang.org/x/crypto/ssh
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/crypto/ssh/agent
|
||||
version: v0.17.0
|
||||
version: v0.19.0
|
||||
type: go
|
||||
summary: Package agent implements the ssh-agent protocol, and provides both a client
|
||||
and a server.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/crypto/ssh/internal/bcrypt_pbkdf
|
||||
version: v0.17.0
|
||||
version: v0.19.0
|
||||
type: go
|
||||
summary: Package bcrypt_pbkdf implements bcrypt_pbkdf(3) from OpenBSD.
|
||||
homepage: https://pkg.go.dev/golang.org/x/crypto/ssh/internal/bcrypt_pbkdf
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/crypto/ssh/knownhosts
|
||||
version: v0.17.0
|
||||
version: v0.19.0
|
||||
type: go
|
||||
summary: Package knownhosts implements a parser for the OpenSSH known_hosts host key
|
||||
database, and provides utility functions for writing OpenSSH compliant known_hosts
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/net/context
|
||||
version: v0.19.0
|
||||
version: v0.21.0
|
||||
type: go
|
||||
summary: Package context defines the Context type, which carries deadlines, cancelation
|
||||
signals, and other request-scoped values across API boundaries and between processes.
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
---
|
||||
name: golang.org/x/net/context/ctxhttp
|
||||
version: v0.19.0
|
||||
type: go
|
||||
summary: Package ctxhttp provides helper functions for performing context-aware HTTP
|
||||
requests.
|
||||
homepage: https://pkg.go.dev/golang.org/x/net/context/ctxhttp
|
||||
license: other
|
||||
licenses:
|
||||
- sources: net/LICENSE
|
||||
text: |
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
- sources: net/PATENTS
|
||||
text: |
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
||||
notices: []
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/net/internal/socks
|
||||
version: v0.19.0
|
||||
version: v0.21.0
|
||||
type: go
|
||||
summary: Package socks provides a SOCKS version 5 client implementation.
|
||||
homepage: https://pkg.go.dev/golang.org/x/net/internal/socks
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/net/proxy
|
||||
version: v0.19.0
|
||||
version: v0.21.0
|
||||
type: go
|
||||
summary: Package proxy provides support for a variety of protocols to proxy network
|
||||
data.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/oauth2
|
||||
version: v0.0.0-20220411215720-9780585627b5
|
||||
version: v0.17.0
|
||||
type: go
|
||||
summary: Package oauth2 provides support for making OAuth2 authorized and authenticated
|
||||
HTTP requests, as specified in RFC 6749.
|
||||
@@ -36,9 +36,4 @@ licenses:
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
notices:
|
||||
- sources: AUTHORS
|
||||
text: |-
|
||||
# This source code refers to The Go Authors for copyright purposes.
|
||||
# The master list of authors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/AUTHORS.
|
||||
notices: []
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/oauth2/internal
|
||||
version: v0.0.0-20220411215720-9780585627b5
|
||||
version: v0.17.0
|
||||
type: go
|
||||
summary: Package internal contains support packages for oauth2 package.
|
||||
homepage: https://pkg.go.dev/golang.org/x/oauth2/internal
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/sys/execabs
|
||||
version: v0.15.0
|
||||
version: v0.17.0
|
||||
type: go
|
||||
summary: Package execabs is a drop-in replacement for os/exec that requires PATH lookups
|
||||
to find absolute paths.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: golang.org/x/sys/unix
|
||||
version: v0.15.0
|
||||
version: v0.17.0
|
||||
type: go
|
||||
summary: Package unix contains an interface to the low-level operating system primitives.
|
||||
homepage: https://pkg.go.dev/golang.org/x/sys/unix
|
||||
|
||||
@@ -5,11 +5,11 @@ go 1.21
|
||||
require (
|
||||
github.com/go-git/go-git/v5 v5.11.0
|
||||
github.com/google/go-github/v43 v43.0.0
|
||||
github.com/gorilla/mux v1.7.4
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/spf13/cobra v1.4.0
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
|
||||
golang.org/x/oauth2 v0.17.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -23,9 +23,9 @@ require (
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.5.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||
@@ -34,13 +34,13 @@ require (
|
||||
github.com/skeema/knownhosts v1.2.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
golang.org/x/crypto v0.17.0 // indirect
|
||||
golang.org/x/crypto v0.19.0 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/net v0.19.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
golang.org/x/sys v0.17.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -1,40 +1,5 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
@@ -45,16 +10,10 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuW
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -64,10 +23,6 @@ github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcej
|
||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
||||
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
@@ -78,47 +33,12 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.11.0 h1:XIZc1p+8YzypNr34itUfSvYJcv+eYdTnTvOZ2vD3cA4=
|
||||
github.com/go-git/go-git/v5 v5.11.0/go.mod h1:6GFcX2P3NM7FPBfpePbpLd21XxsgdAt+lKqXmCUiUCY=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
@@ -127,32 +47,14 @@ github.com/google/go-github/v43 v43.0.0 h1:y+GL7LIsAIF2NZlJ46ZoC/D1W1ivZasT0lnWH
|
||||
github.com/google/go-github/v43 v43.0.0/go.mod h1:ZkTvvmCXBvsfPpTHXnH/d2hP9Y0cTbvN9kr5xqyXOIc=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
@@ -168,8 +70,6 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
@@ -178,8 +78,8 @@ github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNX
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ=
|
||||
github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
|
||||
github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q=
|
||||
github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@@ -189,145 +89,41 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE=
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
|
||||
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -335,18 +131,16 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@@ -356,156 +150,27 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
; https://editorconfig.org/
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[{Makefile,go.mod,go.sum,*.go,.gitmodules}]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[*.md]
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
eclint_indent_style = unset
|
||||
Generated
+1
@@ -0,0 +1 @@
|
||||
coverage.coverprofile
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
# This is the official list of gorilla/mux authors for copyright purposes.
|
||||
#
|
||||
# Please keep the list sorted.
|
||||
|
||||
Google LLC (https://opensource.google.com/)
|
||||
Kamil Kisielk <[email protected]>
|
||||
Matt Silverlock <[email protected]>
|
||||
Rodrigo Moraes (https://github.com/moraes)
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2012-2018 The Gorilla Authors. All rights reserved.
|
||||
Copyright (c) 2023 The Gorilla Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
GO_LINT=$(shell which golangci-lint 2> /dev/null || echo '')
|
||||
GO_LINT_URI=github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||
|
||||
GO_SEC=$(shell which gosec 2> /dev/null || echo '')
|
||||
GO_SEC_URI=github.com/securego/gosec/v2/cmd/gosec@latest
|
||||
|
||||
GO_VULNCHECK=$(shell which govulncheck 2> /dev/null || echo '')
|
||||
GO_VULNCHECK_URI=golang.org/x/vuln/cmd/govulncheck@latest
|
||||
|
||||
.PHONY: golangci-lint
|
||||
golangci-lint:
|
||||
$(if $(GO_LINT), ,go install $(GO_LINT_URI))
|
||||
@echo "##### Running golangci-lint"
|
||||
golangci-lint run -v
|
||||
|
||||
.PHONY: gosec
|
||||
gosec:
|
||||
$(if $(GO_SEC), ,go install $(GO_SEC_URI))
|
||||
@echo "##### Running gosec"
|
||||
gosec ./...
|
||||
|
||||
.PHONY: govulncheck
|
||||
govulncheck:
|
||||
$(if $(GO_VULNCHECK), ,go install $(GO_VULNCHECK_URI))
|
||||
@echo "##### Running govulncheck"
|
||||
govulncheck ./...
|
||||
|
||||
.PHONY: verify
|
||||
verify: golangci-lint gosec govulncheck
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
@echo "##### Running tests"
|
||||
go test -race -cover -coverprofile=coverage.coverprofile -covermode=atomic -v ./...
|
||||
+35
-28
@@ -1,12 +1,12 @@
|
||||
# gorilla/mux
|
||||
|
||||
[](https://godoc.org/github.com/gorilla/mux)
|
||||
[](https://circleci.com/gh/gorilla/mux)
|
||||
[](https://sourcegraph.com/github.com/gorilla/mux?badge)
|
||||

|
||||
[](https://codecov.io/github/gorilla/mux)
|
||||
[](https://godoc.org/github.com/gorilla/mux)
|
||||
[](https://sourcegraph.com/github.com/gorilla/mux?badge)
|
||||
|
||||

|
||||
|
||||
https://www.gorillatoolkit.org/pkg/mux
|
||||

|
||||
|
||||
Package `gorilla/mux` implements a request router and dispatcher for matching incoming requests to
|
||||
their respective handler.
|
||||
@@ -247,32 +247,25 @@ type spaHandler struct {
|
||||
// file located at the index path on the SPA handler will be served. This
|
||||
// is suitable behavior for serving an SPA (single page application).
|
||||
func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// get the absolute path to prevent directory traversal
|
||||
path, err := filepath.Abs(r.URL.Path)
|
||||
if err != nil {
|
||||
// if we failed to get the absolute path respond with a 400 bad request
|
||||
// and stop
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// Join internally call path.Clean to prevent directory traversal
|
||||
path := filepath.Join(h.staticPath, r.URL.Path)
|
||||
|
||||
// prepend the path with the path to the static directory
|
||||
path = filepath.Join(h.staticPath, path)
|
||||
|
||||
// check whether a file exists at the given path
|
||||
_, err = os.Stat(path)
|
||||
if os.IsNotExist(err) {
|
||||
// file does not exist, serve index.html
|
||||
// check whether a file exists or is a directory at the given path
|
||||
fi, err := os.Stat(path)
|
||||
if os.IsNotExist(err) || fi.IsDir() {
|
||||
// file does not exist or path is a directory, serve index.html
|
||||
http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath))
|
||||
return
|
||||
} else if err != nil {
|
||||
// if we got an error (that wasn't that the file doesn't exist) stating the
|
||||
// file, return a 500 internal server error and stop
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// otherwise, use http.FileServer to serve the static dir
|
||||
if err != nil {
|
||||
// if we got an error (that wasn't that the file doesn't exist) stating the
|
||||
// file, return a 500 internal server error and stop
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// otherwise, use http.FileServer to serve the static file
|
||||
http.FileServer(http.Dir(h.staticPath)).ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
@@ -375,6 +368,19 @@ url, err := r.Get("article").URL("subdomain", "news",
|
||||
"id", "42")
|
||||
```
|
||||
|
||||
To find all the required variables for a given route when calling `URL()`, the method `GetVarNames()` is available:
|
||||
```go
|
||||
r := mux.NewRouter()
|
||||
r.Host("{domain}").
|
||||
Path("/{group}/{item_id}").
|
||||
Queries("some_data1", "{some_data1}").
|
||||
Queries("some_data2", "{some_data2}").
|
||||
Name("article")
|
||||
|
||||
// Will print [domain group item_id some_data1 some_data2] <nil>
|
||||
fmt.Println(r.Get("article").GetVarNames())
|
||||
|
||||
```
|
||||
### Walking Routes
|
||||
|
||||
The `Walk` function on `mux.Router` can be used to visit all of the routes that are registered on a router. For example,
|
||||
@@ -572,7 +578,7 @@ func (amw *authenticationMiddleware) Middleware(next http.Handler) http.Handler
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/", handler)
|
||||
|
||||
amw := authenticationMiddleware{}
|
||||
amw := authenticationMiddleware{tokenUsers: make(map[string]string)}
|
||||
amw.Populate()
|
||||
|
||||
r.Use(amw.Middleware)
|
||||
@@ -758,7 +764,8 @@ func TestMetricsHandler(t *testing.T) {
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
// Need to create a router that we can pass the request through so that the vars will be added to the context
|
||||
// To add the vars to the context,
|
||||
// we need to create a router through which we can pass the request.
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/metrics/{type}", MetricsHandler)
|
||||
router.ServeHTTP(rr, req)
|
||||
|
||||
+12
-13
@@ -10,18 +10,18 @@ http.ServeMux, mux.Router matches incoming requests against a list of
|
||||
registered routes and calls a handler for the route that matches the URL
|
||||
or other conditions. The main features are:
|
||||
|
||||
* Requests can be matched based on URL host, path, path prefix, schemes,
|
||||
header and query values, HTTP methods or using custom matchers.
|
||||
* URL hosts, paths and query values can have variables with an optional
|
||||
regular expression.
|
||||
* Registered URLs can be built, or "reversed", which helps maintaining
|
||||
references to resources.
|
||||
* Routes can be used as subrouters: nested routes are only tested if the
|
||||
parent route matches. This is useful to define groups of routes that
|
||||
share common conditions like a host, a path prefix or other repeated
|
||||
attributes. As a bonus, this optimizes request matching.
|
||||
* It implements the http.Handler interface so it is compatible with the
|
||||
standard http.ServeMux.
|
||||
- Requests can be matched based on URL host, path, path prefix, schemes,
|
||||
header and query values, HTTP methods or using custom matchers.
|
||||
- URL hosts, paths and query values can have variables with an optional
|
||||
regular expression.
|
||||
- Registered URLs can be built, or "reversed", which helps maintaining
|
||||
references to resources.
|
||||
- Routes can be used as subrouters: nested routes are only tested if the
|
||||
parent route matches. This is useful to define groups of routes that
|
||||
share common conditions like a host, a path prefix or other repeated
|
||||
attributes. As a bonus, this optimizes request matching.
|
||||
- It implements the http.Handler interface so it is compatible with the
|
||||
standard http.ServeMux.
|
||||
|
||||
Let's start registering a couple of URL paths and handlers:
|
||||
|
||||
@@ -301,6 +301,5 @@ A more complex authentication middleware, which maps session token to users, cou
|
||||
r.Use(amw.Middleware)
|
||||
|
||||
Note: The handler chain will be stopped if your middleware doesn't call `next.ServeHTTP()` with the corresponding parameters. This can be used to abort a request if the middleware writer wants to.
|
||||
|
||||
*/
|
||||
package mux
|
||||
|
||||
+10
-9
@@ -31,24 +31,26 @@ func NewRouter() *Router {
|
||||
// It implements the http.Handler interface, so it can be registered to serve
|
||||
// requests:
|
||||
//
|
||||
// var router = mux.NewRouter()
|
||||
// var router = mux.NewRouter()
|
||||
//
|
||||
// func main() {
|
||||
// http.Handle("/", router)
|
||||
// }
|
||||
// func main() {
|
||||
// http.Handle("/", router)
|
||||
// }
|
||||
//
|
||||
// Or, for Google App Engine, register it in a init() function:
|
||||
//
|
||||
// func init() {
|
||||
// http.Handle("/", router)
|
||||
// }
|
||||
// func init() {
|
||||
// http.Handle("/", router)
|
||||
// }
|
||||
//
|
||||
// This will send all incoming requests to the router.
|
||||
type Router struct {
|
||||
// Configurable Handler to be used when no route matches.
|
||||
// This can be used to render your own 404 Not Found errors.
|
||||
NotFoundHandler http.Handler
|
||||
|
||||
// Configurable Handler to be used when the request method does not match the route.
|
||||
// This can be used to render your own 405 Method Not Allowed errors.
|
||||
MethodNotAllowedHandler http.Handler
|
||||
|
||||
// Routes to be matched, in order.
|
||||
@@ -435,8 +437,7 @@ func Vars(r *http.Request) map[string]string {
|
||||
// CurrentRoute returns the matched route for the current request, if any.
|
||||
// This only works when called inside the handler of the matched route
|
||||
// because the matched route is stored in the request context which is cleared
|
||||
// after the handler returns, unless the KeepContext option is set on the
|
||||
// Router.
|
||||
// after the handler returns.
|
||||
func CurrentRoute(r *http.Request) *Route {
|
||||
if rv := r.Context().Value(routeKey); rv != nil {
|
||||
return rv.(*Route)
|
||||
|
||||
+11
-5
@@ -22,10 +22,10 @@ type routeRegexpOptions struct {
|
||||
type regexpType int
|
||||
|
||||
const (
|
||||
regexpTypePath regexpType = 0
|
||||
regexpTypeHost regexpType = 1
|
||||
regexpTypePrefix regexpType = 2
|
||||
regexpTypeQuery regexpType = 3
|
||||
regexpTypePath regexpType = iota
|
||||
regexpTypeHost
|
||||
regexpTypePrefix
|
||||
regexpTypeQuery
|
||||
)
|
||||
|
||||
// newRouteRegexp parses a route template and returns a routeRegexp,
|
||||
@@ -195,7 +195,7 @@ func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
|
||||
|
||||
// url builds a URL part using the given values.
|
||||
func (r *routeRegexp) url(values map[string]string) (string, error) {
|
||||
urlValues := make([]interface{}, len(r.varsN), len(r.varsN))
|
||||
urlValues := make([]interface{}, len(r.varsN))
|
||||
for k, v := range r.varsN {
|
||||
value, ok := values[v]
|
||||
if !ok {
|
||||
@@ -325,6 +325,12 @@ func (v routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
|
||||
// Store host variables.
|
||||
if v.host != nil {
|
||||
host := getHost(req)
|
||||
if v.host.wildcardHostPort {
|
||||
// Don't be strict on the port match
|
||||
if i := strings.Index(host, ":"); i != -1 {
|
||||
host = host[:i]
|
||||
}
|
||||
}
|
||||
matches := v.host.regexp.FindStringSubmatchIndex(host)
|
||||
if len(matches) > 0 {
|
||||
extractVars(host, matches, v.host.varsN, m.Vars)
|
||||
|
||||
+69
-40
@@ -64,8 +64,18 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
|
||||
match.MatchErr = nil
|
||||
}
|
||||
|
||||
matchErr = nil
|
||||
matchErr = nil // nolint:ineffassign
|
||||
return false
|
||||
} else {
|
||||
// Multiple routes may share the same path but use different HTTP methods. For instance:
|
||||
// Route 1: POST "/users/{id}".
|
||||
// Route 2: GET "/users/{id}", parameters: "id": "[0-9]+".
|
||||
//
|
||||
// The router must handle these cases correctly. For a GET request to "/users/abc" with "id" as "-2",
|
||||
// The router should return a "Not Found" error as no route fully matches this request.
|
||||
if match.MatchErr == ErrMethodMismatch {
|
||||
match.MatchErr = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,9 +240,9 @@ func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
|
||||
// Headers adds a matcher for request header values.
|
||||
// It accepts a sequence of key/value pairs to be matched. For example:
|
||||
//
|
||||
// r := mux.NewRouter()
|
||||
// r.Headers("Content-Type", "application/json",
|
||||
// "X-Requested-With", "XMLHttpRequest")
|
||||
// r := mux.NewRouter().NewRoute()
|
||||
// r.Headers("Content-Type", "application/json",
|
||||
// "X-Requested-With", "XMLHttpRequest")
|
||||
//
|
||||
// The above route will only match if both request header values match.
|
||||
// If the value is an empty string, it will match any value if the key is set.
|
||||
@@ -255,9 +265,9 @@ func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool {
|
||||
// HeadersRegexp accepts a sequence of key/value pairs, where the value has regex
|
||||
// support. For example:
|
||||
//
|
||||
// r := mux.NewRouter()
|
||||
// r.HeadersRegexp("Content-Type", "application/(text|json)",
|
||||
// "X-Requested-With", "XMLHttpRequest")
|
||||
// r := mux.NewRouter().NewRoute()
|
||||
// r.HeadersRegexp("Content-Type", "application/(text|json)",
|
||||
// "X-Requested-With", "XMLHttpRequest")
|
||||
//
|
||||
// The above route will only match if both the request header matches both regular expressions.
|
||||
// If the value is an empty string, it will match any value if the key is set.
|
||||
@@ -283,10 +293,10 @@ func (r *Route) HeadersRegexp(pairs ...string) *Route {
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// r := mux.NewRouter()
|
||||
// r.Host("www.example.com")
|
||||
// r.Host("{subdomain}.domain.com")
|
||||
// r.Host("{subdomain:[a-z]+}.domain.com")
|
||||
// r := mux.NewRouter().NewRoute()
|
||||
// r.Host("www.example.com")
|
||||
// r.Host("{subdomain}.domain.com")
|
||||
// r.Host("{subdomain:[a-z]+}.domain.com")
|
||||
//
|
||||
// Variable names must be unique in a given route. They can be retrieved
|
||||
// calling mux.Vars(request).
|
||||
@@ -342,11 +352,11 @@ func (r *Route) Methods(methods ...string) *Route {
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// r := mux.NewRouter()
|
||||
// r.Path("/products/").Handler(ProductsHandler)
|
||||
// r.Path("/products/{key}").Handler(ProductsHandler)
|
||||
// r.Path("/articles/{category}/{id:[0-9]+}").
|
||||
// Handler(ArticleHandler)
|
||||
// r := mux.NewRouter().NewRoute()
|
||||
// r.Path("/products/").Handler(ProductsHandler)
|
||||
// r.Path("/products/{key}").Handler(ProductsHandler)
|
||||
// r.Path("/articles/{category}/{id:[0-9]+}").
|
||||
// Handler(ArticleHandler)
|
||||
//
|
||||
// Variable names must be unique in a given route. They can be retrieved
|
||||
// calling mux.Vars(request).
|
||||
@@ -377,8 +387,8 @@ func (r *Route) PathPrefix(tpl string) *Route {
|
||||
// It accepts a sequence of key/value pairs. Values may define variables.
|
||||
// For example:
|
||||
//
|
||||
// r := mux.NewRouter()
|
||||
// r.Queries("foo", "bar", "id", "{id:[0-9]+}")
|
||||
// r := mux.NewRouter().NewRoute()
|
||||
// r.Queries("foo", "bar", "id", "{id:[0-9]+}")
|
||||
//
|
||||
// The above route will only match if the URL contains the defined queries
|
||||
// values, e.g.: ?foo=bar&id=42.
|
||||
@@ -473,11 +483,11 @@ func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
|
||||
//
|
||||
// It will test the inner routes only if the parent route matched. For example:
|
||||
//
|
||||
// r := mux.NewRouter()
|
||||
// s := r.Host("www.example.com").Subrouter()
|
||||
// s.HandleFunc("/products/", ProductsHandler)
|
||||
// s.HandleFunc("/products/{key}", ProductHandler)
|
||||
// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
|
||||
// r := mux.NewRouter().NewRoute()
|
||||
// s := r.Host("www.example.com").Subrouter()
|
||||
// s.HandleFunc("/products/", ProductsHandler)
|
||||
// s.HandleFunc("/products/{key}", ProductHandler)
|
||||
// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
|
||||
//
|
||||
// Here, the routes registered in the subrouter won't be tested if the host
|
||||
// doesn't match.
|
||||
@@ -497,36 +507,36 @@ func (r *Route) Subrouter() *Router {
|
||||
// It accepts a sequence of key/value pairs for the route variables. For
|
||||
// example, given this route:
|
||||
//
|
||||
// r := mux.NewRouter()
|
||||
// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
|
||||
// Name("article")
|
||||
// r := mux.NewRouter()
|
||||
// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
|
||||
// Name("article")
|
||||
//
|
||||
// ...a URL for it can be built using:
|
||||
//
|
||||
// url, err := r.Get("article").URL("category", "technology", "id", "42")
|
||||
// url, err := r.Get("article").URL("category", "technology", "id", "42")
|
||||
//
|
||||
// ...which will return an url.URL with the following path:
|
||||
//
|
||||
// "/articles/technology/42"
|
||||
// "/articles/technology/42"
|
||||
//
|
||||
// This also works for host variables:
|
||||
//
|
||||
// r := mux.NewRouter()
|
||||
// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
|
||||
// Host("{subdomain}.domain.com").
|
||||
// Name("article")
|
||||
// r := mux.NewRouter()
|
||||
// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
|
||||
// Host("{subdomain}.domain.com").
|
||||
// Name("article")
|
||||
//
|
||||
// // url.String() will be "http://news.domain.com/articles/technology/42"
|
||||
// url, err := r.Get("article").URL("subdomain", "news",
|
||||
// "category", "technology",
|
||||
// "id", "42")
|
||||
// // url.String() will be "http://news.domain.com/articles/technology/42"
|
||||
// url, err := r.Get("article").URL("subdomain", "news",
|
||||
// "category", "technology",
|
||||
// "id", "42")
|
||||
//
|
||||
// The scheme of the resulting url will be the first argument that was passed to Schemes:
|
||||
//
|
||||
// // url.String() will be "https://example.com"
|
||||
// r := mux.NewRouter()
|
||||
// url, err := r.Host("example.com")
|
||||
// .Schemes("https", "http").URL()
|
||||
// // url.String() will be "https://example.com"
|
||||
// r := mux.NewRouter().NewRoute()
|
||||
// url, err := r.Host("example.com")
|
||||
// .Schemes("https", "http").URL()
|
||||
//
|
||||
// All variables defined in the route are required, and their values must
|
||||
// conform to the corresponding patterns.
|
||||
@@ -718,6 +728,25 @@ func (r *Route) GetHostTemplate() (string, error) {
|
||||
return r.regexp.host.template, nil
|
||||
}
|
||||
|
||||
// GetVarNames returns the names of all variables added by regexp matchers
|
||||
// These can be used to know which route variables should be passed into r.URL()
|
||||
func (r *Route) GetVarNames() ([]string, error) {
|
||||
if r.err != nil {
|
||||
return nil, r.err
|
||||
}
|
||||
var varNames []string
|
||||
if r.regexp.host != nil {
|
||||
varNames = append(varNames, r.regexp.host.varsN...)
|
||||
}
|
||||
if r.regexp.path != nil {
|
||||
varNames = append(varNames, r.regexp.path.varsN...)
|
||||
}
|
||||
for _, regx := range r.regexp.queries {
|
||||
varNames = append(varNames, regx.varsN...)
|
||||
}
|
||||
return varNames, nil
|
||||
}
|
||||
|
||||
// prepareVars converts the route variable pairs into a map. If the route has a
|
||||
// BuildVarsFunc, it is invoked.
|
||||
func (r *Route) prepareVars(pairs ...string) (map[string]string, error) {
|
||||
|
||||
+198
-10
@@ -1,13 +1,201 @@
|
||||
Copyright 2014 Alan Shreve
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
1. Definitions.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2022 Alan Shreve (@inconshreveable)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package mousetrap
|
||||
|
||||
+20
-76
@@ -1,79 +1,30 @@
|
||||
// +build windows
|
||||
// +build !go1.4
|
||||
|
||||
package mousetrap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// defined by the Win32 API
|
||||
th32cs_snapprocess uintptr = 0x2
|
||||
)
|
||||
|
||||
var (
|
||||
kernel = syscall.MustLoadDLL("kernel32.dll")
|
||||
CreateToolhelp32Snapshot = kernel.MustFindProc("CreateToolhelp32Snapshot")
|
||||
Process32First = kernel.MustFindProc("Process32FirstW")
|
||||
Process32Next = kernel.MustFindProc("Process32NextW")
|
||||
)
|
||||
|
||||
// ProcessEntry32 structure defined by the Win32 API
|
||||
type processEntry32 struct {
|
||||
dwSize uint32
|
||||
cntUsage uint32
|
||||
th32ProcessID uint32
|
||||
th32DefaultHeapID int
|
||||
th32ModuleID uint32
|
||||
cntThreads uint32
|
||||
th32ParentProcessID uint32
|
||||
pcPriClassBase int32
|
||||
dwFlags uint32
|
||||
szExeFile [syscall.MAX_PATH]uint16
|
||||
}
|
||||
|
||||
func getProcessEntry(pid int) (pe *processEntry32, err error) {
|
||||
snapshot, _, e1 := CreateToolhelp32Snapshot.Call(th32cs_snapprocess, uintptr(0))
|
||||
if snapshot == uintptr(syscall.InvalidHandle) {
|
||||
err = fmt.Errorf("CreateToolhelp32Snapshot: %v", e1)
|
||||
return
|
||||
}
|
||||
defer syscall.CloseHandle(syscall.Handle(snapshot))
|
||||
|
||||
var processEntry processEntry32
|
||||
processEntry.dwSize = uint32(unsafe.Sizeof(processEntry))
|
||||
ok, _, e1 := Process32First.Call(snapshot, uintptr(unsafe.Pointer(&processEntry)))
|
||||
if ok == 0 {
|
||||
err = fmt.Errorf("Process32First: %v", e1)
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
if processEntry.th32ProcessID == uint32(pid) {
|
||||
pe = &processEntry
|
||||
return
|
||||
}
|
||||
|
||||
ok, _, e1 = Process32Next.Call(snapshot, uintptr(unsafe.Pointer(&processEntry)))
|
||||
if ok == 0 {
|
||||
err = fmt.Errorf("Process32Next: %v", e1)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getppid() (pid int, err error) {
|
||||
pe, err := getProcessEntry(os.Getpid())
|
||||
func getProcessEntry(pid int) (*syscall.ProcessEntry32, error) {
|
||||
snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
defer syscall.CloseHandle(snapshot)
|
||||
var procEntry syscall.ProcessEntry32
|
||||
procEntry.Size = uint32(unsafe.Sizeof(procEntry))
|
||||
if err = syscall.Process32First(snapshot, &procEntry); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for {
|
||||
if procEntry.ProcessID == uint32(pid) {
|
||||
return &procEntry, nil
|
||||
}
|
||||
err = syscall.Process32Next(snapshot, &procEntry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
pid = int(pe.th32ParentProcessID)
|
||||
return
|
||||
}
|
||||
|
||||
// StartedByExplorer returns true if the program was invoked by the user double-clicking
|
||||
@@ -83,16 +34,9 @@ func getppid() (pid int, err error) {
|
||||
// It does not guarantee that the program was run from a terminal. It only can tell you
|
||||
// whether it was launched from explorer.exe
|
||||
func StartedByExplorer() bool {
|
||||
ppid, err := getppid()
|
||||
pe, err := getProcessEntry(syscall.Getppid())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
pe, err := getProcessEntry(ppid)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
name := syscall.UTF16ToString(pe.szExeFile[:])
|
||||
return name == "explorer.exe"
|
||||
return "explorer.exe" == syscall.UTF16ToString(pe.ExeFile[:])
|
||||
}
|
||||
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
// +build windows
|
||||
// +build go1.4
|
||||
|
||||
package mousetrap
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func getProcessEntry(pid int) (*syscall.ProcessEntry32, error) {
|
||||
snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer syscall.CloseHandle(snapshot)
|
||||
var procEntry syscall.ProcessEntry32
|
||||
procEntry.Size = uint32(unsafe.Sizeof(procEntry))
|
||||
if err = syscall.Process32First(snapshot, &procEntry); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for {
|
||||
if procEntry.ProcessID == uint32(pid) {
|
||||
return &procEntry, nil
|
||||
}
|
||||
err = syscall.Process32Next(snapshot, &procEntry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// StartedByExplorer returns true if the program was invoked by the user double-clicking
|
||||
// on the executable from explorer.exe
|
||||
//
|
||||
// It is conservative and returns false if any of the internal calls fail.
|
||||
// It does not guarantee that the program was run from a terminal. It only can tell you
|
||||
// whether it was launched from explorer.exe
|
||||
func StartedByExplorer() bool {
|
||||
pe, err := getProcessEntry(os.Getppid())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return "explorer.exe" == syscall.UTF16ToString(pe.ExeFile[:])
|
||||
}
|
||||
+18
-4
@@ -1,3 +1,17 @@
|
||||
# Copyright 2013-2023 The Cobra Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
run:
|
||||
deadline: 5m
|
||||
|
||||
@@ -5,7 +19,7 @@ linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
#- bodyclose
|
||||
- deadcode
|
||||
# - deadcode ! deprecated since v1.49.0; replaced by 'unused'
|
||||
#- depguard
|
||||
#- dogsled
|
||||
#- dupl
|
||||
@@ -37,12 +51,12 @@ linters:
|
||||
#- rowserrcheck
|
||||
#- scopelint
|
||||
#- staticcheck
|
||||
- structcheck
|
||||
#- structcheck ! deprecated since v1.49.0; replaced by 'unused'
|
||||
#- stylecheck
|
||||
#- typecheck
|
||||
- unconvert
|
||||
#- unparam
|
||||
#- unused
|
||||
- varcheck
|
||||
- unused
|
||||
# - varcheck ! deprecated since v1.49.0; replaced by 'unused'
|
||||
#- whitespace
|
||||
fast: false
|
||||
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
# Cobra Changelog
|
||||
|
||||
## v1.1.3
|
||||
|
||||
* **Fix:** release-branch.cobra1.1 only: Revert "Deprecate Go < 1.14" to maintain backward compatibility
|
||||
|
||||
## v1.1.2
|
||||
|
||||
### Notable Changes
|
||||
|
||||
* Bump license year to 2021 in golden files (#1309) @Bowbaq
|
||||
* Enhance PowerShell completion with custom comp (#1208) @Luap99
|
||||
* Update gopkg.in/yaml.v2 to v2.4.0: The previous breaking change in yaml.v2 v2.3.0 has been reverted, see go-yaml/yaml#670
|
||||
* Documentation readability improvements (#1228 etc.) @zaataylor etc.
|
||||
* Use golangci-lint: Repair warnings and errors resulting from linting (#1044) @umarcor
|
||||
|
||||
## v1.1.1
|
||||
|
||||
* **Fix:** yaml.v2 2.3.0 contained a unintended breaking change. This release reverts to yaml.v2 v2.2.8 which has recent critical CVE fixes, but does not have the breaking changes. See https://github.com/spf13/cobra/pull/1259 for context.
|
||||
* **Fix:** correct internal formatting for go-md2man v2 (which caused man page generation to be broken). See https://github.com/spf13/cobra/issues/1049 for context.
|
||||
|
||||
## v1.1.0
|
||||
|
||||
### Notable Changes
|
||||
|
||||
* Extend Go completions and revamp zsh comp (#1070)
|
||||
* Fix man page doc generation - no auto generated tag when `cmd.DisableAutoGenTag = true` (#1104) @jpmcb
|
||||
* Add completion for help command (#1136)
|
||||
* Complete subcommands when TraverseChildren is set (#1171)
|
||||
* Fix stderr printing functions (#894)
|
||||
* fix: fish output redirection (#1247)
|
||||
|
||||
## v1.0.0
|
||||
|
||||
Announcing v1.0.0 of Cobra. 🎉
|
||||
|
||||
### Notable Changes
|
||||
* Fish completion (including support for Go custom completion) @marckhouzam
|
||||
* API (urgent): Rename BashCompDirectives to ShellCompDirectives @marckhouzam
|
||||
* Remove/replace SetOutput on Command - deprecated @jpmcb
|
||||
* add support for autolabel stale PR @xchapter7x
|
||||
* Add Labeler Actions @xchapter7x
|
||||
* Custom completions coded in Go (instead of Bash) @marckhouzam
|
||||
* Partial Revert of #922 @jharshman
|
||||
* Add Makefile to project @jharshman
|
||||
* Correct documentation for InOrStdin @desponda
|
||||
* Apply formatting to templates @jharshman
|
||||
* Revert change so help is printed on stdout again @marckhouzam
|
||||
* Update md2man to v2.0.0 @pdf
|
||||
* update viper to v1.4.0 @umarcor
|
||||
* Update cmd/root.go example in README.md @jharshman
|
||||
+4
-4
@@ -5,10 +5,6 @@ ifeq (, $(shell which golangci-lint))
|
||||
$(warning "could not find golangci-lint in $(PATH), run: curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh")
|
||||
endif
|
||||
|
||||
ifeq (, $(shell which richgo))
|
||||
$(warning "could not find richgo in $(PATH), run: go get github.com/kyoh86/richgo")
|
||||
endif
|
||||
|
||||
.PHONY: fmt lint test install_deps clean
|
||||
|
||||
default: all
|
||||
@@ -25,6 +21,10 @@ lint:
|
||||
|
||||
test: install_deps
|
||||
$(info ******************** running tests ********************)
|
||||
go test -v ./...
|
||||
|
||||
richtest: install_deps
|
||||
$(info ******************** running tests with kyoh86/richgo ********************)
|
||||
richgo test -v ./...
|
||||
|
||||
install_deps:
|
||||
|
||||
+13
-12
@@ -1,12 +1,12 @@
|
||||

|
||||

|
||||
|
||||
Cobra is a library for creating powerful modern CLI applications.
|
||||
|
||||
Cobra is used in many Go projects such as [Kubernetes](http://kubernetes.io/),
|
||||
[Hugo](https://gohugo.io), and [Github CLI](https://github.com/cli/cli) to
|
||||
name a few. [This list](./projects_using_cobra.md) contains a more extensive list of projects using Cobra.
|
||||
Cobra is used in many Go projects such as [Kubernetes](https://kubernetes.io/),
|
||||
[Hugo](https://gohugo.io), and [GitHub CLI](https://github.com/cli/cli) to
|
||||
name a few. [This list](site/content/projects_using_cobra.md) contains a more extensive list of projects using Cobra.
|
||||
|
||||
[](https://github.com/spf13/cobra/actions?query=workflow%3ATest)
|
||||
[](https://github.com/spf13/cobra/actions?query=workflow%3ATest)
|
||||
[](https://pkg.go.dev/github.com/spf13/cobra)
|
||||
[](https://goreportcard.com/report/github.com/spf13/cobra)
|
||||
[](https://gophers.slack.com/archives/CD3LP1199)
|
||||
@@ -23,12 +23,13 @@ Cobra provides:
|
||||
* Global, local and cascading flags
|
||||
* Intelligent suggestions (`app srver`... did you mean `app server`?)
|
||||
* Automatic help generation for commands and flags
|
||||
* Grouping help for subcommands
|
||||
* Automatic help flag recognition of `-h`, `--help`, etc.
|
||||
* Automatically generated shell autocomplete for your application (bash, zsh, fish, powershell)
|
||||
* Automatically generated man pages for your application
|
||||
* Command aliases so you can change things without breaking them
|
||||
* The flexibility to define your own help, usage, etc.
|
||||
* Optional seamless integration with [viper](http://github.com/spf13/viper) for 12-factor apps
|
||||
* Optional seamless integration with [viper](https://github.com/spf13/viper) for 12-factor apps
|
||||
|
||||
# Concepts
|
||||
|
||||
@@ -40,9 +41,9 @@ The best applications read like sentences when used, and as a result, users
|
||||
intuitively know how to interact with them.
|
||||
|
||||
The pattern to follow is
|
||||
`APPNAME VERB NOUN --ADJECTIVE.`
|
||||
`APPNAME VERB NOUN --ADJECTIVE`
|
||||
or
|
||||
`APPNAME COMMAND ARG --FLAG`
|
||||
`APPNAME COMMAND ARG --FLAG`.
|
||||
|
||||
A few good real world examples may better illustrate this point.
|
||||
|
||||
@@ -79,7 +80,7 @@ which maintains the same interface while adding POSIX compliance.
|
||||
|
||||
# Installing
|
||||
Using Cobra is easy. First, use `go get` to install the latest version
|
||||
of the library.
|
||||
of the library.
|
||||
|
||||
```
|
||||
go get -u github.com/spf13/cobra@latest
|
||||
@@ -102,10 +103,10 @@ It can be installed by running:
|
||||
go install github.com/spf13/cobra-cli@latest
|
||||
```
|
||||
|
||||
For complete details on using the Cobra-CLI generator, please read [The Cobra Generator README](https://github.com/spf13/cobra-cli/blob/master/README.md)
|
||||
For complete details on using the Cobra-CLI generator, please read [The Cobra Generator README](https://github.com/spf13/cobra-cli/blob/main/README.md)
|
||||
|
||||
For complete details on using the Cobra library, please read the [The Cobra User Guide](user_guide.md).
|
||||
For complete details on using the Cobra library, please read the [The Cobra User Guide](site/content/user_guide.md).
|
||||
|
||||
# License
|
||||
|
||||
Cobra is released under the Apache 2.0 license. See [LICENSE.txt](https://github.com/spf13/cobra/blob/master/LICENSE.txt)
|
||||
Cobra is released under the Apache 2.0 license. See [LICENSE.txt](LICENSE.txt)
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
// Copyright 2013-2023 The Cobra Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cobra
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
activeHelpMarker = "_activeHelp_ "
|
||||
// The below values should not be changed: programs will be using them explicitly
|
||||
// in their user documentation, and users will be using them explicitly.
|
||||
activeHelpEnvVarSuffix = "_ACTIVE_HELP"
|
||||
activeHelpGlobalEnvVar = "COBRA_ACTIVE_HELP"
|
||||
activeHelpGlobalDisable = "0"
|
||||
)
|
||||
|
||||
var activeHelpEnvVarPrefixSubstRegexp = regexp.MustCompile(`[^A-Z0-9_]`)
|
||||
|
||||
// AppendActiveHelp adds the specified string to the specified array to be used as ActiveHelp.
|
||||
// Such strings will be processed by the completion script and will be shown as ActiveHelp
|
||||
// to the user.
|
||||
// The array parameter should be the array that will contain the completions.
|
||||
// This function can be called multiple times before and/or after completions are added to
|
||||
// the array. Each time this function is called with the same array, the new
|
||||
// ActiveHelp line will be shown below the previous ones when completion is triggered.
|
||||
func AppendActiveHelp(compArray []string, activeHelpStr string) []string {
|
||||
return append(compArray, fmt.Sprintf("%s%s", activeHelpMarker, activeHelpStr))
|
||||
}
|
||||
|
||||
// GetActiveHelpConfig returns the value of the ActiveHelp environment variable
|
||||
// <PROGRAM>_ACTIVE_HELP where <PROGRAM> is the name of the root command in upper
|
||||
// case, with all non-ASCII-alphanumeric characters replaced by `_`.
|
||||
// It will always return "0" if the global environment variable COBRA_ACTIVE_HELP
|
||||
// is set to "0".
|
||||
func GetActiveHelpConfig(cmd *Command) string {
|
||||
activeHelpCfg := os.Getenv(activeHelpGlobalEnvVar)
|
||||
if activeHelpCfg != activeHelpGlobalDisable {
|
||||
activeHelpCfg = os.Getenv(activeHelpEnvVar(cmd.Root().Name()))
|
||||
}
|
||||
return activeHelpCfg
|
||||
}
|
||||
|
||||
// activeHelpEnvVar returns the name of the program-specific ActiveHelp environment
|
||||
// variable. It has the format <PROGRAM>_ACTIVE_HELP where <PROGRAM> is the name of the
|
||||
// root command in upper case, with all non-ASCII-alphanumeric characters replaced by `_`.
|
||||
func activeHelpEnvVar(name string) string {
|
||||
// This format should not be changed: users will be using it explicitly.
|
||||
activeHelpEnvVar := strings.ToUpper(fmt.Sprintf("%s%s", name, activeHelpEnvVarSuffix))
|
||||
activeHelpEnvVar = activeHelpEnvVarPrefixSubstRegexp.ReplaceAllString(activeHelpEnvVar, "_")
|
||||
return activeHelpEnvVar
|
||||
}
|
||||
+25
-15
@@ -1,3 +1,17 @@
|
||||
// Copyright 2013-2023 The Cobra Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cobra
|
||||
|
||||
import (
|
||||
@@ -7,7 +21,7 @@ import (
|
||||
|
||||
type PositionalArgs func(cmd *Command, args []string) error
|
||||
|
||||
// Legacy arg validation has the following behaviour:
|
||||
// legacyArgs validation has the following behaviour:
|
||||
// - root commands with no subcommands can take arbitrary arguments
|
||||
// - root commands with subcommands will do subcommand validity checking
|
||||
// - subcommands will always accept arbitrary arguments
|
||||
@@ -32,7 +46,8 @@ func NoArgs(cmd *Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnlyValidArgs returns an error if any args are not in the list of ValidArgs.
|
||||
// OnlyValidArgs returns an error if there are any positional args that are not in
|
||||
// the `ValidArgs` field of `Command`
|
||||
func OnlyValidArgs(cmd *Command, args []string) error {
|
||||
if len(cmd.ValidArgs) > 0 {
|
||||
// Remove any description that may be included in ValidArgs.
|
||||
@@ -41,7 +56,6 @@ func OnlyValidArgs(cmd *Command, args []string) error {
|
||||
for _, v := range cmd.ValidArgs {
|
||||
validArgs = append(validArgs, strings.Split(v, "\t")[0])
|
||||
}
|
||||
|
||||
for _, v := range args {
|
||||
if !stringInSlice(v, validArgs) {
|
||||
return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.findSuggestions(args[0]))
|
||||
@@ -86,18 +100,6 @@ func ExactArgs(n int) PositionalArgs {
|
||||
}
|
||||
}
|
||||
|
||||
// ExactValidArgs returns an error if
|
||||
// there are not exactly N positional args OR
|
||||
// there are any positional args that are not in the `ValidArgs` field of `Command`
|
||||
func ExactValidArgs(n int) PositionalArgs {
|
||||
return func(cmd *Command, args []string) error {
|
||||
if err := ExactArgs(n)(cmd, args); err != nil {
|
||||
return err
|
||||
}
|
||||
return OnlyValidArgs(cmd, args)
|
||||
}
|
||||
}
|
||||
|
||||
// RangeArgs returns an error if the number of args is not within the expected range.
|
||||
func RangeArgs(min int, max int) PositionalArgs {
|
||||
return func(cmd *Command, args []string) error {
|
||||
@@ -119,3 +121,11 @@ func MatchAll(pargs ...PositionalArgs) PositionalArgs {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ExactValidArgs returns an error if there are not exactly N positional args OR
|
||||
// there are any positional args that are not in the `ValidArgs` field of `Command`
|
||||
//
|
||||
// Deprecated: use MatchAll(ExactArgs(n), OnlyValidArgs) instead
|
||||
func ExactValidArgs(n int) PositionalArgs {
|
||||
return MatchAll(ExactArgs(n), OnlyValidArgs)
|
||||
}
|
||||
|
||||
+26
-11
@@ -1,3 +1,17 @@
|
||||
// Copyright 2013-2023 The Cobra Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cobra
|
||||
|
||||
import (
|
||||
@@ -71,9 +85,10 @@ __%[1]s_handle_go_custom_completion()
|
||||
local out requestComp lastParam lastChar comp directive args
|
||||
|
||||
# Prepare the command to request completions for the program.
|
||||
# Calling ${words[0]} instead of directly %[1]s allows to handle aliases
|
||||
# Calling ${words[0]} instead of directly %[1]s allows handling aliases
|
||||
args=("${words[@]:1}")
|
||||
requestComp="${words[0]} %[2]s ${args[*]}"
|
||||
# Disable ActiveHelp which is not supported for bash completion v1
|
||||
requestComp="%[8]s=0 ${words[0]} %[2]s ${args[*]}"
|
||||
|
||||
lastParam=${words[$((${#words[@]}-1))]}
|
||||
lastChar=${lastParam:$((${#lastParam}-1)):1}
|
||||
@@ -99,7 +114,7 @@ __%[1]s_handle_go_custom_completion()
|
||||
directive=0
|
||||
fi
|
||||
__%[1]s_debug "${FUNCNAME[0]}: the completion directive is: ${directive}"
|
||||
__%[1]s_debug "${FUNCNAME[0]}: the completions are: ${out[*]}"
|
||||
__%[1]s_debug "${FUNCNAME[0]}: the completions are: ${out}"
|
||||
|
||||
if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then
|
||||
# Error code. No completion.
|
||||
@@ -125,7 +140,7 @@ __%[1]s_handle_go_custom_completion()
|
||||
local fullFilter filter filteringCmd
|
||||
# Do not use quotes around the $out variable or else newline
|
||||
# characters will be kept.
|
||||
for filter in ${out[*]}; do
|
||||
for filter in ${out}; do
|
||||
fullFilter+="$filter|"
|
||||
done
|
||||
|
||||
@@ -136,7 +151,7 @@ __%[1]s_handle_go_custom_completion()
|
||||
# File completion for directories only
|
||||
local subdir
|
||||
# Use printf to strip any trailing newline
|
||||
subdir=$(printf "%%s" "${out[0]}")
|
||||
subdir=$(printf "%%s" "${out}")
|
||||
if [ -n "$subdir" ]; then
|
||||
__%[1]s_debug "Listing directories in $subdir"
|
||||
__%[1]s_handle_subdirs_in_dir_flag "$subdir"
|
||||
@@ -147,7 +162,7 @@ __%[1]s_handle_go_custom_completion()
|
||||
else
|
||||
while IFS='' read -r comp; do
|
||||
COMPREPLY+=("$comp")
|
||||
done < <(compgen -W "${out[*]}" -- "$cur")
|
||||
done < <(compgen -W "${out}" -- "$cur")
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -383,11 +398,11 @@ __%[1]s_handle_word()
|
||||
|
||||
`, name, ShellCompNoDescRequestCmd,
|
||||
ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
|
||||
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
|
||||
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, activeHelpEnvVar(name)))
|
||||
}
|
||||
|
||||
func writePostscript(buf io.StringWriter, name string) {
|
||||
name = strings.Replace(name, ":", "__", -1)
|
||||
name = strings.ReplaceAll(name, ":", "__")
|
||||
WriteStringAndCheck(buf, fmt.Sprintf("__start_%s()\n", name))
|
||||
WriteStringAndCheck(buf, fmt.Sprintf(`{
|
||||
local cur prev words cword split
|
||||
@@ -517,7 +532,7 @@ func writeLocalNonPersistentFlag(buf io.StringWriter, flag *pflag.Flag) {
|
||||
}
|
||||
}
|
||||
|
||||
// Setup annotations for go completions for registered flags
|
||||
// prepareCustomAnnotationsForFlags setup annotations for go completions for registered flags
|
||||
func prepareCustomAnnotationsForFlags(cmd *Command) {
|
||||
flagCompletionMutex.RLock()
|
||||
defer flagCompletionMutex.RUnlock()
|
||||
@@ -645,8 +660,8 @@ func gen(buf io.StringWriter, cmd *Command) {
|
||||
gen(buf, c)
|
||||
}
|
||||
commandName := cmd.CommandPath()
|
||||
commandName = strings.Replace(commandName, " ", "_", -1)
|
||||
commandName = strings.Replace(commandName, ":", "__", -1)
|
||||
commandName = strings.ReplaceAll(commandName, " ", "_")
|
||||
commandName = strings.ReplaceAll(commandName, ":", "__")
|
||||
|
||||
if cmd.Root() == cmd {
|
||||
WriteStringAndCheck(buf, fmt.Sprintf("_%s_root_command()\n{\n", commandName))
|
||||
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
# Generating Bash Completions For Your cobra.Command
|
||||
|
||||
Please refer to [Shell Completions](shell_completions.md) for details.
|
||||
|
||||
## Bash legacy dynamic completions
|
||||
|
||||
For backward compatibility, Cobra still supports its legacy dynamic completion solution (described below). Unlike the `ValidArgsFunction` solution, the legacy solution will only work for Bash shell-completion and not for other shells. This legacy solution can be used along-side `ValidArgsFunction` and `RegisterFlagCompletionFunc()`, as long as both solutions are not used for the same command. This provides a path to gradually migrate from the legacy solution to the new solution.
|
||||
|
||||
**Note**: Cobra's default `completion` command uses bash completion V2. If you are currently using Cobra's legacy dynamic completion solution, you should not use the default `completion` command but continue using your own.
|
||||
|
||||
The legacy solution allows you to inject bash functions into the bash completion script. Those bash functions are responsible for providing the completion choices for your own completions.
|
||||
|
||||
Some code that works in kubernetes:
|
||||
|
||||
```bash
|
||||
const (
|
||||
bash_completion_func = `__kubectl_parse_get()
|
||||
{
|
||||
local kubectl_output out
|
||||
if kubectl_output=$(kubectl get --no-headers "$1" 2>/dev/null); then
|
||||
out=($(echo "${kubectl_output}" | awk '{print $1}'))
|
||||
COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
|
||||
fi
|
||||
}
|
||||
|
||||
__kubectl_get_resource()
|
||||
{
|
||||
if [[ ${#nouns[@]} -eq 0 ]]; then
|
||||
return 1
|
||||
fi
|
||||
__kubectl_parse_get ${nouns[${#nouns[@]} -1]}
|
||||
if [[ $? -eq 0 ]]; then
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
__kubectl_custom_func() {
|
||||
case ${last_command} in
|
||||
kubectl_get | kubectl_describe | kubectl_delete | kubectl_stop)
|
||||
__kubectl_get_resource
|
||||
return
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
}
|
||||
`)
|
||||
```
|
||||
|
||||
And then I set that in my command definition:
|
||||
|
||||
```go
|
||||
cmds := &cobra.Command{
|
||||
Use: "kubectl",
|
||||
Short: "kubectl controls the Kubernetes cluster manager",
|
||||
Long: `kubectl controls the Kubernetes cluster manager.
|
||||
|
||||
Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
|
||||
Run: runHelp,
|
||||
BashCompletionFunction: bash_completion_func,
|
||||
}
|
||||
```
|
||||
|
||||
The `BashCompletionFunction` option is really only valid/useful on the root command. Doing the above will cause `__kubectl_custom_func()` (`__<command-use>_custom_func()`) to be called when the built in processor was unable to find a solution. In the case of kubernetes a valid command might look something like `kubectl get pod [mypod]`. If you type `kubectl get pod [tab][tab]` the `__kubectl_customc_func()` will run because the cobra.Command only understood "kubectl" and "get." `__kubectl_custom_func()` will see that the cobra.Command is "kubectl_get" and will thus call another helper `__kubectl_get_resource()`. `__kubectl_get_resource` will look at the 'nouns' collected. In our example the only noun will be `pod`. So it will call `__kubectl_parse_get pod`. `__kubectl_parse_get` will actually call out to kubernetes and get any pods. It will then set `COMPREPLY` to valid pods!
|
||||
|
||||
Similarly, for flags:
|
||||
|
||||
```go
|
||||
annotation := make(map[string][]string)
|
||||
annotation[cobra.BashCompCustom] = []string{"__kubectl_get_namespaces"}
|
||||
|
||||
flag := &pflag.Flag{
|
||||
Name: "namespace",
|
||||
Usage: usage,
|
||||
Annotations: annotation,
|
||||
}
|
||||
cmd.Flags().AddFlag(flag)
|
||||
```
|
||||
|
||||
In addition add the `__kubectl_get_namespaces` implementation in the `BashCompletionFunction`
|
||||
value, e.g.:
|
||||
|
||||
```bash
|
||||
__kubectl_get_namespaces()
|
||||
{
|
||||
local template
|
||||
template="{{ range .items }}{{ .metadata.name }} {{ end }}"
|
||||
local kubectl_out
|
||||
if kubectl_out=$(kubectl get -o template --template="${template}" namespace 2>/dev/null); then
|
||||
COMPREPLY=( $( compgen -W "${kubectl_out}[*]" -- "$cur" ) )
|
||||
fi
|
||||
}
|
||||
```
|
||||
+153
-88
@@ -1,3 +1,17 @@
|
||||
// Copyright 2013-2023 The Cobra Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cobra
|
||||
|
||||
import (
|
||||
@@ -24,7 +38,7 @@ func genBashComp(buf io.StringWriter, name string, includeDesc bool) {
|
||||
|
||||
__%[1]s_debug()
|
||||
{
|
||||
if [[ -n ${BASH_COMP_DEBUG_FILE:-} ]]; then
|
||||
if [[ -n ${BASH_COMP_DEBUG_FILE-} ]]; then
|
||||
echo "$*" >> "${BASH_COMP_DEBUG_FILE}"
|
||||
fi
|
||||
}
|
||||
@@ -43,7 +57,7 @@ __%[1]s_get_completion_results() {
|
||||
local requestComp lastParam lastChar args
|
||||
|
||||
# Prepare the command to request completions for the program.
|
||||
# Calling ${words[0]} instead of directly %[1]s allows to handle aliases
|
||||
# Calling ${words[0]} instead of directly %[1]s allows handling aliases
|
||||
args=("${words[@]:1}")
|
||||
requestComp="${words[0]} %[2]s ${args[*]}"
|
||||
|
||||
@@ -51,7 +65,7 @@ __%[1]s_get_completion_results() {
|
||||
lastChar=${lastParam:$((${#lastParam}-1)):1}
|
||||
__%[1]s_debug "lastParam ${lastParam}, lastChar ${lastChar}"
|
||||
|
||||
if [ -z "${cur}" ] && [ "${lastChar}" != "=" ]; then
|
||||
if [[ -z ${cur} && ${lastChar} != = ]]; then
|
||||
# If the last parameter is complete (there is a space following it)
|
||||
# We add an extra empty parameter so we can indicate this to the go method.
|
||||
__%[1]s_debug "Adding extra empty parameter"
|
||||
@@ -61,7 +75,7 @@ __%[1]s_get_completion_results() {
|
||||
# When completing a flag with an = (e.g., %[1]s -n=<TAB>)
|
||||
# bash focuses on the part after the =, so we need to remove
|
||||
# the flag part from $cur
|
||||
if [[ "${cur}" == -*=* ]]; then
|
||||
if [[ ${cur} == -*=* ]]; then
|
||||
cur="${cur#*=}"
|
||||
fi
|
||||
|
||||
@@ -73,12 +87,12 @@ __%[1]s_get_completion_results() {
|
||||
directive=${out##*:}
|
||||
# Remove the directive
|
||||
out=${out%%:*}
|
||||
if [ "${directive}" = "${out}" ]; then
|
||||
if [[ ${directive} == "${out}" ]]; then
|
||||
# There is not directive specified
|
||||
directive=0
|
||||
fi
|
||||
__%[1]s_debug "The completion directive is: ${directive}"
|
||||
__%[1]s_debug "The completions are: ${out[*]}"
|
||||
__%[1]s_debug "The completions are: ${out}"
|
||||
}
|
||||
|
||||
__%[1]s_process_completion_results() {
|
||||
@@ -87,22 +101,36 @@ __%[1]s_process_completion_results() {
|
||||
local shellCompDirectiveNoFileComp=%[5]d
|
||||
local shellCompDirectiveFilterFileExt=%[6]d
|
||||
local shellCompDirectiveFilterDirs=%[7]d
|
||||
local shellCompDirectiveKeepOrder=%[8]d
|
||||
|
||||
if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then
|
||||
if (((directive & shellCompDirectiveError) != 0)); then
|
||||
# Error code. No completion.
|
||||
__%[1]s_debug "Received error from custom completion go code"
|
||||
return
|
||||
else
|
||||
if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then
|
||||
if [[ $(type -t compopt) = "builtin" ]]; then
|
||||
if (((directive & shellCompDirectiveNoSpace) != 0)); then
|
||||
if [[ $(type -t compopt) == builtin ]]; then
|
||||
__%[1]s_debug "Activating no space"
|
||||
compopt -o nospace
|
||||
else
|
||||
__%[1]s_debug "No space directive not supported in this version of bash"
|
||||
fi
|
||||
fi
|
||||
if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then
|
||||
if [[ $(type -t compopt) = "builtin" ]]; then
|
||||
if (((directive & shellCompDirectiveKeepOrder) != 0)); then
|
||||
if [[ $(type -t compopt) == builtin ]]; then
|
||||
# no sort isn't supported for bash less than < 4.4
|
||||
if [[ ${BASH_VERSINFO[0]} -lt 4 || ( ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -lt 4 ) ]]; then
|
||||
__%[1]s_debug "No sort directive not supported in this version of bash"
|
||||
else
|
||||
__%[1]s_debug "Activating keep order"
|
||||
compopt -o nosort
|
||||
fi
|
||||
else
|
||||
__%[1]s_debug "No sort directive not supported in this version of bash"
|
||||
fi
|
||||
fi
|
||||
if (((directive & shellCompDirectiveNoFileComp) != 0)); then
|
||||
if [[ $(type -t compopt) == builtin ]]; then
|
||||
__%[1]s_debug "Activating no file completion"
|
||||
compopt +o default
|
||||
else
|
||||
@@ -111,26 +139,30 @@ __%[1]s_process_completion_results() {
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then
|
||||
# Separate activeHelp from normal completions
|
||||
local completions=()
|
||||
local activeHelp=()
|
||||
__%[1]s_extract_activeHelp
|
||||
|
||||
if (((directive & shellCompDirectiveFilterFileExt) != 0)); then
|
||||
# File extension filtering
|
||||
local fullFilter filter filteringCmd
|
||||
|
||||
# Do not use quotes around the $out variable or else newline
|
||||
# Do not use quotes around the $completions variable or else newline
|
||||
# characters will be kept.
|
||||
for filter in ${out[*]}; do
|
||||
for filter in ${completions[*]}; do
|
||||
fullFilter+="$filter|"
|
||||
done
|
||||
|
||||
filteringCmd="_filedir $fullFilter"
|
||||
__%[1]s_debug "File filtering command: $filteringCmd"
|
||||
$filteringCmd
|
||||
elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then
|
||||
elif (((directive & shellCompDirectiveFilterDirs) != 0)); then
|
||||
# File completion for directories only
|
||||
|
||||
# Use printf to strip any trailing newline
|
||||
local subdir
|
||||
subdir=$(printf "%%s" "${out[0]}")
|
||||
if [ -n "$subdir" ]; then
|
||||
subdir=${completions[0]}
|
||||
if [[ -n $subdir ]]; then
|
||||
__%[1]s_debug "Listing directories in $subdir"
|
||||
pushd "$subdir" >/dev/null 2>&1 && _filedir -d && popd >/dev/null 2>&1 || return
|
||||
else
|
||||
@@ -143,6 +175,43 @@ __%[1]s_process_completion_results() {
|
||||
|
||||
__%[1]s_handle_special_char "$cur" :
|
||||
__%[1]s_handle_special_char "$cur" =
|
||||
|
||||
# Print the activeHelp statements before we finish
|
||||
if ((${#activeHelp[*]} != 0)); then
|
||||
printf "\n";
|
||||
printf "%%s\n" "${activeHelp[@]}"
|
||||
printf "\n"
|
||||
|
||||
# The prompt format is only available from bash 4.4.
|
||||
# We test if it is available before using it.
|
||||
if (x=${PS1@P}) 2> /dev/null; then
|
||||
printf "%%s" "${PS1@P}${COMP_LINE[@]}"
|
||||
else
|
||||
# Can't print the prompt. Just print the
|
||||
# text the user had typed, it is workable enough.
|
||||
printf "%%s" "${COMP_LINE[@]}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Separate activeHelp lines from real completions.
|
||||
# Fills the $activeHelp and $completions arrays.
|
||||
__%[1]s_extract_activeHelp() {
|
||||
local activeHelpMarker="%[9]s"
|
||||
local endIndex=${#activeHelpMarker}
|
||||
|
||||
while IFS='' read -r comp; do
|
||||
if [[ ${comp:0:endIndex} == $activeHelpMarker ]]; then
|
||||
comp=${comp:endIndex}
|
||||
__%[1]s_debug "ActiveHelp found: $comp"
|
||||
if [[ -n $comp ]]; then
|
||||
activeHelp+=("$comp")
|
||||
fi
|
||||
else
|
||||
# Not an activeHelp line but a normal completion
|
||||
completions+=("$comp")
|
||||
fi
|
||||
done <<<"${out}"
|
||||
}
|
||||
|
||||
__%[1]s_handle_completion_types() {
|
||||
@@ -154,17 +223,16 @@ __%[1]s_handle_completion_types() {
|
||||
# If the user requested inserting one completion at a time, or all
|
||||
# completions at once on the command-line we must remove the descriptions.
|
||||
# https://github.com/spf13/cobra/issues/1508
|
||||
local tab comp
|
||||
tab=$(printf '\t')
|
||||
local tab=$'\t' comp
|
||||
while IFS='' read -r comp; do
|
||||
[[ -z $comp ]] && continue
|
||||
# Strip any description
|
||||
comp=${comp%%%%$tab*}
|
||||
# Only consider the completions that match
|
||||
comp=$(compgen -W "$comp" -- "$cur")
|
||||
if [ -n "$comp" ]; then
|
||||
if [[ $comp == "$cur"* ]]; then
|
||||
COMPREPLY+=("$comp")
|
||||
fi
|
||||
done < <(printf "%%s\n" "${out[@]}")
|
||||
done < <(printf "%%s\n" "${completions[@]}")
|
||||
;;
|
||||
|
||||
*)
|
||||
@@ -175,44 +243,37 @@ __%[1]s_handle_completion_types() {
|
||||
}
|
||||
|
||||
__%[1]s_handle_standard_completion_case() {
|
||||
local tab comp
|
||||
tab=$(printf '\t')
|
||||
local tab=$'\t' comp
|
||||
|
||||
# Short circuit to optimize if we don't have descriptions
|
||||
if [[ "${completions[*]}" != *$tab* ]]; then
|
||||
IFS=$'\n' read -ra COMPREPLY -d '' < <(compgen -W "${completions[*]}" -- "$cur")
|
||||
return 0
|
||||
fi
|
||||
|
||||
local longest=0
|
||||
local compline
|
||||
# Look for the longest completion so that we can format things nicely
|
||||
while IFS='' read -r comp; do
|
||||
while IFS='' read -r compline; do
|
||||
[[ -z $compline ]] && continue
|
||||
# Strip any description before checking the length
|
||||
comp=${comp%%%%$tab*}
|
||||
comp=${compline%%%%$tab*}
|
||||
# Only consider the completions that match
|
||||
comp=$(compgen -W "$comp" -- "$cur")
|
||||
[[ $comp == "$cur"* ]] || continue
|
||||
COMPREPLY+=("$compline")
|
||||
if ((${#comp}>longest)); then
|
||||
longest=${#comp}
|
||||
fi
|
||||
done < <(printf "%%s\n" "${out[@]}")
|
||||
|
||||
local completions=()
|
||||
while IFS='' read -r comp; do
|
||||
if [ -z "$comp" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
__%[1]s_debug "Original comp: $comp"
|
||||
comp="$(__%[1]s_format_comp_descriptions "$comp" "$longest")"
|
||||
__%[1]s_debug "Final comp: $comp"
|
||||
completions+=("$comp")
|
||||
done < <(printf "%%s\n" "${out[@]}")
|
||||
|
||||
while IFS='' read -r comp; do
|
||||
COMPREPLY+=("$comp")
|
||||
done < <(compgen -W "${completions[*]}" -- "$cur")
|
||||
done < <(printf "%%s\n" "${completions[@]}")
|
||||
|
||||
# If there is a single completion left, remove the description text
|
||||
if [ ${#COMPREPLY[*]} -eq 1 ]; then
|
||||
if ((${#COMPREPLY[*]} == 1)); then
|
||||
__%[1]s_debug "COMPREPLY[0]: ${COMPREPLY[0]}"
|
||||
comp="${COMPREPLY[0]%%%% *}"
|
||||
comp="${COMPREPLY[0]%%%%$tab*}"
|
||||
__%[1]s_debug "Removed description from single completion, which is now: ${comp}"
|
||||
COMPREPLY=()
|
||||
COMPREPLY+=("$comp")
|
||||
COMPREPLY[0]=$comp
|
||||
else # Format the descriptions
|
||||
__%[1]s_format_comp_descriptions $longest
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -223,53 +284,56 @@ __%[1]s_handle_special_char()
|
||||
if [[ "$comp" == *${char}* && "$COMP_WORDBREAKS" == *${char}* ]]; then
|
||||
local word=${comp%%"${comp##*${char}}"}
|
||||
local idx=${#COMPREPLY[*]}
|
||||
while [[ $((--idx)) -ge 0 ]]; do
|
||||
COMPREPLY[$idx]=${COMPREPLY[$idx]#"$word"}
|
||||
while ((--idx >= 0)); do
|
||||
COMPREPLY[idx]=${COMPREPLY[idx]#"$word"}
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
__%[1]s_format_comp_descriptions()
|
||||
{
|
||||
local tab
|
||||
tab=$(printf '\t')
|
||||
local comp="$1"
|
||||
local longest=$2
|
||||
local tab=$'\t'
|
||||
local comp desc maxdesclength
|
||||
local longest=$1
|
||||
|
||||
# Properly format the description string which follows a tab character if there is one
|
||||
if [[ "$comp" == *$tab* ]]; then
|
||||
desc=${comp#*$tab}
|
||||
comp=${comp%%%%$tab*}
|
||||
local i ci
|
||||
for ci in ${!COMPREPLY[*]}; do
|
||||
comp=${COMPREPLY[ci]}
|
||||
# Properly format the description string which follows a tab character if there is one
|
||||
if [[ "$comp" == *$tab* ]]; then
|
||||
__%[1]s_debug "Original comp: $comp"
|
||||
desc=${comp#*$tab}
|
||||
comp=${comp%%%%$tab*}
|
||||
|
||||
# $COLUMNS stores the current shell width.
|
||||
# Remove an extra 4 because we add 2 spaces and 2 parentheses.
|
||||
maxdesclength=$(( COLUMNS - longest - 4 ))
|
||||
# $COLUMNS stores the current shell width.
|
||||
# Remove an extra 4 because we add 2 spaces and 2 parentheses.
|
||||
maxdesclength=$(( COLUMNS - longest - 4 ))
|
||||
|
||||
# Make sure we can fit a description of at least 8 characters
|
||||
# if we are to align the descriptions.
|
||||
if [[ $maxdesclength -gt 8 ]]; then
|
||||
# Add the proper number of spaces to align the descriptions
|
||||
for ((i = ${#comp} ; i < longest ; i++)); do
|
||||
comp+=" "
|
||||
done
|
||||
else
|
||||
# Don't pad the descriptions so we can fit more text after the completion
|
||||
maxdesclength=$(( COLUMNS - ${#comp} - 4 ))
|
||||
fi
|
||||
|
||||
# If there is enough space for any description text,
|
||||
# truncate the descriptions that are too long for the shell width
|
||||
if [ $maxdesclength -gt 0 ]; then
|
||||
if [ ${#desc} -gt $maxdesclength ]; then
|
||||
desc=${desc:0:$(( maxdesclength - 1 ))}
|
||||
desc+="…"
|
||||
# Make sure we can fit a description of at least 8 characters
|
||||
# if we are to align the descriptions.
|
||||
if ((maxdesclength > 8)); then
|
||||
# Add the proper number of spaces to align the descriptions
|
||||
for ((i = ${#comp} ; i < longest ; i++)); do
|
||||
comp+=" "
|
||||
done
|
||||
else
|
||||
# Don't pad the descriptions so we can fit more text after the completion
|
||||
maxdesclength=$(( COLUMNS - ${#comp} - 4 ))
|
||||
fi
|
||||
comp+=" ($desc)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Must use printf to escape all special characters
|
||||
printf "%%q" "${comp}"
|
||||
# If there is enough space for any description text,
|
||||
# truncate the descriptions that are too long for the shell width
|
||||
if ((maxdesclength > 0)); then
|
||||
if ((${#desc} > maxdesclength)); then
|
||||
desc=${desc:0:$(( maxdesclength - 1 ))}
|
||||
desc+="…"
|
||||
fi
|
||||
comp+=" ($desc)"
|
||||
fi
|
||||
COMPREPLY[ci]=$comp
|
||||
__%[1]s_debug "Final comp: $comp"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
__start_%[1]s()
|
||||
@@ -281,9 +345,9 @@ __start_%[1]s()
|
||||
# Call _init_completion from the bash-completion package
|
||||
# to prepare the arguments properly
|
||||
if declare -F _init_completion >/dev/null 2>&1; then
|
||||
_init_completion -n "=:" || return
|
||||
_init_completion -n =: || return
|
||||
else
|
||||
__%[1]s_init_completion -n "=:" || return
|
||||
__%[1]s_init_completion -n =: || return
|
||||
fi
|
||||
|
||||
__%[1]s_debug
|
||||
@@ -310,7 +374,8 @@ fi
|
||||
# ex: ts=4 sw=4 et filetype=sh
|
||||
`, name, compCmd,
|
||||
ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
|
||||
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
|
||||
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder,
|
||||
activeHelpMarker))
|
||||
}
|
||||
|
||||
// GenBashCompletionFileV2 generates Bash completion version 2.
|
||||
|
||||
+29
-7
@@ -1,9 +1,10 @@
|
||||
// Copyright © 2013 Steve Francia <[email protected]>.
|
||||
// Copyright 2013-2023 The Cobra Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -39,15 +40,30 @@ var templateFuncs = template.FuncMap{
|
||||
}
|
||||
|
||||
var initializers []func()
|
||||
var finalizers []func()
|
||||
|
||||
// EnablePrefixMatching allows to set automatic prefix matching. Automatic prefix matching can be a dangerous thing
|
||||
const (
|
||||
defaultPrefixMatching = false
|
||||
defaultCommandSorting = true
|
||||
defaultCaseInsensitive = false
|
||||
defaultTraverseRunHooks = false
|
||||
)
|
||||
|
||||
// EnablePrefixMatching allows setting automatic prefix matching. Automatic prefix matching can be a dangerous thing
|
||||
// to automatically enable in CLI tools.
|
||||
// Set this to true to enable it.
|
||||
var EnablePrefixMatching = false
|
||||
var EnablePrefixMatching = defaultPrefixMatching
|
||||
|
||||
// EnableCommandSorting controls sorting of the slice of commands, which is turned on by default.
|
||||
// To disable sorting, set it to false.
|
||||
var EnableCommandSorting = true
|
||||
var EnableCommandSorting = defaultCommandSorting
|
||||
|
||||
// EnableCaseInsensitive allows case-insensitive commands names. (case sensitive by default)
|
||||
var EnableCaseInsensitive = defaultCaseInsensitive
|
||||
|
||||
// EnableTraverseRunHooks executes persistent pre-run and post-run hooks from all parents.
|
||||
// By default this is disabled, which means only the first run hook to be found is executed.
|
||||
var EnableTraverseRunHooks = defaultTraverseRunHooks
|
||||
|
||||
// MousetrapHelpText enables an information splash screen on Windows
|
||||
// if the CLI is started from explorer.exe.
|
||||
@@ -84,6 +100,12 @@ func OnInitialize(y ...func()) {
|
||||
initializers = append(initializers, y...)
|
||||
}
|
||||
|
||||
// OnFinalize sets the passed functions to be run when each command's
|
||||
// Execute method is terminated.
|
||||
func OnFinalize(y ...func()) {
|
||||
finalizers = append(finalizers, y...)
|
||||
}
|
||||
|
||||
// FIXME Gt is unused by cobra and should be removed in a version 2. It exists only for compatibility with users of cobra.
|
||||
|
||||
// Gt takes two types and checks whether the first type is greater than the second. In case of types Arrays, Chans,
|
||||
@@ -150,8 +172,8 @@ func appendIfNotPresent(s, stringToAppend string) string {
|
||||
|
||||
// rpad adds padding to the right of a string.
|
||||
func rpad(s string, padding int) string {
|
||||
template := fmt.Sprintf("%%-%ds", padding)
|
||||
return fmt.Sprintf(template, s)
|
||||
formattedString := fmt.Sprintf("%%-%ds", padding)
|
||||
return fmt.Sprintf(formattedString, s)
|
||||
}
|
||||
|
||||
// tmpl executes the given template text on data, writing the result to w.
|
||||
|
||||
+240
-35
@@ -1,9 +1,10 @@
|
||||
// Copyright © 2013 Steve Francia <[email protected]>.
|
||||
// Copyright 2013-2023 The Cobra Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@@ -18,6 +19,7 @@ package cobra
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@@ -28,16 +30,27 @@ import (
|
||||
flag "github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
const (
|
||||
FlagSetByCobraAnnotation = "cobra_annotation_flag_set_by_cobra"
|
||||
CommandDisplayNameAnnotation = "cobra_annotation_command_display_name"
|
||||
)
|
||||
|
||||
// FParseErrWhitelist configures Flag parse errors to be ignored
|
||||
type FParseErrWhitelist flag.ParseErrorsWhitelist
|
||||
|
||||
// Group Structure to manage groups for commands
|
||||
type Group struct {
|
||||
ID string
|
||||
Title string
|
||||
}
|
||||
|
||||
// Command is just that, a command for your application.
|
||||
// E.g. 'go run ...' - 'run' is the command. Cobra requires
|
||||
// you to define the usage and description as part of your command
|
||||
// definition to ensure usability.
|
||||
type Command struct {
|
||||
// Use is the one-line usage message.
|
||||
// Recommended syntax is as follow:
|
||||
// Recommended syntax is as follows:
|
||||
// [ ] identifies an optional argument. Arguments that are not enclosed in brackets are required.
|
||||
// ... indicates that you can specify multiple values for the previous argument.
|
||||
// | indicates mutually exclusive information. You can use the argument to the left of the separator or the
|
||||
@@ -57,6 +70,9 @@ type Command struct {
|
||||
// Short is the short description shown in the 'help' output.
|
||||
Short string
|
||||
|
||||
// The group id under which this subcommand is grouped in the 'help' output of its parent.
|
||||
GroupID string
|
||||
|
||||
// Long is the long message shown in the 'help <this-command>' output.
|
||||
Long string
|
||||
|
||||
@@ -86,7 +102,7 @@ type Command struct {
|
||||
Deprecated string
|
||||
|
||||
// Annotations are key/value pairs that can be used by applications to identify or
|
||||
// group commands.
|
||||
// group commands or set special options.
|
||||
Annotations map[string]string
|
||||
|
||||
// Version defines the version for this command. If this value is non-empty and the command does not
|
||||
@@ -102,6 +118,8 @@ type Command struct {
|
||||
// * PostRun()
|
||||
// * PersistentPostRun()
|
||||
// All functions get the same args, the arguments after the command name.
|
||||
// The *PreRun and *PostRun functions will only be executed if the Run function of the current
|
||||
// command has been declared.
|
||||
//
|
||||
// PersistentPreRun: children of this command will inherit and execute.
|
||||
PersistentPreRun func(cmd *Command, args []string)
|
||||
@@ -124,6 +142,9 @@ type Command struct {
|
||||
// PersistentPostRunE: PersistentPostRun but returns an error.
|
||||
PersistentPostRunE func(cmd *Command, args []string) error
|
||||
|
||||
// groups for subcommands
|
||||
commandgroups []*Group
|
||||
|
||||
// args is actual args parsed from flags.
|
||||
args []string
|
||||
// flagErrorBuf contains all error messages from pflag.
|
||||
@@ -156,9 +177,18 @@ type Command struct {
|
||||
// helpCommand is command with usage 'help'. If it's not defined by user,
|
||||
// cobra uses default help command.
|
||||
helpCommand *Command
|
||||
// helpCommandGroupID is the group id for the helpCommand
|
||||
helpCommandGroupID string
|
||||
|
||||
// completionCommandGroupID is the group id for the completion command
|
||||
completionCommandGroupID string
|
||||
|
||||
// versionTemplate is the version template defined by user.
|
||||
versionTemplate string
|
||||
|
||||
// errPrefix is the error message prefix defined by user.
|
||||
errPrefix string
|
||||
|
||||
// inReader is a reader defined by the user that replaces stdin
|
||||
inReader io.Reader
|
||||
// outWriter is a writer defined by the user that replaces stdout
|
||||
@@ -166,7 +196,7 @@ type Command struct {
|
||||
// errWriter is a writer defined by the user that replaces stderr
|
||||
errWriter io.Writer
|
||||
|
||||
//FParseErrWhitelist flag parse errors to be ignored
|
||||
// FParseErrWhitelist flag parse errors to be ignored
|
||||
FParseErrWhitelist FParseErrWhitelist
|
||||
|
||||
// CompletionOptions is a set of options to control the handling of shell completion
|
||||
@@ -224,12 +254,23 @@ type Command struct {
|
||||
SuggestionsMinimumDistance int
|
||||
}
|
||||
|
||||
// Context returns underlying command context. If command wasn't
|
||||
// executed with ExecuteContext Context returns Background context.
|
||||
// Context returns underlying command context. If command was executed
|
||||
// with ExecuteContext or the context was set with SetContext, the
|
||||
// previously set context will be returned. Otherwise, nil is returned.
|
||||
//
|
||||
// Notice that a call to Execute and ExecuteC will replace a nil context of
|
||||
// a command with a context.Background, so a background context will be
|
||||
// returned by Context after one of these functions has been called.
|
||||
func (c *Command) Context() context.Context {
|
||||
return c.ctx
|
||||
}
|
||||
|
||||
// SetContext sets context for the command. This context will be overwritten by
|
||||
// Command.ExecuteContext or Command.ExecuteContextC.
|
||||
func (c *Command) SetContext(ctx context.Context) {
|
||||
c.ctx = ctx
|
||||
}
|
||||
|
||||
// SetArgs sets arguments for the command. It is set to os.Args[1:] by default, if desired, can be overridden
|
||||
// particularly useful when testing.
|
||||
func (c *Command) SetArgs(a []string) {
|
||||
@@ -288,6 +329,21 @@ func (c *Command) SetHelpCommand(cmd *Command) {
|
||||
c.helpCommand = cmd
|
||||
}
|
||||
|
||||
// SetHelpCommandGroupID sets the group id of the help command.
|
||||
func (c *Command) SetHelpCommandGroupID(groupID string) {
|
||||
if c.helpCommand != nil {
|
||||
c.helpCommand.GroupID = groupID
|
||||
}
|
||||
// helpCommandGroupID is used if no helpCommand is defined by the user
|
||||
c.helpCommandGroupID = groupID
|
||||
}
|
||||
|
||||
// SetCompletionCommandGroupID sets the group id of the completion command.
|
||||
func (c *Command) SetCompletionCommandGroupID(groupID string) {
|
||||
// completionCommandGroupID is used if no completion command is defined by the user
|
||||
c.Root().completionCommandGroupID = groupID
|
||||
}
|
||||
|
||||
// SetHelpTemplate sets help template to be used. Application can use it to set custom template.
|
||||
func (c *Command) SetHelpTemplate(s string) {
|
||||
c.helpTemplate = s
|
||||
@@ -298,6 +354,11 @@ func (c *Command) SetVersionTemplate(s string) {
|
||||
c.versionTemplate = s
|
||||
}
|
||||
|
||||
// SetErrPrefix sets error message prefix to be used. Application can use it to set custom prefix.
|
||||
func (c *Command) SetErrPrefix(s string) {
|
||||
c.errPrefix = s
|
||||
}
|
||||
|
||||
// SetGlobalNormalizationFunc sets a normalization function to all flag sets and also to child commands.
|
||||
// The user should not have a cyclic dependency on commands.
|
||||
func (c *Command) SetGlobalNormalizationFunc(n func(f *flag.FlagSet, name string) flag.NormalizedName) {
|
||||
@@ -496,10 +557,16 @@ Aliases:
|
||||
{{.NameAndAliases}}{{end}}{{if .HasExample}}
|
||||
|
||||
Examples:
|
||||
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}
|
||||
{{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}}
|
||||
|
||||
Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
|
||||
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
|
||||
Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
|
||||
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}}
|
||||
|
||||
{{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}}
|
||||
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}}
|
||||
|
||||
Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}}
|
||||
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
|
||||
|
||||
Flags:
|
||||
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}
|
||||
@@ -541,6 +608,18 @@ func (c *Command) VersionTemplate() string {
|
||||
`
|
||||
}
|
||||
|
||||
// ErrPrefix return error message prefix for the command
|
||||
func (c *Command) ErrPrefix() string {
|
||||
if c.errPrefix != "" {
|
||||
return c.errPrefix
|
||||
}
|
||||
|
||||
if c.HasParent() {
|
||||
return c.parent.ErrPrefix()
|
||||
}
|
||||
return "Error:"
|
||||
}
|
||||
|
||||
func hasNoOptDefVal(name string, fs *flag.FlagSet) bool {
|
||||
flag := fs.Lookup(name)
|
||||
if flag == nil {
|
||||
@@ -601,20 +680,44 @@ Loop:
|
||||
|
||||
// argsMinusFirstX removes only the first x from args. Otherwise, commands that look like
|
||||
// openshift admin policy add-role-to-user admin my-user, lose the admin argument (arg[4]).
|
||||
func argsMinusFirstX(args []string, x string) []string {
|
||||
for i, y := range args {
|
||||
if x == y {
|
||||
ret := []string{}
|
||||
ret = append(ret, args[:i]...)
|
||||
ret = append(ret, args[i+1:]...)
|
||||
return ret
|
||||
// Special care needs to be taken not to remove a flag value.
|
||||
func (c *Command) argsMinusFirstX(args []string, x string) []string {
|
||||
if len(args) == 0 {
|
||||
return args
|
||||
}
|
||||
c.mergePersistentFlags()
|
||||
flags := c.Flags()
|
||||
|
||||
Loop:
|
||||
for pos := 0; pos < len(args); pos++ {
|
||||
s := args[pos]
|
||||
switch {
|
||||
case s == "--":
|
||||
// -- means we have reached the end of the parseable args. Break out of the loop now.
|
||||
break Loop
|
||||
case strings.HasPrefix(s, "--") && !strings.Contains(s, "=") && !hasNoOptDefVal(s[2:], flags):
|
||||
fallthrough
|
||||
case strings.HasPrefix(s, "-") && !strings.Contains(s, "=") && len(s) == 2 && !shortHasNoOptDefVal(s[1:], flags):
|
||||
// This is a flag without a default value, and an equal sign is not used. Increment pos in order to skip
|
||||
// over the next arg, because that is the value of this flag.
|
||||
pos++
|
||||
continue
|
||||
case !strings.HasPrefix(s, "-"):
|
||||
// This is not a flag or a flag value. Check to see if it matches what we're looking for, and if so,
|
||||
// return the args, excluding the one at this position.
|
||||
if s == x {
|
||||
ret := []string{}
|
||||
ret = append(ret, args[:pos]...)
|
||||
ret = append(ret, args[pos+1:]...)
|
||||
return ret
|
||||
}
|
||||
}
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func isFlagArg(arg string) bool {
|
||||
return ((len(arg) >= 3 && arg[1] == '-') ||
|
||||
return ((len(arg) >= 3 && arg[0:2] == "--") ||
|
||||
(len(arg) >= 2 && arg[0] == '-' && arg[1] != '-'))
|
||||
}
|
||||
|
||||
@@ -632,7 +735,7 @@ func (c *Command) Find(args []string) (*Command, []string, error) {
|
||||
|
||||
cmd := c.findNext(nextSubCmd)
|
||||
if cmd != nil {
|
||||
return innerfind(cmd, argsMinusFirstX(innerArgs, nextSubCmd))
|
||||
return innerfind(cmd, c.argsMinusFirstX(innerArgs, nextSubCmd))
|
||||
}
|
||||
return c, innerArgs
|
||||
}
|
||||
@@ -664,7 +767,7 @@ func (c *Command) findSuggestions(arg string) string {
|
||||
func (c *Command) findNext(next string) *Command {
|
||||
matches := make([]*Command, 0)
|
||||
for _, cmd := range c.commands {
|
||||
if cmd.Name() == next || cmd.HasAlias(next) {
|
||||
if commandNameMatches(cmd.Name(), next) || cmd.HasAlias(next) {
|
||||
cmd.commandCalledAs.name = next
|
||||
return cmd
|
||||
}
|
||||
@@ -674,7 +777,9 @@ func (c *Command) findNext(next string) *Command {
|
||||
}
|
||||
|
||||
if len(matches) == 1 {
|
||||
return matches[0]
|
||||
// Temporarily disable gosec G602, which produces a false positive.
|
||||
// See https://github.com/securego/gosec/issues/1005.
|
||||
return matches[0] // #nosec G602
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -821,6 +926,8 @@ func (c *Command) execute(a []string) (err error) {
|
||||
|
||||
c.preRun()
|
||||
|
||||
defer c.postRun()
|
||||
|
||||
argWoFlags := c.Flags().Args()
|
||||
if c.DisableFlagParsing {
|
||||
argWoFlags = a
|
||||
@@ -830,15 +937,31 @@ func (c *Command) execute(a []string) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
parents := make([]*Command, 0, 5)
|
||||
for p := c; p != nil; p = p.Parent() {
|
||||
if EnableTraverseRunHooks {
|
||||
// When EnableTraverseRunHooks is set:
|
||||
// - Execute all persistent pre-runs from the root parent till this command.
|
||||
// - Execute all persistent post-runs from this command till the root parent.
|
||||
parents = append([]*Command{p}, parents...)
|
||||
} else {
|
||||
// Otherwise, execute only the first found persistent hook.
|
||||
parents = append(parents, p)
|
||||
}
|
||||
}
|
||||
for _, p := range parents {
|
||||
if p.PersistentPreRunE != nil {
|
||||
if err := p.PersistentPreRunE(c, argWoFlags); err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
if !EnableTraverseRunHooks {
|
||||
break
|
||||
}
|
||||
} else if p.PersistentPreRun != nil {
|
||||
p.PersistentPreRun(c, argWoFlags)
|
||||
break
|
||||
if !EnableTraverseRunHooks {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if c.PreRunE != nil {
|
||||
@@ -849,9 +972,13 @@ func (c *Command) execute(a []string) (err error) {
|
||||
c.PreRun(c, argWoFlags)
|
||||
}
|
||||
|
||||
if err := c.validateRequiredFlags(); err != nil {
|
||||
if err := c.ValidateRequiredFlags(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.ValidateFlagGroups(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.RunE != nil {
|
||||
if err := c.RunE(c, argWoFlags); err != nil {
|
||||
return err
|
||||
@@ -871,10 +998,14 @@ func (c *Command) execute(a []string) (err error) {
|
||||
if err := p.PersistentPostRunE(c, argWoFlags); err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
if !EnableTraverseRunHooks {
|
||||
break
|
||||
}
|
||||
} else if p.PersistentPostRun != nil {
|
||||
p.PersistentPostRun(c, argWoFlags)
|
||||
break
|
||||
if !EnableTraverseRunHooks {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -887,6 +1018,12 @@ func (c *Command) preRun() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Command) postRun() {
|
||||
for _, x := range finalizers {
|
||||
x()
|
||||
}
|
||||
}
|
||||
|
||||
// ExecuteContext is the same as Execute(), but sets the ctx on the command.
|
||||
// Retrieve ctx by calling cmd.Context() inside your *Run lifecycle or ValidArgs
|
||||
// functions.
|
||||
@@ -930,7 +1067,11 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
|
||||
// initialize help at the last point to allow for user overriding
|
||||
c.InitDefaultHelpCmd()
|
||||
// initialize completion at the last point to allow for user overriding
|
||||
c.initDefaultCompletionCmd()
|
||||
c.InitDefaultCompletionCmd()
|
||||
|
||||
// Now that all commands have been created, let's make sure all groups
|
||||
// are properly created also
|
||||
c.checkCommandGroups()
|
||||
|
||||
args := c.args
|
||||
|
||||
@@ -954,7 +1095,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
|
||||
c = cmd
|
||||
}
|
||||
if !c.SilenceErrors {
|
||||
c.PrintErrln("Error:", err.Error())
|
||||
c.PrintErrln(c.ErrPrefix(), err.Error())
|
||||
c.PrintErrf("Run '%v --help' for usage.\n", c.CommandPath())
|
||||
}
|
||||
return c, err
|
||||
@@ -975,7 +1116,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
|
||||
if err != nil {
|
||||
// Always show help if requested, even if SilenceErrors is in
|
||||
// effect
|
||||
if err == flag.ErrHelp {
|
||||
if errors.Is(err, flag.ErrHelp) {
|
||||
cmd.HelpFunc()(cmd, args)
|
||||
return cmd, nil
|
||||
}
|
||||
@@ -983,7 +1124,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
|
||||
// If root command has SilenceErrors flagged,
|
||||
// all subcommands should respect it
|
||||
if !cmd.SilenceErrors && !c.SilenceErrors {
|
||||
c.PrintErrln("Error:", err.Error())
|
||||
c.PrintErrln(cmd.ErrPrefix(), err.Error())
|
||||
}
|
||||
|
||||
// If root command has SilenceUsage flagged,
|
||||
@@ -997,12 +1138,13 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
|
||||
|
||||
func (c *Command) ValidateArgs(args []string) error {
|
||||
if c.Args == nil {
|
||||
return nil
|
||||
return ArbitraryArgs(c, args)
|
||||
}
|
||||
return c.Args(c, args)
|
||||
}
|
||||
|
||||
func (c *Command) validateRequiredFlags() error {
|
||||
// ValidateRequiredFlags validates all required flags are present and returns an error otherwise
|
||||
func (c *Command) ValidateRequiredFlags() error {
|
||||
if c.DisableFlagParsing {
|
||||
return nil
|
||||
}
|
||||
@@ -1025,6 +1167,19 @@ func (c *Command) validateRequiredFlags() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkCommandGroups checks if a command has been added to a group that does not exists.
|
||||
// If so, we panic because it indicates a coding error that should be corrected.
|
||||
func (c *Command) checkCommandGroups() {
|
||||
for _, sub := range c.commands {
|
||||
// if Group is not defined let the developer know right away
|
||||
if sub.GroupID != "" && !c.ContainsGroup(sub.GroupID) {
|
||||
panic(fmt.Sprintf("group id '%s' is not defined for subcommand '%s'", sub.GroupID, sub.CommandPath()))
|
||||
}
|
||||
|
||||
sub.checkCommandGroups()
|
||||
}
|
||||
}
|
||||
|
||||
// InitDefaultHelpFlag adds default help flag to c.
|
||||
// It is called automatically by executing the c or by calling help and usage.
|
||||
// If c already has help flag, it will do nothing.
|
||||
@@ -1038,6 +1193,7 @@ func (c *Command) InitDefaultHelpFlag() {
|
||||
usage += c.Name()
|
||||
}
|
||||
c.Flags().BoolP("help", "h", false, usage)
|
||||
_ = c.Flags().SetAnnotation("help", FlagSetByCobraAnnotation, []string{"true"})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1063,6 +1219,7 @@ func (c *Command) InitDefaultVersionFlag() {
|
||||
} else {
|
||||
c.Flags().Bool("version", false, usage)
|
||||
}
|
||||
_ = c.Flags().SetAnnotation("version", FlagSetByCobraAnnotation, []string{"true"})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1105,10 +1262,12 @@ Simply type ` + c.Name() + ` help [path to command] for full details.`,
|
||||
c.Printf("Unknown help topic %#q\n", args)
|
||||
CheckErr(c.Root().Usage())
|
||||
} else {
|
||||
cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown
|
||||
cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown
|
||||
cmd.InitDefaultVersionFlag() // make possible 'version' flag to be shown
|
||||
CheckErr(cmd.Help())
|
||||
}
|
||||
},
|
||||
GroupID: c.helpCommandGroupID,
|
||||
}
|
||||
}
|
||||
c.RemoveCommand(c.helpCommand)
|
||||
@@ -1169,6 +1328,36 @@ func (c *Command) AddCommand(cmds ...*Command) {
|
||||
}
|
||||
}
|
||||
|
||||
// Groups returns a slice of child command groups.
|
||||
func (c *Command) Groups() []*Group {
|
||||
return c.commandgroups
|
||||
}
|
||||
|
||||
// AllChildCommandsHaveGroup returns if all subcommands are assigned to a group
|
||||
func (c *Command) AllChildCommandsHaveGroup() bool {
|
||||
for _, sub := range c.commands {
|
||||
if (sub.IsAvailableCommand() || sub == c.helpCommand) && sub.GroupID == "" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ContainsGroup return if groupID exists in the list of command groups.
|
||||
func (c *Command) ContainsGroup(groupID string) bool {
|
||||
for _, x := range c.commandgroups {
|
||||
if x.ID == groupID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AddGroup adds one or more command groups to this parent command.
|
||||
func (c *Command) AddGroup(groups ...*Group) {
|
||||
c.commandgroups = append(c.commandgroups, groups...)
|
||||
}
|
||||
|
||||
// RemoveCommand removes one or more commands from a parent command.
|
||||
func (c *Command) RemoveCommand(cmds ...*Command) {
|
||||
commands := []*Command{}
|
||||
@@ -1238,6 +1427,9 @@ func (c *Command) CommandPath() string {
|
||||
if c.HasParent() {
|
||||
return c.Parent().CommandPath() + " " + c.Name()
|
||||
}
|
||||
if displayName, ok := c.Annotations[CommandDisplayNameAnnotation]; ok {
|
||||
return displayName
|
||||
}
|
||||
return c.Name()
|
||||
}
|
||||
|
||||
@@ -1260,6 +1452,7 @@ func (c *Command) UseLine() string {
|
||||
|
||||
// DebugFlags used to determine which flags have been assigned to which commands
|
||||
// and which persist.
|
||||
// nolint:goconst
|
||||
func (c *Command) DebugFlags() {
|
||||
c.Println("DebugFlags called on", c.Name())
|
||||
var debugflags func(*Command)
|
||||
@@ -1312,7 +1505,7 @@ func (c *Command) Name() string {
|
||||
// HasAlias determines if a given string is an alias of the command.
|
||||
func (c *Command) HasAlias(s string) bool {
|
||||
for _, a := range c.Aliases {
|
||||
if a == s {
|
||||
if commandNameMatches(a, s) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1489,7 +1682,8 @@ func (c *Command) LocalFlags() *flag.FlagSet {
|
||||
}
|
||||
|
||||
addToLocal := func(f *flag.Flag) {
|
||||
if c.lflags.Lookup(f.Name) == nil && c.parentsPflags.Lookup(f.Name) == nil {
|
||||
// Add the flag if it is not a parent PFlag, or it shadows a parent PFlag
|
||||
if c.lflags.Lookup(f.Name) == nil && f != c.parentsPflags.Lookup(f.Name) {
|
||||
c.lflags.AddFlag(f)
|
||||
}
|
||||
}
|
||||
@@ -1678,3 +1872,14 @@ func (c *Command) updateParentsPflags() {
|
||||
c.parentsPflags.AddFlagSet(parent.PersistentFlags())
|
||||
})
|
||||
}
|
||||
|
||||
// commandNameMatches checks if two command names are equal
|
||||
// taking into account case sensitivity according to
|
||||
// EnableCaseInsensitive global configuration.
|
||||
func commandNameMatches(s string, t string) bool {
|
||||
if EnableCaseInsensitive {
|
||||
return strings.EqualFold(s, t)
|
||||
}
|
||||
|
||||
return s == t
|
||||
}
|
||||
|
||||
+14
@@ -1,3 +1,17 @@
|
||||
// Copyright 2013-2023 The Cobra Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
|
||||
+14
@@ -1,3 +1,17 @@
|
||||
// Copyright 2013-2023 The Cobra Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
|
||||
+96
-6
@@ -1,3 +1,17 @@
|
||||
// Copyright 2013-2023 The Cobra Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cobra
|
||||
|
||||
import (
|
||||
@@ -63,6 +77,10 @@ const (
|
||||
// obtain the same behavior but only for flags.
|
||||
ShellCompDirectiveFilterDirs
|
||||
|
||||
// ShellCompDirectiveKeepOrder indicates that the shell should preserve the order
|
||||
// in which the completions are provided
|
||||
ShellCompDirectiveKeepOrder
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
// All directives using iota should be above this one.
|
||||
@@ -103,6 +121,14 @@ func NoFileCompletions(cmd *Command, args []string, toComplete string) ([]string
|
||||
return nil, ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
// FixedCompletions can be used to create a completion function which always
|
||||
// returns the same results.
|
||||
func FixedCompletions(choices []string, directive ShellCompDirective) func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
||||
return func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) {
|
||||
return choices, directive
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterFlagCompletionFunc should be called to register a function to provide completion for a flag.
|
||||
func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)) error {
|
||||
flag := c.Flag(flagName)
|
||||
@@ -119,6 +145,20 @@ func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Comman
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFlagCompletionFunc returns the completion function for the given flag of the command, if available.
|
||||
func (c *Command) GetFlagCompletionFunc(flagName string) (func(*Command, []string, string) ([]string, ShellCompDirective), bool) {
|
||||
flag := c.Flag(flagName)
|
||||
if flag == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
flagCompletionMutex.RLock()
|
||||
defer flagCompletionMutex.RUnlock()
|
||||
|
||||
completionFunc, exists := flagCompletionFunctions[flag]
|
||||
return completionFunc, exists
|
||||
}
|
||||
|
||||
// Returns a string listing the different directive enabled in the specified parameter
|
||||
func (d ShellCompDirective) string() string {
|
||||
var directives []string
|
||||
@@ -137,6 +177,9 @@ func (d ShellCompDirective) string() string {
|
||||
if d&ShellCompDirectiveFilterDirs != 0 {
|
||||
directives = append(directives, "ShellCompDirectiveFilterDirs")
|
||||
}
|
||||
if d&ShellCompDirectiveKeepOrder != 0 {
|
||||
directives = append(directives, "ShellCompDirectiveKeepOrder")
|
||||
}
|
||||
if len(directives) == 0 {
|
||||
directives = append(directives, "ShellCompDirectiveDefault")
|
||||
}
|
||||
@@ -147,7 +190,7 @@ func (d ShellCompDirective) string() string {
|
||||
return strings.Join(directives, ", ")
|
||||
}
|
||||
|
||||
// Adds a special hidden command that can be used to request custom completions.
|
||||
// initCompleteCmd adds a special hidden command that can be used to request custom completions.
|
||||
func (c *Command) initCompleteCmd(args []string) {
|
||||
completeCmd := &Command{
|
||||
Use: fmt.Sprintf("%s [command-line]", ShellCompRequestCmd),
|
||||
@@ -170,6 +213,12 @@ func (c *Command) initCompleteCmd(args []string) {
|
||||
|
||||
noDescriptions := (cmd.CalledAs() == ShellCompNoDescRequestCmd)
|
||||
for _, comp := range completions {
|
||||
if GetActiveHelpConfig(finalCmd) == activeHelpGlobalDisable {
|
||||
// Remove all activeHelp entries in this case
|
||||
if strings.HasPrefix(comp, activeHelpMarker) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if noDescriptions {
|
||||
// Remove any description that may be included following a tab character.
|
||||
comp = strings.Split(comp, "\t")[0]
|
||||
@@ -246,6 +295,16 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
|
||||
}
|
||||
finalCmd.ctx = c.ctx
|
||||
|
||||
// These flags are normally added when `execute()` is called on `finalCmd`,
|
||||
// however, when doing completion, we don't call `finalCmd.execute()`.
|
||||
// Let's add the --help and --version flag ourselves but only if the finalCmd
|
||||
// has not disabled flag parsing; if flag parsing is disabled, it is up to the
|
||||
// finalCmd itself to handle the completion of *all* flags.
|
||||
if !finalCmd.DisableFlagParsing {
|
||||
finalCmd.InitDefaultHelpFlag()
|
||||
finalCmd.InitDefaultVersionFlag()
|
||||
}
|
||||
|
||||
// Check if we are doing flag value completion before parsing the flags.
|
||||
// This is important because if we are completing a flag value, we need to also
|
||||
// remove the flag name argument from the list of finalArgs or else the parsing
|
||||
@@ -278,6 +337,12 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
|
||||
}
|
||||
}
|
||||
|
||||
// Look for the --help or --version flags. If they are present,
|
||||
// there should be no further completions.
|
||||
if helpOrVersionFlagPresent(finalCmd) {
|
||||
return finalCmd, []string{}, ShellCompDirectiveNoFileComp, nil
|
||||
}
|
||||
|
||||
// We only remove the flags from the arguments if DisableFlagParsing is not set.
|
||||
// This is important for commands which have requested to do their own flag completion.
|
||||
if !finalCmd.DisableFlagParsing {
|
||||
@@ -311,8 +376,11 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
|
||||
var completions []string
|
||||
var directive ShellCompDirective
|
||||
|
||||
// Enforce flag groups before doing flag completions
|
||||
finalCmd.enforceFlagGroupsForCompletion()
|
||||
|
||||
// Note that we want to perform flagname completion even if finalCmd.DisableFlagParsing==true;
|
||||
// doing this allows for completion of persistant flag names even for commands that disable flag parsing.
|
||||
// doing this allows for completion of persistent flag names even for commands that disable flag parsing.
|
||||
//
|
||||
// When doing completion of a flag name, as soon as an argument starts with
|
||||
// a '-' we know it is a flag. We cannot use isFlagArg() here as it requires
|
||||
@@ -339,6 +407,11 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
|
||||
finalCmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) {
|
||||
doCompleteFlags(flag)
|
||||
})
|
||||
// Try to complete non-inherited flags even if DisableFlagParsing==true.
|
||||
// This allows programs to tell Cobra about flags for completion even
|
||||
// if the actual parsing of flags is not done by Cobra.
|
||||
// For instance, Helm uses this to provide flag name completion for
|
||||
// some of its plugins.
|
||||
finalCmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) {
|
||||
doCompleteFlags(flag)
|
||||
})
|
||||
@@ -446,6 +519,18 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi
|
||||
return finalCmd, completions, directive, nil
|
||||
}
|
||||
|
||||
func helpOrVersionFlagPresent(cmd *Command) bool {
|
||||
if versionFlag := cmd.Flags().Lookup("version"); versionFlag != nil &&
|
||||
len(versionFlag.Annotations[FlagSetByCobraAnnotation]) > 0 && versionFlag.Changed {
|
||||
return true
|
||||
}
|
||||
if helpFlag := cmd.Flags().Lookup("help"); helpFlag != nil &&
|
||||
len(helpFlag.Annotations[FlagSetByCobraAnnotation]) > 0 && helpFlag.Changed {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getFlagNameCompletions(flag *pflag.Flag, toComplete string) []string {
|
||||
if nonCompletableFlag(flag) {
|
||||
return []string{}
|
||||
@@ -590,12 +675,12 @@ func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*p
|
||||
return flag, trimmedArgs, lastArg, nil
|
||||
}
|
||||
|
||||
// initDefaultCompletionCmd adds a default 'completion' command to c.
|
||||
// InitDefaultCompletionCmd adds a default 'completion' command to c.
|
||||
// This function will do nothing if any of the following is true:
|
||||
// 1- the feature has been explicitly disabled by the program,
|
||||
// 2- c has no subcommands (to avoid creating one),
|
||||
// 3- c already has a 'completion' command provided by the program.
|
||||
func (c *Command) initDefaultCompletionCmd() {
|
||||
func (c *Command) InitDefaultCompletionCmd() {
|
||||
if c.CompletionOptions.DisableDefaultCmd || !c.HasSubCommands() {
|
||||
return
|
||||
}
|
||||
@@ -618,6 +703,7 @@ See each sub-command's help for details on how to use the generated script.
|
||||
Args: NoArgs,
|
||||
ValidArgsFunction: NoFileCompletions,
|
||||
Hidden: c.CompletionOptions.HiddenDefaultCmd,
|
||||
GroupID: c.completionCommandGroupID,
|
||||
}
|
||||
c.AddCommand(completionCmd)
|
||||
|
||||
@@ -644,7 +730,7 @@ To load completions for every new session, execute once:
|
||||
|
||||
#### macOS:
|
||||
|
||||
%[1]s completion bash > /usr/local/etc/bash_completion.d/%[1]s
|
||||
%[1]s completion bash > $(brew --prefix)/etc/bash_completion.d/%[1]s
|
||||
|
||||
You will need to start a new shell for this setup to take effect.
|
||||
`, c.Root().Name()),
|
||||
@@ -669,6 +755,10 @@ to enable it. You can execute the following once:
|
||||
|
||||
echo "autoload -U compinit; compinit" >> ~/.zshrc
|
||||
|
||||
To load completions in your current shell session:
|
||||
|
||||
source <(%[1]s completion zsh)
|
||||
|
||||
To load completions for every new session, execute once:
|
||||
|
||||
#### Linux:
|
||||
@@ -677,7 +767,7 @@ To load completions for every new session, execute once:
|
||||
|
||||
#### macOS:
|
||||
|
||||
%[1]s completion zsh > /usr/local/share/zsh/site-functions/_%[1]s
|
||||
%[1]s completion zsh > $(brew --prefix)/share/zsh/site-functions/_%[1]s
|
||||
|
||||
You will need to start a new shell for this setup to take effect.
|
||||
`, c.Root().Name()),
|
||||
|
||||
+84
-11
@@ -1,3 +1,17 @@
|
||||
// Copyright 2013-2023 The Cobra Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cobra
|
||||
|
||||
import (
|
||||
@@ -11,8 +25,8 @@ import (
|
||||
func genFishComp(buf io.StringWriter, name string, includeDesc bool) {
|
||||
// Variables should not contain a '-' or ':' character
|
||||
nameForVar := name
|
||||
nameForVar = strings.Replace(nameForVar, "-", "_", -1)
|
||||
nameForVar = strings.Replace(nameForVar, ":", "_", -1)
|
||||
nameForVar = strings.ReplaceAll(nameForVar, "-", "_")
|
||||
nameForVar = strings.ReplaceAll(nameForVar, ":", "_")
|
||||
|
||||
compCmd := ShellCompRequestCmd
|
||||
if !includeDesc {
|
||||
@@ -38,7 +52,8 @@ function __%[1]s_perform_completion
|
||||
__%[1]s_debug "args: $args"
|
||||
__%[1]s_debug "last arg: $lastArg"
|
||||
|
||||
set -l requestComp "$args[1] %[3]s $args[2..-1] $lastArg"
|
||||
# Disable ActiveHelp which is not supported for fish shell
|
||||
set -l requestComp "%[10]s=0 $args[1] %[3]s $args[2..-1] $lastArg"
|
||||
|
||||
__%[1]s_debug "Calling $requestComp"
|
||||
set -l results (eval $requestComp 2> /dev/null)
|
||||
@@ -74,6 +89,60 @@ function __%[1]s_perform_completion
|
||||
printf "%%s\n" "$directiveLine"
|
||||
end
|
||||
|
||||
# this function limits calls to __%[1]s_perform_completion, by caching the result behind $__%[1]s_perform_completion_once_result
|
||||
function __%[1]s_perform_completion_once
|
||||
__%[1]s_debug "Starting __%[1]s_perform_completion_once"
|
||||
|
||||
if test -n "$__%[1]s_perform_completion_once_result"
|
||||
__%[1]s_debug "Seems like a valid result already exists, skipping __%[1]s_perform_completion"
|
||||
return 0
|
||||
end
|
||||
|
||||
set --global __%[1]s_perform_completion_once_result (__%[1]s_perform_completion)
|
||||
if test -z "$__%[1]s_perform_completion_once_result"
|
||||
__%[1]s_debug "No completions, probably due to a failure"
|
||||
return 1
|
||||
end
|
||||
|
||||
__%[1]s_debug "Performed completions and set __%[1]s_perform_completion_once_result"
|
||||
return 0
|
||||
end
|
||||
|
||||
# this function is used to clear the $__%[1]s_perform_completion_once_result variable after completions are run
|
||||
function __%[1]s_clear_perform_completion_once_result
|
||||
__%[1]s_debug ""
|
||||
__%[1]s_debug "========= clearing previously set __%[1]s_perform_completion_once_result variable =========="
|
||||
set --erase __%[1]s_perform_completion_once_result
|
||||
__%[1]s_debug "Successfully erased the variable __%[1]s_perform_completion_once_result"
|
||||
end
|
||||
|
||||
function __%[1]s_requires_order_preservation
|
||||
__%[1]s_debug ""
|
||||
__%[1]s_debug "========= checking if order preservation is required =========="
|
||||
|
||||
__%[1]s_perform_completion_once
|
||||
if test -z "$__%[1]s_perform_completion_once_result"
|
||||
__%[1]s_debug "Error determining if order preservation is required"
|
||||
return 1
|
||||
end
|
||||
|
||||
set -l directive (string sub --start 2 $__%[1]s_perform_completion_once_result[-1])
|
||||
__%[1]s_debug "Directive is: $directive"
|
||||
|
||||
set -l shellCompDirectiveKeepOrder %[9]d
|
||||
set -l keeporder (math (math --scale 0 $directive / $shellCompDirectiveKeepOrder) %% 2)
|
||||
__%[1]s_debug "Keeporder is: $keeporder"
|
||||
|
||||
if test $keeporder -ne 0
|
||||
__%[1]s_debug "This does require order preservation"
|
||||
return 0
|
||||
end
|
||||
|
||||
__%[1]s_debug "This doesn't require order preservation"
|
||||
return 1
|
||||
end
|
||||
|
||||
|
||||
# This function does two things:
|
||||
# - Obtain the completions and store them in the global __%[1]s_comp_results
|
||||
# - Return false if file completion should be performed
|
||||
@@ -84,17 +153,17 @@ function __%[1]s_prepare_completions
|
||||
# Start fresh
|
||||
set --erase __%[1]s_comp_results
|
||||
|
||||
set -l results (__%[1]s_perform_completion)
|
||||
__%[1]s_debug "Completion results: $results"
|
||||
__%[1]s_perform_completion_once
|
||||
__%[1]s_debug "Completion results: $__%[1]s_perform_completion_once_result"
|
||||
|
||||
if test -z "$results"
|
||||
if test -z "$__%[1]s_perform_completion_once_result"
|
||||
__%[1]s_debug "No completion, probably due to a failure"
|
||||
# Might as well do file completion, in case it helps
|
||||
return 1
|
||||
end
|
||||
|
||||
set -l directive (string sub --start 2 $results[-1])
|
||||
set --global __%[1]s_comp_results $results[1..-2]
|
||||
set -l directive (string sub --start 2 $__%[1]s_perform_completion_once_result[-1])
|
||||
set --global __%[1]s_comp_results $__%[1]s_perform_completion_once_result[1..-2]
|
||||
|
||||
__%[1]s_debug "Completions are: $__%[1]s_comp_results"
|
||||
__%[1]s_debug "Directive is: $directive"
|
||||
@@ -190,13 +259,17 @@ end
|
||||
# Remove any pre-existing completions for the program since we will be handling all of them.
|
||||
complete -c %[2]s -e
|
||||
|
||||
# this will get called after the two calls below and clear the $__%[1]s_perform_completion_once_result global
|
||||
complete -c %[2]s -n '__%[1]s_clear_perform_completion_once_result'
|
||||
# The call to __%[1]s_prepare_completions will setup __%[1]s_comp_results
|
||||
# which provides the program's completion choices.
|
||||
complete -c %[2]s -n '__%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results'
|
||||
|
||||
# If this doesn't require order preservation, we don't use the -k flag
|
||||
complete -c %[2]s -n 'not __%[1]s_requires_order_preservation && __%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results'
|
||||
# otherwise we use the -k flag
|
||||
complete -k -c %[2]s -n '__%[1]s_requires_order_preservation && __%[1]s_prepare_completions' -f -a '$__%[1]s_comp_results'
|
||||
`, nameForVar, name, compCmd,
|
||||
ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
|
||||
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
|
||||
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpEnvVar(name)))
|
||||
}
|
||||
|
||||
// GenFishCompletion generates fish completion file and writes to the passed writer.
|
||||
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
## Generating Fish Completions For Your cobra.Command
|
||||
|
||||
Please refer to [Shell Completions](shell_completions.md) for details.
|
||||
|
||||
+290
@@ -0,0 +1,290 @@
|
||||
// Copyright 2013-2023 The Cobra Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cobra
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
flag "github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
const (
|
||||
requiredAsGroup = "cobra_annotation_required_if_others_set"
|
||||
oneRequired = "cobra_annotation_one_required"
|
||||
mutuallyExclusive = "cobra_annotation_mutually_exclusive"
|
||||
)
|
||||
|
||||
// MarkFlagsRequiredTogether marks the given flags with annotations so that Cobra errors
|
||||
// if the command is invoked with a subset (but not all) of the given flags.
|
||||
func (c *Command) MarkFlagsRequiredTogether(flagNames ...string) {
|
||||
c.mergePersistentFlags()
|
||||
for _, v := range flagNames {
|
||||
f := c.Flags().Lookup(v)
|
||||
if f == nil {
|
||||
panic(fmt.Sprintf("Failed to find flag %q and mark it as being required in a flag group", v))
|
||||
}
|
||||
if err := c.Flags().SetAnnotation(v, requiredAsGroup, append(f.Annotations[requiredAsGroup], strings.Join(flagNames, " "))); err != nil {
|
||||
// Only errs if the flag isn't found.
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MarkFlagsOneRequired marks the given flags with annotations so that Cobra errors
|
||||
// if the command is invoked without at least one flag from the given set of flags.
|
||||
func (c *Command) MarkFlagsOneRequired(flagNames ...string) {
|
||||
c.mergePersistentFlags()
|
||||
for _, v := range flagNames {
|
||||
f := c.Flags().Lookup(v)
|
||||
if f == nil {
|
||||
panic(fmt.Sprintf("Failed to find flag %q and mark it as being in a one-required flag group", v))
|
||||
}
|
||||
if err := c.Flags().SetAnnotation(v, oneRequired, append(f.Annotations[oneRequired], strings.Join(flagNames, " "))); err != nil {
|
||||
// Only errs if the flag isn't found.
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MarkFlagsMutuallyExclusive marks the given flags with annotations so that Cobra errors
|
||||
// if the command is invoked with more than one flag from the given set of flags.
|
||||
func (c *Command) MarkFlagsMutuallyExclusive(flagNames ...string) {
|
||||
c.mergePersistentFlags()
|
||||
for _, v := range flagNames {
|
||||
f := c.Flags().Lookup(v)
|
||||
if f == nil {
|
||||
panic(fmt.Sprintf("Failed to find flag %q and mark it as being in a mutually exclusive flag group", v))
|
||||
}
|
||||
// Each time this is called is a single new entry; this allows it to be a member of multiple groups if needed.
|
||||
if err := c.Flags().SetAnnotation(v, mutuallyExclusive, append(f.Annotations[mutuallyExclusive], strings.Join(flagNames, " "))); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ValidateFlagGroups validates the mutuallyExclusive/oneRequired/requiredAsGroup logic and returns the
|
||||
// first error encountered.
|
||||
func (c *Command) ValidateFlagGroups() error {
|
||||
if c.DisableFlagParsing {
|
||||
return nil
|
||||
}
|
||||
|
||||
flags := c.Flags()
|
||||
|
||||
// groupStatus format is the list of flags as a unique ID,
|
||||
// then a map of each flag name and whether it is set or not.
|
||||
groupStatus := map[string]map[string]bool{}
|
||||
oneRequiredGroupStatus := map[string]map[string]bool{}
|
||||
mutuallyExclusiveGroupStatus := map[string]map[string]bool{}
|
||||
flags.VisitAll(func(pflag *flag.Flag) {
|
||||
processFlagForGroupAnnotation(flags, pflag, requiredAsGroup, groupStatus)
|
||||
processFlagForGroupAnnotation(flags, pflag, oneRequired, oneRequiredGroupStatus)
|
||||
processFlagForGroupAnnotation(flags, pflag, mutuallyExclusive, mutuallyExclusiveGroupStatus)
|
||||
})
|
||||
|
||||
if err := validateRequiredFlagGroups(groupStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateOneRequiredFlagGroups(oneRequiredGroupStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateExclusiveFlagGroups(mutuallyExclusiveGroupStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func hasAllFlags(fs *flag.FlagSet, flagnames ...string) bool {
|
||||
for _, fname := range flagnames {
|
||||
f := fs.Lookup(fname)
|
||||
if f == nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func processFlagForGroupAnnotation(flags *flag.FlagSet, pflag *flag.Flag, annotation string, groupStatus map[string]map[string]bool) {
|
||||
groupInfo, found := pflag.Annotations[annotation]
|
||||
if found {
|
||||
for _, group := range groupInfo {
|
||||
if groupStatus[group] == nil {
|
||||
flagnames := strings.Split(group, " ")
|
||||
|
||||
// Only consider this flag group at all if all the flags are defined.
|
||||
if !hasAllFlags(flags, flagnames...) {
|
||||
continue
|
||||
}
|
||||
|
||||
groupStatus[group] = map[string]bool{}
|
||||
for _, name := range flagnames {
|
||||
groupStatus[group][name] = false
|
||||
}
|
||||
}
|
||||
|
||||
groupStatus[group][pflag.Name] = pflag.Changed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validateRequiredFlagGroups(data map[string]map[string]bool) error {
|
||||
keys := sortedKeys(data)
|
||||
for _, flagList := range keys {
|
||||
flagnameAndStatus := data[flagList]
|
||||
|
||||
unset := []string{}
|
||||
for flagname, isSet := range flagnameAndStatus {
|
||||
if !isSet {
|
||||
unset = append(unset, flagname)
|
||||
}
|
||||
}
|
||||
if len(unset) == len(flagnameAndStatus) || len(unset) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Sort values, so they can be tested/scripted against consistently.
|
||||
sort.Strings(unset)
|
||||
return fmt.Errorf("if any flags in the group [%v] are set they must all be set; missing %v", flagList, unset)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateOneRequiredFlagGroups(data map[string]map[string]bool) error {
|
||||
keys := sortedKeys(data)
|
||||
for _, flagList := range keys {
|
||||
flagnameAndStatus := data[flagList]
|
||||
var set []string
|
||||
for flagname, isSet := range flagnameAndStatus {
|
||||
if isSet {
|
||||
set = append(set, flagname)
|
||||
}
|
||||
}
|
||||
if len(set) >= 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Sort values, so they can be tested/scripted against consistently.
|
||||
sort.Strings(set)
|
||||
return fmt.Errorf("at least one of the flags in the group [%v] is required", flagList)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateExclusiveFlagGroups(data map[string]map[string]bool) error {
|
||||
keys := sortedKeys(data)
|
||||
for _, flagList := range keys {
|
||||
flagnameAndStatus := data[flagList]
|
||||
var set []string
|
||||
for flagname, isSet := range flagnameAndStatus {
|
||||
if isSet {
|
||||
set = append(set, flagname)
|
||||
}
|
||||
}
|
||||
if len(set) == 0 || len(set) == 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Sort values, so they can be tested/scripted against consistently.
|
||||
sort.Strings(set)
|
||||
return fmt.Errorf("if any flags in the group [%v] are set none of the others can be; %v were all set", flagList, set)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func sortedKeys(m map[string]map[string]bool) []string {
|
||||
keys := make([]string, len(m))
|
||||
i := 0
|
||||
for k := range m {
|
||||
keys[i] = k
|
||||
i++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
// enforceFlagGroupsForCompletion will do the following:
|
||||
// - when a flag in a group is present, other flags in the group will be marked required
|
||||
// - when none of the flags in a one-required group are present, all flags in the group will be marked required
|
||||
// - when a flag in a mutually exclusive group is present, other flags in the group will be marked as hidden
|
||||
// This allows the standard completion logic to behave appropriately for flag groups
|
||||
func (c *Command) enforceFlagGroupsForCompletion() {
|
||||
if c.DisableFlagParsing {
|
||||
return
|
||||
}
|
||||
|
||||
flags := c.Flags()
|
||||
groupStatus := map[string]map[string]bool{}
|
||||
oneRequiredGroupStatus := map[string]map[string]bool{}
|
||||
mutuallyExclusiveGroupStatus := map[string]map[string]bool{}
|
||||
c.Flags().VisitAll(func(pflag *flag.Flag) {
|
||||
processFlagForGroupAnnotation(flags, pflag, requiredAsGroup, groupStatus)
|
||||
processFlagForGroupAnnotation(flags, pflag, oneRequired, oneRequiredGroupStatus)
|
||||
processFlagForGroupAnnotation(flags, pflag, mutuallyExclusive, mutuallyExclusiveGroupStatus)
|
||||
})
|
||||
|
||||
// If a flag that is part of a group is present, we make all the other flags
|
||||
// of that group required so that the shell completion suggests them automatically
|
||||
for flagList, flagnameAndStatus := range groupStatus {
|
||||
for _, isSet := range flagnameAndStatus {
|
||||
if isSet {
|
||||
// One of the flags of the group is set, mark the other ones as required
|
||||
for _, fName := range strings.Split(flagList, " ") {
|
||||
_ = c.MarkFlagRequired(fName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If none of the flags of a one-required group are present, we make all the flags
|
||||
// of that group required so that the shell completion suggests them automatically
|
||||
for flagList, flagnameAndStatus := range oneRequiredGroupStatus {
|
||||
set := 0
|
||||
|
||||
for _, isSet := range flagnameAndStatus {
|
||||
if isSet {
|
||||
set++
|
||||
}
|
||||
}
|
||||
|
||||
// None of the flags of the group are set, mark all flags in the group
|
||||
// as required
|
||||
if set == 0 {
|
||||
for _, fName := range strings.Split(flagList, " ") {
|
||||
_ = c.MarkFlagRequired(fName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a flag that is mutually exclusive to others is present, we hide the other
|
||||
// flags of that group so the shell completion does not suggest them
|
||||
for flagList, flagnameAndStatus := range mutuallyExclusiveGroupStatus {
|
||||
for flagName, isSet := range flagnameAndStatus {
|
||||
if isSet {
|
||||
// One of the flags of the mutually exclusive group is set, mark the other ones as hidden
|
||||
// Don't mark the flag that is already set as hidden because it may be an
|
||||
// array or slice flag and therefore must continue being suggested
|
||||
for _, fName := range strings.Split(flagList, " ") {
|
||||
if fName != flagName {
|
||||
flag := c.Flags().Lookup(fName)
|
||||
flag.Hidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+53
-13
@@ -1,3 +1,17 @@
|
||||
// Copyright 2013-2023 The Cobra Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// The generated scripts require PowerShell v5.0+ (which comes Windows 10, but
|
||||
// can be downloaded separately for windows 7 or 8.1).
|
||||
|
||||
@@ -8,9 +22,15 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func genPowerShellComp(buf io.StringWriter, name string, includeDesc bool) {
|
||||
// Variables should not contain a '-' or ':' character
|
||||
nameForVar := name
|
||||
nameForVar = strings.Replace(nameForVar, "-", "_", -1)
|
||||
nameForVar = strings.Replace(nameForVar, ":", "_", -1)
|
||||
|
||||
compCmd := ShellCompRequestCmd
|
||||
if !includeDesc {
|
||||
compCmd = ShellCompNoDescRequestCmd
|
||||
@@ -27,7 +47,7 @@ filter __%[1]s_escapeStringWithSpecialChars {
|
||||
`+" $_ -replace '\\s|#|@|\\$|;|,|''|\\{|\\}|\\(|\\)|\"|`|\\||<|>|&','`$&'"+`
|
||||
}
|
||||
|
||||
Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock {
|
||||
[scriptblock]${__%[2]sCompleterBlock} = {
|
||||
param(
|
||||
$WordToComplete,
|
||||
$CommandAst,
|
||||
@@ -52,16 +72,18 @@ Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock {
|
||||
}
|
||||
__%[1]s_debug "Truncated command: $Command"
|
||||
|
||||
$ShellCompDirectiveError=%[3]d
|
||||
$ShellCompDirectiveNoSpace=%[4]d
|
||||
$ShellCompDirectiveNoFileComp=%[5]d
|
||||
$ShellCompDirectiveFilterFileExt=%[6]d
|
||||
$ShellCompDirectiveFilterDirs=%[7]d
|
||||
$ShellCompDirectiveError=%[4]d
|
||||
$ShellCompDirectiveNoSpace=%[5]d
|
||||
$ShellCompDirectiveNoFileComp=%[6]d
|
||||
$ShellCompDirectiveFilterFileExt=%[7]d
|
||||
$ShellCompDirectiveFilterDirs=%[8]d
|
||||
$ShellCompDirectiveKeepOrder=%[9]d
|
||||
|
||||
# Prepare the command to request completions for the program.
|
||||
# Split the command at the first space to separate the program and arguments.
|
||||
$Program,$Arguments = $Command.Split(" ",2)
|
||||
$RequestComp="$Program %[2]s $Arguments"
|
||||
|
||||
$RequestComp="$Program %[3]s $Arguments"
|
||||
__%[1]s_debug "RequestComp: $RequestComp"
|
||||
|
||||
# we cannot use $WordToComplete because it
|
||||
@@ -85,16 +107,27 @@ Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock {
|
||||
# If the last parameter is complete (there is a space following it)
|
||||
# We add an extra empty parameter so we can indicate this to the go method.
|
||||
__%[1]s_debug "Adding extra empty parameter"
|
||||
`+" # We need to use `\"`\" to pass an empty argument a \"\" or '' does not work!!!"+`
|
||||
`+" $RequestComp=\"$RequestComp\" + ' `\"`\"'"+`
|
||||
# PowerShell 7.2+ changed the way how the arguments are passed to executables,
|
||||
# so for pre-7.2 or when Legacy argument passing is enabled we need to use
|
||||
`+" # `\"`\" to pass an empty argument, a \"\" or '' does not work!!!"+`
|
||||
if ($PSVersionTable.PsVersion -lt [version]'7.2.0' -or
|
||||
($PSVersionTable.PsVersion -lt [version]'7.3.0' -and -not [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -or
|
||||
(($PSVersionTable.PsVersion -ge [version]'7.3.0' -or [ExperimentalFeature]::IsEnabled("PSNativeCommandArgumentPassing")) -and
|
||||
$PSNativeCommandArgumentPassing -eq 'Legacy')) {
|
||||
`+" $RequestComp=\"$RequestComp\" + ' `\"`\"'"+`
|
||||
} else {
|
||||
$RequestComp="$RequestComp" + ' ""'
|
||||
}
|
||||
}
|
||||
|
||||
__%[1]s_debug "Calling $RequestComp"
|
||||
# First disable ActiveHelp which is not supported for Powershell
|
||||
${env:%[10]s}=0
|
||||
|
||||
#call the command store the output in $out and redirect stderr and stdout to null
|
||||
# $Out is an array contains each line per element
|
||||
Invoke-Expression -OutVariable out "$RequestComp" 2>&1 | Out-Null
|
||||
|
||||
|
||||
# get directive from last line
|
||||
[int]$Directive = $Out[-1].TrimStart(':')
|
||||
if ($Directive -eq "") {
|
||||
@@ -114,7 +147,7 @@ Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock {
|
||||
}
|
||||
|
||||
$Longest = 0
|
||||
$Values = $Out | ForEach-Object {
|
||||
[Array]$Values = $Out | ForEach-Object {
|
||||
#Split the output in name and description
|
||||
`+" $Name, $Description = $_.Split(\"`t\",2)"+`
|
||||
__%[1]s_debug "Name: $Name Description: $Description"
|
||||
@@ -159,6 +192,11 @@ Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock {
|
||||
}
|
||||
}
|
||||
|
||||
# we sort the values in ascending order by name if keep order isn't passed
|
||||
if (($Directive -band $ShellCompDirectiveKeepOrder) -eq 0 ) {
|
||||
$Values = $Values | Sort-Object -Property Name
|
||||
}
|
||||
|
||||
if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) {
|
||||
__%[1]s_debug "ShellCompDirectiveNoFileComp is called"
|
||||
|
||||
@@ -240,9 +278,11 @@ Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock {
|
||||
|
||||
}
|
||||
}
|
||||
`, name, compCmd,
|
||||
|
||||
Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock ${__%[2]sCompleterBlock}
|
||||
`, name, nameForVar, compCmd,
|
||||
ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
|
||||
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
|
||||
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpEnvVar(name)))
|
||||
}
|
||||
|
||||
func (c *Command) genPowerShellCompletion(w io.Writer, includeDesc bool) error {
|
||||
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
# Generating PowerShell Completions For Your Own cobra.Command
|
||||
|
||||
Please refer to [Shell Completions](shell_completions.md#powershell-completions) for details.
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
## Projects using Cobra
|
||||
|
||||
- [Arduino CLI](https://github.com/arduino/arduino-cli)
|
||||
- [Bleve](http://www.blevesearch.com/)
|
||||
- [CockroachDB](http://www.cockroachlabs.com/)
|
||||
- [Cosmos SDK](https://github.com/cosmos/cosmos-sdk)
|
||||
- [Datree](https://github.com/datreeio/datree)
|
||||
- [Delve](https://github.com/derekparker/delve)
|
||||
- [Docker (distribution)](https://github.com/docker/distribution)
|
||||
- [Etcd](https://etcd.io/)
|
||||
- [Gardener](https://github.com/gardener/gardenctl)
|
||||
- [Giant Swarm's gsctl](https://github.com/giantswarm/gsctl)
|
||||
- [Git Bump](https://github.com/erdaltsksn/git-bump)
|
||||
- [Github CLI](https://github.com/cli/cli)
|
||||
- [GitHub Labeler](https://github.com/erdaltsksn/gh-label)
|
||||
- [Golangci-lint](https://golangci-lint.run)
|
||||
- [GopherJS](http://www.gopherjs.org/)
|
||||
- [GoReleaser](https://goreleaser.com)
|
||||
- [Helm](https://helm.sh)
|
||||
- [Hugo](https://gohugo.io)
|
||||
- [Infracost](https://github.com/infracost/infracost)
|
||||
- [Istio](https://istio.io)
|
||||
- [Kool](https://github.com/kool-dev/kool)
|
||||
- [Kubernetes](http://kubernetes.io/)
|
||||
- [Linkerd](https://linkerd.io/)
|
||||
- [Mattermost-server](https://github.com/mattermost/mattermost-server)
|
||||
- [Mercure](https://mercure.rocks/)
|
||||
- [Meroxa CLI](https://github.com/meroxa/cli)
|
||||
- [Metal Stack CLI](https://github.com/metal-stack/metalctl)
|
||||
- [Moby (former Docker)](https://github.com/moby/moby)
|
||||
- [Moldy](https://github.com/Moldy-Community/moldy)
|
||||
- [Multi-gitter](https://github.com/lindell/multi-gitter)
|
||||
- [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack)
|
||||
- [nFPM](https://nfpm.goreleaser.com)
|
||||
- [OpenShift](https://www.openshift.com/)
|
||||
- [Ory Hydra](https://github.com/ory/hydra)
|
||||
- [Ory Kratos](https://github.com/ory/kratos)
|
||||
- [Pixie](https://github.com/pixie-io/pixie)
|
||||
- [Pouch](https://github.com/alibaba/pouch)
|
||||
- [ProjectAtomic (enterprise)](http://www.projectatomic.io/)
|
||||
- [Prototool](https://github.com/uber/prototool)
|
||||
- [QRcp](https://github.com/claudiodangelis/qrcp)
|
||||
- [Random](https://github.com/erdaltsksn/random)
|
||||
- [Rclone](https://rclone.org/)
|
||||
- [Scaleway CLI](https://github.com/scaleway/scaleway-cli)
|
||||
- [Skaffold](https://skaffold.dev/)
|
||||
- [Tendermint](https://github.com/tendermint/tendermint)
|
||||
- [Twitch CLI](https://github.com/twitchdev/twitch-cli)
|
||||
- [UpCloud CLI (`upctl`)](https://github.com/UpCloudLtd/upcloud-cli)
|
||||
- VMware's [Tanzu Community Edition](https://github.com/vmware-tanzu/community-edition) & [Tanzu Framework](https://github.com/vmware-tanzu/tanzu-framework)
|
||||
- [Werf](https://werf.io/)
|
||||
+14
@@ -1,3 +1,17 @@
|
||||
// Copyright 2013-2023 The Cobra Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cobra
|
||||
|
||||
import (
|
||||
|
||||
-548
@@ -1,548 +0,0 @@
|
||||
# Generating shell completions
|
||||
|
||||
Cobra can generate shell completions for multiple shells.
|
||||
The currently supported shells are:
|
||||
- Bash
|
||||
- Zsh
|
||||
- fish
|
||||
- PowerShell
|
||||
|
||||
Cobra will automatically provide your program with a fully functional `completion` command,
|
||||
similarly to how it provides the `help` command.
|
||||
|
||||
## Creating your own completion command
|
||||
|
||||
If you do not wish to use the default `completion` command, you can choose to
|
||||
provide your own, which will take precedence over the default one. (This also provides
|
||||
backwards-compatibility with programs that already have their own `completion` command.)
|
||||
|
||||
If you are using the `cobra-cli` generator,
|
||||
which can be found at [spf13/cobra-cli](https://github.com/spf13/cobra-cli),
|
||||
you can create a completion command by running
|
||||
|
||||
```bash
|
||||
cobra-cli add completion
|
||||
```
|
||||
and then modifying the generated `cmd/completion.go` file to look something like this
|
||||
(writing the shell script to stdout allows the most flexible use):
|
||||
|
||||
```go
|
||||
var completionCmd = &cobra.Command{
|
||||
Use: "completion [bash|zsh|fish|powershell]",
|
||||
Short: "Generate completion script",
|
||||
Long: fmt.Sprintf(`To load completions:
|
||||
|
||||
Bash:
|
||||
|
||||
$ source <(%[1]s completion bash)
|
||||
|
||||
# To load completions for each session, execute once:
|
||||
# Linux:
|
||||
$ %[1]s completion bash > /etc/bash_completion.d/%[1]s
|
||||
# macOS:
|
||||
$ %[1]s completion bash > /usr/local/etc/bash_completion.d/%[1]s
|
||||
|
||||
Zsh:
|
||||
|
||||
# If shell completion is not already enabled in your environment,
|
||||
# you will need to enable it. You can execute the following once:
|
||||
|
||||
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
|
||||
|
||||
# To load completions for each session, execute once:
|
||||
$ %[1]s completion zsh > "${fpath[1]}/_%[1]s"
|
||||
|
||||
# You will need to start a new shell for this setup to take effect.
|
||||
|
||||
fish:
|
||||
|
||||
$ %[1]s completion fish | source
|
||||
|
||||
# To load completions for each session, execute once:
|
||||
$ %[1]s completion fish > ~/.config/fish/completions/%[1]s.fish
|
||||
|
||||
PowerShell:
|
||||
|
||||
PS> %[1]s completion powershell | Out-String | Invoke-Expression
|
||||
|
||||
# To load completions for every new session, run:
|
||||
PS> %[1]s completion powershell > %[1]s.ps1
|
||||
# and source this file from your PowerShell profile.
|
||||
`,cmd.Root().Name()),
|
||||
DisableFlagsInUseLine: true,
|
||||
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
|
||||
Args: cobra.ExactValidArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
switch args[0] {
|
||||
case "bash":
|
||||
cmd.Root().GenBashCompletion(os.Stdout)
|
||||
case "zsh":
|
||||
cmd.Root().GenZshCompletion(os.Stdout)
|
||||
case "fish":
|
||||
cmd.Root().GenFishCompletion(os.Stdout, true)
|
||||
case "powershell":
|
||||
cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
|
||||
}
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** The cobra generator may include messages printed to stdout, for example, if the config file is loaded; this will break the auto-completion script so must be removed.
|
||||
|
||||
## Adapting the default completion command
|
||||
|
||||
Cobra provides a few options for the default `completion` command. To configure such options you must set
|
||||
the `CompletionOptions` field on the *root* command.
|
||||
|
||||
To tell Cobra *not* to provide the default `completion` command:
|
||||
```
|
||||
rootCmd.CompletionOptions.DisableDefaultCmd = true
|
||||
```
|
||||
|
||||
To tell Cobra *not* to provide the user with the `--no-descriptions` flag to the completion sub-commands:
|
||||
```
|
||||
rootCmd.CompletionOptions.DisableNoDescFlag = true
|
||||
```
|
||||
|
||||
To tell Cobra to completely disable descriptions for completions:
|
||||
```
|
||||
rootCmd.CompletionOptions.DisableDescriptions = true
|
||||
```
|
||||
|
||||
# Customizing completions
|
||||
|
||||
The generated completion scripts will automatically handle completing commands and flags. However, you can make your completions much more powerful by providing information to complete your program's nouns and flag values.
|
||||
|
||||
## Completion of nouns
|
||||
|
||||
### Static completion of nouns
|
||||
|
||||
Cobra allows you to provide a pre-defined list of completion choices for your nouns using the `ValidArgs` field.
|
||||
For example, if you want `kubectl get [tab][tab]` to show a list of valid "nouns" you have to set them.
|
||||
Some simplified code from `kubectl get` looks like:
|
||||
|
||||
```go
|
||||
validArgs []string = { "pod", "node", "service", "replicationcontroller" }
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "get [(-o|--output=)json|yaml|template|...] (RESOURCE [NAME] | RESOURCE/NAME ...)",
|
||||
Short: "Display one or many resources",
|
||||
Long: get_long,
|
||||
Example: get_example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cobra.CheckErr(RunGet(f, out, cmd, args))
|
||||
},
|
||||
ValidArgs: validArgs,
|
||||
}
|
||||
```
|
||||
|
||||
Notice we put the `ValidArgs` field on the `get` sub-command. Doing so will give results like:
|
||||
|
||||
```bash
|
||||
$ kubectl get [tab][tab]
|
||||
node pod replicationcontroller service
|
||||
```
|
||||
|
||||
#### Aliases for nouns
|
||||
|
||||
If your nouns have aliases, you can define them alongside `ValidArgs` using `ArgAliases`:
|
||||
|
||||
```go
|
||||
argAliases []string = { "pods", "nodes", "services", "svc", "replicationcontrollers", "rc" }
|
||||
|
||||
cmd := &cobra.Command{
|
||||
...
|
||||
ValidArgs: validArgs,
|
||||
ArgAliases: argAliases
|
||||
}
|
||||
```
|
||||
|
||||
The aliases are not shown to the user on tab completion, but they are accepted as valid nouns by
|
||||
the completion algorithm if entered manually, e.g. in:
|
||||
|
||||
```bash
|
||||
$ kubectl get rc [tab][tab]
|
||||
backend frontend database
|
||||
```
|
||||
|
||||
Note that without declaring `rc` as an alias, the completion algorithm would not know to show the list of
|
||||
replication controllers following `rc`.
|
||||
|
||||
### Dynamic completion of nouns
|
||||
|
||||
In some cases it is not possible to provide a list of completions in advance. Instead, the list of completions must be determined at execution-time. In a similar fashion as for static completions, you can use the `ValidArgsFunction` field to provide a Go function that Cobra will execute when it needs the list of completion choices for the nouns of a command. Note that either `ValidArgs` or `ValidArgsFunction` can be used for a single cobra command, but not both.
|
||||
Simplified code from `helm status` looks like:
|
||||
|
||||
```go
|
||||
cmd := &cobra.Command{
|
||||
Use: "status RELEASE_NAME",
|
||||
Short: "Display the status of the named release",
|
||||
Long: status_long,
|
||||
RunE: func(cmd *cobra.Command, args []string) {
|
||||
RunGet(args[0])
|
||||
},
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) != 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
return getReleasesFromCluster(toComplete), cobra.ShellCompDirectiveNoFileComp
|
||||
},
|
||||
}
|
||||
```
|
||||
Where `getReleasesFromCluster()` is a Go function that obtains the list of current Helm releases running on the Kubernetes cluster.
|
||||
Notice we put the `ValidArgsFunction` on the `status` sub-command. Let's assume the Helm releases on the cluster are: `harbor`, `notary`, `rook` and `thanos` then this dynamic completion will give results like:
|
||||
|
||||
```bash
|
||||
$ helm status [tab][tab]
|
||||
harbor notary rook thanos
|
||||
```
|
||||
You may have noticed the use of `cobra.ShellCompDirective`. These directives are bit fields allowing to control some shell completion behaviors for your particular completion. You can combine them with the bit-or operator such as `cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp`
|
||||
```go
|
||||
// Indicates that the shell will perform its default behavior after completions
|
||||
// have been provided (this implies none of the other directives).
|
||||
ShellCompDirectiveDefault
|
||||
|
||||
// Indicates an error occurred and completions should be ignored.
|
||||
ShellCompDirectiveError
|
||||
|
||||
// Indicates that the shell should not add a space after the completion,
|
||||
// even if there is a single completion provided.
|
||||
ShellCompDirectiveNoSpace
|
||||
|
||||
// Indicates that the shell should not provide file completion even when
|
||||
// no completion is provided.
|
||||
ShellCompDirectiveNoFileComp
|
||||
|
||||
// Indicates that the returned completions should be used as file extension filters.
|
||||
// For example, to complete only files of the form *.json or *.yaml:
|
||||
// return []string{"yaml", "json"}, ShellCompDirectiveFilterFileExt
|
||||
// For flags, using MarkFlagFilename() and MarkPersistentFlagFilename()
|
||||
// is a shortcut to using this directive explicitly.
|
||||
//
|
||||
ShellCompDirectiveFilterFileExt
|
||||
|
||||
// Indicates that only directory names should be provided in file completion.
|
||||
// For example:
|
||||
// return nil, ShellCompDirectiveFilterDirs
|
||||
// For flags, using MarkFlagDirname() is a shortcut to using this directive explicitly.
|
||||
//
|
||||
// To request directory names within another directory, the returned completions
|
||||
// should specify a single directory name within which to search. For example,
|
||||
// to complete directories within "themes/":
|
||||
// return []string{"themes"}, ShellCompDirectiveFilterDirs
|
||||
//
|
||||
ShellCompDirectiveFilterDirs
|
||||
```
|
||||
|
||||
***Note***: When using the `ValidArgsFunction`, Cobra will call your registered function after having parsed all flags and arguments provided in the command-line. You therefore don't need to do this parsing yourself. For example, when a user calls `helm status --namespace my-rook-ns [tab][tab]`, Cobra will call your registered `ValidArgsFunction` after having parsed the `--namespace` flag, as it would have done when calling the `RunE` function.
|
||||
|
||||
#### Debugging
|
||||
|
||||
Cobra achieves dynamic completion through the use of a hidden command called by the completion script. To debug your Go completion code, you can call this hidden command directly:
|
||||
```bash
|
||||
$ helm __complete status har<ENTER>
|
||||
harbor
|
||||
:4
|
||||
Completion ended with directive: ShellCompDirectiveNoFileComp # This is on stderr
|
||||
```
|
||||
***Important:*** If the noun to complete is empty (when the user has not yet typed any letters of that noun), you must pass an empty parameter to the `__complete` command:
|
||||
```bash
|
||||
$ helm __complete status ""<ENTER>
|
||||
harbor
|
||||
notary
|
||||
rook
|
||||
thanos
|
||||
:4
|
||||
Completion ended with directive: ShellCompDirectiveNoFileComp # This is on stderr
|
||||
```
|
||||
Calling the `__complete` command directly allows you to run the Go debugger to troubleshoot your code. You can also add printouts to your code; Cobra provides the following functions to use for printouts in Go completion code:
|
||||
```go
|
||||
// Prints to the completion script debug file (if BASH_COMP_DEBUG_FILE
|
||||
// is set to a file path) and optionally prints to stderr.
|
||||
cobra.CompDebug(msg string, printToStdErr bool) {
|
||||
cobra.CompDebugln(msg string, printToStdErr bool)
|
||||
|
||||
// Prints to the completion script debug file (if BASH_COMP_DEBUG_FILE
|
||||
// is set to a file path) and to stderr.
|
||||
cobra.CompError(msg string)
|
||||
cobra.CompErrorln(msg string)
|
||||
```
|
||||
***Important:*** You should **not** leave traces that print directly to stdout in your completion code as they will be interpreted as completion choices by the completion script. Instead, use the cobra-provided debugging traces functions mentioned above.
|
||||
|
||||
## Completions for flags
|
||||
|
||||
### Mark flags as required
|
||||
|
||||
Most of the time completions will only show sub-commands. But if a flag is required to make a sub-command work, you probably want it to show up when the user types [tab][tab]. You can mark a flag as 'Required' like so:
|
||||
|
||||
```go
|
||||
cmd.MarkFlagRequired("pod")
|
||||
cmd.MarkFlagRequired("container")
|
||||
```
|
||||
|
||||
and you'll get something like
|
||||
|
||||
```bash
|
||||
$ kubectl exec [tab][tab]
|
||||
-c --container= -p --pod=
|
||||
```
|
||||
|
||||
### Specify dynamic flag completion
|
||||
|
||||
As for nouns, Cobra provides a way of defining dynamic completion of flags. To provide a Go function that Cobra will execute when it needs the list of completion choices for a flag, you must register the function using the `command.RegisterFlagCompletionFunc()` function.
|
||||
|
||||
```go
|
||||
flagName := "output"
|
||||
cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return []string{"json", "table", "yaml"}, cobra.ShellCompDirectiveDefault
|
||||
})
|
||||
```
|
||||
Notice that calling `RegisterFlagCompletionFunc()` is done through the `command` with which the flag is associated. In our example this dynamic completion will give results like so:
|
||||
|
||||
```bash
|
||||
$ helm status --output [tab][tab]
|
||||
json table yaml
|
||||
```
|
||||
|
||||
#### Debugging
|
||||
|
||||
You can also easily debug your Go completion code for flags:
|
||||
```bash
|
||||
$ helm __complete status --output ""
|
||||
json
|
||||
table
|
||||
yaml
|
||||
:4
|
||||
Completion ended with directive: ShellCompDirectiveNoFileComp # This is on stderr
|
||||
```
|
||||
***Important:*** You should **not** leave traces that print to stdout in your completion code as they will be interpreted as completion choices by the completion script. Instead, use the cobra-provided debugging traces functions mentioned further above.
|
||||
|
||||
### Specify valid filename extensions for flags that take a filename
|
||||
|
||||
To limit completions of flag values to file names with certain extensions you can either use the different `MarkFlagFilename()` functions or a combination of `RegisterFlagCompletionFunc()` and `ShellCompDirectiveFilterFileExt`, like so:
|
||||
```go
|
||||
flagName := "output"
|
||||
cmd.MarkFlagFilename(flagName, "yaml", "json")
|
||||
```
|
||||
or
|
||||
```go
|
||||
flagName := "output"
|
||||
cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return []string{"yaml", "json"}, ShellCompDirectiveFilterFileExt})
|
||||
```
|
||||
|
||||
### Limit flag completions to directory names
|
||||
|
||||
To limit completions of flag values to directory names you can either use the `MarkFlagDirname()` functions or a combination of `RegisterFlagCompletionFunc()` and `ShellCompDirectiveFilterDirs`, like so:
|
||||
```go
|
||||
flagName := "output"
|
||||
cmd.MarkFlagDirname(flagName)
|
||||
```
|
||||
or
|
||||
```go
|
||||
flagName := "output"
|
||||
cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return nil, cobra.ShellCompDirectiveFilterDirs
|
||||
})
|
||||
```
|
||||
To limit completions of flag values to directory names *within another directory* you can use a combination of `RegisterFlagCompletionFunc()` and `ShellCompDirectiveFilterDirs` like so:
|
||||
```go
|
||||
flagName := "output"
|
||||
cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return []string{"themes"}, cobra.ShellCompDirectiveFilterDirs
|
||||
})
|
||||
```
|
||||
### Descriptions for completions
|
||||
|
||||
Cobra provides support for completion descriptions. Such descriptions are supported for each shell
|
||||
(however, for bash, it is only available in the [completion V2 version](#bash-completion-v2)).
|
||||
For commands and flags, Cobra will provide the descriptions automatically, based on usage information.
|
||||
For example, using zsh:
|
||||
```
|
||||
$ helm s[tab]
|
||||
search -- search for a keyword in charts
|
||||
show -- show information of a chart
|
||||
status -- displays the status of the named release
|
||||
```
|
||||
while using fish:
|
||||
```
|
||||
$ helm s[tab]
|
||||
search (search for a keyword in charts) show (show information of a chart) status (displays the status of the named release)
|
||||
```
|
||||
|
||||
Cobra allows you to add descriptions to your own completions. Simply add the description text after each completion, following a `\t` separator. This technique applies to completions returned by `ValidArgs`, `ValidArgsFunction` and `RegisterFlagCompletionFunc()`. For example:
|
||||
```go
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return []string{"harbor\tAn image registry", "thanos\tLong-term metrics"}, cobra.ShellCompDirectiveNoFileComp
|
||||
}}
|
||||
```
|
||||
or
|
||||
```go
|
||||
ValidArgs: []string{"bash\tCompletions for bash", "zsh\tCompletions for zsh"}
|
||||
```
|
||||
## Bash completions
|
||||
|
||||
### Dependencies
|
||||
|
||||
The bash completion script generated by Cobra requires the `bash_completion` package. You should update the help text of your completion command to show how to install the `bash_completion` package ([Kubectl docs](https://kubernetes.io/docs/tasks/tools/install-kubectl/#enabling-shell-autocompletion))
|
||||
|
||||
### Aliases
|
||||
|
||||
You can also configure `bash` aliases for your program and they will also support completions.
|
||||
|
||||
```bash
|
||||
alias aliasname=origcommand
|
||||
complete -o default -F __start_origcommand aliasname
|
||||
|
||||
# and now when you run `aliasname` completion will make
|
||||
# suggestions as it did for `origcommand`.
|
||||
|
||||
$ aliasname <tab><tab>
|
||||
completion firstcommand secondcommand
|
||||
```
|
||||
### Bash legacy dynamic completions
|
||||
|
||||
For backward compatibility, Cobra still supports its bash legacy dynamic completion solution.
|
||||
Please refer to [Bash Completions](bash_completions.md) for details.
|
||||
|
||||
### Bash completion V2
|
||||
|
||||
Cobra provides two versions for bash completion. The original bash completion (which started it all!) can be used by calling
|
||||
`GenBashCompletion()` or `GenBashCompletionFile()`.
|
||||
|
||||
A new V2 bash completion version is also available. This version can be used by calling `GenBashCompletionV2()` or
|
||||
`GenBashCompletionFileV2()`. The V2 version does **not** support the legacy dynamic completion
|
||||
(see [Bash Completions](bash_completions.md)) but instead works only with the Go dynamic completion
|
||||
solution described in this document.
|
||||
Unless your program already uses the legacy dynamic completion solution, it is recommended that you use the bash
|
||||
completion V2 solution which provides the following extra features:
|
||||
- Supports completion descriptions (like the other shells)
|
||||
- Small completion script of less than 300 lines (v1 generates scripts of thousands of lines; `kubectl` for example has a bash v1 completion script of over 13K lines)
|
||||
- Streamlined user experience thanks to a completion behavior aligned with the other shells
|
||||
|
||||
`Bash` completion V2 supports descriptions for completions. When calling `GenBashCompletionV2()` or `GenBashCompletionFileV2()`
|
||||
you must provide these functions with a parameter indicating if the completions should be annotated with a description; Cobra
|
||||
will provide the description automatically based on usage information. You can choose to make this option configurable by
|
||||
your users.
|
||||
|
||||
```
|
||||
# With descriptions
|
||||
$ helm s[tab][tab]
|
||||
search (search for a keyword in charts) status (display the status of the named release)
|
||||
show (show information of a chart)
|
||||
|
||||
# Without descriptions
|
||||
$ helm s[tab][tab]
|
||||
search show status
|
||||
```
|
||||
**Note**: Cobra's default `completion` command uses bash completion V2. If for some reason you need to use bash completion V1, you will need to implement your own `completion` command.
|
||||
## Zsh completions
|
||||
|
||||
Cobra supports native zsh completion generated from the root `cobra.Command`.
|
||||
The generated completion script should be put somewhere in your `$fpath` and be named
|
||||
`_<yourProgram>`. You will need to start a new shell for the completions to become available.
|
||||
|
||||
Zsh supports descriptions for completions. Cobra will provide the description automatically,
|
||||
based on usage information. Cobra provides a way to completely disable such descriptions by
|
||||
using `GenZshCompletionNoDesc()` or `GenZshCompletionFileNoDesc()`. You can choose to make
|
||||
this a configurable option to your users.
|
||||
```
|
||||
# With descriptions
|
||||
$ helm s[tab]
|
||||
search -- search for a keyword in charts
|
||||
show -- show information of a chart
|
||||
status -- displays the status of the named release
|
||||
|
||||
# Without descriptions
|
||||
$ helm s[tab]
|
||||
search show status
|
||||
```
|
||||
*Note*: Because of backward-compatibility requirements, we were forced to have a different API to disable completion descriptions between `zsh` and `fish`.
|
||||
|
||||
### Limitations
|
||||
|
||||
* Custom completions implemented in Bash scripting (legacy) are not supported and will be ignored for `zsh` (including the use of the `BashCompCustom` flag annotation).
|
||||
* You should instead use `ValidArgsFunction` and `RegisterFlagCompletionFunc()` which are portable to the different shells (`bash`, `zsh`, `fish`, `powershell`).
|
||||
* The function `MarkFlagCustom()` is not supported and will be ignored for `zsh`.
|
||||
* You should instead use `RegisterFlagCompletionFunc()`.
|
||||
|
||||
### Zsh completions standardization
|
||||
|
||||
Cobra 1.1 standardized its zsh completion support to align it with its other shell completions. Although the API was kept backward-compatible, some small changes in behavior were introduced.
|
||||
Please refer to [Zsh Completions](zsh_completions.md) for details.
|
||||
|
||||
## fish completions
|
||||
|
||||
Cobra supports native fish completions generated from the root `cobra.Command`. You can use the `command.GenFishCompletion()` or `command.GenFishCompletionFile()` functions. You must provide these functions with a parameter indicating if the completions should be annotated with a description; Cobra will provide the description automatically based on usage information. You can choose to make this option configurable by your users.
|
||||
```
|
||||
# With descriptions
|
||||
$ helm s[tab]
|
||||
search (search for a keyword in charts) show (show information of a chart) status (displays the status of the named release)
|
||||
|
||||
# Without descriptions
|
||||
$ helm s[tab]
|
||||
search show status
|
||||
```
|
||||
*Note*: Because of backward-compatibility requirements, we were forced to have a different API to disable completion descriptions between `zsh` and `fish`.
|
||||
|
||||
### Limitations
|
||||
|
||||
* Custom completions implemented in bash scripting (legacy) are not supported and will be ignored for `fish` (including the use of the `BashCompCustom` flag annotation).
|
||||
* You should instead use `ValidArgsFunction` and `RegisterFlagCompletionFunc()` which are portable to the different shells (`bash`, `zsh`, `fish`, `powershell`).
|
||||
* The function `MarkFlagCustom()` is not supported and will be ignored for `fish`.
|
||||
* You should instead use `RegisterFlagCompletionFunc()`.
|
||||
* The following flag completion annotations are not supported and will be ignored for `fish`:
|
||||
* `BashCompFilenameExt` (filtering by file extension)
|
||||
* `BashCompSubdirsInDir` (filtering by directory)
|
||||
* The functions corresponding to the above annotations are consequently not supported and will be ignored for `fish`:
|
||||
* `MarkFlagFilename()` and `MarkPersistentFlagFilename()` (filtering by file extension)
|
||||
* `MarkFlagDirname()` and `MarkPersistentFlagDirname()` (filtering by directory)
|
||||
* Similarly, the following completion directives are not supported and will be ignored for `fish`:
|
||||
* `ShellCompDirectiveFilterFileExt` (filtering by file extension)
|
||||
* `ShellCompDirectiveFilterDirs` (filtering by directory)
|
||||
|
||||
## PowerShell completions
|
||||
|
||||
Cobra supports native PowerShell completions generated from the root `cobra.Command`. You can use the `command.GenPowerShellCompletion()` or `command.GenPowerShellCompletionFile()` functions. To include descriptions use `command.GenPowerShellCompletionWithDesc()` and `command.GenPowerShellCompletionFileWithDesc()`. Cobra will provide the description automatically based on usage information. You can choose to make this option configurable by your users.
|
||||
|
||||
The script is designed to support all three PowerShell completion modes:
|
||||
|
||||
* TabCompleteNext (default windows style - on each key press the next option is displayed)
|
||||
* Complete (works like bash)
|
||||
* MenuComplete (works like zsh)
|
||||
|
||||
You set the mode with `Set-PSReadLineKeyHandler -Key Tab -Function <mode>`. Descriptions are only displayed when using the `Complete` or `MenuComplete` mode.
|
||||
|
||||
Users need PowerShell version 5.0 or above, which comes with Windows 10 and can be downloaded separately for Windows 7 or 8.1. They can then write the completions to a file and source this file from their PowerShell profile, which is referenced by the `$Profile` environment variable. See `Get-Help about_Profiles` for more info about PowerShell profiles.
|
||||
|
||||
```
|
||||
# With descriptions and Mode 'Complete'
|
||||
$ helm s[tab]
|
||||
search (search for a keyword in charts) show (show information of a chart) status (displays the status of the named release)
|
||||
|
||||
# With descriptions and Mode 'MenuComplete' The description of the current selected value will be displayed below the suggestions.
|
||||
$ helm s[tab]
|
||||
search show status
|
||||
|
||||
search for a keyword in charts
|
||||
|
||||
# Without descriptions
|
||||
$ helm s[tab]
|
||||
search show status
|
||||
```
|
||||
|
||||
### Limitations
|
||||
|
||||
* Custom completions implemented in bash scripting (legacy) are not supported and will be ignored for `powershell` (including the use of the `BashCompCustom` flag annotation).
|
||||
* You should instead use `ValidArgsFunction` and `RegisterFlagCompletionFunc()` which are portable to the different shells (`bash`, `zsh`, `fish`, `powershell`).
|
||||
* The function `MarkFlagCustom()` is not supported and will be ignored for `powershell`.
|
||||
* You should instead use `RegisterFlagCompletionFunc()`.
|
||||
* The following flag completion annotations are not supported and will be ignored for `powershell`:
|
||||
* `BashCompFilenameExt` (filtering by file extension)
|
||||
* `BashCompSubdirsInDir` (filtering by directory)
|
||||
* The functions corresponding to the above annotations are consequently not supported and will be ignored for `powershell`:
|
||||
* `MarkFlagFilename()` and `MarkPersistentFlagFilename()` (filtering by file extension)
|
||||
* `MarkFlagDirname()` and `MarkPersistentFlagDirname()` (filtering by directory)
|
||||
* Similarly, the following completion directives are not supported and will be ignored for `powershell`:
|
||||
* `ShellCompDirectiveFilterFileExt` (filtering by file extension)
|
||||
* `ShellCompDirectiveFilterDirs` (filtering by directory)
|
||||
-638
@@ -1,638 +0,0 @@
|
||||
# User Guide
|
||||
|
||||
While you are welcome to provide your own organization, typically a Cobra-based
|
||||
application will follow the following organizational structure:
|
||||
|
||||
```
|
||||
▾ appName/
|
||||
▾ cmd/
|
||||
add.go
|
||||
your.go
|
||||
commands.go
|
||||
here.go
|
||||
main.go
|
||||
```
|
||||
|
||||
In a Cobra app, typically the main.go file is very bare. It serves one purpose: initializing Cobra.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"{pathToYourApp}/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd.Execute()
|
||||
}
|
||||
```
|
||||
|
||||
## Using the Cobra Generator
|
||||
|
||||
Cobra-CLI is its own program that will create your application and add any
|
||||
commands you want. It's the easiest way to incorporate Cobra into your application.
|
||||
|
||||
For complete details on using the Cobra generator, please refer to [The Cobra-CLI Generator README](https://github.com/spf13/cobra-cli/blob/master/README.md)
|
||||
|
||||
## Using the Cobra Library
|
||||
|
||||
To manually implement Cobra you need to create a bare main.go file and a rootCmd file.
|
||||
You will optionally provide additional commands as you see fit.
|
||||
|
||||
### Create rootCmd
|
||||
|
||||
Cobra doesn't require any special constructors. Simply create your commands.
|
||||
|
||||
Ideally you place this in app/cmd/root.go:
|
||||
|
||||
```go
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "hugo",
|
||||
Short: "Hugo is a very fast static site generator",
|
||||
Long: `A Fast and Flexible Static Site Generator built with
|
||||
love by spf13 and friends in Go.
|
||||
Complete documentation is available at http://hugo.spf13.com`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// Do Stuff Here
|
||||
},
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You will additionally define flags and handle configuration in your init() function.
|
||||
|
||||
For example cmd/root.go:
|
||||
|
||||
```go
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var (
|
||||
// Used for flags.
|
||||
cfgFile string
|
||||
userLicense string
|
||||
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "cobra-cli",
|
||||
Short: "A generator for Cobra based Applications",
|
||||
Long: `Cobra is a CLI library for Go that empowers applications.
|
||||
This application is a tool to generate the needed files
|
||||
to quickly create a Cobra application.`,
|
||||
}
|
||||
)
|
||||
|
||||
// Execute executes the root command.
|
||||
func Execute() error {
|
||||
return rootCmd.Execute()
|
||||
}
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra.yaml)")
|
||||
rootCmd.PersistentFlags().StringP("author", "a", "YOUR NAME", "author name for copyright attribution")
|
||||
rootCmd.PersistentFlags().StringVarP(&userLicense, "license", "l", "", "name of license for the project")
|
||||
rootCmd.PersistentFlags().Bool("viper", true, "use Viper for configuration")
|
||||
viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author"))
|
||||
viper.BindPFlag("useViper", rootCmd.PersistentFlags().Lookup("viper"))
|
||||
viper.SetDefault("author", "NAME HERE <EMAIL ADDRESS>")
|
||||
viper.SetDefault("license", "apache")
|
||||
|
||||
rootCmd.AddCommand(addCmd)
|
||||
rootCmd.AddCommand(initCmd)
|
||||
}
|
||||
|
||||
func initConfig() {
|
||||
if cfgFile != "" {
|
||||
// Use config file from the flag.
|
||||
viper.SetConfigFile(cfgFile)
|
||||
} else {
|
||||
// Find home directory.
|
||||
home, err := os.UserHomeDir()
|
||||
cobra.CheckErr(err)
|
||||
|
||||
// Search config in home directory with name ".cobra" (without extension).
|
||||
viper.AddConfigPath(home)
|
||||
viper.SetConfigType("yaml")
|
||||
viper.SetConfigName(".cobra")
|
||||
}
|
||||
|
||||
viper.AutomaticEnv()
|
||||
|
||||
if err := viper.ReadInConfig(); err == nil {
|
||||
fmt.Println("Using config file:", viper.ConfigFileUsed())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Create your main.go
|
||||
|
||||
With the root command you need to have your main function execute it.
|
||||
Execute should be run on the root for clarity, though it can be called on any command.
|
||||
|
||||
In a Cobra app, typically the main.go file is very bare. It serves one purpose: to initialize Cobra.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"{pathToYourApp}/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd.Execute()
|
||||
}
|
||||
```
|
||||
|
||||
### Create additional commands
|
||||
|
||||
Additional commands can be defined and typically are each given their own file
|
||||
inside of the cmd/ directory.
|
||||
|
||||
If you wanted to create a version command you would create cmd/version.go and
|
||||
populate it with the following:
|
||||
|
||||
```go
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
}
|
||||
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the version number of Hugo",
|
||||
Long: `All software has versions. This is Hugo's`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("Hugo Static Site Generator v0.9 -- HEAD")
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
### Returning and handling errors
|
||||
|
||||
If you wish to return an error to the caller of a command, `RunE` can be used.
|
||||
|
||||
```go
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(tryCmd)
|
||||
}
|
||||
|
||||
var tryCmd = &cobra.Command{
|
||||
Use: "try",
|
||||
Short: "Try and possibly fail at something",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := someFunc(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
The error can then be caught at the execute function call.
|
||||
|
||||
## Working with Flags
|
||||
|
||||
Flags provide modifiers to control how the action command operates.
|
||||
|
||||
### Assign flags to a command
|
||||
|
||||
Since the flags are defined and used in different locations, we need to
|
||||
define a variable outside with the correct scope to assign the flag to
|
||||
work with.
|
||||
|
||||
```go
|
||||
var Verbose bool
|
||||
var Source string
|
||||
```
|
||||
|
||||
There are two different approaches to assign a flag.
|
||||
|
||||
### Persistent Flags
|
||||
|
||||
A flag can be 'persistent', meaning that this flag will be available to the
|
||||
command it's assigned to as well as every command under that command. For
|
||||
global flags, assign a flag as a persistent flag on the root.
|
||||
|
||||
```go
|
||||
rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output")
|
||||
```
|
||||
|
||||
### Local Flags
|
||||
|
||||
A flag can also be assigned locally, which will only apply to that specific command.
|
||||
|
||||
```go
|
||||
localCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from")
|
||||
```
|
||||
|
||||
### Local Flag on Parent Commands
|
||||
|
||||
By default, Cobra only parses local flags on the target command, and any local flags on
|
||||
parent commands are ignored. By enabling `Command.TraverseChildren`, Cobra will
|
||||
parse local flags on each command before executing the target command.
|
||||
|
||||
```go
|
||||
command := cobra.Command{
|
||||
Use: "print [OPTIONS] [COMMANDS]",
|
||||
TraverseChildren: true,
|
||||
}
|
||||
```
|
||||
|
||||
### Bind Flags with Config
|
||||
|
||||
You can also bind your flags with [viper](https://github.com/spf13/viper):
|
||||
```go
|
||||
var author string
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().StringVar(&author, "author", "YOUR NAME", "Author name for copyright attribution")
|
||||
viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author"))
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the persistent flag `author` is bound with `viper`.
|
||||
**Note**: the variable `author` will not be set to the value from config,
|
||||
when the `--author` flag is provided by user.
|
||||
|
||||
More in [viper documentation](https://github.com/spf13/viper#working-with-flags).
|
||||
|
||||
### Required flags
|
||||
|
||||
Flags are optional by default. If instead you wish your command to report an error
|
||||
when a flag has not been set, mark it as required:
|
||||
```go
|
||||
rootCmd.Flags().StringVarP(&Region, "region", "r", "", "AWS region (required)")
|
||||
rootCmd.MarkFlagRequired("region")
|
||||
```
|
||||
|
||||
Or, for persistent flags:
|
||||
```go
|
||||
rootCmd.PersistentFlags().StringVarP(&Region, "region", "r", "", "AWS region (required)")
|
||||
rootCmd.MarkPersistentFlagRequired("region")
|
||||
```
|
||||
|
||||
## Positional and Custom Arguments
|
||||
|
||||
Validation of positional arguments can be specified using the `Args` field
|
||||
of `Command`.
|
||||
|
||||
The following validators are built in:
|
||||
|
||||
- `NoArgs` - the command will report an error if there are any positional args.
|
||||
- `ArbitraryArgs` - the command will accept any args.
|
||||
- `OnlyValidArgs` - the command will report an error if there are any positional args that are not in the `ValidArgs` field of `Command`.
|
||||
- `MinimumNArgs(int)` - the command will report an error if there are not at least N positional args.
|
||||
- `MaximumNArgs(int)` - the command will report an error if there are more than N positional args.
|
||||
- `ExactArgs(int)` - the command will report an error if there are not exactly N positional args.
|
||||
- `ExactValidArgs(int)` - the command will report an error if there are not exactly N positional args OR if there are any positional args that are not in the `ValidArgs` field of `Command`
|
||||
- `RangeArgs(min, max)` - the command will report an error if the number of args is not between the minimum and maximum number of expected args.
|
||||
- `MatchAll(pargs ...PositionalArgs)` - enables combining existing checks with arbitrary other checks (e.g. you want to check the ExactArgs length along with other qualities).
|
||||
|
||||
An example of setting the custom validator:
|
||||
|
||||
```go
|
||||
var cmd = &cobra.Command{
|
||||
Short: "hello",
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return errors.New("requires a color argument")
|
||||
}
|
||||
if myapp.IsValidColor(args[0]) {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("invalid color specified: %s", args[0])
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("Hello, World!")
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
In the example below, we have defined three commands. Two are at the top level
|
||||
and one (cmdTimes) is a child of one of the top commands. In this case the root
|
||||
is not executable, meaning that a subcommand is required. This is accomplished
|
||||
by not providing a 'Run' for the 'rootCmd'.
|
||||
|
||||
We have only defined one flag for a single command.
|
||||
|
||||
More documentation about flags is available at https://github.com/spf13/pflag
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var echoTimes int
|
||||
|
||||
var cmdPrint = &cobra.Command{
|
||||
Use: "print [string to print]",
|
||||
Short: "Print anything to the screen",
|
||||
Long: `print is for printing anything back to the screen.
|
||||
For many years people have printed back to the screen.`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("Print: " + strings.Join(args, " "))
|
||||
},
|
||||
}
|
||||
|
||||
var cmdEcho = &cobra.Command{
|
||||
Use: "echo [string to echo]",
|
||||
Short: "Echo anything to the screen",
|
||||
Long: `echo is for echoing anything back.
|
||||
Echo works a lot like print, except it has a child command.`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println("Echo: " + strings.Join(args, " "))
|
||||
},
|
||||
}
|
||||
|
||||
var cmdTimes = &cobra.Command{
|
||||
Use: "times [string to echo]",
|
||||
Short: "Echo anything to the screen more times",
|
||||
Long: `echo things multiple times back to the user by providing
|
||||
a count and a string.`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
for i := 0; i < echoTimes; i++ {
|
||||
fmt.Println("Echo: " + strings.Join(args, " "))
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
cmdTimes.Flags().IntVarP(&echoTimes, "times", "t", 1, "times to echo the input")
|
||||
|
||||
var rootCmd = &cobra.Command{Use: "app"}
|
||||
rootCmd.AddCommand(cmdPrint, cmdEcho)
|
||||
cmdEcho.AddCommand(cmdTimes)
|
||||
rootCmd.Execute()
|
||||
}
|
||||
```
|
||||
|
||||
For a more complete example of a larger application, please checkout [Hugo](http://gohugo.io/).
|
||||
|
||||
## Help Command
|
||||
|
||||
Cobra automatically adds a help command to your application when you have subcommands.
|
||||
This will be called when a user runs 'app help'. Additionally, help will also
|
||||
support all other commands as input. Say, for instance, you have a command called
|
||||
'create' without any additional configuration; Cobra will work when 'app help
|
||||
create' is called. Every command will automatically have the '--help' flag added.
|
||||
|
||||
### Example
|
||||
|
||||
The following output is automatically generated by Cobra. Nothing beyond the
|
||||
command and flag definitions are needed.
|
||||
|
||||
$ cobra help
|
||||
|
||||
Cobra is a CLI library for Go that empowers applications.
|
||||
This application is a tool to generate the needed files
|
||||
to quickly create a Cobra application.
|
||||
|
||||
Usage:
|
||||
cobra [command]
|
||||
|
||||
Available Commands:
|
||||
add Add a command to a Cobra Application
|
||||
help Help about any command
|
||||
init Initialize a Cobra Application
|
||||
|
||||
Flags:
|
||||
-a, --author string author name for copyright attribution (default "YOUR NAME")
|
||||
--config string config file (default is $HOME/.cobra.yaml)
|
||||
-h, --help help for cobra
|
||||
-l, --license string name of license for the project
|
||||
--viper use Viper for configuration (default true)
|
||||
|
||||
Use "cobra [command] --help" for more information about a command.
|
||||
|
||||
|
||||
Help is just a command like any other. There is no special logic or behavior
|
||||
around it. In fact, you can provide your own if you want.
|
||||
|
||||
### Defining your own help
|
||||
|
||||
You can provide your own Help command or your own template for the default command to use
|
||||
with following functions:
|
||||
|
||||
```go
|
||||
cmd.SetHelpCommand(cmd *Command)
|
||||
cmd.SetHelpFunc(f func(*Command, []string))
|
||||
cmd.SetHelpTemplate(s string)
|
||||
```
|
||||
|
||||
The latter two will also apply to any children commands.
|
||||
|
||||
## Usage Message
|
||||
|
||||
When the user provides an invalid flag or invalid command, Cobra responds by
|
||||
showing the user the 'usage'.
|
||||
|
||||
### Example
|
||||
You may recognize this from the help above. That's because the default help
|
||||
embeds the usage as part of its output.
|
||||
|
||||
$ cobra --invalid
|
||||
Error: unknown flag: --invalid
|
||||
Usage:
|
||||
cobra [command]
|
||||
|
||||
Available Commands:
|
||||
add Add a command to a Cobra Application
|
||||
help Help about any command
|
||||
init Initialize a Cobra Application
|
||||
|
||||
Flags:
|
||||
-a, --author string author name for copyright attribution (default "YOUR NAME")
|
||||
--config string config file (default is $HOME/.cobra.yaml)
|
||||
-h, --help help for cobra
|
||||
-l, --license string name of license for the project
|
||||
--viper use Viper for configuration (default true)
|
||||
|
||||
Use "cobra [command] --help" for more information about a command.
|
||||
|
||||
### Defining your own usage
|
||||
You can provide your own usage function or template for Cobra to use.
|
||||
Like help, the function and template are overridable through public methods:
|
||||
|
||||
```go
|
||||
cmd.SetUsageFunc(f func(*Command) error)
|
||||
cmd.SetUsageTemplate(s string)
|
||||
```
|
||||
|
||||
## Version Flag
|
||||
|
||||
Cobra adds a top-level '--version' flag if the Version field is set on the root command.
|
||||
Running an application with the '--version' flag will print the version to stdout using
|
||||
the version template. The template can be customized using the
|
||||
`cmd.SetVersionTemplate(s string)` function.
|
||||
|
||||
## PreRun and PostRun Hooks
|
||||
|
||||
It is possible to run functions before or after the main `Run` function of your command. The `PersistentPreRun` and `PreRun` functions will be executed before `Run`. `PersistentPostRun` and `PostRun` will be executed after `Run`. The `Persistent*Run` functions will be inherited by children if they do not declare their own. These functions are run in the following order:
|
||||
|
||||
- `PersistentPreRun`
|
||||
- `PreRun`
|
||||
- `Run`
|
||||
- `PostRun`
|
||||
- `PersistentPostRun`
|
||||
|
||||
An example of two commands which use all of these features is below. When the subcommand is executed, it will run the root command's `PersistentPreRun` but not the root command's `PersistentPostRun`:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "root [sub]",
|
||||
Short: "My root command",
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Inside rootCmd PersistentPreRun with args: %v\n", args)
|
||||
},
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Inside rootCmd PreRun with args: %v\n", args)
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Inside rootCmd Run with args: %v\n", args)
|
||||
},
|
||||
PostRun: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Inside rootCmd PostRun with args: %v\n", args)
|
||||
},
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Inside rootCmd PersistentPostRun with args: %v\n", args)
|
||||
},
|
||||
}
|
||||
|
||||
var subCmd = &cobra.Command{
|
||||
Use: "sub [no options!]",
|
||||
Short: "My subcommand",
|
||||
PreRun: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Inside subCmd PreRun with args: %v\n", args)
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Inside subCmd Run with args: %v\n", args)
|
||||
},
|
||||
PostRun: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Inside subCmd PostRun with args: %v\n", args)
|
||||
},
|
||||
PersistentPostRun: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Inside subCmd PersistentPostRun with args: %v\n", args)
|
||||
},
|
||||
}
|
||||
|
||||
rootCmd.AddCommand(subCmd)
|
||||
|
||||
rootCmd.SetArgs([]string{""})
|
||||
rootCmd.Execute()
|
||||
fmt.Println()
|
||||
rootCmd.SetArgs([]string{"sub", "arg1", "arg2"})
|
||||
rootCmd.Execute()
|
||||
}
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
Inside rootCmd PersistentPreRun with args: []
|
||||
Inside rootCmd PreRun with args: []
|
||||
Inside rootCmd Run with args: []
|
||||
Inside rootCmd PostRun with args: []
|
||||
Inside rootCmd PersistentPostRun with args: []
|
||||
|
||||
Inside rootCmd PersistentPreRun with args: [arg1 arg2]
|
||||
Inside subCmd PreRun with args: [arg1 arg2]
|
||||
Inside subCmd Run with args: [arg1 arg2]
|
||||
Inside subCmd PostRun with args: [arg1 arg2]
|
||||
Inside subCmd PersistentPostRun with args: [arg1 arg2]
|
||||
```
|
||||
|
||||
## Suggestions when "unknown command" happens
|
||||
|
||||
Cobra will print automatic suggestions when "unknown command" errors happen. This allows Cobra to behave similarly to the `git` command when a typo happens. For example:
|
||||
|
||||
```
|
||||
$ hugo srever
|
||||
Error: unknown command "srever" for "hugo"
|
||||
|
||||
Did you mean this?
|
||||
server
|
||||
|
||||
Run 'hugo --help' for usage.
|
||||
```
|
||||
|
||||
Suggestions are automatic based on every subcommand registered and use an implementation of [Levenshtein distance](http://en.wikipedia.org/wiki/Levenshtein_distance). Every registered command that matches a minimum distance of 2 (ignoring case) will be displayed as a suggestion.
|
||||
|
||||
If you need to disable suggestions or tweak the string distance in your command, use:
|
||||
|
||||
```go
|
||||
command.DisableSuggestions = true
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```go
|
||||
command.SuggestionsMinimumDistance = 1
|
||||
```
|
||||
|
||||
You can also explicitly set names for which a given command will be suggested using the `SuggestFor` attribute. This allows suggestions for strings that are not close in terms of string distance, but makes sense in your set of commands and for some which you don't want aliases. Example:
|
||||
|
||||
```
|
||||
$ kubectl remove
|
||||
Error: unknown command "remove" for "kubectl"
|
||||
|
||||
Did you mean this?
|
||||
delete
|
||||
|
||||
Run 'kubectl help' for usage.
|
||||
```
|
||||
|
||||
## Generating documentation for your command
|
||||
|
||||
Cobra can generate documentation based on subcommands, flags, etc. Read more about it in the [docs generation documentation](doc/README.md).
|
||||
|
||||
## Generating shell completions
|
||||
|
||||
Cobra can generate a shell-completion file for the following shells: bash, zsh, fish, PowerShell. If you add more information to your commands, these completions can be amazingly powerful and flexible. Read more about it in [Shell Completions](shell_completions.md).
|
||||
+55
-5
@@ -1,3 +1,17 @@
|
||||
// Copyright 2013-2023 The Cobra Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cobra
|
||||
|
||||
import (
|
||||
@@ -75,7 +89,8 @@ func genZshComp(buf io.StringWriter, name string, includeDesc bool) {
|
||||
if !includeDesc {
|
||||
compCmd = ShellCompNoDescRequestCmd
|
||||
}
|
||||
WriteStringAndCheck(buf, fmt.Sprintf(`#compdef _%[1]s %[1]s
|
||||
WriteStringAndCheck(buf, fmt.Sprintf(`#compdef %[1]s
|
||||
compdef _%[1]s %[1]s
|
||||
|
||||
# zsh completion for %-36[1]s -*- shell-script -*-
|
||||
|
||||
@@ -94,8 +109,9 @@ _%[1]s()
|
||||
local shellCompDirectiveNoFileComp=%[5]d
|
||||
local shellCompDirectiveFilterFileExt=%[6]d
|
||||
local shellCompDirectiveFilterDirs=%[7]d
|
||||
local shellCompDirectiveKeepOrder=%[8]d
|
||||
|
||||
local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace
|
||||
local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace keepOrder
|
||||
local -a completions
|
||||
|
||||
__%[1]s_debug "\n========= starting completion logic =========="
|
||||
@@ -163,7 +179,24 @@ _%[1]s()
|
||||
return
|
||||
fi
|
||||
|
||||
local activeHelpMarker="%[9]s"
|
||||
local endIndex=${#activeHelpMarker}
|
||||
local startIndex=$((${#activeHelpMarker}+1))
|
||||
local hasActiveHelp=0
|
||||
while IFS='\n' read -r comp; do
|
||||
# Check if this is an activeHelp statement (i.e., prefixed with $activeHelpMarker)
|
||||
if [ "${comp[1,$endIndex]}" = "$activeHelpMarker" ];then
|
||||
__%[1]s_debug "ActiveHelp found: $comp"
|
||||
comp="${comp[$startIndex,-1]}"
|
||||
if [ -n "$comp" ]; then
|
||||
compadd -x "${comp}"
|
||||
__%[1]s_debug "ActiveHelp will need delimiter"
|
||||
hasActiveHelp=1
|
||||
fi
|
||||
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ -n "$comp" ]; then
|
||||
# If requested, completions are returned with a description.
|
||||
# The description is preceded by a TAB character.
|
||||
@@ -171,7 +204,7 @@ _%[1]s()
|
||||
# We first need to escape any : as part of the completion itself.
|
||||
comp=${comp//:/\\:}
|
||||
|
||||
local tab=$(printf '\t')
|
||||
local tab="$(printf '\t')"
|
||||
comp=${comp//$tab/:}
|
||||
|
||||
__%[1]s_debug "Adding completion: ${comp}"
|
||||
@@ -180,11 +213,27 @@ _%[1]s()
|
||||
fi
|
||||
done < <(printf "%%s\n" "${out[@]}")
|
||||
|
||||
# Add a delimiter after the activeHelp statements, but only if:
|
||||
# - there are completions following the activeHelp statements, or
|
||||
# - file completion will be performed (so there will be choices after the activeHelp)
|
||||
if [ $hasActiveHelp -eq 1 ]; then
|
||||
if [ ${#completions} -ne 0 ] || [ $((directive & shellCompDirectiveNoFileComp)) -eq 0 ]; then
|
||||
__%[1]s_debug "Adding activeHelp delimiter"
|
||||
compadd -x "--"
|
||||
hasActiveHelp=0
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then
|
||||
__%[1]s_debug "Activating nospace."
|
||||
noSpace="-S ''"
|
||||
fi
|
||||
|
||||
if [ $((directive & shellCompDirectiveKeepOrder)) -ne 0 ]; then
|
||||
__%[1]s_debug "Activating keep order."
|
||||
keepOrder="-V"
|
||||
fi
|
||||
|
||||
if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then
|
||||
# File extension filtering
|
||||
local filteringCmd
|
||||
@@ -220,7 +269,7 @@ _%[1]s()
|
||||
return $result
|
||||
else
|
||||
__%[1]s_debug "Calling _describe"
|
||||
if eval _describe "completions" completions $flagPrefix $noSpace; then
|
||||
if eval _describe $keepOrder "completions" completions $flagPrefix $noSpace; then
|
||||
__%[1]s_debug "_describe found some completions"
|
||||
|
||||
# Return the success of having called _describe
|
||||
@@ -254,5 +303,6 @@ if [ "$funcstack[1]" = "_%[1]s" ]; then
|
||||
fi
|
||||
`, name, compCmd,
|
||||
ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
|
||||
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs))
|
||||
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder,
|
||||
activeHelpMarker))
|
||||
}
|
||||
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
## Generating Zsh Completion For Your cobra.Command
|
||||
|
||||
Please refer to [Shell Completions](shell_completions.md) for details.
|
||||
|
||||
## Zsh completions standardization
|
||||
|
||||
Cobra 1.1 standardized its zsh completion support to align it with its other shell completions. Although the API was kept backwards-compatible, some small changes in behavior were introduced.
|
||||
|
||||
### Deprecation summary
|
||||
|
||||
See further below for more details on these deprecations.
|
||||
|
||||
* `cmd.MarkZshCompPositionalArgumentFile(pos, []string{})` is no longer needed. It is therefore **deprecated** and silently ignored.
|
||||
* `cmd.MarkZshCompPositionalArgumentFile(pos, glob[])` is **deprecated** and silently ignored.
|
||||
* Instead use `ValidArgsFunction` with `ShellCompDirectiveFilterFileExt`.
|
||||
* `cmd.MarkZshCompPositionalArgumentWords()` is **deprecated** and silently ignored.
|
||||
* Instead use `ValidArgsFunction`.
|
||||
|
||||
### Behavioral changes
|
||||
|
||||
**Noun completion**
|
||||
|Old behavior|New behavior|
|
||||
|---|---|
|
||||
|No file completion by default (opposite of bash)|File completion by default; use `ValidArgsFunction` with `ShellCompDirectiveNoFileComp` to turn off file completion on a per-argument basis|
|
||||
|Completion of flag names without the `-` prefix having been typed|Flag names are only completed if the user has typed the first `-`|
|
||||
`cmd.MarkZshCompPositionalArgumentFile(pos, []string{})` used to turn on file completion on a per-argument position basis|File completion for all arguments by default; `cmd.MarkZshCompPositionalArgumentFile()` is **deprecated** and silently ignored|
|
||||
|`cmd.MarkZshCompPositionalArgumentFile(pos, glob[])` used to turn on file completion **with glob filtering** on a per-argument position basis (zsh-specific)|`cmd.MarkZshCompPositionalArgumentFile()` is **deprecated** and silently ignored; use `ValidArgsFunction` with `ShellCompDirectiveFilterFileExt` for file **extension** filtering (not full glob filtering)|
|
||||
|`cmd.MarkZshCompPositionalArgumentWords(pos, words[])` used to provide completion choices on a per-argument position basis (zsh-specific)|`cmd.MarkZshCompPositionalArgumentWords()` is **deprecated** and silently ignored; use `ValidArgsFunction` to achieve the same behavior|
|
||||
|
||||
**Flag-value completion**
|
||||
|
||||
|Old behavior|New behavior|
|
||||
|---|---|
|
||||
|No file completion by default (opposite of bash)|File completion by default; use `RegisterFlagCompletionFunc()` with `ShellCompDirectiveNoFileComp` to turn off file completion|
|
||||
|`cmd.MarkFlagFilename(flag, []string{})` and similar used to turn on file completion|File completion by default; `cmd.MarkFlagFilename(flag, []string{})` no longer needed in this context and silently ignored|
|
||||
|`cmd.MarkFlagFilename(flag, glob[])` used to turn on file completion **with glob filtering** (syntax of `[]string{"*.yaml", "*.yml"}` incompatible with bash)|Will continue to work, however, support for bash syntax is added and should be used instead so as to work for all shells (`[]string{"yaml", "yml"}`)|
|
||||
|`cmd.MarkFlagDirname(flag)` only completes directories (zsh-specific)|Has been added for all shells|
|
||||
|Completion of a flag name does not repeat, unless flag is of type `*Array` or `*Slice` (not supported by bash)|Retained for `zsh` and added to `fish`|
|
||||
|Completion of a flag name does not provide the `=` form (unlike bash)|Retained for `zsh` and added to `fish`|
|
||||
|
||||
**Improvements**
|
||||
|
||||
* Custom completion support (`ValidArgsFunction` and `RegisterFlagCompletionFunc()`)
|
||||
* File completion by default if no other completions found
|
||||
* Handling of required flags
|
||||
* File extension filtering no longer mutually exclusive with bash usage
|
||||
* Completion of directory names *within* another directory
|
||||
* Support for `=` form of flags
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.13
|
||||
|
||||
package poly1305
|
||||
|
||||
// Generic fallbacks for the math/bits intrinsics, copied from
|
||||
// src/math/bits/bits.go. They were added in Go 1.12, but Add64 and Sum64 had
|
||||
// variable time fallbacks until Go 1.13.
|
||||
|
||||
func bitsAdd64(x, y, carry uint64) (sum, carryOut uint64) {
|
||||
sum = x + y + carry
|
||||
carryOut = ((x & y) | ((x | y) &^ sum)) >> 63
|
||||
return
|
||||
}
|
||||
|
||||
func bitsSub64(x, y, borrow uint64) (diff, borrowOut uint64) {
|
||||
diff = x - y - borrow
|
||||
borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 63
|
||||
return
|
||||
}
|
||||
|
||||
func bitsMul64(x, y uint64) (hi, lo uint64) {
|
||||
const mask32 = 1<<32 - 1
|
||||
x0 := x & mask32
|
||||
x1 := x >> 32
|
||||
y0 := y & mask32
|
||||
y1 := y >> 32
|
||||
w0 := x0 * y0
|
||||
t := x1*y0 + w0>>32
|
||||
w1 := t & mask32
|
||||
w2 := t >> 32
|
||||
w1 += x0 * y1
|
||||
hi = x1*y1 + w2 + w1>>32
|
||||
lo = x * y
|
||||
return
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.13
|
||||
|
||||
package poly1305
|
||||
|
||||
import "math/bits"
|
||||
|
||||
func bitsAdd64(x, y, carry uint64) (sum, carryOut uint64) {
|
||||
return bits.Add64(x, y, carry)
|
||||
}
|
||||
|
||||
func bitsSub64(x, y, borrow uint64) (diff, borrowOut uint64) {
|
||||
return bits.Sub64(x, y, borrow)
|
||||
}
|
||||
|
||||
func bitsMul64(x, y uint64) (hi, lo uint64) {
|
||||
return bits.Mul64(x, y)
|
||||
}
|
||||
+23
-20
@@ -7,7 +7,10 @@
|
||||
|
||||
package poly1305
|
||||
|
||||
import "encoding/binary"
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// Poly1305 [RFC 7539] is a relatively simple algorithm: the authentication tag
|
||||
// for a 64 bytes message is approximately
|
||||
@@ -114,13 +117,13 @@ type uint128 struct {
|
||||
}
|
||||
|
||||
func mul64(a, b uint64) uint128 {
|
||||
hi, lo := bitsMul64(a, b)
|
||||
hi, lo := bits.Mul64(a, b)
|
||||
return uint128{lo, hi}
|
||||
}
|
||||
|
||||
func add128(a, b uint128) uint128 {
|
||||
lo, c := bitsAdd64(a.lo, b.lo, 0)
|
||||
hi, c := bitsAdd64(a.hi, b.hi, c)
|
||||
lo, c := bits.Add64(a.lo, b.lo, 0)
|
||||
hi, c := bits.Add64(a.hi, b.hi, c)
|
||||
if c != 0 {
|
||||
panic("poly1305: unexpected overflow")
|
||||
}
|
||||
@@ -155,8 +158,8 @@ func updateGeneric(state *macState, msg []byte) {
|
||||
// hide leading zeroes. For full chunks, that's 1 << 128, so we can just
|
||||
// add 1 to the most significant (2¹²⁸) limb, h2.
|
||||
if len(msg) >= TagSize {
|
||||
h0, c = bitsAdd64(h0, binary.LittleEndian.Uint64(msg[0:8]), 0)
|
||||
h1, c = bitsAdd64(h1, binary.LittleEndian.Uint64(msg[8:16]), c)
|
||||
h0, c = bits.Add64(h0, binary.LittleEndian.Uint64(msg[0:8]), 0)
|
||||
h1, c = bits.Add64(h1, binary.LittleEndian.Uint64(msg[8:16]), c)
|
||||
h2 += c + 1
|
||||
|
||||
msg = msg[TagSize:]
|
||||
@@ -165,8 +168,8 @@ func updateGeneric(state *macState, msg []byte) {
|
||||
copy(buf[:], msg)
|
||||
buf[len(msg)] = 1
|
||||
|
||||
h0, c = bitsAdd64(h0, binary.LittleEndian.Uint64(buf[0:8]), 0)
|
||||
h1, c = bitsAdd64(h1, binary.LittleEndian.Uint64(buf[8:16]), c)
|
||||
h0, c = bits.Add64(h0, binary.LittleEndian.Uint64(buf[0:8]), 0)
|
||||
h1, c = bits.Add64(h1, binary.LittleEndian.Uint64(buf[8:16]), c)
|
||||
h2 += c
|
||||
|
||||
msg = nil
|
||||
@@ -219,9 +222,9 @@ func updateGeneric(state *macState, msg []byte) {
|
||||
m3 := h2r1
|
||||
|
||||
t0 := m0.lo
|
||||
t1, c := bitsAdd64(m1.lo, m0.hi, 0)
|
||||
t2, c := bitsAdd64(m2.lo, m1.hi, c)
|
||||
t3, _ := bitsAdd64(m3.lo, m2.hi, c)
|
||||
t1, c := bits.Add64(m1.lo, m0.hi, 0)
|
||||
t2, c := bits.Add64(m2.lo, m1.hi, c)
|
||||
t3, _ := bits.Add64(m3.lo, m2.hi, c)
|
||||
|
||||
// Now we have the result as 4 64-bit limbs, and we need to reduce it
|
||||
// modulo 2¹³⁰ - 5. The special shape of this Crandall prime lets us do
|
||||
@@ -243,14 +246,14 @@ func updateGeneric(state *macState, msg []byte) {
|
||||
|
||||
// To add c * 5 to h, we first add cc = c * 4, and then add (cc >> 2) = c.
|
||||
|
||||
h0, c = bitsAdd64(h0, cc.lo, 0)
|
||||
h1, c = bitsAdd64(h1, cc.hi, c)
|
||||
h0, c = bits.Add64(h0, cc.lo, 0)
|
||||
h1, c = bits.Add64(h1, cc.hi, c)
|
||||
h2 += c
|
||||
|
||||
cc = shiftRightBy2(cc)
|
||||
|
||||
h0, c = bitsAdd64(h0, cc.lo, 0)
|
||||
h1, c = bitsAdd64(h1, cc.hi, c)
|
||||
h0, c = bits.Add64(h0, cc.lo, 0)
|
||||
h1, c = bits.Add64(h1, cc.hi, c)
|
||||
h2 += c
|
||||
|
||||
// h2 is at most 3 + 1 + 1 = 5, making the whole of h at most
|
||||
@@ -287,9 +290,9 @@ func finalize(out *[TagSize]byte, h *[3]uint64, s *[2]uint64) {
|
||||
// in constant time, we compute t = h - (2¹³⁰ - 5), and select h as the
|
||||
// result if the subtraction underflows, and t otherwise.
|
||||
|
||||
hMinusP0, b := bitsSub64(h0, p0, 0)
|
||||
hMinusP1, b := bitsSub64(h1, p1, b)
|
||||
_, b = bitsSub64(h2, p2, b)
|
||||
hMinusP0, b := bits.Sub64(h0, p0, 0)
|
||||
hMinusP1, b := bits.Sub64(h1, p1, b)
|
||||
_, b = bits.Sub64(h2, p2, b)
|
||||
|
||||
// h = h if h < p else h - p
|
||||
h0 = select64(b, h0, hMinusP0)
|
||||
@@ -301,8 +304,8 @@ func finalize(out *[TagSize]byte, h *[3]uint64, s *[2]uint64) {
|
||||
//
|
||||
// by just doing a wide addition with the 128 low bits of h and discarding
|
||||
// the overflow.
|
||||
h0, c := bitsAdd64(h0, s[0], 0)
|
||||
h1, _ = bitsAdd64(h1, s[1], c)
|
||||
h0, c := bits.Add64(h0, s[0], 0)
|
||||
h1, _ = bits.Add64(h1, s[1], c)
|
||||
|
||||
binary.LittleEndian.PutUint64(out[0:8], h0)
|
||||
binary.LittleEndian.PutUint64(out[8:16], h1)
|
||||
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package ctxhttp provides helper functions for performing context-aware HTTP requests.
|
||||
package ctxhttp // import "golang.org/x/net/context/ctxhttp"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Do sends an HTTP request with the provided http.Client and returns
|
||||
// an HTTP response.
|
||||
//
|
||||
// If the client is nil, http.DefaultClient is used.
|
||||
//
|
||||
// The provided ctx must be non-nil. If it is canceled or times out,
|
||||
// ctx.Err() will be returned.
|
||||
func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
|
||||
if client == nil {
|
||||
client = http.DefaultClient
|
||||
}
|
||||
resp, err := client.Do(req.WithContext(ctx))
|
||||
// If we got an error, and the context has been canceled,
|
||||
// the context's error is probably more useful.
|
||||
if err != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
default:
|
||||
}
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// Get issues a GET request via the Do function.
|
||||
func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Do(ctx, client, req)
|
||||
}
|
||||
|
||||
// Head issues a HEAD request via the Do function.
|
||||
func Head(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
|
||||
req, err := http.NewRequest("HEAD", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Do(ctx, client, req)
|
||||
}
|
||||
|
||||
// Post issues a POST request via the Do function.
|
||||
func Post(ctx context.Context, client *http.Client, url string, bodyType string, body io.Reader) (*http.Response, error) {
|
||||
req, err := http.NewRequest("POST", url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", bodyType)
|
||||
return Do(ctx, client, req)
|
||||
}
|
||||
|
||||
// PostForm issues a POST request via the Do function.
|
||||
func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) {
|
||||
return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
|
||||
}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
# This source code refers to The Go Authors for copyright purposes.
|
||||
# The master list of authors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/AUTHORS.
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
# This source code was written by the Go contributors.
|
||||
# The master list of contributors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/CONTRIBUTORS.
|
||||
+8
-4
@@ -19,7 +19,7 @@ See pkg.go.dev for further documentation and examples.
|
||||
* [pkg.go.dev/golang.org/x/oauth2](https://pkg.go.dev/golang.org/x/oauth2)
|
||||
* [pkg.go.dev/golang.org/x/oauth2/google](https://pkg.go.dev/golang.org/x/oauth2/google)
|
||||
|
||||
## Policy for new packages
|
||||
## Policy for new endpoints
|
||||
|
||||
We no longer accept new provider-specific packages in this repo if all
|
||||
they do is add a single endpoint variable. If you just want to add a
|
||||
@@ -29,8 +29,12 @@ package.
|
||||
|
||||
## Report Issues / Send Patches
|
||||
|
||||
This repository uses Gerrit for code changes. To learn how to submit changes to
|
||||
this repository, see https://golang.org/doc/contribute.html.
|
||||
|
||||
The main issue tracker for the oauth2 repository is located at
|
||||
https://github.com/golang/oauth2/issues.
|
||||
|
||||
This repository uses Gerrit for code changes. To learn how to submit changes to
|
||||
this repository, see https://golang.org/doc/contribute.html. In particular:
|
||||
|
||||
* Excluding trivial changes, all contributions should be connected to an existing issue.
|
||||
* API changes must go through the [change proposal process](https://go.dev/s/proposal-process) before they can be accepted.
|
||||
* The code owners are listed at [dev.golang.org/owners](https://dev.golang.org/owners#:~:text=x/oauth2).
|
||||
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2/internal"
|
||||
)
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc8628#section-3.5
|
||||
const (
|
||||
errAuthorizationPending = "authorization_pending"
|
||||
errSlowDown = "slow_down"
|
||||
errAccessDenied = "access_denied"
|
||||
errExpiredToken = "expired_token"
|
||||
)
|
||||
|
||||
// DeviceAuthResponse describes a successful RFC 8628 Device Authorization Response
|
||||
// https://datatracker.ietf.org/doc/html/rfc8628#section-3.2
|
||||
type DeviceAuthResponse struct {
|
||||
// DeviceCode
|
||||
DeviceCode string `json:"device_code"`
|
||||
// UserCode is the code the user should enter at the verification uri
|
||||
UserCode string `json:"user_code"`
|
||||
// VerificationURI is where user should enter the user code
|
||||
VerificationURI string `json:"verification_uri"`
|
||||
// VerificationURIComplete (if populated) includes the user code in the verification URI. This is typically shown to the user in non-textual form, such as a QR code.
|
||||
VerificationURIComplete string `json:"verification_uri_complete,omitempty"`
|
||||
// Expiry is when the device code and user code expire
|
||||
Expiry time.Time `json:"expires_in,omitempty"`
|
||||
// Interval is the duration in seconds that Poll should wait between requests
|
||||
Interval int64 `json:"interval,omitempty"`
|
||||
}
|
||||
|
||||
func (d DeviceAuthResponse) MarshalJSON() ([]byte, error) {
|
||||
type Alias DeviceAuthResponse
|
||||
var expiresIn int64
|
||||
if !d.Expiry.IsZero() {
|
||||
expiresIn = int64(time.Until(d.Expiry).Seconds())
|
||||
}
|
||||
return json.Marshal(&struct {
|
||||
ExpiresIn int64 `json:"expires_in,omitempty"`
|
||||
*Alias
|
||||
}{
|
||||
ExpiresIn: expiresIn,
|
||||
Alias: (*Alias)(&d),
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (c *DeviceAuthResponse) UnmarshalJSON(data []byte) error {
|
||||
type Alias DeviceAuthResponse
|
||||
aux := &struct {
|
||||
ExpiresIn int64 `json:"expires_in"`
|
||||
// workaround misspelling of verification_uri
|
||||
VerificationURL string `json:"verification_url"`
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(c),
|
||||
}
|
||||
if err := json.Unmarshal(data, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
if aux.ExpiresIn != 0 {
|
||||
c.Expiry = time.Now().UTC().Add(time.Second * time.Duration(aux.ExpiresIn))
|
||||
}
|
||||
if c.VerificationURI == "" {
|
||||
c.VerificationURI = aux.VerificationURL
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeviceAuth returns a device auth struct which contains a device code
|
||||
// and authorization information provided for users to enter on another device.
|
||||
func (c *Config) DeviceAuth(ctx context.Context, opts ...AuthCodeOption) (*DeviceAuthResponse, error) {
|
||||
// https://datatracker.ietf.org/doc/html/rfc8628#section-3.1
|
||||
v := url.Values{
|
||||
"client_id": {c.ClientID},
|
||||
}
|
||||
if len(c.Scopes) > 0 {
|
||||
v.Set("scope", strings.Join(c.Scopes, " "))
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt.setValue(v)
|
||||
}
|
||||
return retrieveDeviceAuth(ctx, c, v)
|
||||
}
|
||||
|
||||
func retrieveDeviceAuth(ctx context.Context, c *Config, v url.Values) (*DeviceAuthResponse, error) {
|
||||
if c.Endpoint.DeviceAuthURL == "" {
|
||||
return nil, errors.New("endpoint missing DeviceAuthURL")
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", c.Endpoint.DeviceAuthURL, strings.NewReader(v.Encode()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
t := time.Now()
|
||||
r, err := internal.ContextClient(ctx).Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(io.LimitReader(r.Body, 1<<20))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("oauth2: cannot auth device: %v", err)
|
||||
}
|
||||
if code := r.StatusCode; code < 200 || code > 299 {
|
||||
return nil, &RetrieveError{
|
||||
Response: r,
|
||||
Body: body,
|
||||
}
|
||||
}
|
||||
|
||||
da := &DeviceAuthResponse{}
|
||||
err = json.Unmarshal(body, &da)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unmarshal %s", err)
|
||||
}
|
||||
|
||||
if !da.Expiry.IsZero() {
|
||||
// Make a small adjustment to account for time taken by the request
|
||||
da.Expiry = da.Expiry.Add(-time.Since(t))
|
||||
}
|
||||
|
||||
return da, nil
|
||||
}
|
||||
|
||||
// DeviceAccessToken polls the server to exchange a device code for a token.
|
||||
func (c *Config) DeviceAccessToken(ctx context.Context, da *DeviceAuthResponse, opts ...AuthCodeOption) (*Token, error) {
|
||||
if !da.Expiry.IsZero() {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithDeadline(ctx, da.Expiry)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc8628#section-3.4
|
||||
v := url.Values{
|
||||
"client_id": {c.ClientID},
|
||||
"grant_type": {"urn:ietf:params:oauth:grant-type:device_code"},
|
||||
"device_code": {da.DeviceCode},
|
||||
}
|
||||
if len(c.Scopes) > 0 {
|
||||
v.Set("scope", strings.Join(c.Scopes, " "))
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt.setValue(v)
|
||||
}
|
||||
|
||||
// "If no value is provided, clients MUST use 5 as the default."
|
||||
// https://datatracker.ietf.org/doc/html/rfc8628#section-3.2
|
||||
interval := da.Interval
|
||||
if interval == 0 {
|
||||
interval = 5
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(time.Duration(interval) * time.Second)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case <-ticker.C:
|
||||
tok, err := retrieveToken(ctx, c, v)
|
||||
if err == nil {
|
||||
return tok, nil
|
||||
}
|
||||
|
||||
e, ok := err.(*RetrieveError)
|
||||
if !ok {
|
||||
return nil, err
|
||||
}
|
||||
switch e.ErrorCode {
|
||||
case errSlowDown:
|
||||
// https://datatracker.ietf.org/doc/html/rfc8628#section-3.5
|
||||
// "the interval MUST be increased by 5 seconds for this and all subsequent requests"
|
||||
interval += 5
|
||||
ticker.Reset(time.Duration(interval) * time.Second)
|
||||
case errAuthorizationPending:
|
||||
// Do nothing.
|
||||
case errAccessDenied, errExpiredToken:
|
||||
fallthrough
|
||||
default:
|
||||
return tok, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-1
@@ -3,7 +3,6 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build appengine
|
||||
// +build appengine
|
||||
|
||||
package internal
|
||||
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
// ParseKey converts the binary contents of a private key file
|
||||
// to an *rsa.PrivateKey. It detects whether the private key is in a
|
||||
// PEM container or not. If so, it extracts the the private key
|
||||
// PEM container or not. If so, it extracts the private key
|
||||
// from PEM container before conversion. It only supports PEM
|
||||
// containers with no passphrase.
|
||||
func ParseKey(key []byte) (*rsa.PrivateKey, error) {
|
||||
|
||||
+96
-38
@@ -18,9 +18,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context/ctxhttp"
|
||||
)
|
||||
|
||||
// Token represents the credentials used to authorize
|
||||
@@ -57,12 +56,18 @@ type Token struct {
|
||||
}
|
||||
|
||||
// tokenJSON is the struct representing the HTTP response from OAuth2
|
||||
// providers returning a token in JSON form.
|
||||
// providers returning a token or error in JSON form.
|
||||
// https://datatracker.ietf.org/doc/html/rfc6749#section-5.1
|
||||
type tokenJSON struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
ExpiresIn expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
|
||||
// error fields
|
||||
// https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
|
||||
ErrorCode string `json:"error"`
|
||||
ErrorDescription string `json:"error_description"`
|
||||
ErrorURI string `json:"error_uri"`
|
||||
}
|
||||
|
||||
func (e *tokenJSON) expiry() (t time.Time) {
|
||||
@@ -111,41 +116,60 @@ const (
|
||||
AuthStyleInHeader AuthStyle = 2
|
||||
)
|
||||
|
||||
// authStyleCache is the set of tokenURLs we've successfully used via
|
||||
// LazyAuthStyleCache is a backwards compatibility compromise to let Configs
|
||||
// have a lazily-initialized AuthStyleCache.
|
||||
//
|
||||
// The two users of this, oauth2.Config and oauth2/clientcredentials.Config,
|
||||
// both would ideally just embed an unexported AuthStyleCache but because both
|
||||
// were historically allowed to be copied by value we can't retroactively add an
|
||||
// uncopyable Mutex to them.
|
||||
//
|
||||
// We could use an atomic.Pointer, but that was added recently enough (in Go
|
||||
// 1.18) that we'd break Go 1.17 users where the tests as of 2023-08-03
|
||||
// still pass. By using an atomic.Value, it supports both Go 1.17 and
|
||||
// copying by value, even if that's not ideal.
|
||||
type LazyAuthStyleCache struct {
|
||||
v atomic.Value // of *AuthStyleCache
|
||||
}
|
||||
|
||||
func (lc *LazyAuthStyleCache) Get() *AuthStyleCache {
|
||||
if c, ok := lc.v.Load().(*AuthStyleCache); ok {
|
||||
return c
|
||||
}
|
||||
c := new(AuthStyleCache)
|
||||
if !lc.v.CompareAndSwap(nil, c) {
|
||||
c = lc.v.Load().(*AuthStyleCache)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// AuthStyleCache is the set of tokenURLs we've successfully used via
|
||||
// RetrieveToken and which style auth we ended up using.
|
||||
// It's called a cache, but it doesn't (yet?) shrink. It's expected that
|
||||
// the set of OAuth2 servers a program contacts over time is fixed and
|
||||
// small.
|
||||
var authStyleCache struct {
|
||||
sync.Mutex
|
||||
m map[string]AuthStyle // keyed by tokenURL
|
||||
}
|
||||
|
||||
// ResetAuthCache resets the global authentication style cache used
|
||||
// for AuthStyleUnknown token requests.
|
||||
func ResetAuthCache() {
|
||||
authStyleCache.Lock()
|
||||
defer authStyleCache.Unlock()
|
||||
authStyleCache.m = nil
|
||||
type AuthStyleCache struct {
|
||||
mu sync.Mutex
|
||||
m map[string]AuthStyle // keyed by tokenURL
|
||||
}
|
||||
|
||||
// lookupAuthStyle reports which auth style we last used with tokenURL
|
||||
// when calling RetrieveToken and whether we have ever done so.
|
||||
func lookupAuthStyle(tokenURL string) (style AuthStyle, ok bool) {
|
||||
authStyleCache.Lock()
|
||||
defer authStyleCache.Unlock()
|
||||
style, ok = authStyleCache.m[tokenURL]
|
||||
func (c *AuthStyleCache) lookupAuthStyle(tokenURL string) (style AuthStyle, ok bool) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
style, ok = c.m[tokenURL]
|
||||
return
|
||||
}
|
||||
|
||||
// setAuthStyle adds an entry to authStyleCache, documented above.
|
||||
func setAuthStyle(tokenURL string, v AuthStyle) {
|
||||
authStyleCache.Lock()
|
||||
defer authStyleCache.Unlock()
|
||||
if authStyleCache.m == nil {
|
||||
authStyleCache.m = make(map[string]AuthStyle)
|
||||
func (c *AuthStyleCache) setAuthStyle(tokenURL string, v AuthStyle) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.m == nil {
|
||||
c.m = make(map[string]AuthStyle)
|
||||
}
|
||||
authStyleCache.m[tokenURL] = v
|
||||
c.m[tokenURL] = v
|
||||
}
|
||||
|
||||
// newTokenRequest returns a new *http.Request to retrieve a new token
|
||||
@@ -185,10 +209,10 @@ func cloneURLValues(v url.Values) url.Values {
|
||||
return v2
|
||||
}
|
||||
|
||||
func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values, authStyle AuthStyle) (*Token, error) {
|
||||
func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values, authStyle AuthStyle, styleCache *AuthStyleCache) (*Token, error) {
|
||||
needsAuthStyleProbe := authStyle == 0
|
||||
if needsAuthStyleProbe {
|
||||
if style, ok := lookupAuthStyle(tokenURL); ok {
|
||||
if style, ok := styleCache.lookupAuthStyle(tokenURL); ok {
|
||||
authStyle = style
|
||||
needsAuthStyleProbe = false
|
||||
} else {
|
||||
@@ -218,7 +242,7 @@ func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string,
|
||||
token, err = doTokenRoundTrip(ctx, req)
|
||||
}
|
||||
if needsAuthStyleProbe && err == nil {
|
||||
setAuthStyle(tokenURL, authStyle)
|
||||
styleCache.setAuthStyle(tokenURL, authStyle)
|
||||
}
|
||||
// Don't overwrite `RefreshToken` with an empty value
|
||||
// if this was a token refreshing request.
|
||||
@@ -229,7 +253,7 @@ func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string,
|
||||
}
|
||||
|
||||
func doTokenRoundTrip(ctx context.Context, req *http.Request) (*Token, error) {
|
||||
r, err := ctxhttp.Do(ctx, ContextClient(ctx), req)
|
||||
r, err := ContextClient(ctx).Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -238,21 +262,29 @@ func doTokenRoundTrip(ctx context.Context, req *http.Request) (*Token, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
|
||||
}
|
||||
if code := r.StatusCode; code < 200 || code > 299 {
|
||||
return nil, &RetrieveError{
|
||||
Response: r,
|
||||
Body: body,
|
||||
}
|
||||
|
||||
failureStatus := r.StatusCode < 200 || r.StatusCode > 299
|
||||
retrieveError := &RetrieveError{
|
||||
Response: r,
|
||||
Body: body,
|
||||
// attempt to populate error detail below
|
||||
}
|
||||
|
||||
var token *Token
|
||||
content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||
switch content {
|
||||
case "application/x-www-form-urlencoded", "text/plain":
|
||||
// some endpoints return a query string
|
||||
vals, err := url.ParseQuery(string(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if failureStatus {
|
||||
return nil, retrieveError
|
||||
}
|
||||
return nil, fmt.Errorf("oauth2: cannot parse response: %v", err)
|
||||
}
|
||||
retrieveError.ErrorCode = vals.Get("error")
|
||||
retrieveError.ErrorDescription = vals.Get("error_description")
|
||||
retrieveError.ErrorURI = vals.Get("error_uri")
|
||||
token = &Token{
|
||||
AccessToken: vals.Get("access_token"),
|
||||
TokenType: vals.Get("token_type"),
|
||||
@@ -267,8 +299,14 @@ func doTokenRoundTrip(ctx context.Context, req *http.Request) (*Token, error) {
|
||||
default:
|
||||
var tj tokenJSON
|
||||
if err = json.Unmarshal(body, &tj); err != nil {
|
||||
return nil, err
|
||||
if failureStatus {
|
||||
return nil, retrieveError
|
||||
}
|
||||
return nil, fmt.Errorf("oauth2: cannot parse json: %v", err)
|
||||
}
|
||||
retrieveError.ErrorCode = tj.ErrorCode
|
||||
retrieveError.ErrorDescription = tj.ErrorDescription
|
||||
retrieveError.ErrorURI = tj.ErrorURI
|
||||
token = &Token{
|
||||
AccessToken: tj.AccessToken,
|
||||
TokenType: tj.TokenType,
|
||||
@@ -278,17 +316,37 @@ func doTokenRoundTrip(ctx context.Context, req *http.Request) (*Token, error) {
|
||||
}
|
||||
json.Unmarshal(body, &token.Raw) // no error checks for optional fields
|
||||
}
|
||||
// according to spec, servers should respond status 400 in error case
|
||||
// https://www.rfc-editor.org/rfc/rfc6749#section-5.2
|
||||
// but some unorthodox servers respond 200 in error case
|
||||
if failureStatus || retrieveError.ErrorCode != "" {
|
||||
return nil, retrieveError
|
||||
}
|
||||
if token.AccessToken == "" {
|
||||
return nil, errors.New("oauth2: server response missing access_token")
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// mirrors oauth2.RetrieveError
|
||||
type RetrieveError struct {
|
||||
Response *http.Response
|
||||
Body []byte
|
||||
Response *http.Response
|
||||
Body []byte
|
||||
ErrorCode string
|
||||
ErrorDescription string
|
||||
ErrorURI string
|
||||
}
|
||||
|
||||
func (r *RetrieveError) Error() string {
|
||||
if r.ErrorCode != "" {
|
||||
s := fmt.Sprintf("oauth2: %q", r.ErrorCode)
|
||||
if r.ErrorDescription != "" {
|
||||
s += fmt.Sprintf(" %q", r.ErrorDescription)
|
||||
}
|
||||
if r.ErrorURI != "" {
|
||||
s += fmt.Sprintf(" %q", r.ErrorURI)
|
||||
}
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body)
|
||||
}
|
||||
|
||||
+52
-12
@@ -16,6 +16,7 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2/internal"
|
||||
)
|
||||
@@ -57,6 +58,10 @@ type Config struct {
|
||||
|
||||
// Scope specifies optional requested permissions.
|
||||
Scopes []string
|
||||
|
||||
// authStyleCache caches which auth style to use when Endpoint.AuthStyle is
|
||||
// the zero value (AuthStyleAutoDetect).
|
||||
authStyleCache internal.LazyAuthStyleCache
|
||||
}
|
||||
|
||||
// A TokenSource is anything that can return a token.
|
||||
@@ -70,8 +75,9 @@ type TokenSource interface {
|
||||
// Endpoint represents an OAuth 2.0 provider's authorization and token
|
||||
// endpoint URLs.
|
||||
type Endpoint struct {
|
||||
AuthURL string
|
||||
TokenURL string
|
||||
AuthURL string
|
||||
DeviceAuthURL string
|
||||
TokenURL string
|
||||
|
||||
// AuthStyle optionally specifies how the endpoint wants the
|
||||
// client ID & client secret sent. The zero value means to
|
||||
@@ -138,15 +144,19 @@ func SetAuthURLParam(key, value string) AuthCodeOption {
|
||||
// AuthCodeURL returns a URL to OAuth 2.0 provider's consent page
|
||||
// that asks for permissions for the required scopes explicitly.
|
||||
//
|
||||
// State is a token to protect the user from CSRF attacks. You must
|
||||
// always provide a non-empty string and validate that it matches the
|
||||
// the state query parameter on your redirect callback.
|
||||
// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info.
|
||||
// State is an opaque value used by the client to maintain state between the
|
||||
// request and callback. The authorization server includes this value when
|
||||
// redirecting the user agent back to the client.
|
||||
//
|
||||
// Opts may include AccessTypeOnline or AccessTypeOffline, as well
|
||||
// as ApprovalForce.
|
||||
// It can also be used to pass the PKCE challenge.
|
||||
// See https://www.oauth.com/oauth2-servers/pkce/ for more info.
|
||||
//
|
||||
// To protect against CSRF attacks, opts should include a PKCE challenge
|
||||
// (S256ChallengeOption). Not all servers support PKCE. An alternative is to
|
||||
// generate a random state parameter and verify it after exchange.
|
||||
// See https://datatracker.ietf.org/doc/html/rfc6749#section-10.12 (predating
|
||||
// PKCE), https://www.oauth.com/oauth2-servers/pkce/ and
|
||||
// https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-09.html#name-cross-site-request-forgery (describing both approaches)
|
||||
func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(c.Endpoint.AuthURL)
|
||||
@@ -161,7 +171,6 @@ func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
|
||||
v.Set("scope", strings.Join(c.Scopes, " "))
|
||||
}
|
||||
if state != "" {
|
||||
// TODO(light): Docs say never to omit state; don't allow empty.
|
||||
v.Set("state", state)
|
||||
}
|
||||
for _, opt := range opts {
|
||||
@@ -206,10 +215,11 @@ func (c *Config) PasswordCredentialsToken(ctx context.Context, username, passwor
|
||||
// The provided context optionally controls which HTTP client is used. See the HTTPClient variable.
|
||||
//
|
||||
// The code will be in the *http.Request.FormValue("code"). Before
|
||||
// calling Exchange, be sure to validate FormValue("state").
|
||||
// calling Exchange, be sure to validate FormValue("state") if you are
|
||||
// using it to protect against CSRF attacks.
|
||||
//
|
||||
// Opts may include the PKCE verifier code if previously used in AuthCodeURL.
|
||||
// See https://www.oauth.com/oauth2-servers/pkce/ for more info.
|
||||
// If using PKCE to protect against CSRF attacks, opts should include a
|
||||
// VerifierOption.
|
||||
func (c *Config) Exchange(ctx context.Context, code string, opts ...AuthCodeOption) (*Token, error) {
|
||||
v := url.Values{
|
||||
"grant_type": {"authorization_code"},
|
||||
@@ -290,6 +300,8 @@ type reuseTokenSource struct {
|
||||
|
||||
mu sync.Mutex // guards t
|
||||
t *Token
|
||||
|
||||
expiryDelta time.Duration
|
||||
}
|
||||
|
||||
// Token returns the current token if it's still valid, else will
|
||||
@@ -305,6 +317,7 @@ func (s *reuseTokenSource) Token() (*Token, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.expiryDelta = s.expiryDelta
|
||||
s.t = t
|
||||
return t, nil
|
||||
}
|
||||
@@ -379,3 +392,30 @@ func ReuseTokenSource(t *Token, src TokenSource) TokenSource {
|
||||
new: src,
|
||||
}
|
||||
}
|
||||
|
||||
// ReuseTokenSource returns a TokenSource that acts in the same manner as the
|
||||
// TokenSource returned by ReuseTokenSource, except the expiry buffer is
|
||||
// configurable. The expiration time of a token is calculated as
|
||||
// t.Expiry.Add(-earlyExpiry).
|
||||
func ReuseTokenSourceWithExpiry(t *Token, src TokenSource, earlyExpiry time.Duration) TokenSource {
|
||||
// Don't wrap a reuseTokenSource in itself. That would work,
|
||||
// but cause an unnecessary number of mutex operations.
|
||||
// Just build the equivalent one.
|
||||
if rt, ok := src.(*reuseTokenSource); ok {
|
||||
if t == nil {
|
||||
// Just use it directly, but set the expiryDelta to earlyExpiry,
|
||||
// so the behavior matches what the user expects.
|
||||
rt.expiryDelta = earlyExpiry
|
||||
return rt
|
||||
}
|
||||
src = rt.new
|
||||
}
|
||||
if t != nil {
|
||||
t.expiryDelta = earlyExpiry
|
||||
}
|
||||
return &reuseTokenSource{
|
||||
t: t,
|
||||
new: src,
|
||||
expiryDelta: earlyExpiry,
|
||||
}
|
||||
}
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
// Copyright 2023 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
const (
|
||||
codeChallengeKey = "code_challenge"
|
||||
codeChallengeMethodKey = "code_challenge_method"
|
||||
codeVerifierKey = "code_verifier"
|
||||
)
|
||||
|
||||
// GenerateVerifier generates a PKCE code verifier with 32 octets of randomness.
|
||||
// This follows recommendations in RFC 7636.
|
||||
//
|
||||
// A fresh verifier should be generated for each authorization.
|
||||
// S256ChallengeOption(verifier) should then be passed to Config.AuthCodeURL
|
||||
// (or Config.DeviceAccess) and VerifierOption(verifier) to Config.Exchange
|
||||
// (or Config.DeviceAccessToken).
|
||||
func GenerateVerifier() string {
|
||||
// "RECOMMENDED that the output of a suitable random number generator be
|
||||
// used to create a 32-octet sequence. The octet sequence is then
|
||||
// base64url-encoded to produce a 43-octet URL-safe string to use as the
|
||||
// code verifier."
|
||||
// https://datatracker.ietf.org/doc/html/rfc7636#section-4.1
|
||||
data := make([]byte, 32)
|
||||
if _, err := rand.Read(data); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return base64.RawURLEncoding.EncodeToString(data)
|
||||
}
|
||||
|
||||
// VerifierOption returns a PKCE code verifier AuthCodeOption. It should be
|
||||
// passed to Config.Exchange or Config.DeviceAccessToken only.
|
||||
func VerifierOption(verifier string) AuthCodeOption {
|
||||
return setParam{k: codeVerifierKey, v: verifier}
|
||||
}
|
||||
|
||||
// S256ChallengeFromVerifier returns a PKCE code challenge derived from verifier with method S256.
|
||||
//
|
||||
// Prefer to use S256ChallengeOption where possible.
|
||||
func S256ChallengeFromVerifier(verifier string) string {
|
||||
sha := sha256.Sum256([]byte(verifier))
|
||||
return base64.RawURLEncoding.EncodeToString(sha[:])
|
||||
}
|
||||
|
||||
// S256ChallengeOption derives a PKCE code challenge derived from verifier with
|
||||
// method S256. It should be passed to Config.AuthCodeURL or Config.DeviceAccess
|
||||
// only.
|
||||
func S256ChallengeOption(verifier string) AuthCodeOption {
|
||||
return challengeOption{
|
||||
challenge_method: "S256",
|
||||
challenge: S256ChallengeFromVerifier(verifier),
|
||||
}
|
||||
}
|
||||
|
||||
type challengeOption struct{ challenge_method, challenge string }
|
||||
|
||||
func (p challengeOption) setValue(m url.Values) {
|
||||
m.Set(codeChallengeMethodKey, p.challenge_method)
|
||||
m.Set(codeChallengeKey, p.challenge)
|
||||
}
|
||||
+31
-4
@@ -16,10 +16,10 @@ import (
|
||||
"golang.org/x/oauth2/internal"
|
||||
)
|
||||
|
||||
// expiryDelta determines how earlier a token should be considered
|
||||
// defaultExpiryDelta determines how earlier a token should be considered
|
||||
// expired than its actual expiration time. It is used to avoid late
|
||||
// expirations due to client-server time mismatches.
|
||||
const expiryDelta = 10 * time.Second
|
||||
const defaultExpiryDelta = 10 * time.Second
|
||||
|
||||
// Token represents the credentials used to authorize
|
||||
// the requests to access protected resources on the OAuth 2.0
|
||||
@@ -52,6 +52,11 @@ type Token struct {
|
||||
// raw optionally contains extra metadata from the server
|
||||
// when updating a token.
|
||||
raw interface{}
|
||||
|
||||
// expiryDelta is used to calculate when a token is considered
|
||||
// expired, by subtracting from Expiry. If zero, defaultExpiryDelta
|
||||
// is used.
|
||||
expiryDelta time.Duration
|
||||
}
|
||||
|
||||
// Type returns t.TokenType if non-empty, else "Bearer".
|
||||
@@ -127,6 +132,11 @@ func (t *Token) expired() bool {
|
||||
if t.Expiry.IsZero() {
|
||||
return false
|
||||
}
|
||||
|
||||
expiryDelta := defaultExpiryDelta
|
||||
if t.expiryDelta != 0 {
|
||||
expiryDelta = t.expiryDelta
|
||||
}
|
||||
return t.Expiry.Round(0).Add(-expiryDelta).Before(timeNow())
|
||||
}
|
||||
|
||||
@@ -154,7 +164,7 @@ func tokenFromInternal(t *internal.Token) *Token {
|
||||
// This token is then mapped from *internal.Token into an *oauth2.Token which is returned along
|
||||
// with an error..
|
||||
func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) {
|
||||
tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v, internal.AuthStyle(c.Endpoint.AuthStyle))
|
||||
tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v, internal.AuthStyle(c.Endpoint.AuthStyle), c.authStyleCache.Get())
|
||||
if err != nil {
|
||||
if rErr, ok := err.(*internal.RetrieveError); ok {
|
||||
return nil, (*RetrieveError)(rErr)
|
||||
@@ -165,14 +175,31 @@ func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error)
|
||||
}
|
||||
|
||||
// RetrieveError is the error returned when the token endpoint returns a
|
||||
// non-2XX HTTP status code.
|
||||
// non-2XX HTTP status code or populates RFC 6749's 'error' parameter.
|
||||
// https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
|
||||
type RetrieveError struct {
|
||||
Response *http.Response
|
||||
// Body is the body that was consumed by reading Response.Body.
|
||||
// It may be truncated.
|
||||
Body []byte
|
||||
// ErrorCode is RFC 6749's 'error' parameter.
|
||||
ErrorCode string
|
||||
// ErrorDescription is RFC 6749's 'error_description' parameter.
|
||||
ErrorDescription string
|
||||
// ErrorURI is RFC 6749's 'error_uri' parameter.
|
||||
ErrorURI string
|
||||
}
|
||||
|
||||
func (r *RetrieveError) Error() string {
|
||||
if r.ErrorCode != "" {
|
||||
s := fmt.Sprintf("oauth2: %q", r.ErrorCode)
|
||||
if r.ErrorDescription != "" {
|
||||
s += fmt.Sprintf(" %q", r.ErrorDescription)
|
||||
}
|
||||
if r.ErrorURI != "" {
|
||||
s += fmt.Sprintf(" %q", r.ErrorURI)
|
||||
}
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body)
|
||||
}
|
||||
|
||||
+22
-17
@@ -248,6 +248,7 @@ struct ltchars {
|
||||
#include <linux/module.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/netfilter/nfnetlink.h>
|
||||
#include <linux/netfilter/nf_tables.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/net_namespace.h>
|
||||
#include <linux/nfc.h>
|
||||
@@ -283,10 +284,6 @@ struct ltchars {
|
||||
#include <asm/termbits.h>
|
||||
#endif
|
||||
|
||||
#ifndef MSG_FASTOPEN
|
||||
#define MSG_FASTOPEN 0x20000000
|
||||
#endif
|
||||
|
||||
#ifndef PTRACE_GETREGS
|
||||
#define PTRACE_GETREGS 0xc
|
||||
#endif
|
||||
@@ -295,14 +292,6 @@ struct ltchars {
|
||||
#define PTRACE_SETREGS 0xd
|
||||
#endif
|
||||
|
||||
#ifndef SOL_NETLINK
|
||||
#define SOL_NETLINK 270
|
||||
#endif
|
||||
|
||||
#ifndef SOL_SMC
|
||||
#define SOL_SMC 286
|
||||
#endif
|
||||
|
||||
#ifdef SOL_BLUETOOTH
|
||||
// SPARC includes this in /usr/include/sparc64-linux-gnu/bits/socket.h
|
||||
// but it is already in bluetooth_linux.go
|
||||
@@ -319,10 +308,23 @@ struct ltchars {
|
||||
#undef TIPC_WAIT_FOREVER
|
||||
#define TIPC_WAIT_FOREVER 0xffffffff
|
||||
|
||||
// Copied from linux/l2tp.h
|
||||
// Including linux/l2tp.h here causes conflicts between linux/in.h
|
||||
// and netinet/in.h included via net/route.h above.
|
||||
#define IPPROTO_L2TP 115
|
||||
// Copied from linux/netfilter/nf_nat.h
|
||||
// Including linux/netfilter/nf_nat.h here causes conflicts between linux/in.h
|
||||
// and netinet/in.h.
|
||||
#define NF_NAT_RANGE_MAP_IPS (1 << 0)
|
||||
#define NF_NAT_RANGE_PROTO_SPECIFIED (1 << 1)
|
||||
#define NF_NAT_RANGE_PROTO_RANDOM (1 << 2)
|
||||
#define NF_NAT_RANGE_PERSISTENT (1 << 3)
|
||||
#define NF_NAT_RANGE_PROTO_RANDOM_FULLY (1 << 4)
|
||||
#define NF_NAT_RANGE_PROTO_OFFSET (1 << 5)
|
||||
#define NF_NAT_RANGE_NETMAP (1 << 6)
|
||||
#define NF_NAT_RANGE_PROTO_RANDOM_ALL \
|
||||
(NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PROTO_RANDOM_FULLY)
|
||||
#define NF_NAT_RANGE_MASK \
|
||||
(NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED | \
|
||||
NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PERSISTENT | \
|
||||
NF_NAT_RANGE_PROTO_RANDOM_FULLY | NF_NAT_RANGE_PROTO_OFFSET | \
|
||||
NF_NAT_RANGE_NETMAP)
|
||||
|
||||
// Copied from linux/hid.h.
|
||||
// Keep in sync with the size of the referenced fields.
|
||||
@@ -582,7 +584,7 @@ ccflags="$@"
|
||||
$2 ~ /^KEY_(SPEC|REQKEY_DEFL)_/ ||
|
||||
$2 ~ /^KEYCTL_/ ||
|
||||
$2 ~ /^PERF_/ ||
|
||||
$2 ~ /^SECCOMP_MODE_/ ||
|
||||
$2 ~ /^SECCOMP_/ ||
|
||||
$2 ~ /^SEEK_/ ||
|
||||
$2 ~ /^SCHED_/ ||
|
||||
$2 ~ /^SPLICE_/ ||
|
||||
@@ -603,6 +605,9 @@ ccflags="$@"
|
||||
$2 ~ /^FSOPT_/ ||
|
||||
$2 ~ /^WDIO[CFS]_/ ||
|
||||
$2 ~ /^NFN/ ||
|
||||
$2 !~ /^NFT_META_IIFTYPE/ &&
|
||||
$2 ~ /^NFT_/ ||
|
||||
$2 ~ /^NF_NAT_/ ||
|
||||
$2 ~ /^XDP_/ ||
|
||||
$2 ~ /^RWF_/ ||
|
||||
$2 ~ /^(HDIO|WIN|SMART)_/ ||
|
||||
|
||||
+89
-1
@@ -1785,6 +1785,8 @@ const (
|
||||
LANDLOCK_ACCESS_FS_REMOVE_FILE = 0x20
|
||||
LANDLOCK_ACCESS_FS_TRUNCATE = 0x4000
|
||||
LANDLOCK_ACCESS_FS_WRITE_FILE = 0x2
|
||||
LANDLOCK_ACCESS_NET_BIND_TCP = 0x1
|
||||
LANDLOCK_ACCESS_NET_CONNECT_TCP = 0x2
|
||||
LANDLOCK_CREATE_RULESET_VERSION = 0x1
|
||||
LINUX_REBOOT_CMD_CAD_OFF = 0x0
|
||||
LINUX_REBOOT_CMD_CAD_ON = 0x89abcdef
|
||||
@@ -2127,6 +2129,60 @@ const (
|
||||
NFNL_SUBSYS_QUEUE = 0x3
|
||||
NFNL_SUBSYS_ULOG = 0x4
|
||||
NFS_SUPER_MAGIC = 0x6969
|
||||
NFT_CHAIN_FLAGS = 0x7
|
||||
NFT_CHAIN_MAXNAMELEN = 0x100
|
||||
NFT_CT_MAX = 0x17
|
||||
NFT_DATA_RESERVED_MASK = 0xffffff00
|
||||
NFT_DATA_VALUE_MAXLEN = 0x40
|
||||
NFT_EXTHDR_OP_MAX = 0x4
|
||||
NFT_FIB_RESULT_MAX = 0x3
|
||||
NFT_INNER_MASK = 0xf
|
||||
NFT_LOGLEVEL_MAX = 0x8
|
||||
NFT_NAME_MAXLEN = 0x100
|
||||
NFT_NG_MAX = 0x1
|
||||
NFT_OBJECT_CONNLIMIT = 0x5
|
||||
NFT_OBJECT_COUNTER = 0x1
|
||||
NFT_OBJECT_CT_EXPECT = 0x9
|
||||
NFT_OBJECT_CT_HELPER = 0x3
|
||||
NFT_OBJECT_CT_TIMEOUT = 0x7
|
||||
NFT_OBJECT_LIMIT = 0x4
|
||||
NFT_OBJECT_MAX = 0xa
|
||||
NFT_OBJECT_QUOTA = 0x2
|
||||
NFT_OBJECT_SECMARK = 0x8
|
||||
NFT_OBJECT_SYNPROXY = 0xa
|
||||
NFT_OBJECT_TUNNEL = 0x6
|
||||
NFT_OBJECT_UNSPEC = 0x0
|
||||
NFT_OBJ_MAXNAMELEN = 0x100
|
||||
NFT_OSF_MAXGENRELEN = 0x10
|
||||
NFT_QUEUE_FLAG_BYPASS = 0x1
|
||||
NFT_QUEUE_FLAG_CPU_FANOUT = 0x2
|
||||
NFT_QUEUE_FLAG_MASK = 0x3
|
||||
NFT_REG32_COUNT = 0x10
|
||||
NFT_REG32_SIZE = 0x4
|
||||
NFT_REG_MAX = 0x4
|
||||
NFT_REG_SIZE = 0x10
|
||||
NFT_REJECT_ICMPX_MAX = 0x3
|
||||
NFT_RT_MAX = 0x4
|
||||
NFT_SECMARK_CTX_MAXLEN = 0x100
|
||||
NFT_SET_MAXNAMELEN = 0x100
|
||||
NFT_SOCKET_MAX = 0x3
|
||||
NFT_TABLE_F_MASK = 0x3
|
||||
NFT_TABLE_MAXNAMELEN = 0x100
|
||||
NFT_TRACETYPE_MAX = 0x3
|
||||
NFT_TUNNEL_F_MASK = 0x7
|
||||
NFT_TUNNEL_MAX = 0x1
|
||||
NFT_TUNNEL_MODE_MAX = 0x2
|
||||
NFT_USERDATA_MAXLEN = 0x100
|
||||
NFT_XFRM_KEY_MAX = 0x6
|
||||
NF_NAT_RANGE_MAP_IPS = 0x1
|
||||
NF_NAT_RANGE_MASK = 0x7f
|
||||
NF_NAT_RANGE_NETMAP = 0x40
|
||||
NF_NAT_RANGE_PERSISTENT = 0x8
|
||||
NF_NAT_RANGE_PROTO_OFFSET = 0x20
|
||||
NF_NAT_RANGE_PROTO_RANDOM = 0x4
|
||||
NF_NAT_RANGE_PROTO_RANDOM_ALL = 0x14
|
||||
NF_NAT_RANGE_PROTO_RANDOM_FULLY = 0x10
|
||||
NF_NAT_RANGE_PROTO_SPECIFIED = 0x2
|
||||
NILFS_SUPER_MAGIC = 0x3434
|
||||
NL0 = 0x0
|
||||
NL1 = 0x100
|
||||
@@ -2411,6 +2467,7 @@ const (
|
||||
PR_MCE_KILL_GET = 0x22
|
||||
PR_MCE_KILL_LATE = 0x0
|
||||
PR_MCE_KILL_SET = 0x1
|
||||
PR_MDWE_NO_INHERIT = 0x2
|
||||
PR_MDWE_REFUSE_EXEC_GAIN = 0x1
|
||||
PR_MPX_DISABLE_MANAGEMENT = 0x2c
|
||||
PR_MPX_ENABLE_MANAGEMENT = 0x2b
|
||||
@@ -2615,8 +2672,9 @@ const (
|
||||
RTAX_FEATURES = 0xc
|
||||
RTAX_FEATURE_ALLFRAG = 0x8
|
||||
RTAX_FEATURE_ECN = 0x1
|
||||
RTAX_FEATURE_MASK = 0xf
|
||||
RTAX_FEATURE_MASK = 0x1f
|
||||
RTAX_FEATURE_SACK = 0x2
|
||||
RTAX_FEATURE_TCP_USEC_TS = 0x10
|
||||
RTAX_FEATURE_TIMESTAMP = 0x4
|
||||
RTAX_HOPLIMIT = 0xa
|
||||
RTAX_INITCWND = 0xb
|
||||
@@ -2859,9 +2917,38 @@ const (
|
||||
SCM_RIGHTS = 0x1
|
||||
SCM_TIMESTAMP = 0x1d
|
||||
SC_LOG_FLUSH = 0x100000
|
||||
SECCOMP_ADDFD_FLAG_SEND = 0x2
|
||||
SECCOMP_ADDFD_FLAG_SETFD = 0x1
|
||||
SECCOMP_FILTER_FLAG_LOG = 0x2
|
||||
SECCOMP_FILTER_FLAG_NEW_LISTENER = 0x8
|
||||
SECCOMP_FILTER_FLAG_SPEC_ALLOW = 0x4
|
||||
SECCOMP_FILTER_FLAG_TSYNC = 0x1
|
||||
SECCOMP_FILTER_FLAG_TSYNC_ESRCH = 0x10
|
||||
SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV = 0x20
|
||||
SECCOMP_GET_ACTION_AVAIL = 0x2
|
||||
SECCOMP_GET_NOTIF_SIZES = 0x3
|
||||
SECCOMP_IOCTL_NOTIF_RECV = 0xc0502100
|
||||
SECCOMP_IOCTL_NOTIF_SEND = 0xc0182101
|
||||
SECCOMP_IOC_MAGIC = '!'
|
||||
SECCOMP_MODE_DISABLED = 0x0
|
||||
SECCOMP_MODE_FILTER = 0x2
|
||||
SECCOMP_MODE_STRICT = 0x1
|
||||
SECCOMP_RET_ACTION = 0x7fff0000
|
||||
SECCOMP_RET_ACTION_FULL = 0xffff0000
|
||||
SECCOMP_RET_ALLOW = 0x7fff0000
|
||||
SECCOMP_RET_DATA = 0xffff
|
||||
SECCOMP_RET_ERRNO = 0x50000
|
||||
SECCOMP_RET_KILL = 0x0
|
||||
SECCOMP_RET_KILL_PROCESS = 0x80000000
|
||||
SECCOMP_RET_KILL_THREAD = 0x0
|
||||
SECCOMP_RET_LOG = 0x7ffc0000
|
||||
SECCOMP_RET_TRACE = 0x7ff00000
|
||||
SECCOMP_RET_TRAP = 0x30000
|
||||
SECCOMP_RET_USER_NOTIF = 0x7fc00000
|
||||
SECCOMP_SET_MODE_FILTER = 0x1
|
||||
SECCOMP_SET_MODE_STRICT = 0x0
|
||||
SECCOMP_USER_NOTIF_FD_SYNC_WAKE_UP = 0x1
|
||||
SECCOMP_USER_NOTIF_FLAG_CONTINUE = 0x1
|
||||
SECRETMEM_MAGIC = 0x5345434d
|
||||
SECURITYFS_MAGIC = 0x73636673
|
||||
SEEK_CUR = 0x1
|
||||
@@ -3021,6 +3108,7 @@ const (
|
||||
SOL_TIPC = 0x10f
|
||||
SOL_TLS = 0x11a
|
||||
SOL_UDP = 0x11
|
||||
SOL_VSOCK = 0x11f
|
||||
SOL_X25 = 0x106
|
||||
SOL_XDP = 0x11b
|
||||
SOMAXCONN = 0x1000
|
||||
|
||||
+3
@@ -281,6 +281,9 @@ const (
|
||||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x800
|
||||
SIOCATMARK = 0x8905
|
||||
|
||||
+3
@@ -282,6 +282,9 @@ const (
|
||||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x800
|
||||
SIOCATMARK = 0x8905
|
||||
|
||||
+3
@@ -288,6 +288,9 @@ const (
|
||||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x800
|
||||
SIOCATMARK = 0x8905
|
||||
|
||||
+3
@@ -278,6 +278,9 @@ const (
|
||||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x800
|
||||
SIOCATMARK = 0x8905
|
||||
|
||||
+3
@@ -275,6 +275,9 @@ const (
|
||||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x800
|
||||
SIOCATMARK = 0x8905
|
||||
|
||||
+3
@@ -281,6 +281,9 @@ const (
|
||||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x80
|
||||
SIOCATMARK = 0x40047307
|
||||
|
||||
+3
@@ -281,6 +281,9 @@ const (
|
||||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x80
|
||||
SIOCATMARK = 0x40047307
|
||||
|
||||
+3
@@ -281,6 +281,9 @@ const (
|
||||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x80
|
||||
SIOCATMARK = 0x40047307
|
||||
|
||||
+3
@@ -281,6 +281,9 @@ const (
|
||||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x80
|
||||
SIOCATMARK = 0x40047307
|
||||
|
||||
+3
@@ -336,6 +336,9 @@ const (
|
||||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x800
|
||||
SIOCATMARK = 0x8905
|
||||
|
||||
+3
@@ -340,6 +340,9 @@ const (
|
||||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x800
|
||||
SIOCATMARK = 0x8905
|
||||
|
||||
+3
@@ -340,6 +340,9 @@ const (
|
||||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x800
|
||||
SIOCATMARK = 0x8905
|
||||
|
||||
+3
@@ -272,6 +272,9 @@ const (
|
||||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x800
|
||||
SIOCATMARK = 0x8905
|
||||
|
||||
+3
@@ -344,6 +344,9 @@ const (
|
||||
SCM_TIMESTAMPNS = 0x23
|
||||
SCM_TXTIME = 0x3d
|
||||
SCM_WIFI_STATUS = 0x29
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x40182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x40082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x40082104
|
||||
SFD_CLOEXEC = 0x80000
|
||||
SFD_NONBLOCK = 0x800
|
||||
SIOCATMARK = 0x8905
|
||||
|
||||
+3
@@ -335,6 +335,9 @@ const (
|
||||
SCM_TIMESTAMPNS = 0x21
|
||||
SCM_TXTIME = 0x3f
|
||||
SCM_WIFI_STATUS = 0x25
|
||||
SECCOMP_IOCTL_NOTIF_ADDFD = 0x80182103
|
||||
SECCOMP_IOCTL_NOTIF_ID_VALID = 0x80082102
|
||||
SECCOMP_IOCTL_NOTIF_SET_FLAGS = 0x80082104
|
||||
SFD_CLOEXEC = 0x400000
|
||||
SFD_NONBLOCK = 0x4000
|
||||
SF_FP = 0x38
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user