2018-03-02 23:23:13 -08:00
|
|
|
package db
|
2018-03-02 23:23:04 -08:00
|
|
|
|
2018-03-02 23:23:11 -08:00
|
|
|
import (
|
|
|
|
"encoding/json"
|
2018-03-02 23:23:13 -08:00
|
|
|
"fmt"
|
|
|
|
"io"
|
2018-03-02 23:23:11 -08:00
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
2018-03-02 23:23:13 -08:00
|
|
|
"mcquay.me/fs"
|
2018-03-02 23:23:11 -08:00
|
|
|
"mcquay.me/pm"
|
|
|
|
)
|
|
|
|
|
|
|
|
const an = "var/lib/pm/available.json"
|
2018-03-02 23:23:04 -08:00
|
|
|
|
|
|
|
// Pull updates the available package database.
|
|
|
|
func Pull(root string) error {
|
2018-03-02 23:23:11 -08:00
|
|
|
db, err := load(root)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "loading db")
|
|
|
|
}
|
|
|
|
|
|
|
|
o := pm.Available{}
|
|
|
|
|
|
|
|
// Order here is important: the guarantee made is that any packages that
|
|
|
|
// exist in multiple remotes will be fetched by the first configured
|
|
|
|
// remote, which is why we traverse the database in reverse.
|
|
|
|
//
|
|
|
|
// TODO (sm): make this concurrent
|
|
|
|
for i := range db {
|
|
|
|
u := db[len(db)-i-1]
|
|
|
|
resp, err := http.Get(u.String() + "/available.json")
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "http get")
|
|
|
|
}
|
|
|
|
|
|
|
|
a := pm.Available{}
|
|
|
|
if err := json.NewDecoder(resp.Body).Decode(&a); err != nil {
|
2018-03-02 23:23:14 -08:00
|
|
|
return errors.Wrapf(err, "decode remote available for %q", u.String())
|
2018-03-02 23:23:11 -08:00
|
|
|
}
|
|
|
|
a.SetRemote(u)
|
|
|
|
o.Update(a)
|
|
|
|
}
|
|
|
|
if err := saveAvailable(root, o); err != nil {
|
|
|
|
return errors.Wrap(err, "saving available db")
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-03-02 23:23:13 -08:00
|
|
|
// ListAvailable prints all installable packages
|
|
|
|
func ListAvailable(root string, w io.Writer) error {
|
2018-03-03 21:29:55 -08:00
|
|
|
db, err := LoadAvailable(root)
|
2018-03-02 23:23:13 -08:00
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "loading")
|
|
|
|
}
|
2018-03-02 23:23:16 -08:00
|
|
|
for m := range db.Traverse() {
|
|
|
|
fmt.Fprintf(w, "%v\t%v\t%v\n", m.Name, m.Version, m.Remote.String())
|
2018-03-02 23:23:13 -08:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-03-03 21:29:55 -08:00
|
|
|
// LoadAvailable returns the collection of available packages
|
|
|
|
func LoadAvailable(root string) (pm.Available, error) {
|
2018-03-02 23:23:13 -08:00
|
|
|
r := pm.Available{}
|
|
|
|
dbn := filepath.Join(root, rn)
|
|
|
|
|
|
|
|
if !fs.Exists(dbn) {
|
|
|
|
return r, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := os.Open(filepath.Join(root, an))
|
|
|
|
if err != nil {
|
|
|
|
return r, errors.Wrap(err, "open")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := json.NewDecoder(f).Decode(&r); err != nil {
|
|
|
|
return r, errors.Wrap(err, "decoding db")
|
|
|
|
}
|
|
|
|
|
|
|
|
return r, nil
|
|
|
|
}
|
|
|
|
|
2018-03-02 23:23:11 -08:00
|
|
|
func saveAvailable(root string, db pm.Available) error {
|
|
|
|
f, err := os.Create(filepath.Join(root, an))
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "create")
|
|
|
|
}
|
|
|
|
enc := json.NewEncoder(f)
|
|
|
|
enc.SetIndent("", "\t")
|
|
|
|
if err := enc.Encode(&db); err != nil {
|
|
|
|
return errors.Wrap(err, "decoding db")
|
|
|
|
}
|
|
|
|
if err := f.Close(); err != nil {
|
|
|
|
return errors.Wrap(err, "close db")
|
|
|
|
}
|
|
|
|
return nil
|
2018-03-02 23:23:04 -08:00
|
|
|
}
|