appcast

package module
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 21, 2018 License: MIT Imports: 7 Imported by: 0

README

go-appcast

Travis (.org) Codecov Go Report Card GoDoc

An extendable library which provides functionality for working with different appcasts. It can work in both ways: retrieve data from an already existing appcast (unmarshal) or generate an appcast from the provided data (marshal).

What "appcast" means?

The word "appcast" is usually referred to a remote web page providing information about software updates. This kind of pages is usually created for different software update frameworks like Sparkle or generated by different services that distribute applications (GitHub, SourceForge and etc.).

There are plenty of different methods available, but originally "appcasting" was the practice of using an RSS enclosure to distribute updates and release notes.

What this library does?

As you can find plenty of different ways how vendors distribute their software updates it becomes pretty tedious to extract useful information from their appcasts. Especially, considering the idea that sometimes you would like to transpile one appcast type into another or simply make changes into an already existing one.

Here comes this library handy. It provides the core functionality for working with the supported providers in a reliable and consistent way. In addition, it was designed to be extendable which enables you adding more features or extend the supported providers, sources and even outputs to match your needs.

Features
  • Designed to be extendable
  • Detect release stability from the semantic version
  • Different outputs to save to
  • Different sources to load from
  • Filter releases by stability, title, media type or download URL
  • Guess the supported provider
  • Sort releases by version
  • Transpilation from one provider into another

Providers

Out of the box, 3 providers are supported:

Each provider can be used separately by explicitly importing only those packages you are going to use. This is useful when you don't need any extra stuff in your project and you know which appcast provider you are dealing with.

In other cases importing a single github.com/victorpopkov/go-appcast is the best option. This will give you all the necessary functionality to work with the supported providers as it will automatically detect which is used and then call the appropriate methods.

GitHub Atom Feed

Each project that uses GitHub releases to distribute applications has its own Atom Feed available that can be considered as an appcast.

A good example url is an Atom releases: https://www.adium.im/sparkle/appcast-release.xml. You can find the corresponding GoDoc examples below:

SourceForge RSS Feed

Each project hosted on SourceForge has its own releases RSS feed available that can be considered to be an appcast.

As an example you can take a look at the FileZilla SourceForge releases page here: https://sourceforge.net/projects/filezilla/rss. You can find the corresponding GoDoc examples below:

Sparkle RSS Feed

Appcasts, created by the Sparkle Framework. Originally, Sparkle was created to distribute software updates for macOS applications. However, for Windows, there is a WinSparkle framework which uses the same RSS enclosure technique to distribute updates and release notes.

A good example is how the Adium distributes software updates: https://www.adium.im/sparkle/appcast-release.xml. You can find the corresponding GoDoc examples below:

Sources

Out of the box, 2 sources are supported:

This means that you have by default 2 options from where an appcast can be loaded. You can just choose the appropriate one from the source package or create your own.

You can find a GoDoc standalone package example here: import "github.com/victorpopkov/go-appcast/source". In addition, by digging into the "Providers" you can see how to use them alongside with an appcast in their examples:

source.Remote

This was designed to retrieve an appcast data from the remote location by URL. It should cover most use cases when the appcast is available remotely.

For convenience purposes an Appcast.LoadFromRemoteSource can be used when using the default appcast package. It sets the Appcast to use the source.Remote, loads the source and unmarshals it.

source.Local

This was designed to retrieve an appcast data from the local file by path.

For convenience purposes an Appcast.LoadFromLocalSource can be used when using the default appcast package. It sets the Appcast to use the source.Local, loads the source and unmarshals it.

Outputs

Out of the box, only a single output.Local is available to save an appcast to the local file.

Just like the "Sources", outputs are designed in the same way meaning that you can extend the list of the supported outputs by creating your own.

You can find a GoDoc standalone package example here: import "github.com/victorpopkov/go-appcast/output". In addition, by digging into the "Providers" you can see how to use them alongside with an appcast in their "Marshal" examples:

License

Released under the MIT License.

Documentation

Overview

Package appcast provides functionality for working with appcasts to retrieve valuable information about software releases.

Currently supports 3 providers: "GitHub Atom Feed", "SourceForge RSS Feed" and "Sparkle RSS Feed". However, it can be extended to your own needs if necessary.

See README.md for more info.

Example (GitHub)

Demonstrates the "Github Atom Feed" appcast loading.

// mock the request
content := testdata("github.xml")
httpmock.ActivateNonDefault(source.DefaultClient.HTTPClient)
httpmock.RegisterResponder("GET", "https://github.com/atom/atom/releases.atom", httpmock.NewBytesResponder(200, content))
defer httpmock.DeactivateAndReset()

// example
a := appcast.New()

p, errors := a.LoadFromRemoteSource("https://github.com/atom/atom/releases.atom")
if p != nil && len(errors) > 0 {
	panic(errors[0])
}

fmt.Printf("%-9s %d error(s)\n", "Result:", len(errors))
fmt.Printf("%-9s %s\n", "Type:", reflect.TypeOf(a.Source().Appcast()))
fmt.Printf("%-9s %s\n", "Checksum:", a.Source().Checksum())
fmt.Printf("%-9s %s\n", "Provider:", a.Source().Provider())
fmt.Printf("%-9s %d total\n\n", "Releases:", a.Releases().Len())

r := a.Releases().First()
fmt.Print("First release details:\n\n")
fmt.Printf("%12s %s\n", "Version:", r.Version())
fmt.Printf("%12s %v\n", "Pre-release:", r.IsPreRelease())
fmt.Printf("%12s %s\n", "Title:", r.Title())
fmt.Printf("%12s %v\n\n", "Published:", r.PublishedDateTime())

fmt.Printf("%12s %d total\n", "Downloads:", len(r.Downloads()))
Output:

Result:   0 error(s)
Type:     *github.Appcast
Checksum: 03b6d9b8199ea377036caafa5358512295afa3c740edf9031dc6739b89e3ba05
Provider: GitHub Atom Feed
Releases: 10 total

First release details:

    Version: 1.28.0-beta3
Pre-release: true
      Title: 1.28.0-beta3
  Published: 2018-06-06T20:09:54+03:00

  Downloads: 0 total
Example (SourceForge)

Demonstrates the "SourceForge RSS Feed" appcast loading.

// mock the request
content := testdata("sourceforge.xml")
httpmock.ActivateNonDefault(source.DefaultClient.HTTPClient)
httpmock.RegisterResponder("GET", "https://sourceforge.net/projects/wesnoth/rss", httpmock.NewBytesResponder(200, content))
defer httpmock.DeactivateAndReset()

// example
a := appcast.New()

p, errors := a.LoadFromRemoteSource("https://sourceforge.net/projects/wesnoth/rss")
if p == nil && len(errors) > 0 {
	panic(errors[0])
}

fmt.Print("Errors:\n\n")
for _, err := range errors {
	fmt.Println(err)
}

fmt.Printf("%-10s %s\n", "\nType:", reflect.TypeOf(a.Source().Appcast()))
fmt.Printf("%-9s %s\n", "Checksum:", a.Source().Checksum())
fmt.Printf("%-9s %s\n", "Provider:", a.Source().Provider())
fmt.Printf("%-9s %d total\n\n", "Releases:", a.Releases().Len())

fmt.Print("Filtering:\n\n")
fmt.Printf("%12s %d total\n", "Before:", a.Releases().Len())

// apply some filters
a.Releases().FilterByMediaType("application/x-bzip2")
a.Releases().FilterByTitle("wesnoth-1.14", true)
a.Releases().FilterByUrl("dmg")
defer a.Releases().ResetFilters() // reset

fmt.Printf("%12s %d total\n\n", "After:", a.Releases().Len())

if a.Releases().Len() > 0 {
	r := a.Releases().First()
	fmt.Print("First release details:\n\n")
	fmt.Printf("%12s %s\n", "Version:", r.Version())
	fmt.Printf("%12s %v\n", "Pre-release:", r.IsPreRelease())
	fmt.Printf("%12s %s\n", "Title:", r.Title())
	fmt.Printf("%12s %v\n\n", "Published:", r.PublishedDateTime())

	d := r.Downloads()[0]
	fmt.Printf("%12s %d total\n\n", "Downloads:", len(r.Downloads()))
	fmt.Printf("%12s %s\n", "URL:", d.Url())
	fmt.Printf("%12s %s\n", "Type:", d.Filetype())
	fmt.Printf("%12s %d\n", "Length:", d.Length())
}
Output:

Errors:

release #51 (no version)
release #71 (no version)
release #72 (no version)
release #73 (no version)
release #92 (no version)

Type:     *sourceforge.Appcast
Checksum: 880cf7f2f6aa0aa0d859f1fc06e4fbcbba3d4de15fa9736bf73c07accb93ce36
Provider: SourceForge RSS Feed
Releases: 95 total

Filtering:

     Before: 95 total
      After: 10 total

First release details:

    Version: 1.13.14
Pre-release: false
      Title: /wesnoth/wesnoth-1.13.14/Wesnoth_1.13.14.dmg
  Published: Sun, 15 Apr 2018 08:45:18 UTC

  Downloads: 1 total

        URL: https://sourceforge.net/projects/wesnoth/files/wesnoth/wesnoth-1.13.14/Wesnoth_1.13.14.dmg/download
       Type: application/x-bzip2; charset=binary
     Length: 439082409
Example (Sparkle)

Demonstrates the "Sparkle RSS Feed" appcast loading.

// mock the request
content := testdata("sparkle.xml")
httpmock.ActivateNonDefault(source.DefaultClient.HTTPClient)
httpmock.RegisterResponder("GET", "https://www.adium.im/sparkle/appcast-release.xml", httpmock.NewBytesResponder(200, content))
defer httpmock.DeactivateAndReset()

// example
a := appcast.New()

p, errors := a.LoadFromRemoteSource("https://www.adium.im/sparkle/appcast-release.xml")
if p != nil && len(errors) > 0 {
	panic(errors[0])
}

a.Releases().SortByVersions(release.DESC)

fmt.Printf("%-9s %d error(s)\n", "Result:", len(errors))
fmt.Printf("%-9s %s\n", "Type:", reflect.TypeOf(a.Source().Appcast()))
fmt.Printf("%-9s %s\n", "Checksum:", a.Source().Checksum())
fmt.Printf("%-9s %s\n", "Provider:", a.Source().Provider())
fmt.Printf("%-9s %d total\n\n", "Releases:", a.Releases().Len())

r := a.Releases().First()
fmt.Print("First release details:\n\n")
fmt.Printf("%23s %s\n", "Version:", r.Version())
fmt.Printf("%23s %s\n", "Build:", r.Build())
fmt.Printf("%23s %v\n", "Pre-release:", r.IsPreRelease())
fmt.Printf("%23s %s\n", "Title:", r.Title())
fmt.Printf("%23s %v\n", "Published:", r.PublishedDateTime())
fmt.Printf("%23s %v\n", "Release notes:", r.ReleaseNotesLink())
fmt.Printf("%23s %v\n\n", "Minimum system version:", r.MinimumSystemVersion())

d := r.Downloads()[0]
fmt.Printf("%23s %d total\n\n", "Downloads:", len(r.Downloads()))
fmt.Printf("%23s %s\n", "URL:", d.Url())
fmt.Printf("%23s %s\n", "Type:", d.Filetype())
fmt.Printf("%23s %d\n", "Length:", d.Length())
fmt.Printf("%23s %s\n", "DSA Signature:", d.DsaSignature())
Output:

Result:   0 error(s)
Type:     *sparkle.Appcast
Checksum: 6ec7c5abcaa78457cc4bf3c2196584446cca1461c65505cbaf0382a2f62128db
Provider: Sparkle RSS Feed
Releases: 5 total

First release details:

               Version: 1.5.10.4
                 Build: 1.5.10.4
           Pre-release: false
                 Title: Adium 1.5.10.4
             Published: Sun, 14 May 2017 05:04:01 -0700
         Release notes: https://www.adium.im/changelogs/1.5.10.4.html
Minimum system version: 10.7.5

             Downloads: 1 total

                   URL: https://adiumx.cachefly.net/Adium_1.5.10.4.dmg
                  Type: application/octet-stream
                Length: 21140435
         DSA Signature: MC4CFQCeqQ/MxlFt2H3rQfCPimChDPibCgIVAJhZmHcU8ZHylc7EjvbkVr3ardLp

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Appcast added in v0.2.0

type Appcast struct {
	appcaster.Appcast
}

Appcast represents the appcast itself.

func New

func New(src ...interface{}) *Appcast

New returns a new Appcast instance pointer. The source can be passed as a parameter.

func (*Appcast) GuessSourceProvider added in v0.6.0

func (a *Appcast) GuessSourceProvider()

GuessSourceProvider attempts to guess the supported provider based on the Appcast.source.content.

func (*Appcast) LoadFromLocalSource added in v0.2.0

func (a *Appcast) LoadFromLocalSource(path string) (appcaster.Appcaster, []error)

LoadFromLocalSource creates a new LocalSource instance and loads the data from the local file by using the LocalSource.Load method.

It returns both: the supported provider-specific appcast implementing the Appcaster interface and an errors slice.

func (*Appcast) LoadFromRemoteSource added in v0.2.0

func (a *Appcast) LoadFromRemoteSource(i interface{}) (appcaster.Appcaster, []error)

LoadFromRemoteSource creates a new RemoteSource instance and loads the data from the remote location by using the RemoteSource.Load method.

It returns both: the supported provider-specific appcast implementing the Appcaster interface and an errors slice.

func (*Appcast) LoadSource added in v0.2.0

func (a *Appcast) LoadSource() error

LoadSource sets the Appcast.source.content field value depending on the source type. It should call the appropriate Appcast.Source.Load methods chain.

func (*Appcast) Uncomment added in v0.2.0

func (a *Appcast) Uncomment() error

Uncomment uncomments the commented out lines by calling the appropriate provider-specific Uncomment method from the supported providers.

func (*Appcast) Unmarshal added in v0.5.0

func (a *Appcast) Unmarshal() (appcaster.Appcaster, []error)

Unmarshal unmarshals the Appcast.source.content into the Appcast.releases by calling the appropriate provider-specific Unmarshal method from the supported providers.

It returns both: the supported provider-specific appcast implementing the Appcaster interface and an errors slice.

type Appcaster added in v0.2.0

type Appcaster interface {
	appcaster.Appcaster
	LoadFromRemoteSource(i interface{}) (appcaster.Appcaster, []error)
	LoadFromLocalSource(path string) (appcaster.Appcaster, []error)
}

Appcaster is the interface that wraps the Appcast methods.

Directories

Path Synopsis
Package appcaster provides the base for creating an appcast type.
Package appcaster provides the base for creating an appcast type.
Package client provides functionality for making remote client requests.
Package client provides functionality for making remote client requests.
Package output holds the supported outputs.
Package output holds the supported outputs.
Package provider holds the supported providers.
Package provider holds the supported providers.
github
Package github adds support for the GitHub releases Atom feed.
Package github adds support for the GitHub releases Atom feed.
sourceforge
Package sourceforge adds support for the SourceForge releases RSS feed.
Package sourceforge adds support for the SourceForge releases RSS feed.
sparkle
Package sparkle adds support for the Sparkle Framework releases RSS feed.
Package sparkle adds support for the Sparkle Framework releases RSS feed.
Package release provides an appcast release(s) that suits most appcast providers.
Package release provides an appcast release(s) that suits most appcast providers.
Package output holds the supported sources.
Package output holds the supported sources.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL