[
  {
    "path": ".gitignore",
    "content": "*.test\nbin\nlatest/pkg/\n.envrc\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "## 0.1.1 (2015-04-14)\n\nAdd `latest` command\n\n### Added\n\n- `DeleveFrontV()` function to delete font `v` charactor\n- `latest` command in `cmd` diretory, to check version is latest or not from command line\n\n### Deprecated\n\n- Nothing\n\n### Removed\n\n- Nothing\n\n### Fixed\n\n- More documentation\n\n\n## 0.1.0 (2015-04-07)\n\nInitial release\n\n### Added\n\n- Fundamental features\n\n### Deprecated\n\n- Nothing\n\n### Removed\n\n- Nothing\n\n### Fixed\n\n- Nothing\n\n\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2015 Taichi Nakashima\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE."
  },
  {
    "path": "README.md",
    "content": "go-latest \n====\n\n[![GitHub release](http://img.shields.io/github/release/tcnksm/go-latest.svg?style=flat-square)][release]\n[![Wercker](http://img.shields.io/wercker/ci/551e58c16b7badb977000128.svg?style=flat-square)][wercker]\n[![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)][license]\n[![Go Documentation](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)][godocs]\n\n[release]: https://github.com/tcnksm/go-latest/releases\n[wercker]: https://app.wercker.com/project/bykey/1059e8b0cf3bde5fc220477d39a1bf0e\n[license]: https://github.com/tcnksm/go-latest/blob/master/LICENSE\n[godocs]: http://godoc.org/github.com/tcnksm/go-latest\n\n\n`go-latest` is a package to check a provided version is latest or not from various sources.\n\nOnce you distribute your tool by golang and user start to use it, it's difficult to tell users that new version is released and encourage them to use new one. `go-latest` enables you to do that by just preparing simple source. For sources, currently you can use tags on Github, [HTML meta tag](doc/html_meta.md) (HTML scraping) and JSON response. \n\nSee more details in document at [https://godoc.org/github.com/tcnksm/go-latest](https://godoc.org/github.com/tcnksm/go-latest).\n\n## Install\n\nTo install, use `go get`:\n\n```bash\n$ go get -d github.com/tcnksm/go-latest\n```\n\n## Usage\n\nFor sources to check, currently you can use tags on Github, [HTML meta tag](doc/html_meta.md) (HTML scraping) and JSON response. \n\n### Github Tag\n\nTo check `0.1.0` is the latest in tags on GitHub.\n\n```golang\ngithubTag := &latest.GithubTag{\n    Owner: \"username\",\n    Repository: \"reponame\",\n}\n\nres, _ := latest.Check(githubTag, \"0.1.0\")\nif res.Outdated {\n    fmt.Printf(\"0.1.0 is not latest, you should upgrade to %s\", res.Current)\n}\n```\n\n`go-latest` uses [Semantic Versioning](http://semver.org/) to compare versions. If tagging name strategy on GitHub is different from it, you need to fix it with `FixVersionStrFunc`. For example, if you add `v` charactor in the begining of version string like `v0.1.0`, you need to transform it to `0.1.0`, you can use `DeleteFrontV()` function like below,  \n\n```golang\ngithubTag := &latest.GithubTag{\n    Owner:             \"username\",\n    Repository:        \"reponame\",\n    FixVersionStrFunc: latest.DeleteFrontV(),\n}\n```\n\nYou can define your own `FixVersionStrFunc`. See more on [https://godoc.org/github.com/tcnksm/go-latest](https://godoc.org/github.com/tcnksm/go-latest)\n\n### HTML meta tag\n\nYou can use simple HTTP+HTML meta tag for a checking source.\n\nFor example, if you have a tool named `reduce-worker` and want to check `0.1.0` is latest or not, prepare HTML page which includes following meta tag, \n\n```html\n<meta name=\"go-latest\" content=\"reduce-worker 0.1.1 New version include security update\">\n```\n\nAnd make request,\n\n```golang\nhtml := &latest.HTMLMeta{\n    URL: \"http://example.com/info\",\n    Name: \"reduce-worker\",\n}\n\nres, _ := latest.Check(html, \"0.1.0\")\nif res.Outdated {\n    fmt.Printf(\"0.1.0 is not latest, %s, upgrade to %s\", res.Meta.Message, res.Current)\n}\n```\n\nTo know about HTML meta tag specification, see [HTML Meta tag](doc/html_meta.md).\n\nYou can prepare your own HTML page and its scraping function. See more details in document at [https://godoc.org/github.com/tcnksm/go-latest](https://godoc.org/github.com/tcnksm/go-latest).\n\n### JSON\n\nYou can also use a JSON response.\n\nIf you want to check `0.1.0` is latest or not, prepare an API server which returns a following response,\n\n```json\n{\n    \"version\":\"1.2.3\",\n    \"message\":\"New version include security update, you should update soon\",\n    \"url\":\"http://example.com/info\"\n}\n```\n\nAnd make request,\n\n```golang\njson := &latest.JSON{\n    URL: \"http://example.com/json\",\n}\n\nres, _ := latest.Check(json, \"0.1.0\")\nif res.Outdated {\n    fmt.Printf(\"0.1.0 is not latest, %s, upgrade to %s\", res.Meta.Message, res.Current)\n}\n```\n\nYou can use your own json schema by defining `JSONReceive` interface. See more details in document at [https://godoc.org/github.com/tcnksm/go-latest](https://godoc.org/github.com/tcnksm/go-latest).\n\n## Version comparing\n\nTo compare version, we use [hashicorp/go-version](https://github.com/hashicorp/go-version). `go-version` follows [Semantic Versioning](http://semver.org/). So to use `go-latest` you need to follow SemVer format.\n\nFor user who doesn't use SemVer format, `go-latest` has function to transform it into SemVer format.\n\n\n## Contribution\n\n1. Fork ([https://github.com/tcnksm/go-latest/fork](https://github.com/tcnksm/go-latest/fork))\n1. Create a feature branch\n1. Commit your changes\n1. Rebase your local changes against the master branch\n1. Run test suite with the `go test ./...` command and confirm that it passes\n1. Run `gofmt -s`\n1. Create new Pull Request\n\n## Author\n\n[Taichi Nakashima](https://github.com/tcnksm)\n"
  },
  {
    "path": "doc/html_meta.md",
    "content": "# HTML meta tag version discovery\n\n`go-latest.HTMLMeta` uses HTML meta tag to check latest version of your tool. It will request provided `URL` and inspec the HTML returned for meta tags that have the following format:\n\n```bash\n<meta name=\"go-latest\" content=\"product-name SemVer message\">\n```\n\n- `product-name` is your tool name. It MUST be filled\n- `SemVer` is your tool version by [Semantic Versioning](http://semver.org/). It MUST be filled\n- `message` is a message. It MAY be filled\n\nFor example, if you want to check latest version of `reduce-worker`, you just prepare a HTML page which contains following tags.\n\n```bash\n<meta name=\"go-latest\" content=\"reduce-worker 1.2.3\">\n```\n\nYou can know latest version is `1.2.3`. \n\n## References\n\n`go-latest`'s HTML meta tag version discovery specification refers following:\n\n- [Golang Remote import paths](https://golang.org/cmd/go/#hdr-Remote_import_paths)\n- [App Container Image Discovery](https://github.com/appc/spec/blob/master/SPEC.md#app-container-image-discovery)\n\n\n\n"
  },
  {
    "path": "github.go",
    "content": "package latest\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/google/go-github/github\"\n\t\"github.com/hashicorp/go-version\"\n)\n\n// FixVersionStrFunc is function to fix version string\n// so that it can be interpreted as Semantic Versiongin by\n// http://godoc.org/github.com/hashicorp/go-version\ntype FixVersionStrFunc func(string) string\n\n// TagFilterFunc is fucntion to filter unexpected tags\n// from GitHub. Check a given tag as string (before FixVersionStr)\n// and return bool. If it's expected, return true. If not return false.\ntype TagFilterFunc func(string) bool\n\nvar (\n\tdefaultFixVersionStrFunc FixVersionStrFunc\n\tdefaultTagFilterFunc     TagFilterFunc\n)\n\nfunc init() {\n\tdefaultFixVersionStrFunc = fixNothing()\n\tdefaultTagFilterFunc = filterNothing()\n}\n\n// GithubTag is used to fetch version(tag) information from Github.\ntype GithubTag struct {\n\t// Owner and Repository are GitHub owner name and its repository name.\n\t// e.g., If you want to check https://github.com/tcnksm/ghr version\n\t// Repository is `ghr`, and Owner is `tcnksm`.\n\tOwner      string\n\tRepository string\n\n\t// FixVersionStrFunc is function to fix version string (in this case tag\n\t// name string) on GitHub so that it can be interpreted as Semantic Versioning\n\t// by hashicorp/go-version. By default, it does nothing.\n\tFixVersionStrFunc FixVersionStrFunc\n\n\t// TagFilterFunc is function to filter tags from GitHub. Some project includes\n\t// tags you don't want to use for version comparing. It can be used to exclude\n\t// such tags. By default, it does nothing.\n\tTagFilterFunc TagFilterFunc\n\n\t// URL & Token is used for GitHub Enterprise\n\tURL   string\n\tToken string\n}\n\nfunc (g *GithubTag) fixVersionStrFunc() FixVersionStrFunc {\n\tif g.FixVersionStrFunc == nil {\n\t\treturn defaultFixVersionStrFunc\n\t}\n\n\treturn g.FixVersionStrFunc\n}\n\nfunc (g *GithubTag) tagFilterFunc() TagFilterFunc {\n\tif g.TagFilterFunc == nil {\n\t\treturn defaultTagFilterFunc\n\t}\n\n\treturn g.TagFilterFunc\n}\n\n// fixNothing does nothing. This is a default function of FixVersionStrFunc.\nfunc fixNothing() FixVersionStrFunc {\n\treturn func(s string) string {\n\t\treturn s\n\t}\n}\n\nfunc filterNothing() TagFilterFunc {\n\treturn func(s string) bool {\n\t\treturn true\n\t}\n}\n\n// DeleteFrontV delete first `v` charactor on version string.\n// For example version name `v0.1.1` becomes `0.1.1`\nfunc DeleteFrontV() FixVersionStrFunc {\n\treturn func(s string) string {\n\t\treturn strings.Replace(s, \"v\", \"\", 1)\n\t}\n}\n\nfunc (g *GithubTag) newClient() *github.Client {\n\tclient := github.NewClient(nil)\n\tif g.URL != \"\" {\n\t\tclient.BaseURL, _ = url.Parse(g.URL)\n\t}\n\treturn client\n}\n\nfunc (g *GithubTag) Validate() error {\n\n\tif len(g.Repository) == 0 {\n\t\treturn fmt.Errorf(\"GitHub repository name must be set\")\n\t}\n\n\tif len(g.Owner) == 0 {\n\t\treturn fmt.Errorf(\"GitHub owner name must be set\")\n\t}\n\n\tif g.URL != \"\" {\n\t\tif _, err := url.Parse(g.URL); err != nil {\n\t\t\treturn fmt.Errorf(\"GitHub API Url invalid: %s\", err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (g *GithubTag) Fetch() (*FetchResponse, error) {\n\n\tfr := newFetchResponse()\n\n\t// Create a client\n\tclient := g.newClient()\n\ttags, resp, err := client.Repositories.ListTags(context.Background(), g.Owner, g.Repository, nil)\n\tif err != nil {\n\t\treturn fr, err\n\t}\n\n\tif resp.StatusCode != 200 {\n\t\treturn fr, fmt.Errorf(\"Unknown status: %d\", resp.StatusCode)\n\t}\n\n\t// fixF is FixVersionStrFunc transform tag name string into SemVer string\n\t// By default, it does nothing.\n\tfixF := g.fixVersionStrFunc()\n\n\t// filterF is TagFilterFunc to filter unexpected tags\n\t// By default, it filter nothing.\n\tfilterF := g.tagFilterFunc()\n\n\tfor _, tag := range tags {\n\t\tif !filterF(*tag.Name) {\n\t\t\tfr.Malformeds = append(fr.Malformeds, *tag.Name)\n\t\t\tcontinue\n\t\t}\n\t\tv, err := version.NewVersion(fixF(*tag.Name))\n\t\tif err != nil {\n\t\t\tfr.Malformeds = append(fr.Malformeds, fixF(*tag.Name))\n\t\t\tcontinue\n\t\t}\n\t\tfr.Versions = append(fr.Versions, v)\n\t}\n\n\treturn fr, nil\n}\n"
  },
  {
    "path": "github_test.go",
    "content": "package latest\n\nimport (\n\t\"testing\"\n)\n\nfunc TestGithubTag_implement(t *testing.T) {\n\tvar _ Source = &GithubTag{}\n}\n"
  },
  {
    "path": "helper_test.go",
    "content": "package latest\n\nimport (\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"os\"\n)\n\nfunc fakeServer(fixture string) *httptest.Server {\n\treturn httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tf, err := os.Open(fixture)\n\t\tif err != nil {\n\t\t\t// Should not reach here\n\t\t\tpanic(err)\n\t\t}\n\t\tio.Copy(w, f)\n\t}))\n}\n"
  },
  {
    "path": "html.go",
    "content": "package latest\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/hashicorp/go-version\"\n)\n\n// HTML is used to fetch version information from a single HTML page.\ntype HTML struct {\n\t// URL is HTML page URL which include version information.\n\tURL string\n\n\t// Scrap is used to scrap a single HTML page and extract version information.\n\t// See more about HTMLScrap interface.\n\t// By default, it does nothing, just return HTML contents.\n\tScrap HTMLScrap\n}\n\n// HTMLScrap is used to scrap a single HTML page and extract version information.\ntype HTMLScrap interface {\n\t// Exec is called from Fetch after fetching a HTMl page from source.\n\t// It must return version information as string list format.\n\tExec(r io.Reader) ([]string, *Meta, error)\n}\n\ntype defaultHTMLScrap struct{}\n\nfunc (s *defaultHTMLScrap) Exec(r io.Reader) ([]string, *Meta, error) {\n\tmeta := &Meta{}\n\tb, err := ioutil.ReadAll(r)\n\tif err != nil {\n\t\treturn []string{}, meta, err\n\t}\n\n\tb = bytes.Replace(b, []byte(\"\\n\"), []byte(\"\"), -1)\n\treturn []string{string(b[:])}, meta, nil\n}\n\nfunc (h *HTML) scrap() HTMLScrap {\n\tif h.Scrap == nil {\n\t\treturn &defaultHTMLScrap{}\n\t}\n\n\treturn h.Scrap\n}\n\nfunc (h *HTML) Validate() error {\n\n\tif len(h.URL) == 0 {\n\t\treturn fmt.Errorf(\"URL must be set\")\n\t}\n\n\t// Check URL can be parsed\n\tif _, err := url.Parse(h.URL); err != nil {\n\t\treturn fmt.Errorf(\"%s is invalid URL: %s\", h.URL, err.Error())\n\t}\n\n\treturn nil\n}\n\nfunc (h *HTML) Fetch() (*FetchResponse, error) {\n\n\tfr := newFetchResponse()\n\n\t// URL is validated before call\n\tu, _ := url.Parse(h.URL)\n\n\t// Create a new request\n\treq, err := http.NewRequest(\"GET\", u.String(), nil)\n\tif err != nil {\n\t\treturn fr, err\n\t}\n\treq.Header.Add(\"Accept\", \"application/json\")\n\n\t// Create client\n\tt := &http.Transport{\n\t\tProxy: http.ProxyFromEnvironment,\n\t\tDial: func(n, a string) (net.Conn, error) {\n\t\t\treturn net.DialTimeout(n, a, defaultDialTimeout)\n\t\t},\n\t}\n\n\tclient := &http.Client{\n\t\tTransport: t,\n\t}\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn fr, err\n\t}\n\n\tif resp.StatusCode != 200 {\n\t\treturn fr, fmt.Errorf(\"unknown status: %d\", resp.StatusCode)\n\t}\n\n\tscrap := h.scrap()\n\tverStrs, meta, err := scrap.Exec(resp.Body)\n\tif err != nil {\n\t\treturn fr, err\n\t}\n\n\tif len(verStrs) == 0 {\n\t\treturn fr, fmt.Errorf(\"version info is not found on %s\", h.URL)\n\t}\n\n\tfor _, verStr := range verStrs {\n\t\tv, err := version.NewVersion(verStr)\n\t\tif err != nil {\n\t\t\tfr.Malformeds = append(fr.Malformeds, verStr)\n\t\t\tcontinue\n\t\t}\n\t\tfr.Versions = append(fr.Versions, v)\n\t}\n\n\tfr.Meta = meta\n\n\treturn fr, nil\n}\n"
  },
  {
    "path": "html_meta.go",
    "content": "package latest\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\n\t\"golang.org/x/net/html\"\n\t\"golang.org/x/net/html/atom\"\n)\n\n// MetaTagName is common HTML meta tag name which is defined on https://github.com/tcnksm/go-latest/blob/master/doc/html_meta.md\nconst MetaTagName = \"go-latest\"\n\n// HTMLMeta is used to fetch a single HTML page and extract version information from\n// specific meta tag. See meta tag specification that HTMLMeta tries to extract on https://github.com/tcnksm/go-latest/blob/master/doc/html_meta.md\ntype HTMLMeta struct {\n\t// URL is HTML page URL which include version information.\n\tURL string\n\n\t// Name is tool name which you want to check. This name must be\n\t// written in HTML meta tag content field. HTMLMeta use this to\n\t// extract version information.\n\tName string\n}\n\nfunc (hm *HTMLMeta) newHTML() *HTML {\n\treturn &HTML{\n\t\tURL:   hm.URL,\n\t\tScrap: &metaTagScrap{Name: hm.Name},\n\t}\n}\n\nfunc (hm *HTMLMeta) Validate() error {\n\treturn hm.newHTML().Validate()\n}\n\nfunc (hm *HTMLMeta) Fetch() (*FetchResponse, error) {\n\treturn hm.newHTML().Fetch()\n}\n\ntype metaTagScrap struct {\n\tName string\n}\n\ntype tagInside struct {\n\tname    string\n\tprefix  string\n\tversion string\n\tmeta    *Meta\n}\n\nfunc (mt *metaTagScrap) Exec(r io.Reader) ([]string, *Meta, error) {\n\n\tz := html.NewTokenizer(r)\n\n\tfor {\n\t\tswitch z.Next() {\n\t\tcase html.ErrorToken:\n\t\t\treturn []string{}, &Meta{}, fmt.Errorf(\"meta tag for %s is not found\", mt.Name)\n\n\t\tcase html.StartTagToken, html.SelfClosingTagToken:\n\t\t\ttok := z.Token()\n\t\t\tif tok.DataAtom == atom.Meta {\n\t\t\t\tproduct, version, message := attrAnalizer(tok.Attr)\n\t\t\t\t// Return first founded version.\n\t\t\t\t// Assumes that mata tag exist only one for each product\n\t\t\t\tif product == mt.Name {\n\t\t\t\t\treturn []string{version}, &Meta{Message: message}, nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc attrAnalizer(attrs []html.Attribute) (product, version, message string) {\n\n\tfor _, a := range attrs {\n\n\t\tif a.Namespace != \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch a.Key {\n\t\tcase \"name\":\n\t\t\tif a.Val != MetaTagName {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\tcase \"content\":\n\t\t\tparts := strings.SplitN(strings.TrimSpace(a.Val), \" \", 3)\n\t\t\tif len(parts) < 2 {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tproduct = parts[0]\n\t\t\tversion = parts[1]\n\n\t\t\t// message is optional\n\t\t\tif len(parts) == 3 {\n\t\t\t\tmessage = parts[2]\n\t\t\t}\n\t\t}\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "html_meta_test.go",
    "content": "package latest\n\nimport (\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestHTMLMeta_implement(t *testing.T) {\n\tvar _ Source = &HTMLMeta{}\n}\n\nfunc TestHTMLMetaFetch(t *testing.T) {\n\ttests := []struct {\n\t\tname          string\n\t\ttestServer    *httptest.Server\n\t\texpectCurrent string\n\t\texpectMessage string\n\t}{\n\t\t{\n\t\t\tname:          \"reduce-worker\",\n\t\t\ttestServer:    fakeServer(\"test-fixtures/meta.html\"),\n\t\t\texpectCurrent: \"1.2.1\",\n\t\t\texpectMessage: \"New version include security update\",\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tts := tt.testServer\n\t\tdefer ts.Close()\n\n\t\th := &HTMLMeta{\n\t\t\tURL:  ts.URL,\n\t\t\tName: tt.name,\n\t\t}\n\n\t\tfr, err := h.Fetch()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"#%d Fetch() expects error:%q to be nil\", i, err.Error())\n\t\t}\n\n\t\tversions := fr.Versions\n\t\tif len(versions) == 0 {\n\t\t\tt.Fatalf(\"#%d Fetch() expects number of versions found from HTML not to be 0\", i)\n\t\t}\n\n\t\tcurrent := versions[0].String()\n\t\tif current != tt.expectCurrent {\n\t\t\tt.Fatalf(\"#%d Fetch() expects %s to be %s\", i, current, tt.expectCurrent)\n\t\t}\n\n\t\tmessage := fr.Meta.Message\n\t\tif message != tt.expectMessage {\n\t\t\tt.Fatalf(\"#%d Fetch() expects %q to be %q\", i, message, tt.expectMessage)\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "html_test.go",
    "content": "package latest\n\nimport (\n\t\"io\"\n\t\"net/http/httptest\"\n\t\"sort\"\n\t\"testing\"\n\n\t\"github.com/hashicorp/go-version\"\n\t\"golang.org/x/net/html\"\n\t\"golang.org/x/net/html/atom\"\n)\n\nfunc TestHTML_implement(t *testing.T) {\n\tvar _ Source = &HTML{}\n}\n\nfunc TestHTMLFetch(t *testing.T) {\n\ttests := []struct {\n\t\ttestServer    *httptest.Server\n\t\texpectCurrent string\n\t\texpectMessage string\n\t\tscrap         HTMLScrap\n\t}{\n\t\t{\n\t\t\ttestServer:    fakeServer(\"test-fixtures/default.html\"),\n\t\t\texpectCurrent: \"1.2.3\",\n\t\t},\n\t\t{\n\t\t\ttestServer:    fakeServer(\"test-fixtures/original.html\"),\n\t\t\texpectCurrent: \"1.2.5\",\n\t\t\texpectMessage: \"New version include security update, you should update soon\",\n\t\t\tscrap:         &DivAttributeScrap{},\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tts := tt.testServer\n\t\tdefer ts.Close()\n\n\t\th := &HTML{\n\t\t\tURL:   ts.URL,\n\t\t\tScrap: tt.scrap,\n\t\t}\n\n\t\tfr, err := h.Fetch()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"#%d Fetch() expects error:%q to be nil\", i, err.Error())\n\t\t}\n\n\t\tversions := fr.Versions\n\t\tif len(versions) == 0 {\n\t\t\tt.Fatalf(\"#%d Fetch() expects number of versions found from HTML not to be 0\", i)\n\t\t}\n\n\t\tsort.Sort(version.Collection(versions))\n\t\tcurrent := versions[len(versions)-1].String()\n\t\tif current != tt.expectCurrent {\n\t\t\tt.Fatalf(\"#%d Fetch() expects %s to be %s\", i, current, tt.expectCurrent)\n\t\t}\n\n\t\tmessage := fr.Meta.Message\n\t\tif message != tt.expectMessage {\n\t\t\tt.Fatalf(\"#%d Fetch() expects %q to be %q\", i, message, tt.expectMessage)\n\t\t}\n\t}\n\n}\n\ntype DivAttributeScrap struct {\n}\n\nfunc (s *DivAttributeScrap) Exec(r io.Reader) ([]string, *Meta, error) {\n\n\t// Check function attrs has correct class=\"val\" key&value\n\tisTarget := func(targetVal string, attrs []html.Attribute) bool {\n\t\tfor _, a := range attrs {\n\t\t\tif a.Namespace != \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif a.Key == \"class\" && a.Val == targetVal {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n\n\tvar verStrs []string\n\n\tmeta := &Meta{}\n\n\tz := html.NewTokenizer(r)\n\n\tfor {\n\t\tswitch z.Next() {\n\t\tcase html.ErrorToken:\n\t\t\treturn verStrs, meta, nil\n\n\t\tcase html.StartTagToken:\n\t\t\ttok := z.Token()\n\n\t\t\t// <div class=\"version\">VERSION</div>\n\t\t\tif tok.DataAtom == atom.Div && isTarget(\"version\", tok.Attr) {\n\t\t\t\tz.Next()\n\t\t\t\tnewTok := z.Token()\n\t\t\t\tverStrs = append(verStrs, newTok.String())\n\t\t\t}\n\n\t\t\t// <div class=\"message\">MESSAGE</div>\n\t\t\tif tok.DataAtom == atom.Div && isTarget(\"message\", tok.Attr) {\n\t\t\t\tz.Next()\n\t\t\t\tnewTok := z.Token()\n\t\t\t\tmeta.Message = newTok.String()\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "json.go",
    "content": "package latest\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/hashicorp/go-version\"\n)\n\nvar (\n\tdefaultDialTimeout = 5 * time.Second\n)\n\n// JSON is used to get version information as json format from remote host.\ntype JSON struct {\n\t// URL is URL which return json response with version information.\n\tURL string\n\n\t// Response is used to decode json as Struct and extract version information.\n\t// See JSONResponse interface. By Default, it is used defaultJSONResponse.\n\tResponse JSONResponse\n}\n\n// JSONResponse is used to decode json as Struct and extract information.\ntype JSONResponse interface {\n\t// VersionInfo is called from Fetch to extract version info.\n\t// It must return Semantic Version format version string list.\n\tVersionInfo() ([]string, error)\n\n\t// MetaInfo is called from Fetch to extract meta info.\n\tMetaInfo() (*Meta, error)\n}\n\ntype defaultJSONResponse struct {\n\tVersion string `json:\"version\"`\n\tMessage string `json:\"message\"`\n\tURL     string `json:\"url\"`\n}\n\nfunc (res *defaultJSONResponse) VersionInfo() ([]string, error) {\n\treturn []string{res.Version}, nil\n}\n\nfunc (res *defaultJSONResponse) MetaInfo() (*Meta, error) {\n\treturn &Meta{\n\t\tMessage: res.Message,\n\t\tURL:     res.URL,\n\t}, nil\n}\n\nfunc (j *JSON) response() JSONResponse {\n\tif j.Response == nil {\n\t\treturn &defaultJSONResponse{}\n\t}\n\n\treturn j.Response\n}\n\nfunc (j *JSON) Validate() error {\n\n\tif len(j.URL) == 0 {\n\t\treturn fmt.Errorf(\"URL must be set\")\n\t}\n\n\t// Check URL can be parsed by net.URL\n\tif _, err := url.Parse(j.URL); err != nil {\n\t\treturn fmt.Errorf(\"%s is invalid URL: %s\", j.URL, err.Error())\n\t}\n\n\treturn nil\n}\n\nfunc (j *JSON) Fetch() (*FetchResponse, error) {\n\n\tfr := newFetchResponse()\n\n\t// URL is validated before call\n\tu, _ := url.Parse(j.URL)\n\n\t// Create a new request\n\treq, err := http.NewRequest(\"GET\", u.String(), nil)\n\tif err != nil {\n\t\treturn fr, err\n\t}\n\treq.Header.Add(\"Accept\", \"application/json\")\n\n\t// Create client\n\tt := &http.Transport{\n\t\tProxy: http.ProxyFromEnvironment,\n\t\tDial: func(n, a string) (net.Conn, error) {\n\t\t\treturn net.DialTimeout(n, a, defaultDialTimeout)\n\t\t},\n\t}\n\n\tclient := &http.Client{\n\t\tTransport: t,\n\t}\n\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn fr, err\n\t}\n\n\tif resp.StatusCode != 200 {\n\t\treturn fr, fmt.Errorf(\"unknown status: %d\", resp.StatusCode)\n\t}\n\n\tresult := j.response()\n\tdec := json.NewDecoder(resp.Body)\n\tif err := dec.Decode(&result); err != nil {\n\t\treturn fr, err\n\t}\n\n\tverStrs, err := result.VersionInfo()\n\tif err != nil {\n\t\treturn fr, err\n\t}\n\n\tif len(verStrs) == 0 {\n\t\treturn fr, fmt.Errorf(\"version info is not found on %s\", j.URL)\n\t}\n\n\tfor _, verStr := range verStrs {\n\t\tv, err := version.NewVersion(verStr)\n\t\tif err != nil {\n\t\t\tfr.Malformeds = append(fr.Malformeds, verStr)\n\t\t}\n\t\tfr.Versions = append(fr.Versions, v)\n\t}\n\n\tfr.Meta, err = result.MetaInfo()\n\tif err != nil {\n\t\treturn fr, err\n\t}\n\n\treturn fr, nil\n}\n"
  },
  {
    "path": "json_test.go",
    "content": "package latest\n\nimport (\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestJSON_implement(t *testing.T) {\n\tvar _ Source = &JSON{}\n}\n\nfunc TestJSONValidate(t *testing.T) {\n\n\ttests := []struct {\n\t\tJSON      *JSON\n\t\texpectErr bool\n\t}{\n\t\t{\n\t\t\tJSON: &JSON{\n\t\t\t\tURL: \"http://good.com\",\n\t\t\t},\n\t\t\texpectErr: false,\n\t\t},\n\t\t{\n\t\t\tJSON: &JSON{\n\t\t\t\tURL: \"\",\n\t\t\t},\n\t\t\texpectErr: true,\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tj := tt.JSON\n\t\terr := j.Validate()\n\t\tif tt.expectErr == (err == nil) {\n\t\t\tt.Fatalf(\"#%d Validate() expects err == nil to eq %t\", i, tt.expectErr)\n\t\t}\n\t}\n}\n\n// OriginalResponse implements Receiver and receives test-fixtures/original.json\ntype OriginalResponse struct {\n\tName    string `json:\"name\"`\n\tVersion string `json:\"version_info\"`\n\tStatus  string `json:\"status\"`\n}\n\nfunc (r *OriginalResponse) VersionInfo() ([]string, error) {\n\tverStr := strings.Replace(r.Version, \"v\", \"\", 1)\n\treturn []string{verStr}, nil\n}\n\nfunc (r *OriginalResponse) MetaInfo() (*Meta, error) {\n\treturn &Meta{\n\t\tMessage: r.Status,\n\t}, nil\n}\n\nfunc TestJSONFetch(t *testing.T) {\n\n\ttests := []struct {\n\t\ttestServer    *httptest.Server\n\t\tresponse      JSONResponse\n\t\texpectCurrent string\n\t\texpectMessage string\n\t\texpectURL     string\n\t}{\n\t\t{\n\t\t\ttestServer:    fakeServer(\"test-fixtures/default.json\"),\n\t\t\texpectCurrent: \"1.2.3\",\n\t\t\texpectMessage: \"New version include security update, you should update soon\",\n\t\t\texpectURL:     \"http://example.com/info\",\n\t\t},\n\t\t{\n\t\t\ttestServer:    fakeServer(\"test-fixtures/original.json\"),\n\t\t\texpectCurrent: \"1.0.0\",\n\t\t\texpectMessage: \"We are releasing now\",\n\t\t\tresponse:      &OriginalResponse{},\n\t\t},\n\t}\n\n\tfor i, tt := range tests {\n\t\tts := tt.testServer\n\t\tdefer ts.Close()\n\n\t\tj := &JSON{\n\t\t\tURL:      ts.URL,\n\t\t\tResponse: tt.response,\n\t\t}\n\n\t\tfr, err := j.Fetch()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"#%d Fetch() expects error:%q to be nil\", i, err.Error())\n\t\t}\n\n\t\tversions := fr.Versions\n\t\tcurrent := versions[0].String()\n\t\tif current != tt.expectCurrent {\n\t\t\tt.Fatalf(\"#%d Fetch() expects %s to be %s\", i, current, tt.expectCurrent)\n\t\t}\n\n\t\tmessage := fr.Meta.Message\n\t\tif message != tt.expectMessage {\n\t\t\tt.Fatalf(\"#%d Fetch() expects %q to be %q\", i, message, tt.expectMessage)\n\t\t}\n\n\t\turl := fr.Meta.URL\n\t\tif url != tt.expectURL {\n\t\t\tt.Fatalf(\"#%d Fetch() expects %q to be %q\", i, url, tt.expectURL)\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "latest/README.md",
    "content": "# latest\n\n`latest` is a command to check a provided version is latest or not in GitHub. \n\n## Usage\n\nTo check cloned repository is latest or not, just run with owner name and repository name which you want to check. If it is not latest version, it returns non-zero exit code.\n\n```bash\n$ latest -owner=tcnksm -repo=go-latest 2.4.1\n$ echo $?\n0\n```\n\nYou can check version is new, it means version is not exist on GitHub and greater than others, and more outputs can be enabled with `-debug` flag, \n\n```bash\n$ latest -debug -new -owner=tcnksm repo=go-latest 2.4.1\n2.2.1 is new\n```\n\nSee more usage with `-help` options.\n\n## Install\n\nTo install `latest` command just run `go get`,\n\n```bash\n$ go get github.com/tcnksm/go-latest/latest\n```\n"
  },
  {
    "path": "latest/cli.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\n\t\"github.com/tcnksm/go-latest\"\n)\n\ntype CLI struct {\n\t// out/err stream is the stdout and stderr\n\t// to write message from CLI\n\toutStream, errStream io.Writer\n}\n\n// Run executes CLI and return its exit code\nfunc (c *CLI) Run(args []string) int {\n\tvar githubTag latest.GithubTag\n\n\tflags := flag.NewFlagSet(Name, flag.ExitOnError)\n\tflags.Usage = func() { fmt.Fprintf(c.errStream, helpText) }\n\tflags.SetOutput(c.errStream)\n\n\tflags.StringVar(&githubTag.Repository,\n\t\t\"repo\", \"\", \"Repository name\")\n\tflags.StringVar(&githubTag.Owner,\n\t\t\"owner\", \"\", \"Repository owner name\")\n\n\tflgNew := flags.Bool(\"new\",\n\t\tfalse, \"Check TAG(VERSION) is new and greater\")\n\tflgFixVerStrFunc := flags.String(\"fix\",\n\t\t\"none\", \"Specify FixVersionStrFunc\")\n\tflgVersion := flags.Bool(\"version\",\n\t\tfalse, \"Print version information\")\n\tflgHelp := flags.Bool(\"help\",\n\t\tfalse, \"Print this message and quit\")\n\tflgDebug := flags.Bool(\"debug\",\n\t\tfalse, \"Print verbose(debug) output\")\n\n\tif err := flags.Parse(args[1:]); err != nil {\n\t\tfmt.Fprint(c.errStream, \"Failed to parse flag\\n\")\n\t\treturn 1\n\t}\n\n\t// Show version and quit\n\tif *flgVersion {\n\t\tfmt.Fprintf(c.errStream, \"%s Version v%s build %s\\n\", Name, Version, GitCommit)\n\t\treturn 0\n\t}\n\n\t// Show help and quit\n\tif *flgHelp {\n\t\tfmt.Fprintf(c.errStream, helpText)\n\t\treturn 0\n\t}\n\n\t// Run as debug mode\n\tif os.Getenv(envDebug) != \"\" {\n\t\t*flgDebug = true\n\t}\n\n\tparsedArgs := flags.Args()\n\tif len(parsedArgs) != 1 {\n\t\tfmt.Fprintf(c.errStream, \"Invalid arguments\\n\")\n\t\treturn 1\n\t}\n\ttarget := parsedArgs[0]\n\n\t// Specify FixVersionStrFunc\n\t// e.g., if version is v0.3.1 it should be 0.3.1 (SemVer format)\n\tvar f latest.FixVersionStrFunc\n\tswitch *flgFixVerStrFunc {\n\tcase \"none\":\n\t\tf = nil\n\tcase \"frontv\":\n\t\tf = latest.DeleteFrontV()\n\t\ttarget = f(target)\n\tdefault:\n\t\tfmt.Fprintf(c.errStream, \"Invalid fix func: %s\\n\", *flgFixVerStrFunc)\n\t\treturn 1\n\t}\n\n\tgithubTag.FixVersionStrFunc = f\n\tres, err := latest.Check(&githubTag, target)\n\tif err != nil {\n\t\tfmt.Fprintf(c.errStream, \"Failed to check: %s\\n\", err.Error())\n\t\treturn 1\n\t}\n\n\t// Default variables\n\texitCode := 0\n\toutput := fmt.Sprintf(\"%s is latest\\n\", target)\n\n\t// Check version is `new`\n\tif *flgNew {\n\t\tif !res.New {\n\t\t\texitCode = 1\n\t\t\toutput = fmt.Sprintf(\"%s is not new\\n\", target)\n\t\t} else {\n\t\t\toutput = fmt.Sprintf(\"%s is new\\n\", target)\n\t\t}\n\t} else {\n\t\tif !res.Latest {\n\t\t\texitCode = 1\n\t\t\toutput = fmt.Sprintf(\"%s is not latest\\n\", target)\n\t\t}\n\t}\n\n\tif *flgDebug {\n\t\tfmt.Fprint(c.outStream, output)\n\t}\n\n\treturn exitCode\n}\n\nconst helpText = `Usage: latest [options] TAG\n\n    latest command check TAG(VERSION) is latest. If is not latest,\n    it returns non-zero value. It try to compare version by Semantic\n    Versioning. \n\nOptions:\n\n    -owner=NAME    Set GitHub repository owner name.\n\n    -repo=NAME     Set Github repository name.\n\n    -new           Check TAG(VERSION) is new. 'new' means TAG(VERSION)\n                   is not exist and greater than others.\n\n    -fix=none      Specify FixVersionStrFunc (Fix version string to SemVer)\n                   'none': does nothing (default)\n                   'front': deletes front 'v' charactor\n\n    -help          Print this message and quit.\n\n    -debug         Print verbose(debug) output.\n\nExample:\n\n    $ latest -debug 0.2.0\n`\n"
  },
  {
    "path": "latest/main.go",
    "content": "package main\n\nimport \"os\"\n\n// envDebug is used for changing verbose outoput\nvar envDebug = \"DEBUG\"\n\nfunc main() {\n\tcli := &CLI{outStream: os.Stdout, errStream: os.Stderr}\n\tos.Exit(cli.Run(os.Args))\n}\n"
  },
  {
    "path": "latest/scripts/compile.sh",
    "content": "#!/bin/bash\nset -e\n\nDIR=$(cd $(dirname ${0})/.. && pwd)\ncd ${DIR}\n\nXC_ARCH=${XC_ARCH:-386 amd64}\nXC_OS=${XC_OS:-darwin linux windows}\n\nCOMMIT=`git describe --always`\n\nrm -rf pkg/\ngox \\\n    -ldflags \"-X main.GitCommit \\\"${COMMIT}\\\"\" \\\n    -os=\"${XC_OS}\" \\\n    -arch=\"${XC_ARCH}\" \\\n    -output \"pkg/{{.OS}}_{{.Arch}}/{{.Dir}}\"\n"
  },
  {
    "path": "latest/scripts/package.sh",
    "content": "#!/bin/bash\nset -e\n\nDIR=$(cd $(dirname ${0})/.. && pwd)\ncd ${DIR}\n\nVERSION=$(grep \"const Version \" version.go | sed -E 's/.*\"(.+)\"$/\\1/')\nREPO=\"latest\"\n\n# Run Compile\n./scripts/compile.sh\n\nif [ -d pkg ];then\n    rm -rf ./pkg/dist\nfi \n\n# Package all binary as .zip\nmkdir -p ./pkg/dist/${VERSION}\nfor PLATFORM in $(find ./pkg -mindepth 1 -maxdepth 1 -type d); do\n    PLATFORM_NAME=$(basename ${PLATFORM})\n    ARCHIVE_NAME=${REPO}_${VERSION}_${PLATFORM_NAME}\n\n    if [ $PLATFORM_NAME = \"dist\" ]; then\n        continue\n    fi\n\n    pushd ${PLATFORM}\n    zip ${DIR}/pkg/dist/${VERSION}/${ARCHIVE_NAME}.zip ./*\n    popd\ndone\n\n# Generate shasum\npushd ./pkg/dist/${VERSION}\nshasum * > ./${VERSION}_SHASUMS\npopd\n"
  },
  {
    "path": "latest/version.go",
    "content": "package main\n\nconst Name = \"latest\"\nconst Version = \"0.1.1\"\n\nvar GitCommit = \"\"\n"
  },
  {
    "path": "latest.go",
    "content": "/*\ngo-latest is pacakge to check a provided version is latest from various sources.\n\nhttp://github.com/tcnksm/go-latest\n\n  package main\n\n  import (\n      \"github.com/tcnksm/go-latest\"\n  )\n\n  githubTag := &latest.GithubTag{\n      Owner: \"tcnksm\",\n      Repository: \"ghr\"\n  }\n\n  res, _ := latest.Check(\"0.1.0\",githubTag)\n  if res.Outdated {\n      fmt.Printf(\"version 0.1.0 is out of date, you can upgrade to %s\", res.Current)\n  }\n\n*/\npackage latest\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"sort\"\n\n\t\"github.com/hashicorp/go-version\"\n)\n\n// EnvGoLatestDisable is environmental variable to disable go-latest\n// execution.\nconst EnvGoLatestDisable = \"GOLATEST_DISABLE\"\n\n// Source is the interface that every version information source must implement.\ntype Source interface {\n\t// Validate is called before Fetch in Check.\n\t// Source may need to have various information like URL or product name,\n\t// so it is used for check each variables are correctly set.\n\t// If it is failed, Fetch() will not executed.\n\tValidate() error\n\n\t// Fetch is called in Check to fetch information from remote sources.\n\t// After fetching, it will convert it into common expression (FetchResponse)\n\tFetch() (*FetchResponse, error)\n}\n\n// FetchResponse the commom response of Fetch request.\ntype FetchResponse struct {\n\tVersions   []*version.Version\n\tMalformeds []string\n\tMeta       *Meta\n}\n\n// Meta is meta information from Fetch request.\ntype Meta struct {\n\tMessage string\n\tURL     string\n}\n\n// CheckResponse is a response for a Check request.\ntype CheckResponse struct {\n\t// Current is current latest version on source.\n\tCurrent string\n\n\t// Outdate is true when target version is less than Curernt on source.\n\tOutdated bool\n\n\t// Latest is true when target version is equal to Current on source.\n\tLatest bool\n\n\t// New is true when target version is greater than Current on source.\n\tNew bool\n\n\t// Malformed store versions or tags which can not be parsed as\n\t// Semantic versioning (not compared with target).\n\tMalformeds []string\n\n\t// Meta is meta information from source.\n\tMeta *Meta\n}\n\n// Check fetches last version information from its source\n// and compares with target and return result (CheckResponse).\nfunc Check(s Source, target string) (*CheckResponse, error) {\n\n\tif os.Getenv(EnvGoLatestDisable) != \"\" {\n\t\treturn &CheckResponse{}, nil\n\t}\n\n\t// Convert target to *version.Version\n\ttargetV, err := version.NewVersion(target)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse %s, %s\", target, err.Error())\n\t}\n\n\t// Validate source\n\tif err = s.Validate(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tfr, err := s.Fetch()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Source must has at leaset one version information\n\tversions := fr.Versions\n\tif len(fr.Versions) == 0 {\n\t\treturn nil, fmt.Errorf(\"no version to compare\")\n\t}\n\tsort.Sort(version.Collection(versions))\n\tcurrentV := versions[len(versions)-1]\n\n\tvar outdated, latest, new bool\n\tif targetV.LessThan(currentV) {\n\t\toutdated = true\n\t}\n\n\t// If target = current, target is `latest`\n\tif targetV.Equal(currentV) {\n\t\tlatest = true\n\t}\n\n\t// If target > current, target is `latest` and `new`\n\tif targetV.GreaterThan(currentV) {\n\t\tlatest, new = true, true\n\t}\n\n\treturn &CheckResponse{\n\t\tCurrent:    currentV.String(),\n\t\tOutdated:   outdated,\n\t\tLatest:     latest,\n\t\tNew:        new,\n\t\tMalformeds: fr.Malformeds,\n\t\tMeta:       fr.Meta,\n\t}, nil\n}\n\n// newFetchResponse is constructor of FetchResponse. This is only for\n// implement your own Source\nfunc newFetchResponse() *FetchResponse {\n\tvar versions []*version.Version\n\tvar malformeds []string\n\treturn &FetchResponse{\n\t\tVersions:   versions,\n\t\tMalformeds: malformeds,\n\t\tMeta:       &Meta{},\n\t}\n}\n"
  },
  {
    "path": "latest_test.go",
    "content": "package latest\n"
  },
  {
    "path": "test-fixtures/default.html",
    "content": "1.2.3\n"
  },
  {
    "path": "test-fixtures/default.json",
    "content": "{\n    \"version\":\"1.2.3\",\n    \"message\":\"New version include security update, you should update soon\",\n    \"url\":\"http://example.com/info\"\n}\n"
  },
  {
    "path": "test-fixtures/meta.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>go-latest</title>\n    <meta name=\"go-latest\" content=\"reduce-worker 1.2.1 New version include security update\">\n    <meta name=\"go-latest\" content=\"reduce-worker 1.2.0\">\n    <meta name=\"go-latest\" content=\"great-worker 0.1.1\">\n  </head>\n  \n  <body>\n    <h1>go-latest</h1>\n    <p>This is sample HTML page for go-latest</p>\n  </body>\n</html>\n"
  },
  {
    "path": "test-fixtures/original.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>go-latest</title>\n  </head>\n  <body>\n    <div class=\"product\">go-latest</div>\n    <div class=\"version\">1.2.3</div>\n    <div class=\"version\">1.2.4</div>\n    <div class=\"version\">1.2.5</div>\n    <div class=\"message\">New version include security update, you should update soon</div>\n  </body>\n</html>\n"
  },
  {
    "path": "test-fixtures/original.json",
    "content": "{\n    \"name\":\"go-latest\",\n    \"version_info\":\"v1.0.0\",\n    \"status\": \"We are releasing now\"\n}\n"
  },
  {
    "path": "wercker.yml",
    "content": "box: tcnksm/gox\nbuild:\n    steps:\n      - setup-go-workspace\n      - script:\n          name: go version\n          code: |\n            go version        \n      - script:\n          name: go get\n          code: |            \n            go get -t ./...\n      - script:\n          name: go test\n          code: |\n            go test"
  }
]