github.com/AlekSi/nut@v0.3.1-0.20130607203728-cce108d4135e/nut/get.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"go/build"
     8  	"io/ioutil"
     9  	"log"
    10  	"net"
    11  	"net/http"
    12  	"net/url"
    13  	"path/filepath"
    14  	"sort"
    15  	"strings"
    16  
    17  	. "github.com/AlekSi/nut"
    18  )
    19  
    20  var (
    21  	cmdGet = &Command{
    22  		Run:       runGet,
    23  		UsageLine: "get [-p prefix] [-v] [name, import path or URL]",
    24  		Short:     "download and install nut and dependencies",
    25  	}
    26  
    27  	getP string
    28  	getV bool
    29  )
    30  
    31  func init() {
    32  	cmdGet.Long = `
    33  Downloads and installs nut and dependencies from http://gonuts.io/ or specified URL.
    34  
    35  Examples:
    36      nut install aleksi/nut
    37      nut install aleksi/nut/0.2.0
    38      nut install gonuts.io/aleksi/nut
    39      nut install gonuts.io/aleksi/nut/0.2.0
    40      nut install http://gonuts.io/aleksi/nut
    41      nut install http://gonuts.io/aleksi/nut/0.2.0
    42  `
    43  
    44  	cmdGet.Flag.StringVar(&getP, "p", "", "install prefix in workspace, uses hostname from URL if omitted")
    45  	cmdGet.Flag.BoolVar(&getV, "v", false, vHelp)
    46  }
    47  
    48  // Parse argument, return URL to get nut from and install prefix.
    49  func ParseArg(s string) (u *url.URL, prefix string) {
    50  	var p []string
    51  	var host string
    52  	var ok bool
    53  
    54  	// full URL - as is
    55  	if strings.HasPrefix(s, "http://") || strings.HasPrefix(s, "https://") {
    56  		goto parse
    57  	}
    58  
    59  	p = strings.Split(s, "/")
    60  	if len(p) > 0 {
    61  		prefix = p[0]
    62  		host, ok = NutImportPrefixes[prefix]
    63  	}
    64  	if ok {
    65  		// import path style
    66  		p[0] = "http://" + host
    67  		s = strings.Join(p, "/")
    68  	} else {
    69  		// short style
    70  		prefix = "gonuts.io"
    71  		host = NutImportPrefixes[prefix]
    72  		s = fmt.Sprintf("http://%s/%s", host, s)
    73  	}
    74  
    75  parse:
    76  	u, err := url.Parse(s)
    77  	FatalIfErr(err)
    78  	if prefix == "" {
    79  		prefix = u.Host
    80  		if strings.Contains(prefix, ":") {
    81  			prefix, _, err = net.SplitHostPort(prefix)
    82  			FatalIfErr(err)
    83  		}
    84  		if strings.HasPrefix(prefix, "www.") {
    85  			prefix = prefix[4:]
    86  		}
    87  	}
    88  
    89  	return
    90  }
    91  
    92  func get(url *url.URL) (b []byte, err error) {
    93  	if getV {
    94  		log.Printf("Getting %s ...", url)
    95  	}
    96  
    97  	req, err := http.NewRequest("GET", url.String(), nil)
    98  	if err != nil {
    99  		return
   100  	}
   101  	req.Header.Set("User-Agent", "nut getter")
   102  	req.Header.Set("Accept", "application/zip")
   103  
   104  	res, err := http.DefaultClient.Do(req)
   105  	if err != nil {
   106  		return
   107  	}
   108  	defer res.Body.Close()
   109  
   110  	b, err = ioutil.ReadAll(res.Body)
   111  	if err != nil {
   112  		return
   113  	}
   114  
   115  	if res.StatusCode/100 != 2 {
   116  		err = fmt.Errorf("Status code %d", res.StatusCode)
   117  		return
   118  	}
   119  
   120  	if getV {
   121  		log.Printf("Status code %d", res.StatusCode)
   122  	}
   123  
   124  	return
   125  }
   126  
   127  func runGet(cmd *Command) {
   128  	if !getV {
   129  		getV = Config.V
   130  	}
   131  
   132  	args := cmd.Flag.Args()
   133  
   134  	// zero arguments is a special case – install dependencies for package in current directory
   135  	if len(args) == 0 {
   136  		pack, err := build.ImportDir(".", 0)
   137  		FatalIfErr(err)
   138  		args = NutImports(pack.Imports)
   139  		if getV && len(args) != 0 {
   140  			log.Printf("%s depends on nuts: %s", pack.Name, strings.Join(args, ","))
   141  		}
   142  	}
   143  
   144  	urlsToPaths := make(map[string]string, len(args))
   145  	for len(args) != 0 {
   146  		arg := args[0]
   147  		args = args[1:]
   148  
   149  		url, prefix := ParseArg(arg)
   150  
   151  		// do not download twice
   152  		_, present := urlsToPaths[url.String()]
   153  		if present {
   154  			continue
   155  		}
   156  
   157  		b, err := get(url)
   158  		if err != nil {
   159  			log.Print(err)
   160  
   161  			var body map[string]interface{}
   162  			err = json.Unmarshal(b, &body)
   163  			if err != nil {
   164  				log.Print(err)
   165  			}
   166  			m, ok := body["Message"]
   167  			if ok {
   168  				log.Fatalf("%s", m)
   169  			} else {
   170  				log.Fatalf("Response: %#q", body)
   171  			}
   172  		}
   173  
   174  		nf := new(NutFile)
   175  		_, err = nf.ReadFrom(bytes.NewReader(b))
   176  		FatalIfErr(err)
   177  		deps := NutImports(nf.Imports)
   178  		if getV && len(deps) != 0 {
   179  			log.Printf("%s depends on nuts: %s", nf.Name, strings.Join(deps, ", "))
   180  		}
   181  		args = append(args, deps...)
   182  
   183  		p := getP
   184  		if p == "" {
   185  			p = prefix
   186  		}
   187  		fileName := WriteNut(b, p, getV)
   188  		path := nf.ImportPath(p)
   189  		UnpackNut(fileName, filepath.Join(SrcDir, path), true, getV)
   190  		urlsToPaths[url.String()] = path
   191  	}
   192  
   193  	// install in lexical order (useful in integration tests)
   194  	paths := make([]string, 0, len(urlsToPaths))
   195  	for _, path := range urlsToPaths {
   196  		paths = append(paths, path)
   197  	}
   198  	sort.Strings(paths)
   199  	for _, path := range paths {
   200  		InstallPackage(path, getV)
   201  	}
   202  }