pm/cmd/pm/main.go

261 lines
6.6 KiB
Go
Raw Normal View History

2018-02-25 01:08:57 -08:00
package main
import (
2018-02-25 01:39:11 -08:00
"bufio"
2018-02-25 01:08:57 -08:00
"fmt"
"os"
"path/filepath"
"github.com/pkg/errors"
"mcquay.me/fs"
2018-03-02 23:23:13 -08:00
"mcquay.me/pm/db"
"mcquay.me/pm/keyring"
2018-02-26 19:58:28 -08:00
"mcquay.me/pm/pkg"
2018-02-25 01:08:57 -08:00
)
2018-03-02 23:23:21 -08:00
// Version stores the current version, and is updated at build time.
2018-03-02 23:23:18 -08:00
const Version = "dev"
2018-02-25 01:08:57 -08:00
const usage = `pm: simple, cross-platform system package manager
subcommands:
2018-03-02 23:23:13 -08:00
available (av) -- print out all installable packages
2018-02-25 01:23:38 -08:00
environ (env) -- print environment information
2018-03-03 21:28:24 -08:00
install (in) -- install packages
2018-02-25 01:08:57 -08:00
keyring (key) -- interact with pm's OpenPGP keyring
2018-03-06 00:10:46 -08:00
ls -- list installed packages
2018-02-26 19:58:28 -08:00
package (pkg) -- create packages
2018-03-02 23:23:04 -08:00
pull -- fetch all available packages from all configured remotes
remote -- configure remote pmd servers
2018-03-02 23:23:18 -08:00
version (v) -- print version information
2018-02-25 01:08:57 -08:00
`
2018-02-25 01:18:41 -08:00
const keyUsage = `pm keyring: interact with pm's OpenPGP keyring
subcommands:
create (c) -- create a fresh keypair
2018-02-26 19:07:33 -08:00
export (e) -- export a public key to stdout
import (i) -- import a public key from stdin
2018-02-28 19:50:55 -08:00
ls -- list configured key info
rm -- remove a key from the keyring
2018-02-26 19:07:33 -08:00
sign (s) -- sign a file
verify (v) -- verify a detached signature
2018-02-25 01:18:41 -08:00
`
2018-02-26 19:58:28 -08:00
const pkgUsage = `pm package: generate pm-compatible packages
subcommands:
create (c) -- create a fresh keypair
`
2018-02-28 20:00:25 -08:00
const remoteUsage = `pm remote: configure remote pmd servers
subcommands:
add (a) -- add a URI
ls -- list configured remotes
rm -- remove a URI
`
2018-02-25 01:08:57 -08:00
func main() {
if len(os.Args) < 2 {
2018-02-25 01:18:41 -08:00
fatalf("pm: missing subcommand\n\n%v", usage)
2018-02-25 01:08:57 -08:00
}
cmd := os.Args[1]
2018-02-25 01:23:38 -08:00
root := os.Getenv("PM_ROOT")
if root == "" {
root = "/usr/local"
}
2018-02-25 19:36:13 -08:00
signID := os.Getenv("PM_PGP_ID")
2018-02-25 01:23:38 -08:00
2018-02-25 01:08:57 -08:00
switch cmd {
2018-02-25 01:23:38 -08:00
case "env", "environ":
fmt.Printf("PM_ROOT=%q\n", root)
2018-02-25 19:36:13 -08:00
fmt.Printf("PM_PGP_ID=%q\n", signID)
2018-02-25 01:08:57 -08:00
case "key", "keyring":
2018-02-25 01:18:41 -08:00
if len(os.Args[1:]) < 2 {
fatalf("pm keyring: insufficient args\n\nusage: %v", keyUsage)
}
2018-02-25 10:10:08 -08:00
sub, args := os.Args[2], os.Args[3:]
2018-02-25 01:18:41 -08:00
switch sub {
2018-02-28 19:50:55 -08:00
case "ls":
2018-02-25 03:25:49 -08:00
if err := keyring.ListKeys(root, os.Stdout); err != nil {
fatalf("listing keypair: %v\n", err)
}
2018-02-25 01:18:41 -08:00
case "c", "create":
2018-02-25 01:39:11 -08:00
var name, email string
s := bufio.NewScanner(os.Stdin)
fmt.Printf("name: ")
s.Scan()
if err := s.Err(); err != nil {
fatalf("reading name: %v\n", err)
}
name = s.Text()
fmt.Printf("email: ")
s.Scan()
if err := s.Err(); err != nil {
fatalf("reading email: %v\n", err)
}
email = s.Text()
if err := os.Stdin.Close(); err != nil {
fatalf("%v\n", err)
}
if err := keyring.NewKeyPair(root, name, email); err != nil {
fatalf("creating keypair: %v\n", err)
}
2018-02-25 10:10:08 -08:00
case "export", "e":
if len(args) != 1 {
fatalf("missing email argument\n")
}
email := args[0]
if err := keyring.Export(root, os.Stdout, email); err != nil {
fatalf("exporting public key for %q: %v\n", email, err)
}
2018-02-25 19:36:13 -08:00
case "sign", "s":
if signID == "" {
fatalf("must set PM_PGP_ID\n")
}
2018-02-27 20:41:57 -08:00
e, err := keyring.FindSecretEntity(root, signID)
if err != nil {
fatalf("find secret key: %v\n", err)
}
if err := keyring.Sign(e, os.Stdin, os.Stdout); err != nil {
2018-02-25 19:36:13 -08:00
fatalf("signing: %v\n", err)
}
2018-02-25 19:56:04 -08:00
case "verify", "v":
if len(args) != 2 {
fatalf("usage: pm key verify <file> <sig>\n")
}
fn, sn := args[0], args[1]
ff, err := os.Open(fn)
if err != nil {
fatalf("opening %q: %v\n", fn, err)
}
defer ff.Close()
sf, err := os.Open(sn)
if err != nil {
fatalf("opening %q: %v\n", fn, err)
}
defer sf.Close()
if err := keyring.Verify(root, ff, sf); err != nil {
fatalf("detached sig verify: %v\n", err)
}
2018-02-25 11:03:40 -08:00
case "i", "import":
if err := keyring.Import(root, os.Stdin); err != nil {
fatalf("importing key: %v\n", err)
}
2018-02-28 19:50:55 -08:00
case "rm":
2018-02-26 19:31:31 -08:00
if len(args) != 1 {
fatalf("missing key id\n\nusage: pm key remove <id>\n")
}
id := args[0]
if err := keyring.Remove(root, id); err != nil {
fatalf("removing key for %q: %v\n", id, err)
}
2018-02-25 01:18:41 -08:00
default:
fatalf("unknown keyring subcommand: %q\n\nusage: %v", sub, keyUsage)
}
2018-02-26 19:58:28 -08:00
case "package", "pkg":
if len(os.Args[1:]) < 2 {
fatalf("pm package: insufficient args\n\nusage: %v", pkgUsage)
}
sub := os.Args[2]
switch sub {
case "create", "creat", "c":
if signID == "" {
fatalf("must set PM_PGP_ID\n")
}
args := os.Args[3:]
if len(args) != 1 {
fatalf("usage: pm package create <directory>\n")
}
dir := args[0]
2018-02-27 18:24:41 -08:00
e, err := keyring.FindSecretEntity(root, signID)
if err != nil {
fatalf("find secret key: %v\n", err)
}
if err := pkg.Create(e, dir); err != nil {
2018-02-26 19:58:28 -08:00
fatalf("creating package: %v\n", err)
}
default:
fatalf("unknown package subcommand: %q\n\nusage: %v", sub, pkgUsage)
}
2018-02-28 20:00:25 -08:00
case "remote":
if len(os.Args[1:]) < 2 {
fatalf("pm remote: insufficient args\n\nusage: %v", remoteUsage)
}
sub := os.Args[2]
args := os.Args[3:]
if err := mkdirs(root); err != nil {
fatalf("making pm var directories: %v\n", err)
}
2018-02-28 20:00:25 -08:00
switch sub {
case "add", "a":
2018-02-28 20:41:25 -08:00
if len(args) < 1 {
fatalf("missing arg\n\nusage: pm remote add [<uris>]\n")
}
2018-03-02 23:23:13 -08:00
if err := db.AddRemotes(root, args); err != nil {
2018-02-28 20:00:25 -08:00
fatalf("remote add: %v\n", err)
}
case "rm":
2018-02-28 20:41:25 -08:00
if len(args) < 1 {
fatalf("missing arg\n\nusage: pm remote rm [<uris>]\n")
}
2018-03-02 23:23:13 -08:00
if err := db.RemoveRemotes(root, args); err != nil {
2018-02-28 20:00:25 -08:00
fatalf("remote remove: %v\n", err)
}
case "ls":
2018-03-02 23:23:13 -08:00
if err := db.ListRemotes(root, os.Stdout); err != nil {
2018-02-28 20:00:25 -08:00
fatalf("list: %v\n", err)
}
default:
fatalf("unknown package subcommand: %q\n\nusage: %v", sub, remoteUsage)
}
2018-03-02 23:23:04 -08:00
case "pull":
2018-03-02 23:23:13 -08:00
if err := db.Pull(root); err != nil {
fatalf("pulling available packages: %v\n", err)
}
case "available", "av":
if err := db.ListAvailable(root, os.Stdout); err != nil {
2018-03-02 23:23:04 -08:00
fatalf("pulling available packages: %v\n", err)
}
2018-03-03 21:28:24 -08:00
case "install", "in":
if len(os.Args[1:]) < 2 {
fatalf("pm install: insufficient args\n\nusage: pm install [pkg1, pkg2, ..., pkgN]\n")
}
pkgs := os.Args[2:]
if err := pkg.Install(root, pkgs); err != nil {
fatalf("installing: %v\n", err)
}
2018-03-06 00:10:46 -08:00
case "ls":
if err := db.ListInstalled(root, os.Stdout); err != nil {
fatalf("listing installed: %v\n", err)
}
2018-03-02 23:23:18 -08:00
case "version", "v":
fmt.Printf("pm: version %v\n", Version)
2018-02-25 01:08:57 -08:00
default:
2018-02-25 01:18:41 -08:00
fatalf("uknown subcommand %q\n\nusage: %v", cmd, usage)
2018-02-25 01:08:57 -08:00
}
}
2018-02-25 01:18:41 -08:00
func fatalf(f string, args ...interface{}) {
2018-02-25 01:08:57 -08:00
fmt.Fprintf(os.Stderr, f, args...)
os.Exit(1)
}
func mkdirs(root string) error {
d := filepath.Join(root, "var", "lib", "pm")
if !fs.Exists(d) {
if err := os.MkdirAll(d, 0700); err != nil {
return errors.Wrap(err, "mk pm dir")
}
}
return nil
}