Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
jbowes committed Aug 10, 2021
0 parents commit b745ed0
Show file tree
Hide file tree
Showing 14 changed files with 620 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "daily"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
38 changes: 38 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Go

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:

test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.16

- name: Build
run: go build -v ./...

- name: Test
run: go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic

- name: Upload coverage to Codecov
run: bash <(curl -s https://codecov.io/bash)

golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
version: latest
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
testdata/update-cache.json
15 changes: 15 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
run:
skip-dirs-use-default: false

linters:
enable: [ goheader ]

linters-settings:
goheader:
template: |-
Copyright (c) {{ YEAR-RANGE }} {{ AUTHOR }}. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
values:
const:
AUTHOR: James Bowes
29 changes: 29 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
BSD 3-Clause License

Copyright (c) 2021, James Bowes
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
87 changes: 87 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<!--
Attractive html formatting for rendering in github. sorry text editor
readers! Besides the header and section links, everything should be clean and
readable.
-->
<h1 align="center">whatsnew</h1>
<p align="center"><i>Check for new github releases of your Golang application 🎊</i></p>

<div align="center">
<a href="https://pkg.go.dev/github.com/jbowes/whatsnew"><img src="https://pkg.go.dev/badge/github.com/jbowes/whatsnew.svg" alt="Go Reference"></a>
<img alt="Alpha Quality" src="https://img.shields.io/badge/status-ALPHA-orange.svg" >
<a href="https://github.com/jbowes/whatsnew/releases/latest"><img alt="GitHub tag" src="https://img.shields.io/github/tag/jbowes/whatsnew.svg"></a>
<a href="https://github.com/jbowes/whatsnew/actions/workflows/go.yml"><img alt="Build Status" src="https://github.com/jbowes/whatsnew/actions/workflows/go.yml/badge.svg?branch=main"></a>
<a href="./LICENSE"><img alt="BSD license" src="https://img.shields.io/badge/license-BSD-blue.svg"></a>
<a href="https://codecov.io/gh/jbowes/whatsnew"><img alt="codecov" src="https://img.shields.io/codecov/c/github/jbowes/whatsnew.svg"></a>
<a href="https://goreportcard.com/report/github.com/jbowes/whatsnew"><img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/jbowes/whatsnew"></a>
</div><br /><br />

---

🚧 ___Disclaimer___: _`whatsnew` is alpha quality software. The API may change
without warning between revisions._ 🚧

[`whatsnew`][godoc] provides a simple way to check GitHub for new releases of
your Go application. It saves results between runs, uses etags to speed up responses, and tries to minimize the overhead it adds to an otherwise fast
application CLI run.

If caching to disk, or reading from GitHub don't work for you, you can
customize the behaviour.

## Quick start

```go
import (
"context"
"github.com/jbowes/whatsnew"
)

func main() {
ctx := context.Background()

// Start a whatsnew Check
fut := whatsnew.Check(ctx, &whatsnew.Options{
Slug: "you/your-app",
Cache: "testdata/update-cache.json",
Version: "v0.0.1",
})

// Run your CLI code and whatnot

// Wait for the Check to complete, and show the results
if v, _ := fut.Get(); v != "" {
fmt.Printf("new release available: %s\n", v)
}
}
```

For more usage and examples, see the [GoDoc Reference][godoc]

## Alternatives

`whatsnew` only **checks** for releases. If you're looking for a package that
will let your application **update itself**, or you prefer packages that start
with `go-`, consider one of these:
- [go-github-selfupdate](https://github.com/rhysd/go-github-selfupdate)
- [go-selfupdate](https://github.com/sanbornm/go-selfupdate)
- [go-update](https://github.com/inconshreveable/go-update)

## Contributing

I would love your help!

`whatsnew` is still a work in progress. You can help by:

- Opening a pull request to resolve an [open issue][issues].
- Adding a feature or enhancement of your own! If it might be big, please
[open an issue][enhancement] first so we can discuss it.
- Improving this `README` or adding other documentation to `whatsnew`.
- Letting [me] know if you're using `whatsnew`.

[godoc]: https://pkg.go.dev/github.com/jbowes/whatsnew

[issues]: ./issues
[bug]: ./issues/new?labels=bug
[enhancement]: ./issues/new?labels=enhancement

[me]: https://twitter.com/jrbowes
73 changes: 73 additions & 0 deletions examples_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (c) 2021 James Bowes. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package whatsnew_test

import (
"context"
"fmt"
"net/http"
"time"

"github.com/jbowes/whatsnew"
"github.com/jbowes/whatsnew/impl"
)

func Example() {
ctx := context.Background()
fut := whatsnew.Check(ctx, &whatsnew.Options{
Slug: "you/your-app",
Cache: "testdata/update-cache.json",
Version: "v0.0.1",
})

// Run your CLI code and whatnot

if v, _ := fut.Get(); v != "" {
fmt.Printf("new release available: %s\n", v)
}

// Output:
// new release available: v0.2.0
}

// This example test isn't really needed, but it keeps the file
// from being an example program, so we can replace the http
// transport etc.
func Example_customFrequency() {
ctx := context.Background()
fut := whatsnew.Check(ctx, &whatsnew.Options{
Slug: "you/your-app",
Cache: "testdata/update-cache.json",
Version: "0.0.1",
Frequency: 24 * time.Hour,
})

// Run your CLI code and whatnot

if v, _ := fut.Get(); v != "" {
fmt.Printf("new release available: %s\n", v)
}

// Output:
// new release available: 0.30.0
}

func init() {
ctx := context.Background()

// setup. write out a cache that will hit for one test and
// miss on the other
cache := impl.FileCacher{Path: "testdata/update-cache.json"}
_ = cache.Set(ctx, &impl.Info{
Version: "v0.2.0",
CheckTime: time.Now().Add(-25 * time.Hour),
Etag: "whatever",
})

// replace http default transport.
http.DefaultTransport = http.NewFileTransport(
http.Dir("testdata/example"),
)
}
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/jbowes/whatsnew

go 1.16

require golang.org/x/mod v0.4.2
13 changes: 13 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
42 changes: 42 additions & 0 deletions impl/file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) 2021 James Bowes. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package impl

import (
"context"
"encoding/json"
"os"
)

// FileCacher is the default Cacher used in whatsnew.
type FileCacher struct {
Path string
}

// Get cached release Info.
func (f *FileCacher) Get(context.Context) (*Info, error) {
r, err := os.Open(f.Path)
if err != nil {
return nil, err
}

var i Info
dec := json.NewDecoder(r)
err = dec.Decode(&i)
return &i, err
}

// Set cached release Info.
func (f *FileCacher) Set(_ context.Context, i *Info) error {
w, err := os.Create(f.Path)
if err != nil {
return err
}

enc := json.NewEncoder(w)
enc.SetIndent("", " ")

return enc.Encode(i)
}
60 changes: 60 additions & 0 deletions impl/github.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) 2021 James Bowes. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package impl

import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"
)

// GitHubReleaser is the default Releaser used in whatsnew.
type GitHubReleaser struct {
URL string // a complete URL to the releases API.
}

// Get a list of releases.
func (g *GitHubReleaser) Get(ctx context.Context, etag string) ([]Release, string, error) {
req, err := http.NewRequest(http.MethodGet, g.URL, nil)
if err != nil {
return nil, "", err
}

req.Header.Set("Accept", "application/vnd.github.v3+json")
if etag != "" {
req.Header.Set("If-None-Match", etag)
}

// TODO: configurable timeout? relying on ctx doesn't seem like a great API
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

req = req.WithContext(ctx)

resp, err := http.DefaultClient.Do(req)
if err != nil {

return nil, "", err
}
defer resp.Body.Close()

if etag != "" && resp.StatusCode == http.StatusNotModified {
return nil, etag, nil // this will fall back to existing stuff.
}

if resp.StatusCode != http.StatusOK {
return nil, "", fmt.Errorf("error getting updates: %s", resp.Status)
}

var rels []Release
dec := json.NewDecoder(resp.Body)
if err := dec.Decode(&rels); err != nil {
return nil, "", err
}

return rels, resp.Header.Get("Etag"), nil
}
Loading

0 comments on commit b745ed0

Please sign in to comment.