Gowebdav - A golang WebDAV client library and command line tool.

Overview

GoWebDAV

Build Status GoDoc Go Report Card

A golang WebDAV client library.

Main features

gowebdav library allows to perform following actions on the remote WebDAV server:

Usage

First of all you should create Client instance using NewClient() function:

root := "https://webdav.mydomain.me"
user := "user"
password := "password"

c := gowebdav.NewClient(root, user, password)

After you can use this Client to perform actions, described below.

NOTICE: we will not check errors in examples, to focus you on the gowebdav library's code, but you should do it in your code!

Create path on a WebDAV server

err := c.Mkdir("folder", 0644)

In case you want to create several folders you can use c.MkdirAll():

err := c.MkdirAll("folder/subfolder/subfolder2", 0644)

Get files list

files, _ := c.ReadDir("folder/subfolder")
for _, file := range files {
    //notice that [file] has os.FileInfo type
    fmt.Println(file.Name())
}

Download file to byte array

webdavFilePath := "folder/subfolder/file.txt"
localFilePath := "/tmp/webdav/file.txt"

bytes, _ := c.Read(webdavFilePath)
ioutil.WriteFile(localFilePath, bytes, 0644)

Download file via reader

Also you can use c.ReadStream() method:

webdavFilePath := "folder/subfolder/file.txt"
localFilePath := "/tmp/webdav/file.txt"

reader, _ := c.ReadStream(webdavFilePath)

file, _ := os.Create(localFilePath)
defer file.Close()

io.Copy(file, reader)

Upload file from byte array

webdavFilePath := "folder/subfolder/file.txt"
localFilePath := "/tmp/webdav/file.txt"

bytes, _ := ioutil.ReadFile(localFilePath)

c.Write(webdavFilePath, bytes, 0644)

Upload file via writer

webdavFilePath := "folder/subfolder/file.txt"
localFilePath := "/tmp/webdav/file.txt"

file, _ := os.Open(localFilePath)
defer file.Close()

c.WriteStream(webdavFilePath, file, 0644)

Get information about specified file/folder

webdavFilePath := "folder/subfolder/file.txt"

info := c.Stat(webdavFilePath)
//notice that [info] has os.FileInfo type
fmt.Println(info)

Move file to another location

oldPath := "folder/subfolder/file.txt"
newPath := "folder/subfolder/moved.txt"
isOverwrite := true

c.Rename(oldPath, newPath, isOverwrite)

Copy file to another location

oldPath := "folder/subfolder/file.txt"
newPath := "folder/subfolder/file-copy.txt"
isOverwrite := true

c.Copy(oldPath, newPath, isOverwrite)

Delete file

webdavFilePath := "folder/subfolder/file.txt"

c.Remove(webdavFilePath)

Links

More details about WebDAV server you can read from following resources:

NOTICE: RFC 2518 is obsoleted by RFC 4918 in June 2007

Contributing

All contributing are welcome. If you have any suggestions or find some bug - please create an Issue to let us make this project better. We appreciate your help!

License

This library is distributed under the BSD 3-Clause license found in the LICENSE file.

API

import "github.com/studio-b12/gowebdav"

Overview

Package gowebdav is a WebDAV client library with a command line tool included.

Index

Examples
Package files

basicAuth.go client.go digestAuth.go doc.go file.go netrc.go requests.go utils.go

func FixSlash

func FixSlash(s string) string

FixSlash appends a trailing / to our string

func FixSlashes

func FixSlashes(s string) string

FixSlashes appends and prepends a / if they are missing

func Join

func Join(path0 string, path1 string) string

Join joins two paths

func PathEscape

func PathEscape(path string) string

PathEscape escapes all segemnts of a given path

func ReadConfig

func ReadConfig(uri, netrc string) (string, string)

ReadConfig reads login and password configuration from ~/.netrc machine foo.com login username password 123456

func String

func String(r io.Reader) string

String pulls a string out of our io.Reader

type Authenticator

type Authenticator interface {
    Type() string
    User() string
    Pass() string
    Authorize(*http.Request, string, string)
}

Authenticator stub

type BasicAuth

type BasicAuth struct {
    // contains filtered or unexported fields
}

BasicAuth structure holds our credentials

func (*BasicAuth) Authorize

func (b *BasicAuth) Authorize(req *http.Request, method string, path string)

Authorize the current request

func (*BasicAuth) Pass

func (b *BasicAuth) Pass() string

Pass holds the BasicAuth password

func (*BasicAuth) Type

func (b *BasicAuth) Type() string

Type identifies the BasicAuthenticator

func (*BasicAuth) User

func (b *BasicAuth) User() string

User holds the BasicAuth username

type Client

type Client struct {
    // contains filtered or unexported fields
}

Client defines our structure

func NewClient

func NewClient(uri, user, pw string) *Client

NewClient creates a new instance of client

func (*Client) Connect

func (c *Client) Connect() error

Connect connects to our dav server

func (*Client) Copy

func (c *Client) Copy(oldpath, newpath string, overwrite bool) error

Copy copies a file from A to B

func (*Client) Mkdir

func (c *Client) Mkdir(path string, _ os.FileMode) error

Mkdir makes a directory

func (*Client) MkdirAll

func (c *Client) MkdirAll(path string, _ os.FileMode) error

MkdirAll like mkdir -p, but for webdav

func (*Client) Read

func (c *Client) Read(path string) ([]byte, error)

Read reads the contents of a remote file

func (*Client) ReadDir

func (c *Client) ReadDir(path string) ([]os.FileInfo, error)

ReadDir reads the contents of a remote directory

func (*Client) ReadStream

func (c *Client) ReadStream(path string) (io.ReadCloser, error)

ReadStream reads the stream for a given path

func (*Client) Remove

func (c *Client) Remove(path string) error

Remove removes a remote file

func (*Client) RemoveAll

func (c *Client) RemoveAll(path string) error

RemoveAll removes remote files

func (*Client) Rename

func (c *Client) Rename(oldpath, newpath string, overwrite bool) error

Rename moves a file from A to B

func (*Client) SetHeader

func (c *Client) SetHeader(key, value string)

SetHeader lets us set arbitrary headers for a given client

func (*Client) SetInterceptor

func (c *Client) SetInterceptor(interceptor func(method string, rq *http.Request))

SetInterceptor lets us set an arbitrary interceptor for a given client

func (*Client) SetTimeout

func (c *Client) SetTimeout(timeout time.Duration)

SetTimeout exposes the ability to set a time limit for requests

func (*Client) SetTransport

func (c *Client) SetTransport(transport http.RoundTripper)

SetTransport exposes the ability to define custom transports

func (*Client) Stat

func (c *Client) Stat(path string) (os.FileInfo, error)

Stat returns the file stats for a specified path

func (*Client) Write

func (c *Client) Write(path string, data []byte, _ os.FileMode) error

Write writes data to a given path

func (*Client) WriteStream

func (c *Client) WriteStream(path string, stream io.Reader, _ os.FileMode) error

WriteStream writes a stream

type DigestAuth

type DigestAuth struct {
    // contains filtered or unexported fields
}

DigestAuth structure holds our credentials

func (*DigestAuth) Authorize

func (d *DigestAuth) Authorize(req *http.Request, method string, path string)

Authorize the current request

func (*DigestAuth) Pass

func (d *DigestAuth) Pass() string

Pass holds the DigestAuth password

func (*DigestAuth) Type

func (d *DigestAuth) Type() string

Type identifies the DigestAuthenticator

func (*DigestAuth) User

func (d *DigestAuth) User() string

User holds the DigestAuth username

type File

type File struct {
    // contains filtered or unexported fields
}

File is our structure for a given file

func (File) ContentType

func (f File) ContentType() string

ContentType returns the content type of a file

func (File) ETag

func (f File) ETag() string

ETag returns the ETag of a file

func (File) IsDir

func (f File) IsDir() bool

IsDir let us see if a given file is a directory or not

func (File) ModTime

func (f File) ModTime() time.Time

ModTime returns the modified time of a file

func (File) Mode

func (f File) Mode() os.FileMode

Mode will return the mode of a given file

func (File) Name

func (f File) Name() string

Name returns the name of a file

func (File) Path

func (f File) Path() string

Path returns the full path of a file

func (File) Size

func (f File) Size() int64

Size returns the size of a file

func (File) String

func (f File) String() string

String lets us see file information

func (File) Sys

func (f File) Sys() interface{}

Sys ????

type NoAuth

type NoAuth struct {
    // contains filtered or unexported fields
}

NoAuth structure holds our credentials

func (*NoAuth) Authorize

func (n *NoAuth) Authorize(req *http.Request, method string, path string)

Authorize the current request

func (*NoAuth) Pass

func (n *NoAuth) Pass() string

Pass returns the current password

func (*NoAuth) Type

func (n *NoAuth) Type() string

Type identifies the authenticator

func (*NoAuth) User

func (n *NoAuth) User() string

User returns the current user


Generated by godoc2md

Comments
  • 401 Unauthorized when trying to login to Apache HTTPS Server

    401 Unauthorized when trying to login to Apache HTTPS Server

    Hello, studio-b12!

    I use following software: OS: Windows 10 x64 Golang: 1.10.1

    I trying to use your app to connect to my WebDav sever (Apache HTTPS Server), via following commands in cmd:

    set ROOT=mywebdav.lan
    set USER=user
    set PASSWORD=password
    
    gowebdav -X LS /user
    

    and expect to see my files and folders in /user folder:

    folder-1
    folder-2
    folder-3
    file.txt
    

    but instead I got following:

    ReadDir /data/: 401 Unauthorized - PROPFIND /data/
    

    My server works good and I have no problem to connect to it via Google Chrome.

    How can I fix this? Thanks in advance.

    opened by MrVine 11
  • File upload uploads files with 0 bytes - No error given

    File upload uploads files with 0 bytes - No error given

    Describe the bug I discovered a bug in the file upload that has some relation to #20. When uploading a file to Nextcloud, the command succeeds and exits without any error, but the uploaded file has a size of 0 bytes.

    Software

    • OS: Windows (also tested on Linux)
    • Golang: go1.14.6 windows/amd64
    • Version: current master

    To Reproduce

    1. checkout this project
    2. Setup your environment with the ROOT, USER and PASSWORD
    3. Pick a file you want to upload
    4. Upload the file using the command
    gowebdav -X PUT /webdav_test.png .\Chart_KAD.PNG
    
    Put: .\Chart_KAD.PNG -> /webdav_test.png`
    
    1. Look at the Nextcloud instance to see that the actual uploaded file-size is 0 bytes.

    image image

    Apache access log (nextcloud)

    80.187.108.153 - - [12/Aug/2020:12:32:26 +0200] "PUT /remote.php/dav/files/RAYs3T/webdav_test.png HTTP/1.1" 401 557
    80.187.108.153 - - [12/Aug/2020:12:32:27 +0200] "PUT /remote.php/dav/files/RAYs3T/webdav_test.png HTTP/1.1" 201 -
    

    Expected The file should upload completly or fail with an error

    Additional context I initially discovered this issue via PhotoPrism (which uses this client for the uploads). At least one other user is facing the same issue (https://github.com/photoprism/photoprism/issues/443).

    opened by RAYs3T 9
  • Apache HTTPS Server does not return `Dav` or `DAV` header on `OPTIONS` request

    Apache HTTPS Server does not return `Dav` or `DAV` header on `OPTIONS` request

    Here is the current version of Connect function:

    // Connect connects to our dav server
    func (c *Client) Connect() error {
    	rs, err := c.options("/")
    	if err != nil {
    		return err
    	}
    
    	err = rs.Body.Close()
    	if err != nil {
    		return err
    	}
    
    	if rs.StatusCode != 200 || (rs.Header.Get("Dav") == "" && rs.Header.Get("DAV") == "") {
    		return newPathError("Connect", c.root, rs.StatusCode)
    	}
    
    	return nil
    }
    

    but Apache HTTPS Server does not return Dav or DAV header, so application returns following message on any request:

    Failed to connect due to: Connect https://mywebdav.me/: 200
    

    Is it bug in library, or wrong settings of Apache HTTPS Server?

    question 
    opened by MrVine 9
  • panic due to concurrent map writes

    panic due to concurrent map writes

    I'm sometimes getting this panic when running multiple requests in parallel using a single webdav client:

    	/usr/local/Cellar/go/1.14.6/libexec/src/runtime/panic.go:1116 +0x72 fp=0xc0001d9448 sp=0xc0001d9418 pc=0x10351d2
    runtime.mapassign_faststr(0x1c15400, 0xc00001f0b0, 0x1ce5cd4, 0xd, 0x64)
    	/usr/local/Cellar/go/1.14.6/libexec/src/runtime/map_faststr.go:291 +0x3de fp=0xc0001d94b0 sp=0xc0001d9448 pc=0x101529e
    net/textproto.MIMEHeader.Set(...)
    	/usr/local/Cellar/go/1.14.6/libexec/src/net/textproto/header.go:22
    net/http.Header.Set(...)
    	/usr/local/Cellar/go/1.14.6/libexec/src/net/http/header.go:37
    github.com/studio-b12/gowebdav.(*BasicAuth).Authorize(0xc0005585c0, 0xc00001f0e0, 0x1ce1500, 0x8, 0x1cdc5ce, 0x1)
    	/Users/jarek/go/pkg/mod/github.com/studio-b12/[email protected]/basicAuth.go:32 +0x1d0 fp=0xc0001d9578 sp=0xc0001d94b0 pc=0x18d1c80
    github.com/studio-b12/gowebdav.(*Client).req(0xc00001f0e0, 0x1ce1500, 0x8, 0x1cdc5ce, 0x1, 0x1e96e40, 0xc0002807a0, 0xc0001d97a0, 0x0, 0x0, ...)
    	/Users/jarek/go/pkg/mod/github.com/studio-b12/[email protected]/requests.go:28 +0x283 fp=0xc0001d9740 sp=0xc0001d9578 pc=0x18d6353
    github.com/studio-b12/gowebdav.(*Client).propfind(0xc00001f0e0, 0x1cdc5ce, 0x1, 0x26cdc00, 0x1d1eebf, 0xcb, 0x1acf280, 0xc00028d2f0, 0xc0001d98b0, 0x0, ...)
    	/Users/jarek/go/pkg/mod/github.com/studio-b12/[email protected]/requests.go:93 +0x114 fp=0xc0001d9808 sp=0xc0001d9740 pc=0x18d7544
    github.com/studio-b12/gowebdav.(*Client).ReadDir(0xc00001f0e0, 0x1cdc5ce, 0x1, 0x1cdc5ce, 0x1, 0x1cdc5ce, 0x1, 0xc0002a8520)
    	/Users/jarek/go/pkg/mod/github.com/studio-b12/[email protected]/client.go:164 +0x1a6 fp=0xc0001d98e8 sp=0xc0001d9808 pc=0x18d2476
    

    Should parallel calls using single webdav client be supported?

    probably bug 
    opened by jkowalski 8
  • gowebdav library should not hush up errors

    gowebdav library should not hush up errors

    If we carefully see on following methods:

    • client.mkcol(), client.Mkdir(), client.MkdirAll()
    • client.copymove(), client.doCopyMove()
    • client.Write() and client.WriteStream()
    • it suppress real errors and returns only status code in the best case.

    I propose to change this behaviour to allow user of our library to get original error message.

    enhancement 
    opened by MrVine 6
  • Creating NewClient with Proxy

    Creating NewClient with Proxy

    Hello,

    I have used this module in one of my projects and I was missing functionality for defining proxy server directly from program. In my case I am not able to use env variables HTTP/HTTPS_PROXY. This new function NewClientProxy() solves that issue.

    opened by hrncacz 3
  • Return actual status codes instead of 400

    Return actual status codes instead of 400

    Status codes sometimes get rewritten to simply 400. This happens e.g. when on a Write when there is an authentication issue that should actually return code 401.

    With this PR the actual status codes are propagated.

    opened by fischor 3
  • Error on Folders with '+'

    Error on Folders with '+'

    Hello Collaborators,

    Describe the bug

    When we want to Propfind a folder which contains folders with "+", the call fails. Due to the QueryUnescape at https://github.com/studio-b12/gowebdav/blob/master/client.go#L141 the "+" results in a wrong foldername. in this case "lost+found" becomes "lost found".

    Not sure what the purpose of QueryUnescape is, but once we remove it, it works just fine.

    Software

    • OS: linux
    • Golang: 1.15
    • Version: latest master

    To Reproduce

    1. Create a folder 'lost+found' in a folder 'a'
    2. Propfind folder 'a'
    3. Propfind returns 'lost found' as the folder name

    Expected Expected the folder name "lost+found" to stay that way

    opened by needsaholiday 3
  • Www-Authenticate header's token should be treated as case-insensitive

    Www-Authenticate header's token should be treated as case-insensitive

    Hi,

    I've run into trouble using this library with a server that returns a Www-Authenticate header of "BASIC" instead of "Basic". This is allowed as per https://tools.ietf.org/html/rfc2617, but the check on Lines 46 and 48 of requests.go does a case-sensitive check against 'Digest' or 'Basic'.

    Can this be changed to a case-insensitive match like strings.Index(strings.ToLower(rs.Header.Get("Www-Authenticate")), "digest")?

    Is there any workaround? I can't see any way to manually specify the authentication method.

    Regards,

    Wade

    opened by wade-tattersall 3
  • nil pointer exception in req() method

    nil pointer exception in req() method

    Software

    OS: Windows 10 x64 GO: go1.9.2 windows/amd64 gowebdav: latest version from master

    go env

    set GOARCH=386
    set GOBIN=
    set GOEXE=.exe
    set GOHOSTARCH=amd64
    set GOHOSTOS=windows
    set GOOS=windows
    set GOPATH=D:\Go\
    set GORACE=
    set GOROOT=D:\Programs\Go
    set GOTOOLDIR=D:\Programs\Go\pkg\tool\windows_amd64
    set GCCGO=gccgo
    set GO386=sse2
    set CC=gcc
    set GOGCCFLAGS=-m32 -fmessage-length=0 -fdebug-prefix-map=C:\Users\PC\AppData\Local\Temp\go-build886287271=/tmp/go-build -gno-record-gcc-switches
    set CXX=g++
    set CGO_ENABLED=0
    set CGO_CFLAGS=-g -O2
    set CGO_CPPFLAGS=
    set CGO_CXXFLAGS=-g -O2
    set CGO_FFLAGS=-g -O2
    set CGO_LDFLAGS=-g -O2
    set PKG_CONFIG=pkg-config
    

    Issue description

    We use http.Do() method inside c.req(), and do not check for errors returned. So, it is possible to receive nil response, and got nil pointer dereference panic in line 42. http.Do() method will return non-nil error in following cases:

    1. Request is nil
    2. Response missing Location header
    3. Client failed to parse Location header
    4. Method "request.GetBody()" returns error
    5. Http.Client.Send() returns error
    6. Client timeout was exceeded In all cases except of (5) this method returns nil as a poiner to http.Response, which will force app to panic later.

    Conclusion

    Taking into account the above mentioned, we need to perform error-checking for http.Do() method results.

    opened by MrVine 3
  • % and # in the URL

    % and # in the URL

    The package did not work with files and folders in the name of which there are the characters '#' and '%'. Correction checked in work with webdav from cloud.mail.ru

    opened by artem1996 3
  • [err:WriteStream] file write problem: err:WriteStream

    [err:WriteStream] file write problem: err:WriteStream

    Hello Collaborators,

    Describe the bug A short description of what you think the bug is.

    Software

    • OS: win 11
    • Golang: 1.18.1
    • Version: 8

    To Reproduce

    1. Go to '...'
    2. Click on '....'
    3. Scroll down to '....'
    4. See error

    Expected A short description of what you expected to happen.

    err:WriteStream xx: 500
    

    Additional context Add any other context about the problem here.

    server is on WSL2 of docker, images is derkades/webdav.
    I try to upload a image file
    

    dav_error

    opened by cffycls 0
  • support Walk func

    support Walk func

    Webdav does not require the server to support recursive traversal, but recursive traversal is commonly used in some scenarios, so we should support the walk function for users. just like filepath.Walk()

    opened by zhijian-pro 0
  • feat: stat returns fs.errnotexist on 404

    feat: stat returns fs.errnotexist on 404

    Client.Stat was not returning a proper Go err for not found files, the ideal way to check this is using errors.Is(err, fs.ErrNotExist) but the client was returning a generic error.

    I've updated the propfind to take 404 errors into account, retuning the above error making easier to evaluate that kind of situations.

    opened by fmartingr 2
  • Prevent body from being closed by the Request RoundTripper

    Prevent body from being closed by the Request RoundTripper

    Because the Request RoundTripper closes passed body streams if they implement io.Close, seeking the body on re-trying the request after an authentication error fails because the body stream is already closed.

    This implementation prevents the RoudTripper from closing the body so we can postpone the closing until we are done with the body stream.

    opened by zekroTJA 2
  • Set GetBody on http.Request

    Set GetBody on http.Request

    A user of Kopia reported this error in https://github.com/kopia/kopia/issues/1509:

    Propfind "https://myhost.mydomain.com/kopia/p5f/7be/": http2: Transport: cannot retry err [http2: Transport received Serv
    er's graceful shutdown GOAWAY] after Request.Body was written; define Request.GetBody to avoid this error
    

    There appears to be an issue with some proxy servers that force connection closure after receiving the request and Go cannot automatically retry the request unless http.Request.GetBody is set.

    Could this be added in gowebdav ?

    opened by jkowalski 2
Owner
Studio-B12
Studio-B12
An open-source GitLab command line tool bringing GitLab's cool features to your command line

GLab is an open source GitLab CLI tool bringing GitLab to your terminal next to where you are already working with git and your code without switching

Clement Sam 2.1k Sep 22, 2022
A command line tool to prompt for a value to be included in another command line.

readval is a command line tool which is designed for one specific purpose—to prompt for a value to be included in another command line. readval prints

Venky 0 Dec 22, 2021
git-xargs is a command-line tool (CLI) for making updates across multiple Github repositories with a single command.

Table of contents Introduction Reference Contributing Introduction Overview git-xargs is a command-line tool (CLI) for making updates across multiple

Gruntwork 644 Sep 6, 2022
git-xargs is a command-line tool (CLI) for making updates across multiple GitHub repositories with a single command

git-xargs is a command-line tool (CLI) for making updates across multiple GitHub repositories with a single command. You give git-xargs:

Maxar Infrastructure 1 Feb 5, 2022
A command line tool for simplified docker volume command built with go

dockervol A command line tool for simplified docker volume command built with go. Features: Remove anonymous volume (beta) Remove volume by matching n

Moh Achun Armando 0 Dec 18, 2021
Command-line tool to customize the official Spotify client. Supports Windows, MacOS and Linux.

Command-line tool to customize the official Spotify client. Supports Windows, MacOS and Linux. Features Change colors whole UI Inject CSS for advanced

null 12.3k Sep 20, 2022
Command line tool for processing client transaction data in CSV format

command line tool for processing client transaction data in CSV format. thank you for looking! build instructions: $ git clone https://github.com/lnit

Luke Nittmann 0 Oct 29, 2021
fofax is a fofa query tool written in go, positioned as a command-line tool and characterized by simplicity and speed.

fofaX 0x00 Introduction fofax is a fofa query tool written in go, positioned as

null 461 Sep 24, 2022
A command-line tool and library for generating regular expressions from user-provided test cases

Table of Contents What does this tool do? Do I still need to learn to write regexes then? Current features How to install? 4.1 The command-line tool 4

Peter M. Stahl 5.6k Sep 25, 2022
A Go library and a command-line tool to manage Docker Swarm clusters

go-swarm go-swarm is a Go library and command-line tool for managing the creation and maintenance of Docker Swarm cluster. Features: Creates new Swarm

AUCloud 3 Jul 26, 2022
Library and command line tool for reading xDSL stats

xDSL Stats Parser This is a library and command line tool for reading basic xDSL stats, as well bitloading, SNR, QLN and Hlog data. It supports many d

Jan Hoffmann 10 Sep 14, 2022
Watcher - A simple command line app to watch files in a directory for changes and run a command when files change!

Watcher - Develop your programs easily Watcher watches all the files present in the directory it is run from of the directory that is specified while

Geet Sethi 1 Mar 27, 2022
Go package to make lightweight ASCII line graph ╭┈╯ in command line apps with no other dependencies.

asciigraph Go package to make lightweight ASCII line graphs ╭┈╯. Installation go get github.com/guptarohit/asciigraph Usage Basic graph package main

Rohit Gupta 2k Sep 22, 2022
Basic command line example using golang grpc client tools

This is a basic command line interface that demonstrates using the golang GRPC API. The API is reused between dishy and the wifi router, however both

null 37 Aug 15, 2022
Command -line graphql client written in golang

go-graphql-cli Command -line graphql client written in golang.

miko 2 Nov 24, 2021
Package command provide simple API to create modern command-line interface

Package command Package command provide simple API to create modern command-line interface, mainly for lightweight usage, inspired by cobra Usage pack

chenen 0 Jan 16, 2022
Golang library with POSIX-compliant command-line UI (CLI) and Hierarchical-configuration. Better substitute for stdlib flag.

cmdr cmdr is a POSIX-compliant, command-line UI (CLI) library in Golang. It is a getopt-like parser of command-line options, be compatible with the ge

hz 112 Aug 14, 2022
Gomphotherium (/ˌɡɒmfəˈθɪəriəm/; "welded beast"), a command line Mastodon client.

Gomphotherium Gomphotherium (/ˌɡɒmfəˈθɪəriəm/; "welded beast"), a command line Mastodon client. Description Gomphotherium is a Mastodon client for the

マリウス 35 Aug 29, 2022
Canard. A command line TUI client for the journalist RSS aggregator.

Canard Canard. A command line TUI client for the Journalist RSS aggregator. Installation Download a binary from the releases page. Or build it yoursel

マリウス 94 Sep 2, 2022