fetcher/fetcher_github.go
func (h *Github) Fetch() (io.Reader, error) {
	//delay fetches after first
	if h.delay {
		time.Sleep(h.Interval)
	}
	h.delay = true
	//check release status
	resp, err := http.Get(h.releaseURL)
	if err != nil {
		return nil, fmt.Errorf("release info request failed (%s)", err)
	}
	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return nil, fmt.Errorf("release info request failed (status code %d)", resp.StatusCode)
	}
	//clear assets
	h.latestRelease.Assets = nil
	if err := json.NewDecoder(resp.Body).Decode(&h.latestRelease); err != nil {
		return nil, fmt.Errorf("invalid request info (%s)", err)
	}
	resp.Body.Close()
	//find appropriate asset
	assetURL := ""
	for _, a := range h.latestRelease.Assets {
		if h.Asset(a.Name) {
			assetURL = a.URL
			break
		}
	}
	if assetURL == "" {
		return nil, fmt.Errorf("no matching assets in this release (%s)", h.latestRelease.TagName)
	}
	//fetch location
	req, _ := http.NewRequest("HEAD", assetURL, nil)
	resp, err = http.DefaultTransport.RoundTrip(req)
	if err != nil {
		return nil, fmt.Errorf("release location request failed (%s)", err)
	}
	resp.Body.Close()
	if resp.StatusCode != http.StatusFound {
		return nil, fmt.Errorf("release location request failed (status code %d)", resp.StatusCode)
	}
	s3URL := resp.Header.Get("Location")
	//pseudo-HEAD request
	req, err = http.NewRequest("GET", s3URL, nil)
	if err != nil {
		return nil, fmt.Errorf("release location url error (%s)", err)
	}
	req.Header.Set("Range", "bytes=0-0") // HEAD not allowed so we request for 1 byte
	resp, err = http.DefaultTransport.RoundTrip(req)
	if err != nil {
		return nil, fmt.Errorf("release location request failed (%s)", err)
	}
	resp.Body.Close()
	if resp.StatusCode != http.StatusPartialContent {
		return nil, fmt.Errorf("release location request failed (status code %d)", resp.StatusCode)
	}
	etag := resp.Header.Get("ETag")
	if etag != "" && h.lastETag == etag {
		return nil, nil //skip, hash match
	}
	//get binary request
	resp, err = http.Get(s3URL)
	if err != nil {
		return nil, fmt.Errorf("release binary request failed (%s)", err)
	}
	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return nil, fmt.Errorf("release binary request failed (status code %d)", resp.StatusCode)
	}
	h.lastETag = etag
	//success!
	//extract gz files
	if strings.HasSuffix(assetURL, ".gz") && resp.Header.Get("Content-Encoding") != "gzip" {
		return gzip.NewReader(resp.Body)
	}
	return resp.Body, nil
}
func (h *Github) Fetch() (io.Reader, error) {
	//delay fetches after first
	if h.delay {
		time.Sleep(h.Interval)
	}
	h.delay = true
	//check release status
	resp, err := http.Get(h.releaseURL)
	if err != nil {
		return nil, fmt.Errorf("release info request failed (%s)", err)
	}
	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return nil, fmt.Errorf("release info request failed (status code %d)", resp.StatusCode)
	}
	//clear assets
	h.latestRelease.Assets = nil
	if err := json.NewDecoder(resp.Body).Decode(&h.latestRelease); err != nil {
		return nil, fmt.Errorf("invalid request info (%s)", err)
	}
	resp.Body.Close()
	//find appropriate asset
	assetURL := ""
	for _, a := range h.latestRelease.Assets {
		if h.Asset(a.Name) {
			assetURL = a.URL
			break
		}
	}
	if assetURL == "" {
		return nil, fmt.Errorf("no matching assets in this release (%s)", h.latestRelease.TagName)
	}
	//fetch location
	req, _ := http.NewRequest("HEAD", assetURL, nil)
	resp, err = http.DefaultTransport.RoundTrip(req)
	if err != nil {
		return nil, fmt.Errorf("release location request failed (%s)", err)
	}
	resp.Body.Close()
	if resp.StatusCode != http.StatusFound {
		return nil, fmt.Errorf("release location request failed (status code %d)", resp.StatusCode)
	}
	s3URL := resp.Header.Get("Location")
	//pseudo-HEAD request
	req, err = http.NewRequest("GET", s3URL, nil)
	if err != nil {
		return nil, fmt.Errorf("release location url error (%s)", err)
	}
	req.Header.Set("Range", "bytes=0-0") // HEAD not allowed so we request for 1 byte
	resp, err = http.DefaultTransport.RoundTrip(req)
	if err != nil {
		return nil, fmt.Errorf("release location request failed (%s)", err)
	}
	resp.Body.Close()
	if resp.StatusCode != http.StatusPartialContent {
		return nil, fmt.Errorf("release location request failed (status code %d)", resp.StatusCode)
	}
	etag := resp.Header.Get("ETag")
	if etag != "" && h.lastETag == etag {
		return nil, nil //skip, hash match
	}
	//get binary request
	resp, err = http.Get(s3URL)
	if err != nil {
		return nil, fmt.Errorf("release binary request failed (%s)", err)
	}
	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return nil, fmt.Errorf("release binary request failed (status code %d)", resp.StatusCode)
	}
	h.lastETag = etag
	//success!
	//extract gz files
	if strings.HasSuffix(assetURL, ".gz") && resp.Header.Get("Content-Encoding") != "gzip" {
		return gzip.NewReader(resp.Body)
	}
	return resp.Body, nil
}
// Fetch the binary from the provided Repository