├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── cmd └── gitio │ └── main.go └── gitio.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright © 2015 Maxim Kupriianov 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the “Software”), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TAG ?= v1 2 | 3 | all: 4 | # noop 5 | release_osx: 6 | go build github.com/xlab/gitio/cmd/gitio 7 | zip -9 gitio_$(TAG)_osx.zip gitio 8 | rm gitio 9 | release_linux: 10 | gobldock github.com/xlab/gitio/cmd/gitio 11 | zip -9 gitio_$(TAG)_linux.zip gitio 12 | rm gitio 13 | release: release_osx release_linux 14 | build: 15 | go build github.com/xlab/gitio/cmd/gitio 16 | clean: 17 | rm -f gitio 18 | rm -f gitio_$(TAG)_linux.zip gitio_$(TAG)_osx.zip 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## gitio [![GoDoc](https://godoc.org/github.com/xlab/gitio?status.svg)](https://godoc.org/github.com/xlab/gitio) 2 | 3 | This is a Go client for [git.io](https://git.io). 4 | 5 | Read more about git.io [here](https://github.com/blog/985-git-io-github-url-shortener). 6 | 7 | ### Installation 8 | ``` 9 | $ go get github.com/xlab/gitio/... 10 | ``` 11 | Or grab a binary in the [Releases](https://github.com/xlab/gitio/releases) section. 12 | 13 | ### Usage 14 | ``` 15 | $ gitio -h 16 | Usage: gitio 17 | -c, --code="" A custom code for the short link, e.g. https://git.io/mycode 18 | -f, --force=false Try to shorten link even if the custom code has been used previously. 19 | 20 | $ gitio -c gitio.go https://github.com/xlab/gitio/blob/master/gitio.go 21 | https://git.io/gitio.go 22 | ``` 23 | 24 | ### License 25 | 26 | [MIT](http://xlab.mit-license.org) 27 | -------------------------------------------------------------------------------- /cmd/gitio/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/url" 6 | "os" 7 | 8 | "github.com/xlab/gitio" 9 | "gopkg.in/mflag.v1" 10 | ) 11 | 12 | var ( 13 | longURL string 14 | codeOpt string 15 | forceOpt bool 16 | ) 17 | 18 | func init() { 19 | mflag.StringVar(&codeOpt, []string{"c", "-code"}, "", "A custom code for the short link, e.g. https://git.io/mycode") 20 | mflag.BoolVar(&forceOpt, []string{"f", "-force"}, false, "Try to shorten link even if the custom code has been used previously.") 21 | 22 | mflag.Usage = func() { 23 | fmt.Fprintf(os.Stderr, "Usage: %s \n", os.Args[0]) 24 | mflag.PrintDefaults() 25 | } 26 | mflag.Parse() 27 | if longURL = mflag.Arg(0); len(longURL) == 0 { 28 | mflag.Usage() 29 | return 30 | } 31 | } 32 | 33 | func main() { 34 | if len(codeOpt) > 0 && !forceOpt { 35 | taken, err := gitio.CheckTaken(codeOpt) 36 | if err != nil { 37 | fmt.Printf("error: %v\n", err) 38 | os.Exit(1) 39 | } 40 | if taken { 41 | fmt.Printf("warning: custom code \"%s\" has already been taken.\n", codeOpt) 42 | fmt.Println("tip: use the --force flag to suppress this check.") 43 | os.Exit(1) 44 | } 45 | } 46 | if u, err := url.Parse(longURL); err != nil { 47 | fmt.Printf("error: %v\n", err) 48 | os.Exit(1) 49 | } else if u.Scheme != "https" { 50 | fmt.Println("error: only HTTPS is supported, git.io will refuse HTTP links") 51 | os.Exit(1) 52 | } 53 | shortURL, err := gitio.Shorten(longURL, codeOpt) 54 | if err != nil { 55 | fmt.Printf("error: %v\n", err) 56 | os.Exit(1) 57 | } 58 | fmt.Println(shortURL) 59 | } 60 | -------------------------------------------------------------------------------- /gitio.go: -------------------------------------------------------------------------------- 1 | // Package gitio is a client for https://git.io URL shortener. 2 | package gitio 3 | 4 | import ( 5 | "errors" 6 | "fmt" 7 | "io/ioutil" 8 | "net/http" 9 | "net/url" 10 | "path/filepath" 11 | ) 12 | 13 | const ( 14 | gitioPostAPI = "https://git.io/create" 15 | gitioPutAPI = "https://git.io" 16 | gitioGetAPI = "https://git.io" 17 | ) 18 | 19 | const gitioAPI = "https://git.io/create" 20 | 21 | // Shorten returns a short version of an URL, or an error otherwise. 22 | // Please note that it's not guaranteed the code will be accepted by git.io, 23 | // the random one may be used instead. 24 | func Shorten(longURL, code string) (shortURL *url.URL, err error) { 25 | if len(longURL) == 0 { 26 | return nil, errors.New("no URL provided") 27 | } 28 | 29 | form := make(url.Values) 30 | form.Add("url", longURL) 31 | 32 | var api string 33 | if len(code) > 0 { 34 | form.Add("code", code) 35 | api = gitioPutAPI 36 | } else { 37 | api = gitioPostAPI 38 | } 39 | 40 | resp, err := http.PostForm(api, form) 41 | if err != nil { 42 | return nil, err 43 | } 44 | defer resp.Body.Close() 45 | 46 | switch resp.StatusCode { 47 | case http.StatusCreated: // for PUT 48 | return resp.Location() 49 | case http.StatusOK: // for POST 50 | randomCode, err := ioutil.ReadAll(resp.Body) 51 | if err != nil { 52 | return nil, err 53 | } else if len(randomCode) == 0 { 54 | return nil, errors.New("unknown error") 55 | } 56 | u, _ := url.Parse(gitioGetAPI) 57 | u.Path = filepath.Join(u.Path, string(randomCode)) 58 | return u, nil 59 | case http.StatusInternalServerError, 422: 60 | return nil, errors.New("only GitHub/Gist links are accepted") 61 | default: 62 | return nil, fmt.Errorf("bad status: %s", resp.Status) 63 | } 64 | } 65 | 66 | // CheckTaken checks if the provided custom code has already been taken on git.io. 67 | func CheckTaken(code string) (bool, error) { 68 | if len(code) == 0 { 69 | return false, errors.New("no code provided") 70 | } 71 | u, _ := url.Parse(gitioGetAPI) 72 | u.Path = filepath.Join(u.Path, code) 73 | resp, err := http.Get(u.String()) 74 | if err != nil { 75 | return false, err 76 | } 77 | defer resp.Body.Close() 78 | switch resp.StatusCode { 79 | case http.StatusNotFound: 80 | if resp.Request.URL.String() != u.String() { 81 | // was redirected -> found 82 | return true, nil 83 | } 84 | return false, nil 85 | case http.StatusFound, http.StatusOK: 86 | return true, nil 87 | default: 88 | // probably it's taken 89 | return true, nil 90 | } 91 | } 92 | --------------------------------------------------------------------------------