marcosnils / bin

Effortless binary manager

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

I did some thinking about this. I understand, that the initial idea of `bin` is defined in the README.md as follows:

luis33338 opened this issue · comments

I did some thinking about this. I understand, that the initial idea of bin is defined in the README.md as follows:

bin started as an idea given the popularity of single binary releases ...

So currently the scope is limited to single binary releases. Nevertheless I would like to propose a slight extension of the current code structure, which would allow for easier addition of other use cases like support for extracting the full content of archives (#53) and deb / rpm (#71, this issue).

As I understand bin, it has roughly the following process for the install, update and ensure commands:

┌──────────────────┐
│                  │
│      Start       │ (mainly for the commands install, update and ensure)
│                  │
└────────┬─────────┘
         │
         │resolve Provider
         │
┌────────▼─────────┐
│                  │
│     Provider     │ (like today, select the provider based on characteristics of the URL)
│                  │
└────────┬─────────┘
         │
         │list
         │
┌────────▼─────────┐
│                  │
│    Candidates    │
│                  │
└────────┬─────────┘
         │
         │filter / select (automatically by Arch, OS, Version, prefered Archive type; manually)
         │
┌────────▼─────────┐
│                  │
│     Target       │
│                  │
└────────┬─────────┘
         │
         │get
         │
┌────────▼─────────┐
│                  │
│     Archive      │ (regular archives like zip, tar.gz, but also "archives" like single executable, deb or rpm)
│                  │
└────────┬─────────┘
         │
         │unarchive
         │
┌────────▼─────────┐
│                  │
│     Content      │ (maybe this could be implemented as in memory io/fs.FS)
│                  │
└────────┬─────────┘
         │
         │process / install (this could mean different things depending on user selection or type of archive)
         │
┌────────▼─────────┐
│                  │
│       End        │
│                  │
└──────────────────┘

Some parts are already abstracted pretty well (e.g. support for different providers, support for different archive types), some parts are currently not abstracted at all (e.g. saveToDisk).
If we could agree some easy to implement interfaces, several different implementations could life next to each other and the user would have the possibility to select the outcome he would like to have.

So for the stages mentioned above I propose interfaces like this (the interfaces might not yet be complete):

type Provider interface {
  List() ([]assets.Asset, error)
  Get(asset.Asset) (io.Reader, error)
}

// Filter work like middlewares and are therefore chainable,
// the result is an ordered list of assets.Asset, where the asset.Asset with the highest priority has index 0.
// So there could be strict filters, that remove non matching items from the list or even
// a manual filter, which just returns a list with one (the maually selected) item.
type Filter func([]assets.Asset) []assets.Asset

// Archive converts a io.Reader to a fs.FS (more specific testing/fstest.MemFS). For a zip archive, the fs.FS contains all files.
// For an executable binary, the fs.FS just contains a single file, the executable binary it self.
// The same would apply for deb / rpm files, because we do not unarchive these files in bin
// but pass them "as is" to the next stage.
type Archive interface {
  Unarchive(io.Reader) (fs.FS, error)
}

// Install functions take a fs.FS and perform the actual installation to disk.
// For the current behavior of bin, this boils down to selecting the binary executable
// and copy it to the target directory. For a different implementation of Install this could
// mean, to save the whole content of the fs.FS to an other target directory
// and only link the binary to the binaries directory. For deb / rpm, this would store
// the content of the fs.FS to a temporary folder and launch dpkg or rpm for the
// installation.
//
// Eventually, the install command could be wrapped with "middlewares" as well, which
// would allow to filter the content of the fs.FS (e.g. automatically exclude files or manually
// select the files, that should get installed.
type Install func(fs.FS) error

I am pretty sure, the signatures of the functions are not yet correct nor the interfaces complete. But I hope to provide some thoughts how bin could be evolved further.

I look forward to your thoughts.

Originally posted by @breml in #71 (comment)

This is a dupe of #71 (comment)