├── debian ├── compat ├── docs ├── source │ ├── format │ └── options ├── changelog ├── copyright ├── README.Debian ├── README.source ├── control └── rules ├── .gitignore ├── osx └── build_pkg.sh ├── .gitmodules ├── qpm ├── commands │ ├── base.go │ ├── list.go │ ├── ping.go │ ├── search.go │ ├── help.go │ ├── info.go │ ├── uninstall.go │ ├── check.go │ ├── verify.go │ ├── publish.go │ ├── sign.go │ ├── init.go │ └── install.go ├── core │ ├── context.go │ └── utils.go ├── registry.go ├── qpm.go └── vcs │ ├── vcs.go │ ├── mercurial.go │ ├── git.go │ └── github.go ├── .travis.yml ├── Makefile ├── common ├── messages │ ├── qpm.proto │ └── qpm.pb.go └── package.go ├── tools └── packager │ └── packager.go ├── LICENSE └── README.md /debian/compat: -------------------------------------------------------------------------------- 1 | 8 2 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | README.md 2 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /debian/source/options: -------------------------------------------------------------------------------- 1 | compression = gzip 2 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | qpm (0.10.0-1) unstable; urgency=low 2 | 3 | * Initial release 4 | 5 | -- Ben Lau Thu, 31 Dec 2015 08:13:02 +0000 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # WebStorm settings 2 | .idea/* 3 | 4 | # generated parts of GOPATH 5 | bin/ 6 | pkg/ 7 | 8 | # build files 9 | staging/ 10 | repository/ 11 | 12 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://dep.debian.net/deps/dep5 2 | Upstream-Name: qpm 3 | Source: https://github.com/Cutehacks/qpm/ 4 | Copyright: 2015 Cutehacks AS 5 | License: Artistic 2.0 6 | -------------------------------------------------------------------------------- /osx/build_pkg.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | mkdir ./dstroot 3 | install -m 755 ../bin/qpm ./dstroot/ 4 | pkgbuild --identifier io.qpm --root ./dstroot/ --install-location /usr/local/bin qpm.pkg -------------------------------------------------------------------------------- /debian/README.Debian: -------------------------------------------------------------------------------- 1 | qpm for Debian 2 | -------------- 3 | 4 | 5 | 6 | -- Ben Lau Thu, 31 Dec 2015 08:13:02 +0000 7 | -------------------------------------------------------------------------------- /debian/README.source: -------------------------------------------------------------------------------- 1 | qpm for Debian 2 | -------------- 3 | 4 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: qpm 2 | Section: unknown 3 | Priority: extra 4 | Maintainer: Ben Lau 5 | Build-Depends: debhelper (>= 8.0.0), golang-go 6 | Standards-Version: 3.9.2 7 | Homepage: http://qpm.io 8 | #Vcs-Git: git://git.debian.org/collab-maint/qpm.git 9 | #Vcs-Browser: http://git.debian.org/?p=collab-maint/qpm.git;a=summary 10 | 11 | Package: qpm 12 | Architecture: any 13 | Depends: 14 | Description: A Package Manager for Qt 15 | 16 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "protobuf"] 2 | path = vendor/github.com/golang/protobuf 3 | url = https://github.com/golang/protobuf.git 4 | [submodule "net"] 5 | path = vendor/golang.org/x/net 6 | url = https://go.googlesource.com/net 7 | [submodule "grpc"] 8 | path = vendor/google.golang.org/grpc 9 | url = https://github.com/grpc/grpc-go 10 | [submodule "gopass"] 11 | path = vendor/github.com/howeyc/gopass 12 | url = https://github.com/howeyc/gopass 13 | [submodule "crypto"] 14 | path = vendor/golang.org/x/crypto 15 | url = https://go.googlesource.com/crypto 16 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | # Sample debian/rules that uses debhelper. 4 | # This file was originally written by Joey Hess and Craig Small. 5 | # As a special exception, when this file is copied by dh-make into a 6 | # dh-make output file, you may use that output file without restriction. 7 | # This special exception was added by Craig Small in version 0.37 of dh-make. 8 | 9 | # Uncomment this to turn on verbose mode. 10 | export DH_VERBOSE=1 11 | export GOPATH=$(CURDIR) 12 | 13 | %: 14 | dh $@ 15 | 16 | override_dh_auto_build: 17 | go build qpm.io/qpm 18 | 19 | override_dh_auto_install: 20 | mkdir -p $(CURDIR)/debian/qpm/usr/bin 21 | cp qpm $(CURDIR)/debian/qpm/usr/bin 22 | -------------------------------------------------------------------------------- /qpm/commands/base.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package commands 5 | 6 | import ( 7 | "qpm.io/qpm/core" 8 | ) 9 | 10 | type BaseCommand struct { 11 | Ctx core.Context 12 | } 13 | 14 | func (bc BaseCommand) Log(msg string) { 15 | bc.Ctx.Log.Print(msg) 16 | } 17 | 18 | func (bc BaseCommand) Info(msg string) { 19 | bc.Ctx.Log.Print("INFO: " + msg) 20 | } 21 | 22 | func (bc BaseCommand) Warning(msg string) { 23 | bc.Ctx.Log.Print("WARNING: " + msg) 24 | } 25 | 26 | func (bc BaseCommand) Error(err error) { 27 | bc.Ctx.Log.Print("ERROR: " + err.Error()) 28 | } 29 | 30 | func (bc BaseCommand) Fatal(msg string) { 31 | bc.Ctx.Log.Fatal(msg) 32 | } 33 | -------------------------------------------------------------------------------- /qpm/commands/list.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package commands 5 | 6 | import ( 7 | "flag" 8 | "golang.org/x/net/context" 9 | msg "qpm.io/common/messages" 10 | "qpm.io/qpm/core" 11 | ) 12 | 13 | type ListCommand struct { 14 | BaseCommand 15 | } 16 | 17 | func NewListCommand(ctx core.Context) *ListCommand { 18 | return &ListCommand{ 19 | BaseCommand: BaseCommand{ 20 | Ctx: ctx, 21 | }, 22 | } 23 | } 24 | 25 | func (sc ListCommand) Description() string { 26 | return "Lists all packages in the registry" 27 | } 28 | 29 | func (sc *ListCommand) RegisterFlags(flags *flag.FlagSet) { 30 | } 31 | 32 | func (sc *ListCommand) Run() error { 33 | 34 | req := &msg.ListRequest{} 35 | 36 | response, err := sc.Ctx.Client.List(context.Background(), req) 37 | if err != nil { 38 | sc.Fatal("ERROR:" + err.Error()) 39 | } 40 | 41 | results := response.GetResults() 42 | core.PrintSearchResults(results) 43 | 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /qpm/commands/ping.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package commands 5 | 6 | import ( 7 | "flag" 8 | "fmt" 9 | "golang.org/x/net/context" 10 | msg "qpm.io/common/messages" 11 | "qpm.io/qpm/core" 12 | "time" 13 | ) 14 | 15 | type PingCommand struct { 16 | BaseCommand 17 | } 18 | 19 | func NewPingCommand(ctx core.Context) *PingCommand { 20 | return &PingCommand{ 21 | BaseCommand: BaseCommand{ 22 | Ctx: ctx, 23 | }, 24 | } 25 | } 26 | 27 | func (p PingCommand) Description() string { 28 | return "Pings the server" 29 | } 30 | 31 | func (p *PingCommand) RegisterFlags(flags *flag.FlagSet) { 32 | } 33 | 34 | func (p *PingCommand) Run() error { 35 | 36 | before := time.Now() 37 | 38 | _, err := p.Ctx.Client.Ping(context.Background(), &msg.PingRequest{}) 39 | 40 | if err != nil { 41 | p.Fatal("Cannot ping server:" + err.Error()) 42 | } 43 | 44 | d := time.Since(before) 45 | 46 | fmt.Printf("SUCCESS! Ping took %v\n", d) 47 | 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /qpm/commands/search.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package commands 5 | 6 | import ( 7 | "flag" 8 | "golang.org/x/net/context" 9 | msg "qpm.io/common/messages" 10 | "qpm.io/qpm/core" 11 | ) 12 | 13 | type SearchCommand struct { 14 | BaseCommand 15 | fs *flag.FlagSet 16 | } 17 | 18 | func NewSearchCommand(ctx core.Context) *SearchCommand { 19 | return &SearchCommand{ 20 | BaseCommand: BaseCommand{ 21 | Ctx: ctx, 22 | }, 23 | } 24 | } 25 | 26 | func (sc SearchCommand) Description() string { 27 | return "Searches for packages containing the given string" 28 | } 29 | 30 | func (sc *SearchCommand) RegisterFlags(flags *flag.FlagSet) { 31 | sc.fs = flags 32 | } 33 | 34 | func (sc *SearchCommand) Run() error { 35 | 36 | packageName := sc.fs.Arg(0) 37 | 38 | req := &msg.SearchRequest{PackageName: packageName} 39 | 40 | response, err := sc.Ctx.Client.Search(context.Background(), req) 41 | if err != nil { 42 | sc.Fatal("ERROR:" + err.Error()) 43 | } 44 | 45 | results := response.GetResults() 46 | core.PrintSearchResults(results) 47 | 48 | return nil 49 | } 50 | -------------------------------------------------------------------------------- /qpm/core/context.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | "google.golang.org/grpc" 6 | "google.golang.org/grpc/credentials" 7 | "log" 8 | "os" 9 | msg "qpm.io/common/messages" 10 | "runtime" 11 | ) 12 | 13 | var ( 14 | Version = "0.X.x" 15 | Build = "master" 16 | ) 17 | 18 | const ( 19 | PackageFile = "qpm.json" 20 | SignatureFile = "qpm.asc" 21 | Vendor = "vendor" 22 | Address = "pkg.qpm.io:7000" 23 | LicenseFile = "LICENSE" 24 | ) 25 | 26 | var UA = fmt.Sprintf("qpm/%v (%s; %s)", Version, runtime.GOOS, runtime.GOARCH) 27 | 28 | type Context struct { 29 | Log *log.Logger 30 | Client msg.QpmClient 31 | } 32 | 33 | func NewContext() *Context { 34 | log := log.New(os.Stderr, "QPM: ", log.LstdFlags) 35 | 36 | address := os.Getenv("SERVER") 37 | if address == "" { 38 | address = Address 39 | } 40 | 41 | noTls := os.Getenv("NO_TLS") == "1" 42 | 43 | var tlsOption grpc.DialOption 44 | if noTls { 45 | tlsOption = grpc.WithInsecure() 46 | } else { 47 | tlsOption = grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")) 48 | } 49 | 50 | conn, err := grpc.Dial(address, tlsOption, grpc.WithUserAgent(UA)) 51 | 52 | if err != nil { 53 | log.Fatalf("did not connect: %v", err) 54 | } 55 | 56 | return &Context{ 57 | Log: log, 58 | Client: msg.NewQpmClient(conn), 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /qpm/registry.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package main 5 | 6 | import ( 7 | "flag" 8 | "fmt" 9 | ) 10 | 11 | type SubCommand interface { 12 | Run() error 13 | Description() string 14 | RegisterFlags(*flag.FlagSet) 15 | } 16 | 17 | type CommandRegistry struct { 18 | Commands map[string]SubCommand 19 | } 20 | 21 | func NewCommandRegistry() *CommandRegistry { 22 | return &CommandRegistry{ 23 | Commands: make(map[string]SubCommand), 24 | } 25 | } 26 | 27 | func (c *CommandRegistry) RegisterSubCommand(command string, handler SubCommand) error { 28 | if c.Exists(command) { 29 | return fmt.Errorf("Command already exists: %s", command) 30 | } 31 | 32 | c.Commands[command] = handler 33 | return nil 34 | } 35 | 36 | func (c CommandRegistry) Exists(command string) bool { 37 | _, exists := c.Commands[command] 38 | return exists 39 | } 40 | 41 | func (c CommandRegistry) Get(command string) SubCommand { 42 | return c.Commands[command] 43 | } 44 | 45 | func (c CommandRegistry) CommandUsage() { 46 | fmt.Println(` 47 | The currently supported commands are: 48 | `) 49 | for k, v := range c.Commands { 50 | t := "\t\t" 51 | l := 2 - (len(k) / 4) 52 | for i := 0; i < l; i++ { 53 | t += "\t" 54 | } 55 | fmt.Printf("\t%s%s%s\n", k, t, v.Description()) 56 | } 57 | fmt.Println("") 58 | } 59 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | sudo: false 4 | 5 | go_import_path: qpm.io 6 | 7 | cache: 8 | directories: 9 | - $HOME/protobuf 10 | 11 | before_install: 12 | - wget https://github.com/google/protobuf/archive/v3.0.2.tar.gz 13 | - tar -xzvf v3.0.2.tar.gz 14 | - if [ ! -d "$HOME/protobuf/lib" ]; then pushd protobuf-3.0.2 && ./autogen.sh && ./configure --prefix=$HOME/protobuf && make && make install && popd; else echo 'Using cached directory.'; fi 15 | 16 | install: 17 | - go get -u github.com/golang/protobuf/protoc-gen-go 18 | 19 | before_script: 20 | - export PATH=$HOME/protobuf/bin:$PATH 21 | 22 | script: 23 | - make .all 24 | 25 | go: 26 | - 1.14 27 | 28 | deploy: 29 | skip_cleanup: true 30 | provider: gcs 31 | access_key_id: GOOGWRTZDIQHLUSTM5H7 32 | secret_access_key: 33 | secure: HtrM7Cauk8TNt92G2b/CIEUtJRsR/rHyEwdRzsFlmC1NnBE9lZ2dWCKo/nm6f4wcBpMNdvWVav5bWJn2JeTBzkFw3HhLNpBtc973TJaJAbIIGj9xAjlKOcNNRIg4FqZRGOrsGHtuisqJ1F/7hoUH8m2WufW95A+3y0UsOlyOx0N4KfGo54fVMKGpcOYj0zrKxz+Roam79S12L8pELnA+rpwlEzaePPqiz8CVn1u6hDsTCJTgI2Q0p3xGN1jMrei3H8aEVU/5TPAKIIyfprghqCYHMxwZnWEyDQ/88PxnhqOZ91pigA3g0xQtMUKokLqLboGFDjdWjqzUHWnAlGS9e3FiPdQrhbYHNWlRSV1/hxdgxwTvHMiaAbCPEUuZGL8WpFuZ0T7qaTuYVxxJzGKnD/AiCNEPx37XvbUYEhqbXX2NzO3cXY7pAI7wj4slCuug/MKoOQbL01JsqU4DiSUukCr7xzcH8Aj7O0ZlNeFG/3BAjvrUR9+vSyGGYe4bKIQ/IC7xicFFJM1YDshx2lSG2TVfmxm8Dg4XgdKKr3l5ji/M9prQiNW1Uxx17QaWmLI35+pZeVEUy85+gWPNP+1Be7s43V3fWwqdzhzPdzBV8RE+1vA+C70nXmwpftz2ycxnzC7GdfoHnzUPolSm147J0OjywkmzPeaVjfLEfofB49A= 34 | bucket: www.qpm.io 35 | local-dir: $GOPATH/bin 36 | upload-dir: download/latest 37 | acl: public-read 38 | on: 39 | repo: Cutehacks/qpm 40 | -------------------------------------------------------------------------------- /qpm/qpm.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package main 5 | 6 | import ( 7 | "flag" 8 | "fmt" 9 | "os" 10 | cmd "qpm.io/qpm/commands" 11 | "qpm.io/qpm/core" 12 | ) 13 | 14 | var registry *CommandRegistry 15 | 16 | func Usage() { 17 | fmt.Println(` 18 | qpm is a tool for managing Qt dependencies 19 | 20 | Usage: 21 | qpm COMMAND [args]`) 22 | 23 | registry.CommandUsage() 24 | 25 | fmt.Printf("qpm@%s (built from %s)\n\n", core.Version, core.Build) 26 | } 27 | 28 | func main() { 29 | 30 | ctx := *core.NewContext() 31 | 32 | // Register new sub-commands here 33 | registry = NewCommandRegistry() 34 | registry.RegisterSubCommand("ping", cmd.NewPingCommand(ctx)) 35 | registry.RegisterSubCommand("init", cmd.NewInitCommand(ctx)) 36 | registry.RegisterSubCommand("search", cmd.NewSearchCommand(ctx)) 37 | registry.RegisterSubCommand("list", cmd.NewListCommand(ctx)) 38 | registry.RegisterSubCommand("info", cmd.NewInfoCommand(ctx)) 39 | registry.RegisterSubCommand("install", cmd.NewInstallCommand(ctx)) 40 | registry.RegisterSubCommand("uninstall", cmd.NewUninstallCommand(ctx)) 41 | registry.RegisterSubCommand("publish", cmd.NewPublishCommand(ctx)) 42 | registry.RegisterSubCommand("help", cmd.NewHelpCommand(ctx)) 43 | registry.RegisterSubCommand("check", cmd.NewCheckCommand(ctx)) 44 | registry.RegisterSubCommand("sign", cmd.NewSignCommand(ctx)) 45 | registry.RegisterSubCommand("verify", cmd.NewVerifyCommand(ctx)) 46 | //registry.RegisterSubCommand("deprecate", cmd.NewDeprecateCommand(ctx)) 47 | //registry.RegisterSubCommand("prune", cmd.NewPruneCommand(ctx)) 48 | 49 | if len(os.Args) < 2 { 50 | Usage() 51 | os.Exit(1) 52 | return 53 | } 54 | 55 | subCmd := os.Args[1] 56 | 57 | if !registry.Exists(subCmd) { 58 | Usage() 59 | os.Exit(1) 60 | return 61 | } 62 | 63 | command := registry.Get(subCmd) 64 | 65 | fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError) 66 | fs.Usage = func() { 67 | Usage() 68 | fs.PrintDefaults() 69 | } 70 | 71 | command.RegisterFlags(fs) 72 | 73 | fs.Parse(os.Args[2:]) 74 | 75 | if command.Run() != nil { 76 | os.Exit(1) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /qpm/commands/help.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package commands 5 | 6 | import ( 7 | "flag" 8 | "fmt" 9 | "qpm.io/qpm/core" 10 | ) 11 | 12 | type HelpCommand struct { 13 | BaseCommand 14 | fs *flag.FlagSet 15 | } 16 | 17 | func NewHelpCommand(ctx core.Context) *HelpCommand { 18 | return &HelpCommand{ 19 | BaseCommand: BaseCommand{ 20 | Ctx: ctx, 21 | }, 22 | } 23 | } 24 | 25 | func (h HelpCommand) Description() string { 26 | return "Shows the help text for a command" 27 | } 28 | 29 | func (h *HelpCommand) RegisterFlags(flags *flag.FlagSet) { 30 | h.fs = flags 31 | } 32 | 33 | func (h *HelpCommand) Run() error { 34 | 35 | commandName := h.fs.Arg(0) 36 | 37 | switch commandName { 38 | case "ping": 39 | fmt.Println(` 40 | Checks if we are able to reach the server. 41 | 42 | Usage: 43 | qpm ping 44 | `) 45 | 46 | case "init": 47 | fmt.Println(` 48 | Generates the necessary files for publishing a package to the qpm registry. 49 | 50 | Usage: 51 | qpm init 52 | `) 53 | 54 | case "install": 55 | fmt.Println(` 56 | Installs the packages listed as dependencies in the package file or the given [PACKAGE]. 57 | 58 | Usage: 59 | qpm install [PACKAGE] 60 | `) 61 | 62 | case "uninstall": 63 | fmt.Println(` 64 | Removes the given [PACKAGE] from the project and deletes the associated files. 65 | 66 | Usage: 67 | qpm uninstall [PACKAGE] 68 | `) 69 | 70 | case "publish": 71 | fmt.Println(` 72 | Publishes project as a package in the qpm registry. 73 | 74 | Usage: 75 | qpm publish 76 | `) 77 | 78 | case "sign": 79 | fmt.Println(` 80 | Creates a PGP signature for contents of the project. 81 | 82 | Usage: 83 | qpm sign 84 | `) 85 | 86 | case "verify": 87 | fmt.Println(` 88 | Verifies the the content and publisher of the given [PACKAGE], provided the package has been signed. 89 | 90 | Usage: 91 | qpm verify [PACKAGE] 92 | `) 93 | 94 | case "help": 95 | fallthrough 96 | 97 | default: 98 | fmt.Println(` 99 | Shows the help text for the given [COMMAND]. If [COMMAND] is empty, it shows this text. 100 | 101 | Usage: 102 | qpm help COMMAND 103 | `) 104 | 105 | } 106 | 107 | return nil 108 | } 109 | -------------------------------------------------------------------------------- /qpm/commands/info.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package commands 5 | 6 | import ( 7 | "flag" 8 | "os" 9 | "text/template" 10 | 11 | "golang.org/x/net/context" 12 | msg "qpm.io/common/messages" 13 | "qpm.io/qpm/core" 14 | ) 15 | 16 | const infoBody = ` 17 | Name: {{.Package.Name}} 18 | Author: {{.Package.Author.Name}} ({{.Package.Author.Email}}) 19 | Webpage: {{.Package.Webpage}} 20 | License: {{.Package.License.String}} 21 | Repository: {{.Package.Repository.Url}} 22 | Description: {{.Package.Description}} 23 | Dependencies: 24 | {{- with .Dependencies}} 25 | {{range $index, $dependency := . }} 26 | {{$dependency.Name}}@{{$dependency.Version}} 27 | {{end}} 28 | {{else}} 29 | None. 30 | {{end}} 31 | Versions: 32 | {{- with .Versions}} 33 | {{range $index, $version := .}} 34 | {{- $date := $version.DatePublished | toDate -}} 35 | {{$version.Version.Label}} [{{$date.Format "02/01/06 15:04"}}] 36 | {{end -}} 37 | {{else}} 38 | No versions have been published. 39 | {{end}} 40 | Installation Statistics: 41 | {{- with .InstallStats}} 42 | Today: {{.Daily}} 43 | This week: {{.Weekly}} 44 | This month: {{.Monthly}} 45 | This year: {{.Yearly}} 46 | Total: {{.Total}} 47 | {{else}} 48 | Not available. 49 | {{end -}} 50 | ` 51 | 52 | var funcs = template.FuncMap{ 53 | "toDate": core.ToDateTime, 54 | } 55 | 56 | var infoTemplate = template.Must(template.New("info").Funcs(funcs).Parse(infoBody)) 57 | 58 | type InfoCommand struct { 59 | BaseCommand 60 | fs *flag.FlagSet 61 | } 62 | 63 | func NewInfoCommand(ctx core.Context) *InfoCommand { 64 | return &InfoCommand{ 65 | BaseCommand: BaseCommand{ 66 | Ctx: ctx, 67 | }, 68 | } 69 | } 70 | 71 | func (p InfoCommand) Description() string { 72 | return "Displays information about the specified package" 73 | } 74 | 75 | func (p *InfoCommand) RegisterFlags(flags *flag.FlagSet) { 76 | p.fs = flags 77 | } 78 | 79 | func (p *InfoCommand) Run() error { 80 | 81 | packageName := p.fs.Arg(0) 82 | 83 | response, err := p.Ctx.Client.Info(context.Background(), &msg.InfoRequest{PackageName: packageName}) 84 | 85 | if err != nil { 86 | p.Fatal(err.Error()) 87 | } 88 | 89 | if err := infoTemplate.Execute(os.Stdout, response); err != nil { 90 | p.Fatal(err.Error()) 91 | } 92 | 93 | return nil 94 | } 95 | -------------------------------------------------------------------------------- /qpm/vcs/vcs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package vcs 5 | 6 | import ( 7 | "fmt" 8 | "os" 9 | 10 | "qpm.io/common" 11 | msg "qpm.io/common/messages" 12 | ) 13 | 14 | // Installer - generic interface to functionality needed to install packages 15 | type Installer interface { 16 | Install(repository *msg.Package_Repository, version *msg.Package_Version, destination string) (*common.PackageWrapper, error) 17 | } 18 | 19 | func CreateInstaller(repository *msg.Package_Repository) (Installer, error) { 20 | 21 | switch repository.Type { 22 | case msg.RepoType_GIT: 23 | git := NewGit() 24 | if err := git.Test(); err == nil { 25 | return git, nil 26 | } 27 | case msg.RepoType_GITHUB: 28 | git := NewGit() 29 | if err := git.Test(); err == nil { 30 | return git, nil 31 | } 32 | return NewGitHub(), nil 33 | case msg.RepoType_MERCURIAL: 34 | hg := NewMercurial() 35 | if err := hg.Test(); err == nil { 36 | return hg, nil 37 | } 38 | } 39 | 40 | return nil, fmt.Errorf("Repository type %s is not supported", msg.RepoType_name[int32(repository.Type)]) 41 | } 42 | 43 | // Publisher - generic interface to VCS functionality need to publish packages 44 | type Publisher interface { 45 | Test() error 46 | CreateTag(name string) error 47 | ValidateCommit(commit string) error 48 | RepositoryURL() (string, error) 49 | LastCommitRevision() (string, error) 50 | LastCommitAuthorName() (string, error) 51 | LastCommitEmail() (string, error) 52 | RepositoryFileList() ([]string, error) 53 | } 54 | 55 | func CreatePublisher(repository *msg.Package_Repository) (Publisher, error) { 56 | 57 | switch repository.Type { 58 | case msg.RepoType_GIT: 59 | fallthrough 60 | case msg.RepoType_GITHUB: 61 | git := NewGit() 62 | if err := git.Test(); err != nil { 63 | return nil, err 64 | } 65 | return git, nil 66 | case msg.RepoType_MERCURIAL: 67 | hg := NewMercurial() 68 | if err := hg.Test(); err != nil { 69 | return nil, err 70 | } 71 | return hg, nil 72 | } 73 | 74 | return nil, fmt.Errorf("Repository type %s is not supported", msg.RepoType_name[int32(repository.Type)]) 75 | } 76 | 77 | func exists(path string) (bool, error) { 78 | _, err := os.Stat(path) 79 | if err == nil { 80 | return true, nil 81 | } 82 | if os.IsNotExist(err) { 83 | return false, nil 84 | } 85 | return true, err 86 | } 87 | 88 | func RepoType() (msg.RepoType, error) { 89 | if yes, _ := exists(".git"); yes { 90 | return msg.RepoType_GIT, nil 91 | } 92 | if yes, _ := exists(".hg"); yes { 93 | return msg.RepoType_MERCURIAL, nil 94 | } 95 | return msg.RepoType_AUTO, fmt.Errorf("Could not auto-detect the repository type") 96 | } 97 | -------------------------------------------------------------------------------- /qpm/commands/uninstall.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package commands 5 | 6 | import ( 7 | "flag" 8 | "fmt" 9 | "io" 10 | "os" 11 | "path" 12 | "path/filepath" 13 | 14 | "qpm.io/common" 15 | "qpm.io/qpm/core" 16 | ) 17 | 18 | type UninstallCommand struct { 19 | BaseCommand 20 | fs *flag.FlagSet 21 | vendorDir string 22 | } 23 | 24 | func NewUninstallCommand(ctx core.Context) *UninstallCommand { 25 | return &UninstallCommand{ 26 | BaseCommand: BaseCommand{ 27 | Ctx: ctx, 28 | }, 29 | } 30 | } 31 | 32 | func (u UninstallCommand) Description() string { 33 | return "Uninstalls a package" 34 | } 35 | 36 | func (u *UninstallCommand) RegisterFlags(flags *flag.FlagSet) { 37 | u.fs = flags 38 | 39 | var err error 40 | u.vendorDir, err = filepath.Abs(core.Vendor) 41 | if err != nil { 42 | u.vendorDir = core.Vendor 43 | } 44 | } 45 | 46 | func (u UninstallCommand) isEmpty(name string) (error, bool) { 47 | f, err := os.Open(name) 48 | if err != nil { 49 | return err, false 50 | } 51 | defer f.Close() 52 | 53 | _, err = f.Readdirnames(1) 54 | if err == io.EOF { 55 | return nil, true 56 | } 57 | return err, false 58 | } 59 | 60 | func (u *UninstallCommand) Run() error { 61 | 62 | packageName := u.fs.Arg(0) 63 | 64 | if packageName == "" { 65 | err := fmt.Errorf("Must supply a package to uninstall") 66 | u.Error(err) 67 | return err 68 | } 69 | 70 | dependencyMap, err := common.LoadPackages(u.vendorDir) 71 | if err != nil { 72 | u.Error(err) 73 | return err 74 | } 75 | 76 | toRemove, exists := dependencyMap[packageName] 77 | if !exists { 78 | err := fmt.Errorf("Package %s was not found", packageName) 79 | u.Error(err) 80 | return err 81 | } 82 | 83 | // Does the current directory contain a package file that needs updating? 84 | pkg, err := common.LoadPackage("") 85 | if err != nil && !os.IsNotExist(err) { 86 | u.Error(err) 87 | return err 88 | } else if err == nil { 89 | pkg.RemoveDependency(toRemove) 90 | if err := pkg.Save(); err != nil { 91 | u.Error(err) 92 | return err 93 | } 94 | } 95 | 96 | fmt.Println("Uninstalling", toRemove.Name) 97 | 98 | // Final step is to delete the dependency's directory. This should 99 | // be done last since after this step, the info about the package is 100 | // gone. 101 | 102 | if err := os.RemoveAll(toRemove.RootDir()); err != nil { 103 | u.Error(err) 104 | return err 105 | } 106 | 107 | // Cleanup empty leaf directories in parent dirs 108 | dir := path.Clean(toRemove.RootDir() + "/..") 109 | for dir != u.vendorDir { 110 | if _, empty := u.isEmpty(dir); empty { 111 | os.Remove(dir) 112 | } 113 | dir = path.Clean(dir + "/..") 114 | } 115 | 116 | // Regenerate vendor.pri 117 | if err := GenerateVendorPri(u.vendorDir, pkg); err != nil { 118 | u.Error(err) 119 | return err 120 | } 121 | 122 | return nil 123 | } 124 | -------------------------------------------------------------------------------- /qpm/vcs/mercurial.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package vcs 5 | 6 | import ( 7 | "fmt" 8 | "os" 9 | "os/exec" 10 | "strings" 11 | 12 | "qpm.io/common" 13 | msg "qpm.io/common/messages" 14 | ) 15 | 16 | type Mercurial struct { 17 | } 18 | 19 | func NewMercurial() *Mercurial { 20 | return &Mercurial{} 21 | } 22 | 23 | func (m *Mercurial) Install(repository *msg.Package_Repository, version *msg.Package_Version, destination string) (*common.PackageWrapper, error) { 24 | 25 | err := os.RemoveAll(destination) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | err = m.cloneRepository(repository.Url, version.Revision, destination) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | return common.LoadPackage(destination) 36 | } 37 | 38 | func (m *Mercurial) Test() error { 39 | _, err := exec.Command("hg", "version").Output() 40 | if err != nil { 41 | return err 42 | } 43 | return nil 44 | } 45 | 46 | func (m *Mercurial) cloneRepository(url string, revision string, destdir string) error { 47 | _, err := exec.Command("hg", "clone", "-r", revision, url, destdir).Output() 48 | if err != nil { 49 | return err 50 | } 51 | return nil 52 | } 53 | 54 | func (m *Mercurial) CreateTag(name string) error { 55 | _, err := exec.Command("hg", "tag", name).Output() 56 | if err != nil { 57 | return err 58 | } 59 | return nil 60 | } 61 | 62 | func (m *Mercurial) ValidateCommit(commit string) error { 63 | url, err := m.RepositoryURL() 64 | if err != nil { 65 | return err 66 | } 67 | _, err = exec.Command("hg", "identify", url, "-r", commit).Output() 68 | return err 69 | } 70 | 71 | func (m *Mercurial) RepositoryURL() (string, error) { 72 | out, err := exec.Command("hg", "paths", "default").Output() 73 | if err != nil { 74 | return "", fmt.Errorf("We could not get the repository default path URL.") 75 | } 76 | return strings.TrimSpace(string(out)), err 77 | } 78 | 79 | func (m *Mercurial) RepositoryFileList() ([]string, error) { 80 | var paths []string 81 | out, err := exec.Command("hg", "locate").Output() 82 | if err != nil { 83 | return paths, err 84 | } 85 | 86 | // TODO: this may not work on Windows - we need to test this 87 | output := string(out) 88 | paths = strings.Split(strings.Trim(output, "\n"), "\n") 89 | 90 | return paths, nil 91 | } 92 | 93 | func (m *Mercurial) LastCommitRevision() (string, error) { 94 | out, err := exec.Command("hg", "log", "--template", "{node}", "--limit", "1").Output() 95 | return strings.TrimSpace(string(out)), err 96 | } 97 | 98 | func (m *Mercurial) LastCommitAuthorName() (string, error) { 99 | out, err := exec.Command("hg", "log", "--template", "{author|person}", "--limit", "1").Output() 100 | return strings.TrimSpace(string(out)), err 101 | } 102 | 103 | func (m *Mercurial) LastCommitEmail() (string, error) { 104 | out, err := exec.Command("hg", "log", "--template", "{author|email}", "--limit", "1").Output() 105 | return strings.TrimSpace(string(out)), err 106 | } 107 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | BINARY := qpm 3 | VERSION := 0.11.0 4 | BUILD := $(shell git rev-parse head | cut -c1-8) 5 | TS := $(shell /bin/date "+%Y-%m-%d---%H-%M-%S") 6 | SOURCES := $(shell find . -name '*.go') 7 | LDFLAGS := -ldflags "-X qpm.io/qpm/core.Version=${VERSION} -X qpm.io/qpm/core.Build=${BUILD}" 8 | go_build = GOOS=$(1) GOARCH=$(2) go build ${LDFLAGS} -o ${GOPATH}/bin/$(1)_$(2)/$(3) qpm.io/qpm 9 | 10 | default: $(SOURCES) 11 | go install ${LDFLAGS} qpm.io/qpm 12 | 13 | ## Supported Platforms ## 14 | 15 | .windows: .windows_386 .windows_amd64 16 | 17 | .linux: .linux_386 .linux_amd64 18 | 19 | .darwin: .darwin_386 .darwin_amd64 20 | 21 | .freebsd: .freebsd_386 .freebsd_amd64 22 | 23 | .all: .windows .linux .darwin .freebsd 24 | 25 | ## Platform targets ## 26 | 27 | .windows_386: ${GOPATH}/bin/windows_386/qpm.exe 28 | 29 | .windows_amd64: ${GOPATH}/bin/windows_amd64/qpm.exe 30 | 31 | .linux_386: ${GOPATH}/bin/linux_386/qpm 32 | 33 | .linux_amd64: ${GOPATH}/bin/linux_amd64/qpm 34 | 35 | .darwin_386: ${GOPATH}/bin/darwin_386/qpm 36 | 37 | .darwin_amd64: ${GOPATH}/bin/darwin_amd64/qpm 38 | 39 | .freebsd_386: ${GOPATH}/bin/freebsd_386/qpm 40 | 41 | .freebsd_amd64: ${GOPATH}/bin/freebsd_amd64/qpm 42 | 43 | ## Target build steps ## 44 | 45 | ${GOPATH}/bin/windows_386/qpm.exe: $(SOURCES) 46 | $(call go_build,windows,386,qpm.exe) 47 | 48 | ${GOPATH}/bin/windows_amd64/qpm.exe: $(SOURCES) 49 | $(call go_build,windows,amd64,qpm.exe) 50 | 51 | ${GOPATH}/bin/linux_386/qpm: $(SOURCES) 52 | $(call go_build,linux,386,qpm) 53 | 54 | ${GOPATH}/bin/linux_amd64/qpm: $(SOURCES) 55 | $(call go_build,linux,amd64,qpm) 56 | 57 | ${GOPATH}/bin/darwin_386/qpm: $(SOURCES) 58 | $(call go_build,darwin,386,qpm) 59 | 60 | ${GOPATH}/bin/darwin_amd64/qpm: $(SOURCES) 61 | $(call go_build,darwin,amd64,qpm) 62 | 63 | ${GOPATH}/bin/freebsd_386/qpm: $(SOURCES) 64 | $(call go_build,freebsd,386,qpm) 65 | 66 | ${GOPATH}/bin/freebsd_amd64/qpm: $(SOURCES) 67 | $(call go_build,freebsd,amd64,qpm) 68 | 69 | ## Protobuf generation ## 70 | 71 | .protobuf: common/messages/qpm.proto ${GOPATH}/bin/protoc-gen-go 72 | cd common/messages; \ 73 | protoc --plugin=$$GOPATH/bin/protoc-gen-go --go_out=plugins=grpc:. *.proto 74 | 75 | ${GOPATH}/bin/protoc-gen-go: 76 | go get -u github.com/golang/protobuf/protoc-gen-go 77 | 78 | clean: 79 | @rm -rf $(BINARIES) 80 | @rm -rf staging/ 81 | @rm -rf repository/ 82 | 83 | ## Targets for building the Qt Maintence Tool Repository ## 84 | 85 | ${GOPATH}/bin/packager: $(SOURCES) 86 | go install ${LDFLAGS} qpm.io/tools/packager 87 | 88 | .staging: .all ${GOPATH}/bin/packager 89 | ${GOPATH}/bin/packager staging 90 | 91 | .repository: .staging 92 | repogen -p staging/packages -r repository 93 | gsutil -m cp -r gs://dev.qpm.io/repository gs://dev.qpm.io/repository_$(TS) 94 | gsutil -m rsync -r repository gs://dev.qpm.io/repository 95 | 96 | .downloads: .all 97 | gsutil -m cp -r gs://www.qpm.io/download gs://www.qpm.io/download_$(TS) 98 | gsutil -m rsync -x 'qpm|packager' -r bin gs://www.qpm.io/download/v$(VERSION) 99 | 100 | .PHONY: default clean .protobuf .all \ 101 | .downloads .repository .staging/packages \ 102 | .windows .windows_386 .windows_amd64 \ 103 | .linux .linux_386 .linux_amd64 \ 104 | .darwin .darwin_386 .darwin_amd64 \ 105 | .freebsd .freebsd_386 .freebsd_amd64 106 | -------------------------------------------------------------------------------- /qpm/commands/check.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package commands 5 | 6 | import ( 7 | "bufio" 8 | "encoding/xml" 9 | "flag" 10 | "fmt" 11 | "io/ioutil" 12 | "os" 13 | "qpm.io/common" 14 | "qpm.io/qpm/core" 15 | "strings" 16 | ) 17 | 18 | type CheckCommand struct { 19 | BaseCommand 20 | pkg *common.PackageWrapper 21 | } 22 | 23 | func NewCheckCommand(ctx core.Context) *CheckCommand { 24 | return &CheckCommand{ 25 | BaseCommand: BaseCommand{ 26 | Ctx: ctx, 27 | }, 28 | } 29 | } 30 | 31 | func (c CheckCommand) Description() string { 32 | return "Checks the package for common errors" 33 | } 34 | 35 | func (c *CheckCommand) RegisterFlags(flags *flag.FlagSet) { 36 | } 37 | 38 | func (c *CheckCommand) Run() error { 39 | 40 | // check the package file 41 | var err error 42 | c.pkg, err = common.LoadPackage("") 43 | if err != nil { 44 | c.Error(err) 45 | return err 46 | } 47 | 48 | c.pkg.Validate() 49 | 50 | // check the LICENSE file 51 | _, err = os.Stat(core.LicenseFile) 52 | if err != nil { 53 | c.Error(err) 54 | return err 55 | } 56 | 57 | // check the .pri file 58 | _, err = os.Stat(c.pkg.PriFile()) 59 | if err != nil { 60 | c.Error(err) 61 | return err 62 | } 63 | 64 | // check the .qrc file 65 | _, err = os.Stat(c.pkg.QrcFile()) 66 | if err != nil { 67 | c.Warning(err.Error()) 68 | } 69 | 70 | var prefix string 71 | prefix, err = c.qrc() 72 | if err != nil { 73 | c.Warning(err.Error()) 74 | } else if prefix != c.pkg.QrcPrefix() && prefix[1:] != c.pkg.QrcPrefix() { 75 | c.Error(fmt.Errorf("the QRC prefix (%s) does not equal (%s)", prefix, c.pkg.QrcPrefix())) 76 | } 77 | 78 | // check the qmldir file 79 | _, err = os.Stat("qmldir") 80 | if err != nil { 81 | c.Warning(err.Error()) 82 | } else { 83 | var module string 84 | module, err = c.qmldir() 85 | if err != nil { 86 | c.Error(err) 87 | return err 88 | } 89 | if module != c.pkg.Name { 90 | c.Error(fmt.Errorf("the qmldir module (%s) does not equal (%s)", module, c.pkg.Name)) 91 | } 92 | } 93 | 94 | fmt.Printf("OK!\n") 95 | 96 | return nil 97 | } 98 | 99 | type QRC_File struct { 100 | XMLName xml.Name `xml:"file"` 101 | Content string `xml:",chardata"` 102 | } 103 | 104 | type QRC_Resource struct { 105 | XMLName xml.Name `xml:"qresource"` 106 | Prefix string `xml:"prefix,attr"` 107 | Files []QRC_File `xml:"file"` 108 | } 109 | 110 | type QRC_RCC struct { 111 | XMLName xml.Name `xml:"RCC"` 112 | Resources []QRC_Resource `xml:"qresource"` 113 | } 114 | 115 | func (c *CheckCommand) qrc() (prefix string, err error) { 116 | 117 | file, err := os.Open(c.pkg.QrcFile()) 118 | if err != nil { 119 | return "", err 120 | } 121 | defer file.Close() 122 | 123 | data, _ := ioutil.ReadAll(file) 124 | 125 | var rcc QRC_RCC 126 | xml.Unmarshal(data, &rcc) 127 | 128 | if rcc.Resources != nil { 129 | return rcc.Resources[0].Prefix, nil 130 | } 131 | 132 | return "", err 133 | } 134 | 135 | func (c *CheckCommand) qmldir() (prefix string, err error) { 136 | 137 | file, err := os.Open("qmldir") 138 | if err != nil { 139 | return "", err 140 | } 141 | defer file.Close() 142 | 143 | const key = "module " 144 | const key_len = len(key) 145 | 146 | scanner := bufio.NewScanner(file) 147 | for scanner.Scan() { 148 | line := scanner.Text() 149 | i := strings.Index(line, key) 150 | if i != -1 { 151 | return line[i+key_len : len(line)], nil 152 | } 153 | } 154 | 155 | return "", err 156 | } 157 | -------------------------------------------------------------------------------- /qpm/commands/verify.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package commands 5 | 6 | import ( 7 | "bytes" 8 | "crypto" 9 | "flag" 10 | "fmt" 11 | "golang.org/x/crypto/openpgp/armor" 12 | "golang.org/x/crypto/openpgp/packet" 13 | "io/ioutil" 14 | "os" 15 | "path/filepath" 16 | "qpm.io/common" 17 | "qpm.io/qpm/core" 18 | "strings" 19 | ) 20 | 21 | type VerifyCommand struct { 22 | BaseCommand 23 | pkg *common.PackageWrapper 24 | fs *flag.FlagSet 25 | paths []string 26 | } 27 | 28 | func NewVerifyCommand(ctx core.Context) *VerifyCommand { 29 | return &VerifyCommand{ 30 | BaseCommand: BaseCommand{ 31 | Ctx: ctx, 32 | }, 33 | } 34 | } 35 | 36 | func (v VerifyCommand) Description() string { 37 | return "Verifies the package PGP signature (experimental)" 38 | } 39 | 40 | func (v *VerifyCommand) RegisterFlags(flags *flag.FlagSet) { 41 | v.fs = flags 42 | } 43 | 44 | func (v *VerifyCommand) Run() error { 45 | 46 | var path string 47 | if v.fs.NArg() > 0 { 48 | packageName := v.fs.Arg(0) 49 | path = filepath.Join(core.Vendor, strings.Replace(packageName, ".", string(filepath.Separator), -1)) 50 | } else { 51 | path = "." 52 | } 53 | 54 | var err error 55 | v.pkg, err = common.LoadPackage("") 56 | if err != nil { 57 | v.Error(err) 58 | return err 59 | } 60 | 61 | // Hash the package 62 | 63 | hash, err := v.hashTree(path) 64 | if err != nil { 65 | v.Error(err) 66 | return err 67 | } 68 | fmt.Println("Package SHA-256: " + hash) 69 | 70 | // Verify the signature 71 | 72 | if v.pkg.Version.Fingerprint == "" { 73 | err = fmt.Errorf("no fingerprint set in " + core.PackageFile) 74 | v.Error(err) 75 | return err 76 | } 77 | 78 | entity, err := entityFromLocal("pubring.gpg", v.pkg.Version.Fingerprint) 79 | if err != nil { 80 | v.Error(err) 81 | return err 82 | } 83 | 84 | sig, err := ioutil.ReadFile(core.SignatureFile) 85 | if err != nil { 86 | v.Error(err) 87 | return err 88 | } 89 | 90 | err = Verify(hash, sig, entity.PrimaryKey) 91 | if err != nil { 92 | v.Error(err) 93 | return err 94 | } 95 | 96 | fmt.Println("Signature verified") 97 | 98 | return nil 99 | } 100 | 101 | func (v *VerifyCommand) visit(path string, f os.FileInfo, err error) error { 102 | 103 | if f.IsDir() { 104 | if strings.HasPrefix(f.Name(), ".git") { 105 | return filepath.SkipDir 106 | } 107 | } else { 108 | v.paths = append(v.paths, path) 109 | } 110 | return nil 111 | } 112 | 113 | func (v *VerifyCommand) hashTree(directory string) (string, error) { 114 | 115 | v.paths = []string{} 116 | 117 | err := filepath.Walk(directory, v.visit) 118 | if err != nil { 119 | return "", err 120 | } 121 | 122 | return HashPaths(v.paths) 123 | } 124 | 125 | func Verify(payload string, signature []byte, pubkey *packet.PublicKey) error { 126 | 127 | // decode and read the signature 128 | 129 | block, err := armor.Decode(bytes.NewBuffer(signature)) 130 | if err != nil { 131 | return err 132 | } 133 | 134 | pkt, err := packet.Read(block.Body) 135 | if err != nil { 136 | return err 137 | } 138 | 139 | sig, ok := pkt.(*packet.Signature) 140 | if !ok { 141 | return fmt.Errorf("could not parse the signature") 142 | } 143 | 144 | if sig.Hash != crypto.SHA256 || sig.Hash != crypto.SHA512 { 145 | return fmt.Errorf("was not a SHA-256 or SHA-512 signature") 146 | } 147 | 148 | if sig.SigType != packet.SigTypeBinary { 149 | return fmt.Errorf("was not a binary signature") 150 | } 151 | 152 | // verify the signature 153 | 154 | hash := sig.Hash.New() 155 | _, err = hash.Write([]byte(payload)) 156 | if err != nil { 157 | return err 158 | } 159 | 160 | err = pubkey.VerifySignature(hash, sig) 161 | if err != nil { 162 | return err 163 | } 164 | 165 | return nil 166 | } 167 | -------------------------------------------------------------------------------- /common/messages/qpm.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package messages; 4 | 5 | enum RepoType { 6 | AUTO = 0; 7 | GITHUB = 1; 8 | GIT = 2; 9 | MERCURIAL = 3; 10 | } 11 | 12 | // The values in this enum should correspond to an SPDX identifier 13 | enum LicenseType { 14 | NONE = 0; 15 | MIT = 1; 16 | AGPL_3_0 = 2; 17 | APACHE_2_0 = 3; 18 | ARTISTIC_2_0 = 4; 19 | BSD_2_CLAUSE = 5; 20 | BSD_3_CLAUSE =6; 21 | CC0_1_0 = 7; 22 | EPL_1_0 = 8; 23 | GPL_2_0 = 9; 24 | GPL_3_0 = 10; 25 | ISC = 11; 26 | LGPL_2_1 = 12; 27 | LGPL_3_0 = 13; 28 | UNLICENSE = 14; 29 | MPL_2_0 = 15; 30 | } 31 | 32 | enum MessageType { 33 | INFO = 0; 34 | WARNING = 1; 35 | ERROR = 2; 36 | } 37 | 38 | message DependencyMessage { 39 | MessageType type = 1; 40 | string title = 2; 41 | string body = 3; 42 | bool prompt = 4; 43 | } 44 | 45 | message Package { 46 | 47 | message Repository { 48 | RepoType type = 1; 49 | string url = 2; 50 | } 51 | 52 | message Version { 53 | string label = 1; 54 | string revision = 2; 55 | string fingerprint = 3; 56 | } 57 | 58 | message Author { 59 | string name = 1; 60 | string email = 2; 61 | } 62 | 63 | string name = 1; 64 | string description = 2; 65 | Author author = 3; 66 | Repository repository = 4; 67 | Version version = 5; 68 | repeated string dependencies = 6; 69 | LicenseType license = 7; 70 | string pri_filename = 8; 71 | string webpage = 10; 72 | } 73 | 74 | message Dependency { 75 | string name = 1; 76 | Package.Repository repository = 2; 77 | Package.Version version = 3; 78 | } 79 | 80 | message VersionInfo { 81 | Package.Version version = 1; 82 | string date_published = 2; 83 | } 84 | 85 | message SearchResult { 86 | string name = 1; 87 | string version = 2; 88 | Package.Author author = 3; 89 | string description = 4; 90 | LicenseType license = 5; 91 | string webpage = 6; 92 | } 93 | 94 | message InstallStats { 95 | uint32 daily = 1; 96 | uint32 weekly = 2; 97 | uint32 monthly = 3; 98 | uint32 yearly = 4; 99 | uint32 total = 5; 100 | } 101 | 102 | message PingRequest { 103 | 104 | } 105 | 106 | message PingResponse { 107 | 108 | } 109 | 110 | message PublishRequest { 111 | Package package_description = 1; 112 | string token = 2; 113 | } 114 | 115 | message PublishResponse { 116 | 117 | } 118 | 119 | message DependencyRequest { 120 | repeated string package_names = 1; 121 | LicenseType compat_license = 4; 122 | } 123 | 124 | message DependencyResponse { 125 | repeated Dependency dependencies = 1; 126 | repeated DependencyMessage messages = 2; 127 | } 128 | 129 | message SearchRequest { 130 | string package_name = 1; 131 | } 132 | 133 | message SearchResponse { 134 | repeated SearchResult results = 1; 135 | } 136 | 137 | message ListRequest { 138 | } 139 | 140 | message ListResponse { 141 | repeated SearchResult results = 1; 142 | } 143 | 144 | message LoginRequest { 145 | string email = 1; 146 | string password = 2; 147 | bool create = 3; 148 | } 149 | 150 | message LoginResponse { 151 | string token = 1; 152 | } 153 | 154 | message InfoRequest { 155 | string package_name = 1; 156 | } 157 | 158 | message InfoResponse { 159 | Package package = 1; 160 | repeated VersionInfo versions = 2; 161 | repeated Dependency dependencies = 3; 162 | InstallStats install_stats = 4; 163 | } 164 | 165 | message LicenseRequest { 166 | Package package = 1; 167 | } 168 | 169 | message LicenseResponse { 170 | string body = 1; 171 | } 172 | 173 | service Qpm { 174 | 175 | rpc Ping(PingRequest) returns (PingResponse) {} 176 | 177 | rpc Publish(PublishRequest) returns (PublishResponse) {} 178 | 179 | rpc GetDependencies(DependencyRequest) returns (DependencyResponse) {} 180 | 181 | rpc Search(SearchRequest) returns (SearchResponse) {} 182 | 183 | rpc List(ListRequest) returns (ListResponse) {} 184 | 185 | rpc Login(LoginRequest) returns (LoginResponse) {} 186 | 187 | rpc Info(InfoRequest) returns (InfoResponse) {} 188 | 189 | rpc GetLicense(LicenseRequest) returns (LicenseResponse) {} 190 | } 191 | -------------------------------------------------------------------------------- /qpm/commands/publish.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package commands 5 | 6 | import ( 7 | "flag" 8 | "fmt" 9 | "strings" 10 | 11 | "golang.org/x/net/context" 12 | "google.golang.org/grpc" 13 | "google.golang.org/grpc/codes" 14 | "qpm.io/common" 15 | msg "qpm.io/common/messages" 16 | "qpm.io/qpm/core" 17 | "qpm.io/qpm/vcs" 18 | ) 19 | 20 | type PublishCommand struct { 21 | BaseCommand 22 | PackageName string 23 | } 24 | 25 | func NewPublishCommand(ctx core.Context) *PublishCommand { 26 | return &PublishCommand{ 27 | BaseCommand: BaseCommand{ 28 | Ctx: ctx, 29 | }, 30 | } 31 | } 32 | 33 | func (p PublishCommand) Description() string { 34 | return "Publishes a new module" 35 | } 36 | 37 | func (p *PublishCommand) RegisterFlags(flags *flag.FlagSet) { 38 | 39 | } 40 | 41 | func get(name string, echoOff bool) string { 42 | var val string 43 | for { 44 | if echoOff { 45 | val = <-PromptPassword(name + ":") 46 | } else { 47 | val = <-Prompt(name+":", "") 48 | } 49 | if val == "" { 50 | fmt.Printf("ERROR: Must enter a %s\n", name) 51 | } else { 52 | break 53 | } 54 | } 55 | return val 56 | } 57 | 58 | func LoginPrompt(ctx context.Context, client msg.QpmClient) (string, error) { 59 | 60 | email := get("email", false) 61 | password := get("password", true) 62 | 63 | loginRequest := &msg.LoginRequest{ 64 | Email: email, 65 | Password: password, 66 | Create: false, 67 | } 68 | 69 | loginResp, err := client.Login(context.Background(), loginRequest) 70 | 71 | if err != nil { 72 | if grpc.Code(err) == codes.NotFound { 73 | fmt.Println("User not found. Confirm password to create a new user.") 74 | fmt.Println("Your name, email and password will only be used to identify you as the package author.") 75 | fmt.Println("This data will not be shared with any 3rd party or used for any other purpose.") 76 | fmt.Println("Use to abort if you do not agree to this.") 77 | confirm := get("password", true) 78 | if password != confirm { 79 | return "", fmt.Errorf("Passwords do not match.") 80 | } 81 | 82 | loginRequest.Create = true 83 | if loginResp, err = client.Login(context.Background(), loginRequest); err != nil { 84 | return "", err 85 | } 86 | } else { 87 | return "", err 88 | } 89 | } 90 | 91 | return loginResp.Token, nil 92 | } 93 | 94 | func (p *PublishCommand) Run() error { 95 | 96 | token, err := LoginPrompt(context.Background(), p.Ctx.Client) 97 | 98 | if err != nil { 99 | fmt.Printf("ERROR: %v\n", err) 100 | return err 101 | } 102 | 103 | fmt.Println("Running check") 104 | if err := NewCheckCommand(p.Ctx).Run(); err != nil { 105 | p.Fatal(err.Error()) 106 | } 107 | 108 | wrapper, err := common.LoadPackage("") 109 | 110 | if err != nil { 111 | p.Fatal("Cannot read " + core.PackageFile + ": " + err.Error()) 112 | } 113 | 114 | publisher, err := vcs.CreatePublisher(wrapper.Repository) 115 | if err != nil { 116 | p.Fatal("Cannot find VCS: " + err.Error()) 117 | } 118 | 119 | wrapper.Version.Revision, err = publisher.LastCommitRevision() 120 | 121 | if err != nil { 122 | p.Fatal("Cannot get the last commit SHA1: " + err.Error()) 123 | } 124 | 125 | if err := publisher.ValidateCommit(wrapper.Version.Revision); err != nil { 126 | p.Fatal(err.Error()) 127 | } 128 | 129 | fmt.Println("Publishing") 130 | _, err = p.Ctx.Client.Publish(context.Background(), &msg.PublishRequest{ 131 | Token: token, 132 | PackageDescription: wrapper.Package, 133 | }) 134 | 135 | if err != nil { 136 | p.Fatal("ERROR:" + err.Error()) 137 | } 138 | 139 | tag := <-Prompt("Tag release:", "Y/n") 140 | if len(tag) == 0 || strings.ToLower(string(tag[0])) == "y" { 141 | publisher.CreateTag("qpm/" + wrapper.Version.Label) 142 | } 143 | 144 | fmt.Println("SUCCESS!") 145 | signature := strings.Join([]string{wrapper.Name, wrapper.Version.Label}, "@") 146 | fmt.Println("Publised package: " + signature) 147 | fmt.Println("Revision: " + wrapper.Version.Revision) 148 | 149 | return nil 150 | } 151 | -------------------------------------------------------------------------------- /qpm/core/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package core 5 | 6 | import ( 7 | "fmt" 8 | "golang.org/x/crypto/ssh/terminal" 9 | "os" 10 | msg "qpm.io/common/messages" 11 | "strings" 12 | "syscall" 13 | "text/template" 14 | "time" 15 | ) 16 | 17 | const ( 18 | columnName = "Name" 19 | columnDescription = "Description" 20 | columnAuthor = "Author" 21 | columnVersion = "Version" 22 | columnLicense = "License" 23 | ) 24 | 25 | func IntMax(a, b int) int { 26 | if a > b { 27 | return a 28 | } 29 | return b 30 | } 31 | 32 | func IntMin(a, b int) int { 33 | if a <= b { 34 | return a 35 | } 36 | return b 37 | } 38 | 39 | func ToDateTime(in string) time.Time { 40 | t, _ := time.Parse(time.RFC3339, in) 41 | return t 42 | } 43 | 44 | // Pretty prints a table of package SearchResults. 45 | func PrintSearchResults(results []*msg.SearchResult) { 46 | columnWidths := map[string]int{ 47 | columnName: len(columnName), 48 | columnDescription: len(columnDescription), 49 | columnAuthor: len(columnAuthor), 50 | columnVersion: len(columnVersion), 51 | columnLicense: len(columnLicense), 52 | } 53 | 54 | if len(results) == 0 { 55 | fmt.Printf("No packages found.\n") 56 | return 57 | } else if len(results) < 1000 { 58 | // pre-process the list to get column widths 59 | for _, r := range results { 60 | columnWidths[columnName] = IntMin(IntMax(columnWidths[columnName], len(r.Name)), 40) 61 | columnWidths[columnDescription] = IntMin(IntMax(columnWidths[columnDescription], len(r.Description)), 80) 62 | columnWidths[columnAuthor] = IntMin(IntMax(columnWidths[columnAuthor], len(r.GetAuthor().Name)), 20) 63 | columnWidths[columnVersion] = IntMin(IntMax(columnWidths[columnVersion], len(r.Version)), 10) 64 | columnWidths[columnLicense] = IntMin(IntMax(columnWidths[columnLicense], len(msg.LicenseType_name[int32(r.License)])), 10) 65 | } 66 | } else { 67 | // Too many results to pre-process so use sensible defaults 68 | columnWidths[columnName] = 40 69 | columnWidths[columnDescription] = 60 70 | columnWidths[columnAuthor] = 20 71 | columnWidths[columnVersion] = 10 72 | columnWidths[columnLicense] = 10 73 | } 74 | 75 | width, _, err := terminal.GetSize(int(syscall.Stdout)) 76 | if err != nil { 77 | fmt.Printf("Couldn't get terminal width: %s\n", err.Error()) 78 | // gracefully fallback to something sensible 79 | width = 110 80 | } 81 | 82 | const columnSpacing = 3 83 | 84 | fmt.Println("") 85 | columns := []string{columnName, columnDescription, columnAuthor, columnVersion, columnLicense} 86 | widths := make([]int, len(columns)) 87 | for i, col := range columns { 88 | widths[i] = columnWidths[col] 89 | } 90 | 91 | // Print the headers 92 | printRow(width, columnSpacing, widths, columns) 93 | 94 | // Print a horizontal line 95 | fmt.Printf("%s\n", strings.Repeat("-", width)) 96 | 97 | // Print the search results 98 | for _, r := range results { 99 | columns := []string{ 100 | r.Name, 101 | r.Description, 102 | r.GetAuthor().Name, 103 | r.Version, 104 | msg.LicenseType_name[int32(r.License)], 105 | } 106 | printRow(width, columnSpacing, widths, columns) 107 | } 108 | } 109 | 110 | func printRow(screenWidth int, columnSpacing int, columnWidths []int, columns []string) { 111 | remaining := screenWidth 112 | for i, col := range columns { 113 | if remaining <= 0 { 114 | break 115 | } 116 | 117 | // convert to []rune since we want the char count not bytes 118 | runes := []rune(col) 119 | 120 | // truncate the string if we are out of space 121 | maxLength := IntMin(remaining, columnWidths[i]) 122 | if len(runes) > maxLength { 123 | runes = runes[:maxLength] 124 | } 125 | 126 | fmt.Printf("%s", string(runes)) 127 | w := columnWidths[i] 128 | remaining -= len(runes) 129 | toNextCol := IntMax(IntMin(w-len(runes)+columnSpacing, remaining), 0) 130 | fmt.Printf("%s", strings.Repeat(" ", toNextCol)) 131 | remaining -= toNextCol 132 | } 133 | fmt.Printf("\n") 134 | } 135 | 136 | // WriteTemplate renders the given template using the fields contained in the data parameter and 137 | // outputs the result to the given file. If the file already exists, it will be 138 | // overwritten. 139 | func WriteTemplate(filename string, tpl *template.Template, data interface{}) error { 140 | file, err := os.Create(filename) 141 | if err != nil { 142 | return err 143 | } 144 | defer file.Close() 145 | 146 | err = tpl.Execute(file, data) 147 | if err != nil { 148 | return err 149 | } 150 | 151 | return nil 152 | } 153 | -------------------------------------------------------------------------------- /qpm/vcs/git.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package vcs 5 | 6 | import ( 7 | "bufio" 8 | "fmt" 9 | "os" 10 | "os/exec" 11 | "strings" 12 | 13 | "qpm.io/common" 14 | msg "qpm.io/common/messages" 15 | ) 16 | 17 | type Git struct { 18 | } 19 | 20 | func NewGit() *Git { 21 | return &Git{} 22 | } 23 | 24 | func (g *Git) Install(repository *msg.Package_Repository, version *msg.Package_Version, destination string) (*common.PackageWrapper, error) { 25 | 26 | err := os.RemoveAll(destination) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | err = g.cloneRepository(repository.Url, destination) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | pwd, err := os.Getwd() 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | err = os.Chdir(destination) 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | err = g.checkoutRevision(version.Revision) 47 | if err != nil { 48 | return nil, err 49 | } 50 | 51 | err = os.Chdir(pwd) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | return common.LoadPackage(destination) 57 | } 58 | 59 | func (g *Git) Test() error { 60 | _, err := exec.Command("git", "version").Output() 61 | if err != nil { 62 | return err 63 | } 64 | return nil 65 | } 66 | 67 | func (g *Git) cloneRepository(url string, destdir string) error { 68 | //log.Print("git clone ", url, " ", destdir) 69 | 70 | // Only clone over HTTPS for now. 71 | url = strings.Replace(url, "git@github.com:", "https://github.com/", 1) 72 | 73 | _, err := exec.Command("git", "clone", "--recursive", url, destdir).Output() 74 | if err != nil { 75 | return err 76 | } 77 | return nil 78 | } 79 | 80 | func (g *Git) checkoutRevision(revision string) error { 81 | //log.Print("git checkout ", revision) 82 | _, err := exec.Command("git", "checkout", revision).Output() 83 | if err != nil { 84 | return err 85 | } 86 | return nil 87 | } 88 | 89 | func (g *Git) CreateTag(name string) error { 90 | _, err := exec.Command("git", "tag", name).Output() 91 | if err != nil { 92 | return err 93 | } 94 | return nil 95 | } 96 | 97 | func (g *Git) ValidateCommit(commit string) error { 98 | // First run 'git ls-remote' to get the HEADs of all published remotes 99 | lsRemote := exec.Command("git", "ls-remote") 100 | stdout, err := lsRemote.StdoutPipe() 101 | if err != nil { 102 | return err 103 | } 104 | 105 | if err = lsRemote.Start(); err != nil { 106 | return err 107 | } 108 | 109 | remotes := make(map[string]string) 110 | scanner := bufio.NewScanner(stdout) 111 | scanner.Split(bufio.ScanWords) 112 | for scanner.Scan() { 113 | sha1 := scanner.Text() 114 | if !scanner.Scan() { 115 | break 116 | } 117 | remote := scanner.Text() 118 | remotes[remote] = sha1 119 | } 120 | 121 | if err = lsRemote.Wait(); err != nil { 122 | return err 123 | } 124 | 125 | // Check if commit is an ancestor of a published remote 126 | for _, s := range remotes { 127 | err = exec.Command("git", "merge-base", "--is-ancestor", commit, s).Run() 128 | if err == nil { 129 | return nil 130 | } 131 | } 132 | 133 | if err != nil { 134 | err = fmt.Errorf("Commit %s has not been pushed yet.", commit[:8]) 135 | } 136 | 137 | return err 138 | } 139 | 140 | func (g *Git) RepositoryURL() (string, error) { 141 | out, err := exec.Command("git", "config", "remote.origin.url").Output() 142 | if err != nil { 143 | return "", fmt.Errorf("We could not get the repository remote origin URL.") 144 | } 145 | return strings.TrimSpace(string(out)), err 146 | } 147 | 148 | func (g *Git) RepositoryFileList() ([]string, error) { 149 | var paths []string 150 | out, err := exec.Command("git", "ls-files").Output() 151 | if err != nil { 152 | return paths, err 153 | } 154 | 155 | // TODO: this may not work on Windows - we need to test this 156 | output := string(out) 157 | paths = strings.Split(strings.Trim(output, "\n"), "\n") 158 | 159 | return paths, nil 160 | } 161 | 162 | func (g *Git) LastCommitRevision() (string, error) { 163 | out, err := exec.Command("git", "rev-parse", "HEAD").Output() 164 | return strings.TrimSpace(string(out)), err 165 | } 166 | 167 | func (g *Git) LastCommitAuthorName() (string, error) { 168 | args := []string{"log", "-1", "--format=%an"} 169 | out, err := exec.Command("git", args...).Output() 170 | return strings.TrimSpace(string(out)), err 171 | } 172 | 173 | func (g *Git) LastCommitEmail() (string, error) { 174 | args := []string{"log", "-1", "--format=%ae"} 175 | out, err := exec.Command("git", args...).Output() 176 | return strings.TrimSpace(string(out)), err 177 | } 178 | -------------------------------------------------------------------------------- /qpm/vcs/github.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package vcs 5 | 6 | import ( 7 | "archive/tar" 8 | "compress/gzip" 9 | "encoding/json" 10 | "fmt" 11 | "io" 12 | "net/http" 13 | "os" 14 | "path/filepath" 15 | "strings" 16 | 17 | "qpm.io/common" 18 | msg "qpm.io/common/messages" 19 | ) 20 | 21 | const ( 22 | GitHubURL = "https://api.github.com/repos" 23 | Tarball = "tarball" 24 | TarSuffix = ".tar.gz" 25 | ) 26 | 27 | type GitHub struct { 28 | } 29 | 30 | func NewGitHub() *GitHub { 31 | return &GitHub{} 32 | } 33 | 34 | func (g *GitHub) Install(repository *msg.Package_Repository, version *msg.Package_Version, destination string) (*common.PackageWrapper, error) { 35 | 36 | destinationTokens := strings.Split(destination, string(filepath.Separator)) 37 | destinationSuffix := destinationTokens[len(destinationTokens)-1] 38 | fileDestination := strings.TrimSuffix(destination, destinationSuffix) 39 | 40 | os.RemoveAll(destination) 41 | 42 | if err := os.MkdirAll(fileDestination, 0755); err != nil { 43 | return nil, err 44 | } 45 | 46 | fileName, err := g.download(repository, version, fileDestination) 47 | if err != nil { 48 | return nil, err 49 | } 50 | 51 | pkg, err := g.extract(fileName, fileDestination, destinationSuffix) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | return pkg, os.Remove(fileName) 57 | } 58 | 59 | func (g *GitHub) download(repository *msg.Package_Repository, version *msg.Package_Version, destination string) (fileName string, err error) { 60 | 61 | repo := repository.Url 62 | if strings.HasPrefix(repo, "git@github.com:") { 63 | repo = strings.TrimPrefix(repo, "git@github.com:") 64 | repo = strings.TrimSuffix(repo, ".git") 65 | } else if strings.HasPrefix(repo, "https://github.com/") { 66 | repo = strings.TrimPrefix(repo, "https://github.com/") 67 | repo = strings.TrimSuffix(repo, ".git") 68 | } else { 69 | return "", fmt.Errorf("This does not seem to be a GitHub repository.") 70 | } 71 | 72 | url := GitHubURL + "/" + repo + "/" + Tarball + "/" + version.Revision 73 | urlTokens := strings.Split(url, "/") 74 | 75 | fileName = destination + string(filepath.Separator) + urlTokens[len(urlTokens)-1] + TarSuffix // FIXME: we assume it's a tarball 76 | 77 | var output *os.File 78 | output, err = os.Create(fileName) 79 | if err != nil { 80 | // TODO: check file existence first with os.IsExist(err) 81 | return "", err 82 | } 83 | defer output.Close() 84 | 85 | var response *http.Response 86 | response, err = http.Get(url) 87 | if err != nil { 88 | return "", err 89 | } 90 | defer response.Body.Close() 91 | 92 | if response.StatusCode >= 400 { 93 | errResp := make(map[string]string) 94 | dec := json.NewDecoder(response.Body) 95 | if err = dec.Decode(&errResp); err != nil { 96 | return "", err 97 | } 98 | errMsg, ok := errResp["message"] 99 | if !ok { 100 | errMsg = response.Status 101 | } 102 | err = fmt.Errorf("Error fetching %s: %s", url, errMsg) 103 | return "", err 104 | } 105 | 106 | //proxy := &ProgressProxyReader{ Reader: response.Body, length: response.ContentLength } 107 | //var written int64 108 | _, err = io.Copy(output, response.Body) 109 | if err != nil { 110 | return "", err 111 | } 112 | 113 | return fileName, nil 114 | } 115 | 116 | func (g *GitHub) extract(fileName string, destination string, suffix string) (*common.PackageWrapper, error) { 117 | 118 | file, err := os.Open(fileName) 119 | 120 | if err != nil { 121 | return nil, err 122 | } 123 | 124 | defer file.Close() 125 | 126 | var fileReader io.ReadCloser = file 127 | 128 | // add a filter to handle gzipped file 129 | if strings.HasSuffix(fileName, ".gz") { 130 | if fileReader, err = gzip.NewReader(file); err != nil { 131 | return nil, err 132 | } 133 | defer fileReader.Close() 134 | } 135 | 136 | tarBallReader := tar.NewReader(fileReader) 137 | var topDir string 138 | 139 | for { 140 | header, err := tarBallReader.Next() 141 | if err != nil { 142 | if err == io.EOF { 143 | break 144 | } 145 | return nil, err 146 | } 147 | 148 | filename := destination + string(filepath.Separator) + header.Name 149 | 150 | switch header.Typeflag { 151 | case tar.TypeDir: 152 | tokens := strings.Split(header.Name, "/") 153 | topDir = tokens[0] 154 | err = os.MkdirAll(filename, os.FileMode(header.Mode)) // or use 0755 155 | if err != nil { 156 | return nil, err 157 | } 158 | 159 | case tar.TypeReg: 160 | writer, err := os.Create(filename) 161 | if err != nil { 162 | return nil, err 163 | } 164 | io.Copy(writer, tarBallReader) 165 | err = os.Chmod(filename, os.FileMode(header.Mode)) 166 | if err != nil { 167 | return nil, err 168 | } 169 | writer.Close() 170 | 171 | case tar.TypeXGlobalHeader: 172 | // Ignore this 173 | 174 | default: 175 | //i.Info("Unable to extract type : %c in file %s\n", header.Typeflag, filename) 176 | } 177 | } 178 | 179 | if topDir != "" { 180 | 181 | src := filepath.Join(destination, topDir) 182 | 183 | pkg, err := common.LoadPackage(src) 184 | if err != nil { 185 | return pkg, err 186 | } 187 | 188 | path := filepath.Join(destination, suffix) 189 | 190 | if err = os.Rename(src, path); err != nil { 191 | return pkg, err 192 | } 193 | 194 | return pkg, err 195 | } 196 | 197 | return nil, nil 198 | } 199 | -------------------------------------------------------------------------------- /qpm/commands/sign.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package commands 5 | 6 | import ( 7 | "bytes" 8 | "crypto/sha256" 9 | "encoding/hex" 10 | "flag" 11 | "fmt" 12 | "io" 13 | "os" 14 | "path/filepath" 15 | "sort" 16 | "strings" 17 | 18 | "golang.org/x/crypto/openpgp" 19 | "golang.org/x/crypto/openpgp/packet" 20 | "qpm.io/common" 21 | msg "qpm.io/common/messages" 22 | "qpm.io/qpm/core" 23 | "qpm.io/qpm/vcs" 24 | ) 25 | 26 | type SignCommand struct { 27 | BaseCommand 28 | pkg *common.PackageWrapper 29 | paths []string 30 | } 31 | 32 | func NewSignCommand(ctx core.Context) *SignCommand { 33 | return &SignCommand{ 34 | BaseCommand: BaseCommand{ 35 | Ctx: ctx, 36 | }, 37 | } 38 | } 39 | 40 | func (s SignCommand) Description() string { 41 | return "Creates a PGP signature for the package (experimental)" 42 | } 43 | 44 | func (s *SignCommand) RegisterFlags(flags *flag.FlagSet) { 45 | 46 | } 47 | 48 | func (s *SignCommand) Run() error { 49 | 50 | var err error 51 | s.pkg, err = common.LoadPackage("") 52 | if err != nil { 53 | s.Error(err) 54 | return err 55 | } 56 | 57 | // Hash the repo 58 | 59 | hash, err := hashRepo(s.pkg.Repository) 60 | if err != nil { 61 | s.Error(err) 62 | return err 63 | } 64 | fmt.Println("Package SHA-256: " + hash) 65 | 66 | // Sign the SHA 67 | 68 | fmt.Println("Loading the GnuPG private key") 69 | 70 | if s.pkg.Version.Fingerprint == "" { 71 | err = fmt.Errorf("no fingerprint set in " + core.PackageFile) 72 | s.Error(err) 73 | return err 74 | } 75 | 76 | signer, err := entityFromLocal("secring.gpg", s.pkg.Version.Fingerprint) 77 | if err != nil { 78 | s.Error(err) 79 | return err 80 | } 81 | 82 | fmt.Println("Creating the signature") 83 | sig, err := Sign(hash, signer) 84 | if err != nil { 85 | s.Error(err) 86 | return err 87 | } 88 | 89 | // Write the signature file 90 | 91 | fmt.Println("Creating " + core.SignatureFile) 92 | file, err := os.Create(core.SignatureFile) 93 | if err != nil { 94 | s.Error(err) 95 | return err 96 | } 97 | defer file.Close() 98 | 99 | _, err = file.Write(sig) 100 | if err != nil { 101 | s.Error(err) 102 | return err 103 | } 104 | 105 | // Verify the signature 106 | 107 | fmt.Println("Verifying the signature") 108 | entity, err := entityFromLocal("pubring.gpg", s.pkg.Version.Fingerprint) 109 | if err != nil { 110 | s.Error(err) 111 | return err 112 | } 113 | 114 | err = Verify(hash, sig, entity.PrimaryKey) 115 | if err != nil { 116 | s.Error(err) 117 | return err 118 | } 119 | 120 | fmt.Println("Done") 121 | 122 | return nil 123 | } 124 | 125 | // SHA-256 hashing 126 | 127 | func hash(path string) ([]byte, error) { 128 | 129 | var result []byte 130 | file, err := os.Open(path) 131 | if err != nil { 132 | return result, err 133 | } 134 | defer file.Close() 135 | 136 | hash := sha256.New() 137 | if _, err := io.Copy(hash, file); err != nil { 138 | return result, err 139 | } 140 | 141 | return hash.Sum(result), nil 142 | } 143 | 144 | func HashPaths(paths []string) (string, error) { 145 | 146 | var result []byte 147 | 148 | // we need to sort to get consistent results 149 | sort.Strings(paths) 150 | 151 | master := sha256.New() 152 | 153 | for _, p := range paths { 154 | f, err := os.Stat(p) 155 | if err != nil { 156 | return "", err 157 | } 158 | if strings.HasSuffix(p, core.SignatureFile) { 159 | continue 160 | } 161 | if !f.IsDir() { 162 | sha, err := hash(p) 163 | if err != nil { 164 | fmt.Println(err) 165 | return "", err 166 | } 167 | master.Write(sha) 168 | } 169 | } 170 | 171 | result = master.Sum(nil) 172 | 173 | return hex.EncodeToString(result), nil 174 | } 175 | 176 | func hashRepo(repository *msg.Package_Repository) (string, error) { 177 | 178 | publisher, err := vcs.CreatePublisher(repository) 179 | if err != nil { 180 | return "", err 181 | } 182 | 183 | paths, err := publisher.RepositoryFileList() 184 | if err != nil { 185 | return "", err 186 | } 187 | 188 | return HashPaths(paths) 189 | } 190 | 191 | // PGP signing 192 | 193 | func decryptEntity(entity *openpgp.Entity) error { 194 | 195 | passwd := <-PromptPassword("Password:") 196 | err := entity.PrivateKey.Decrypt([]byte(passwd)) 197 | if err == nil { 198 | return nil 199 | } 200 | return nil 201 | } 202 | 203 | func entityFromLocal(fileName string, fingerprint string) (*openpgp.Entity, error) { 204 | 205 | path := os.Getenv("GNUPGHOME") 206 | if len(path) == 0 { 207 | return nil, fmt.Errorf("cound not find GNUPGHOME in ENV") 208 | } 209 | 210 | file, err := os.Open(filepath.Join(path, fileName)) 211 | if err != nil { 212 | return nil, err 213 | } 214 | defer file.Close() 215 | 216 | keyring, err := openpgp.ReadKeyRing(file) 217 | if err != nil { 218 | return nil, err 219 | } 220 | 221 | decoded, err := hex.DecodeString(fingerprint) 222 | if err != nil { 223 | return nil, err 224 | } 225 | 226 | if len(decoded) != 20 { 227 | return nil, fmt.Errorf("the fingerprint is not 20 bytes") 228 | } 229 | 230 | var fp [20]byte 231 | copy(fp[:], decoded[:20]) 232 | 233 | for _, entity := range keyring { 234 | 235 | if entity.PrimaryKey.Fingerprint != fp { 236 | continue 237 | } 238 | 239 | if entity != nil && entity.PrivateKey != nil && entity.PrivateKey.Encrypted { 240 | if err := decryptEntity(entity); err != nil { 241 | return nil, err 242 | } 243 | return entity, nil 244 | } 245 | return entity, nil 246 | } 247 | 248 | return nil, fmt.Errorf("entity for %s not found in %s", fingerprint, fileName) 249 | } 250 | 251 | func Sign(unsigned string, signer *openpgp.Entity) ([]byte, error) { 252 | 253 | var buffer bytes.Buffer 254 | err := openpgp.ArmoredDetachSign(&buffer, signer, strings.NewReader(unsigned), &packet.Config{}) 255 | if err != nil { 256 | return nil, err 257 | } 258 | return buffer.Bytes(), nil 259 | } 260 | -------------------------------------------------------------------------------- /common/package.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package common 5 | 6 | import ( 7 | // "crypto/x509/pkix" 8 | // "debug/elf" 9 | "fmt" 10 | json "github.com/golang/protobuf/jsonpb" 11 | "os" 12 | "path/filepath" 13 | msg "qpm.io/common/messages" 14 | "qpm.io/qpm/core" 15 | "regexp" 16 | "strings" 17 | ) 18 | 19 | const ( 20 | ERR_REQUIRED_FIELD = "%s is a required field" 21 | ERR_FORMATTED_FIELD = "%s requires a specific format" 22 | ) 23 | 24 | var ( 25 | regexPackageName = regexp.MustCompile("^[a-zA-Z]{2,}\\.[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]?(\\.[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]?)+$") 26 | regexVersion = regexp.MustCompile("[0-9].[0-9].[0-9]*") 27 | regexAuthorName = regexp.MustCompile("^[\\p{L}\\s'.-]+$") 28 | regexAuthorEmail = regexp.MustCompile(".+@.+\\..+") 29 | regexGitSha1 = regexp.MustCompile("^[a-fA-F0-9]{8,}$") 30 | ) 31 | 32 | func dotSlash(dots string) string { 33 | return strings.Replace(dots, ".", "/", -1) 34 | } 35 | 36 | func dotUnderscore(dots string) string { 37 | return strings.Replace(dots, ".", "_", -1) 38 | } 39 | 40 | // Takes a name@version string and return just name 41 | func packageName(release string) string { 42 | return strings.Split(release, "@")[0] 43 | } 44 | 45 | type DependencyList map[string]string 46 | 47 | // Creates a new DependencyList (which is really a map) which takes a list of package 48 | // names of the form "package@version" and produces a map of "package => "version". 49 | // Passing in multiple versions of the same package will overwrite with the last one. 50 | func NewDependencyList(packages []string) DependencyList { 51 | deps := DependencyList{} 52 | for _, dep := range packages { 53 | parts := strings.Split(dep, "@") 54 | pName := strings.ToLower(parts[0]) 55 | 56 | if len(parts) > 1 { 57 | deps[pName] = strings.ToLower(parts[1]) 58 | } else { 59 | deps[pName] = "" 60 | } 61 | } 62 | return deps 63 | } 64 | 65 | func NewPackage() *msg.Package { 66 | return &msg.Package{ 67 | Name: "", 68 | Description: "", 69 | Version: &msg.Package_Version{ 70 | Label: "0.0.1", 71 | Fingerprint: "", 72 | }, 73 | Author: &msg.Package_Author{ 74 | Name: "", 75 | Email: "", 76 | }, 77 | Dependencies: []string{}, 78 | Repository: &msg.Package_Repository{ 79 | Type: msg.RepoType_GITHUB, 80 | Url: "", 81 | }, 82 | License: msg.LicenseType_MIT, 83 | } 84 | } 85 | 86 | type PackageWrapper struct { 87 | *msg.Package 88 | ID int // only used on server 89 | FilePath string 90 | } 91 | 92 | func NewPackageWrapper(file string) *PackageWrapper { 93 | return &PackageWrapper{ 94 | Package: &msg.Package{}, 95 | FilePath: file, 96 | } 97 | } 98 | 99 | func LoadPackage(path string) (*PackageWrapper, error) { 100 | var err error 101 | pw := &PackageWrapper{Package: &msg.Package{}} 102 | 103 | packageFile := filepath.Join(path, core.PackageFile) 104 | 105 | if _, err = os.Stat(packageFile); err == nil { 106 | var file *os.File 107 | 108 | if file, err = os.Open(packageFile); err != nil { 109 | return pw, err 110 | } 111 | defer file.Close() 112 | 113 | if err = json.Unmarshal(file, pw.Package); err != nil { 114 | return pw, err 115 | } 116 | 117 | pw.FilePath, err = filepath.Abs(file.Name()) 118 | } 119 | 120 | return pw, err 121 | } 122 | 123 | func LoadPackages(vendorDir string) (map[string]*PackageWrapper, error) { 124 | packageMap := make(map[string]*PackageWrapper) 125 | 126 | if _, err := os.Stat(vendorDir); err != nil { 127 | return packageMap, err 128 | } 129 | 130 | err := filepath.Walk(vendorDir, func(path string, info os.FileInfo, err error) error { 131 | if !info.IsDir() && filepath.Base(path) == core.PackageFile { 132 | pkg, err := LoadPackage(filepath.Dir(path)) 133 | if err != nil { 134 | return err 135 | } 136 | packageMap[pkg.Name] = pkg 137 | 138 | // found what we're looking for so skip the rest 139 | return filepath.SkipDir 140 | } 141 | return nil 142 | }) 143 | 144 | return packageMap, err 145 | } 146 | 147 | func (pw PackageWrapper) Save() error { 148 | var file *os.File 149 | var err error 150 | 151 | // re-create the core.PackageFile file 152 | file, err = os.Create(core.PackageFile) 153 | 154 | if err != nil { 155 | return err 156 | } 157 | defer file.Close() 158 | 159 | marshaller := &json.Marshaler{ 160 | EnumsAsInts: false, 161 | Indent: " ", 162 | } 163 | return marshaller.Marshal(file, pw.Package) 164 | } 165 | 166 | // RemoveDependency removes a package from this package's list of dependencies. 167 | func (pw *PackageWrapper) RemoveDependency(dep *PackageWrapper) { 168 | for i, d := range pw.Dependencies { 169 | if packageName(d) == dep.Name { 170 | pw.Dependencies = append(pw.Dependencies[:i], pw.Dependencies[i+1:]...) 171 | return 172 | } 173 | } 174 | } 175 | 176 | func (pw PackageWrapper) ParseDependencies() DependencyList { 177 | return NewDependencyList(pw.Dependencies) 178 | } 179 | 180 | func (pw PackageWrapper) Validate() error { 181 | if pw.Name == "" { 182 | return fmt.Errorf(ERR_REQUIRED_FIELD, "name") 183 | } else { 184 | // Validate name 185 | if !regexPackageName.MatchString(pw.Name) { 186 | return fmt.Errorf(ERR_FORMATTED_FIELD, "name") 187 | } 188 | } 189 | if pw.Version == nil { 190 | return fmt.Errorf(ERR_REQUIRED_FIELD, "version") 191 | } else { 192 | // Validate version label 193 | if !regexVersion.MatchString(pw.Version.Label) { 194 | return fmt.Errorf(ERR_FORMATTED_FIELD, "version label") 195 | } 196 | // Validate version revision 197 | if pw.Version.Revision == "" { 198 | return fmt.Errorf(ERR_REQUIRED_FIELD, "version revision") 199 | } 200 | } 201 | if pw.Author == nil { 202 | return fmt.Errorf(ERR_REQUIRED_FIELD, "author") 203 | } else { 204 | // Validate author name 205 | if !regexAuthorName.MatchString(pw.Author.Name) { 206 | return fmt.Errorf(ERR_FORMATTED_FIELD, "author name") 207 | } 208 | //Validate author email 209 | if !regexAuthorEmail.MatchString(pw.Author.Email) { 210 | return fmt.Errorf(ERR_FORMATTED_FIELD, "author email") 211 | } 212 | } 213 | 214 | return nil 215 | } 216 | 217 | func (pw PackageWrapper) RootDir() string { 218 | return filepath.Dir(pw.FilePath) 219 | } 220 | 221 | func (pw PackageWrapper) PriFile() string { 222 | if pw.PriFilename != "" { 223 | return pw.PriFilename 224 | } 225 | return dotUnderscore(pw.Package.Name) + ".pri" 226 | } 227 | 228 | func (pw PackageWrapper) QrcFile() string { 229 | return dotUnderscore(pw.Package.Name) + ".qrc" 230 | } 231 | 232 | func (pw PackageWrapper) QrcPrefix() string { 233 | return dotSlash(pw.Package.Name) 234 | } 235 | 236 | func (pw PackageWrapper) GetDependencySignature() string { 237 | return strings.Join([]string{pw.Name, pw.Version.Label}, "@") 238 | } 239 | -------------------------------------------------------------------------------- /qpm/commands/init.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package commands 5 | 6 | import ( 7 | "bufio" 8 | "errors" 9 | "flag" 10 | "fmt" 11 | "os" 12 | "path/filepath" 13 | "regexp" 14 | "strings" 15 | "text/template" 16 | 17 | "github.com/howeyc/gopass" 18 | "golang.org/x/net/context" 19 | "qpm.io/common" 20 | msg "qpm.io/common/messages" 21 | "qpm.io/qpm/core" 22 | "qpm.io/qpm/vcs" 23 | ) 24 | 25 | var regexGitHubLicense = regexp.MustCompile("[-\\.]") 26 | 27 | func Prompt(prompt string, def string) chan string { 28 | replyChannel := make(chan string, 1) 29 | 30 | if def == "" { 31 | fmt.Printf(prompt + " ") 32 | } else { 33 | fmt.Printf(prompt+" [%s] ", def) 34 | } 35 | 36 | in := bufio.NewReader(os.Stdin) 37 | answer, _ := in.ReadString('\n') 38 | 39 | answer = strings.TrimSpace(answer) 40 | 41 | if len(answer) > 0 { 42 | replyChannel <- answer 43 | } else { 44 | replyChannel <- def 45 | } 46 | 47 | return replyChannel 48 | } 49 | 50 | func PromptPassword(prompt string) chan string { 51 | replyChannel := make(chan string, 1) 52 | fmt.Printf(prompt + " ") 53 | pass, err := gopass.GetPasswd() 54 | if err == gopass.ErrInterrupted { 55 | os.Exit(0) //SIGINT? 56 | } 57 | replyChannel <- string(pass) 58 | return replyChannel 59 | } 60 | 61 | func extractReverseDomain(email string) string { 62 | emailParts := strings.Split(email, "@") 63 | if len(emailParts) != 2 { 64 | return "" 65 | } 66 | domainParts := strings.Split(emailParts[1], ".") 67 | for i, j := 0, len(domainParts)-1; i < j; i, j = i+1, j-1 { 68 | domainParts[i], domainParts[j] = domainParts[j], domainParts[i] 69 | } 70 | return strings.Join(domainParts, ".") 71 | } 72 | 73 | type InitCommand struct { 74 | BaseCommand 75 | Pkg *common.PackageWrapper 76 | } 77 | 78 | func NewInitCommand(ctx core.Context) *InitCommand { 79 | return &InitCommand{ 80 | BaseCommand: BaseCommand{ 81 | Ctx: ctx, 82 | }, 83 | } 84 | } 85 | 86 | func (ic InitCommand) Description() string { 87 | return "Initializes a new module in the current directory" 88 | } 89 | 90 | func (ic *InitCommand) RegisterFlags(flag *flag.FlagSet) { 91 | 92 | } 93 | 94 | func (ic *InitCommand) Run() error { 95 | 96 | ic.Pkg = &common.PackageWrapper{Package: common.NewPackage()} 97 | 98 | if t, err := vcs.RepoType(); err != nil { 99 | fmt.Println("WARNING: Could not auto-detect repository type.") 100 | } else { 101 | ic.Pkg.Repository.Type = t 102 | } 103 | 104 | publisher, err := vcs.CreatePublisher(ic.Pkg.Repository) 105 | if err != nil { 106 | ic.Error(err) 107 | return err 108 | } 109 | 110 | ic.Pkg.Author.Name, _ = publisher.LastCommitAuthorName() 111 | ic.Pkg.Author.Name, _ = <-Prompt("Your name:", ic.Pkg.Author.Name) 112 | 113 | ic.Pkg.Author.Email, _ = publisher.LastCommitEmail() 114 | ic.Pkg.Author.Email = <-Prompt("Your email:", ic.Pkg.Author.Email) 115 | 116 | cwd, err := os.Getwd() 117 | if err != nil { 118 | ic.Error(err) 119 | cwd = "" 120 | } else { 121 | cwd = filepath.Base(cwd) 122 | } 123 | 124 | suggestedName := extractReverseDomain(ic.Pkg.Author.Email) + "." + cwd 125 | 126 | ic.Pkg.Name = <-Prompt("Unique package name:", suggestedName) 127 | ic.Pkg.Version.Label = <-Prompt("Initial version:", ic.Pkg.Version.Label) 128 | 129 | ic.Pkg.Repository.Url, err = publisher.RepositoryURL() 130 | if err != nil { 131 | fmt.Println("WARNING: Could not auto-detect repository URL.") 132 | } 133 | 134 | ic.Pkg.Repository.Url = <-Prompt("Clone URL:", ic.Pkg.Repository.Url) 135 | 136 | filename, _ := ic.findPriFile() 137 | if len(filename) == 0 { 138 | filename = ic.Pkg.PriFile() 139 | } 140 | 141 | license := <-Prompt("License:", "MIT") 142 | 143 | // convert Github style license strings 144 | license = strings.ToUpper(regexGitHubLicense.ReplaceAllString(license, "_")) 145 | 146 | if licenseType, err := msg.LicenseType_value[license]; !err { 147 | errMsg := fmt.Sprintf("ERROR: Non-supported license type: %s\n", license) 148 | fmt.Print(errMsg) 149 | fmt.Printf("Valid values are:\n") 150 | for i := 0; i < len(msg.LicenseType_name); i++ { 151 | fmt.Println("\t" + msg.LicenseType_name[int32(i)]) 152 | } 153 | return errors.New(errMsg) 154 | } else { 155 | ic.Pkg.License = msg.LicenseType(licenseType) 156 | } 157 | 158 | ic.Pkg.PriFilename = <-Prompt("Package .pri file:", filename) 159 | 160 | if err := ic.Pkg.Save(); err != nil { 161 | ic.Error(err) 162 | return err 163 | } 164 | 165 | bootstrap := <-Prompt("Generate boilerplate:", "Y/n") 166 | if len(bootstrap) == 0 || strings.ToLower(string(bootstrap[0])) == "y" { 167 | if err := ic.GenerateBoilerplate(); err != nil { 168 | return err 169 | } 170 | 171 | ic.GenerateLicense() 172 | } 173 | return nil 174 | } 175 | 176 | var ( 177 | modulePri = template.Must(template.New("modulePri").Parse(` 178 | RESOURCES += \ 179 | $$PWD/{{.QrcFile}} 180 | `)) 181 | moduleQrc = template.Must(template.New("moduleQrc").Parse(` 182 | 183 | 184 | qmldir 185 | 186 | 187 | `)) 188 | qmldir = template.Must(template.New("qmldir").Parse(` 189 | module {{.Package.Name}} 190 | `)) 191 | ) 192 | 193 | func (ic InitCommand) GenerateBoilerplate() error { 194 | 195 | module := struct { 196 | Package *common.PackageWrapper 197 | PriFile string 198 | QrcFile string 199 | QrcPrefix string 200 | }{ 201 | Package: ic.Pkg, 202 | PriFile: ic.Pkg.PriFile(), 203 | QrcFile: ic.Pkg.QrcFile(), 204 | QrcPrefix: ic.Pkg.QrcPrefix(), 205 | } 206 | 207 | if err := core.WriteTemplate(module.PriFile, modulePri, module); err != nil { 208 | return err 209 | } 210 | if err := core.WriteTemplate(module.QrcFile, moduleQrc, module); err != nil { 211 | return err 212 | } 213 | if err := core.WriteTemplate("qmldir", qmldir, module); err != nil { 214 | return err 215 | } 216 | return nil 217 | } 218 | 219 | func (ic *InitCommand) GenerateLicense() error { 220 | 221 | req := &msg.LicenseRequest{ 222 | Package: ic.Pkg.Package, 223 | } 224 | license, err := ic.Ctx.Client.GetLicense(context.Background(), req) 225 | if err != nil { 226 | return err 227 | } 228 | 229 | file, err := os.Create(core.LicenseFile) 230 | if err != nil { 231 | return err 232 | } 233 | defer file.Close() 234 | 235 | _, err = file.WriteString(license.Body) 236 | return err 237 | } 238 | 239 | func (ic *InitCommand) findPriFile() (string, error) { 240 | dirname := "." + string(filepath.Separator) 241 | 242 | d, err := os.Open(dirname) 243 | if err != nil { 244 | return "", err 245 | } 246 | defer d.Close() 247 | 248 | files, err := d.Readdir(-1) 249 | if err != nil { 250 | return "", err 251 | } 252 | 253 | for _, file := range files { 254 | if file.Mode().IsRegular() { 255 | if filepath.Ext(file.Name()) == ".pri" { 256 | return file.Name(), nil 257 | } 258 | } 259 | } 260 | 261 | return "", nil 262 | } 263 | -------------------------------------------------------------------------------- /tools/packager/packager.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "golang.org/x/net/context" 6 | "io" 7 | "log" 8 | "os" 9 | "path/filepath" 10 | msg "qpm.io/common/messages" 11 | "qpm.io/qpm/core" 12 | "text/template" 13 | "time" 14 | ) 15 | 16 | const ( 17 | packageName = "io.qpm.cli" 18 | stagingDir = "packages" 19 | repositoryDir = "repository" 20 | qpmLicense = msg.LicenseType_ARTISTIC_2_0 21 | licenseAddendum = ` 22 | -------------------------- 23 | 24 | The following additional terms shall apply to use of the qpm software, the qpm.io 25 | website and repository: 26 | 27 | "qpm" and "qpm.io" are owned by Cutehacks AS. All rights reserved. 28 | 29 | Modules published on the qpm registry are not officially endorsed by Cutehacks AS. 30 | 31 | Data published to the qpm registry is not part of qpm itself, and is the sole 32 | property of the publisher. While every effort is made to ensure accountability, 33 | there is absolutely no guarantee, warranty, or assertion expressed or implied 34 | as to the quality, fitness for a specific purpose, or lack of malice in any 35 | given qpm package. Packages downloaded through qpm.io are independently licensed 36 | and are not covered by this license. 37 | ` 38 | ) 39 | 40 | var ( 41 | platforms = map[string]string{ 42 | "windows_386": "qpm.exe", 43 | "windows_amd64": "qpm.exe", 44 | "linux_386": "qpm", 45 | "linux_amd64": "qpm", 46 | "darwin_386": "qpm", 47 | "darwin_amd64": "qpm", 48 | "freebsd_386": "qpm", 49 | "freebsd_amd64": "qpm", 50 | } 51 | 52 | rootPackageXml = template.Must(template.New("rootPackageXml").Parse( 53 | ` 54 | 55 | qpm 56 | qpm is a command line tool for installing packages from the qpm.io repository. 57 | {{.Version}} 58 | {{.ReleaseDate}} 59 | {{.Root}} 60 | 61 | 62 | 63 | Initial release 64 | true 65 | false 66 | false 67 | 68 | `)) 69 | 70 | platformPackageXml = template.Must(template.New("platformPackageXml").Parse( 71 | ` 72 | 73 | {{.Platform}} binaries 74 | qpm binaries for running on {{.Platform}} 75 | {{.Version}} 76 | {{.ReleaseDate}} 77 | {{.Root}}.{{.Platform}} 78 | false 79 | 80 | `)) 81 | ) 82 | 83 | type packageInfo struct { 84 | Root string 85 | Platform string 86 | ReleaseDate string 87 | Version string 88 | } 89 | 90 | func newDir(dir string) string { 91 | if err := os.MkdirAll(dir, os.FileMode(0755)); err != nil { 92 | log.Fatalf("could not create %s, %s", dir, err.Error()) 93 | } 94 | return dir 95 | } 96 | 97 | func copyBinary(src, dest string) error { 98 | s, err := os.Open(src) 99 | if err != nil { 100 | return err 101 | } 102 | defer s.Close() 103 | 104 | d, err := os.Create(dest) 105 | if err != nil { 106 | return err 107 | } 108 | defer d.Close() 109 | 110 | if _, err = io.Copy(d, s); err != nil { 111 | return err 112 | } 113 | if err := d.Sync(); err != nil { 114 | return err 115 | } 116 | 117 | return d.Chmod(0755) 118 | } 119 | 120 | func writeText(filename, text string) error { 121 | file, err := os.Create(filename) 122 | if err != nil { 123 | log.Fatalf("Could not create file %s: %s", filename, err.Error()) 124 | return err 125 | } 126 | defer file.Close() 127 | 128 | _, err = file.WriteString(text) 129 | return err 130 | } 131 | 132 | func writeTemplate(filename string, tpl *template.Template, pkg *packageInfo) error { 133 | file, err := os.Create(filename) 134 | if err != nil { 135 | log.Fatalf("Could not create file %s: %s", filename, err.Error()) 136 | return err 137 | } 138 | defer file.Close() 139 | 140 | err = tpl.Execute(file, pkg) 141 | if err != nil { 142 | log.Fatalf("Could not generate file %s: %s", filename, err.Error()) 143 | return err 144 | } 145 | 146 | return nil 147 | } 148 | 149 | func main() { 150 | 151 | binDir, err := filepath.Abs(filepath.Dir(os.Args[0])) 152 | if err != nil { 153 | log.Fatalf("Error getting bin dir: %v\n", err) 154 | } 155 | 156 | var outputDir string 157 | if len(os.Args) < 2 { 158 | outputDir, err = filepath.Abs(repositoryDir) 159 | } else { 160 | outputDir, err = filepath.Abs(os.Args[1]) 161 | } 162 | if err != nil { 163 | log.Fatalf("Error getting output dir: %v\n", err) 164 | } 165 | 166 | staging := newDir(filepath.Join(outputDir, stagingDir)) 167 | log.Printf("Generated repository at %s\n", outputDir) 168 | 169 | buffer := bytes.NewBufferString(staging) 170 | reset := buffer.Len() 171 | 172 | pkg := &packageInfo{ 173 | Root: packageName, 174 | Version: core.Version, 175 | ReleaseDate: time.Now().Format("2006-01-02"), 176 | } 177 | 178 | rootMetaDir := newDir(filepath.Join(staging, packageName, "meta")) 179 | newDir(filepath.Join(staging, packageName, "data")) 180 | 181 | if err = writeTemplate(filepath.Join(rootMetaDir, "package.xml"), rootPackageXml, pkg); err != nil { 182 | log.Fatalf("Could not generate package.xml for root %s", err.Error()) 183 | } 184 | 185 | req := &msg.LicenseRequest{ 186 | Package: &msg.Package{ 187 | Name: "qpm", 188 | Description: "A package manager for Qt", 189 | License: qpmLicense, 190 | Version: &msg.Package_Version{ 191 | Label: core.Version, 192 | }, 193 | Author: &msg.Package_Author{ 194 | Name: "Cutehacks AS", 195 | }, 196 | }, 197 | } 198 | 199 | ctx := core.NewContext() 200 | 201 | license, err := ctx.Client.GetLicense(context.Background(), req) 202 | if err != nil { 203 | log.Fatalf("Could not fetch license info: %s", err.Error()) 204 | } 205 | 206 | licenseTxt := license.Body + licenseAddendum 207 | 208 | if err = writeText(filepath.Join(rootMetaDir, "license.txt"), licenseTxt); err != nil { 209 | log.Fatalf("Could not generate license.txt for root %s", err.Error()) 210 | } 211 | 212 | for platform, binary := range platforms { 213 | srcDir := filepath.Join(binDir, platform) 214 | if _, err := os.Stat(srcDir); err != nil { 215 | log.Printf("Platform %s does not exist", platform) 216 | continue 217 | } 218 | 219 | // Create meta dir 220 | buffer.Truncate(reset) 221 | buffer.WriteString("/" + packageName + ".") 222 | buffer.WriteString(platform) 223 | buffer.WriteString("/meta/") 224 | metaDir := buffer.String() 225 | newDir(metaDir) 226 | 227 | // Create data dir 228 | buffer.Truncate(reset) 229 | buffer.WriteString("/" + packageName + ".") 230 | buffer.WriteString(platform) 231 | buffer.WriteString("/data/qpm/") 232 | dataDir := buffer.String() 233 | newDir(dataDir) 234 | 235 | if err := copyBinary(filepath.Join(srcDir, binary), filepath.Join(dataDir, binary)); err != nil { 236 | log.Fatalf("Cannot copy binary to %s: %s", dataDir, err.Error()) 237 | } 238 | 239 | pkg.Platform = platform 240 | if err := writeTemplate(filepath.Join(metaDir, "package.xml"), platformPackageXml, pkg); err != nil { 241 | log.Fatalf("Could not generate package.xml for %s: %s", platform, err.Error()) 242 | } 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /qpm/commands/install.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Cutehacks AS. All rights reserved. 2 | // License can be found in the LICENSE file. 3 | 4 | package commands 5 | 6 | import ( 7 | "flag" 8 | "fmt" 9 | "io" 10 | "os" 11 | "path/filepath" 12 | "strings" 13 | "text/template" 14 | 15 | "golang.org/x/net/context" 16 | "qpm.io/common" 17 | msg "qpm.io/common/messages" 18 | "qpm.io/qpm/core" 19 | "qpm.io/qpm/vcs" 20 | ) 21 | 22 | var packageFuncs = template.FuncMap{ 23 | "relPriFile": func(vendorDir string, dep *common.PackageWrapper) string { 24 | abs := filepath.Join(dep.RootDir(), dep.PriFile()) 25 | rel, err := filepath.Rel(vendorDir, abs) 26 | if err == nil { 27 | return rel 28 | } else { 29 | return abs 30 | } 31 | }, 32 | } 33 | 34 | var ( 35 | // This template is very dense to avoid excessive whitespace in the generated code. 36 | // We can address this in a future version of Go (1.6?): 37 | // https://github.com/golang/go/commit/e6ee26a03b79d0e8b658463bdb29349ca68e1460 38 | vendorPri = template.Must(template.New("vendorPri").Funcs(packageFuncs).Parse(` 39 | DEFINES += QPM_INIT\\(E\\)=\"E.addImportPath(QStringLiteral(\\\"qrc:/\\\"));\" 40 | DEFINES += QPM_USE_NS 41 | INCLUDEPATH += $$PWD 42 | QML_IMPORT_PATH += $$PWD 43 | {{$vendirDir := .VendorDir}} 44 | {{range $dep := .Dependencies}} 45 | include($$PWD/{{relPriFile $vendirDir $dep}}){{end}} 46 | `)) 47 | ) 48 | 49 | type ProgressProxyReader struct { 50 | io.Reader 51 | total int64 52 | length int64 53 | progress float64 54 | } 55 | 56 | func (r *ProgressProxyReader) Read(p []byte) (int, error) { 57 | n, err := r.Reader.Read(p) 58 | if n > 0 { 59 | r.total += int64(n) 60 | percentage := float64(r.total) / float64(r.length) * float64(100) 61 | i := int(percentage / float64(10)) 62 | is := fmt.Sprintf("%v", i) 63 | if percentage-r.progress > 2 { 64 | fmt.Fprintf(os.Stderr, is) 65 | r.progress = percentage 66 | } 67 | } 68 | return n, err 69 | } 70 | 71 | type InstallCommand struct { 72 | BaseCommand 73 | pkg *common.PackageWrapper 74 | fs *flag.FlagSet 75 | vendorDir string 76 | } 77 | 78 | func NewInstallCommand(ctx core.Context) *InstallCommand { 79 | return &InstallCommand{ 80 | BaseCommand: BaseCommand{ 81 | Ctx: ctx, 82 | }, 83 | } 84 | } 85 | 86 | func (i InstallCommand) Description() string { 87 | return "Installs a new package" 88 | } 89 | 90 | func (i *InstallCommand) RegisterFlags(flags *flag.FlagSet) { 91 | i.fs = flags 92 | 93 | // TODO: Support other directory names on the command line? 94 | var err error 95 | i.vendorDir, err = filepath.Abs(core.Vendor) 96 | if err != nil { 97 | i.vendorDir = core.Vendor 98 | } 99 | } 100 | 101 | func (i *InstallCommand) Run() error { 102 | 103 | packageName := i.fs.Arg(0) 104 | 105 | var err error 106 | i.pkg, err = common.LoadPackage("") 107 | if err != nil { 108 | // A missing package file is only an error if packageName is empty 109 | if os.IsNotExist(err) { 110 | if packageName == "" { 111 | err = fmt.Errorf("No %s file found", core.PackageFile) 112 | i.Error(err) 113 | return err 114 | } else { 115 | // Create a new package 116 | file, err := filepath.Abs(core.PackageFile) 117 | if err != nil { 118 | i.Error(err) 119 | return err 120 | } 121 | i.pkg = common.NewPackageWrapper(file) 122 | } 123 | } else { 124 | i.Error(err) 125 | return err 126 | } 127 | } 128 | 129 | var packageNames []string 130 | if packageName == "" { 131 | packageNames = i.pkg.Dependencies 132 | } else { 133 | packageNames = []string{packageName} 134 | } 135 | 136 | // Get list of dependencies from the server 137 | response, err := i.Ctx.Client.GetDependencies(context.Background(), &msg.DependencyRequest{ 138 | packageNames, 139 | i.pkg.License, 140 | }) 141 | if err != nil { 142 | i.Error(err) 143 | return err 144 | } 145 | 146 | if len(response.Dependencies) == 0 { 147 | i.Info("No package(s) found") 148 | return nil 149 | } 150 | 151 | // Show info, warnings, errors and address prompts before continuing 152 | for _, msg := range response.Messages { 153 | fmt.Printf("%s: %s\n", msg.Type.String(), msg.Title) 154 | 155 | if msg.Body != "" { 156 | fmt.Println(msg.Body) 157 | } 158 | 159 | if msg.Prompt { 160 | continueAnyway := <-Prompt("Continue anyway?", "Y/n") 161 | if len(continueAnyway) == 0 || strings.ToLower(string(continueAnyway[0])) == "y" { 162 | continue 163 | } else { 164 | return fmt.Errorf("Installation aborted.") 165 | } 166 | } 167 | } 168 | 169 | // create the vendor directory if needed 170 | if _, err = os.Stat(i.vendorDir); err != nil { 171 | err = os.Mkdir(i.vendorDir, 0755) 172 | } 173 | 174 | // Download and extract the packages 175 | packages := []*common.PackageWrapper{} 176 | for _, d := range response.Dependencies { 177 | p, err := i.install(d) 178 | if err != nil { 179 | return err 180 | } 181 | packages = append(packages, p) 182 | } 183 | 184 | // Save the dependencies in the package file 185 | err = i.save(packages) 186 | // FIXME: should we continue installing ? 187 | if err != nil { 188 | return err 189 | } 190 | 191 | err = i.postInstall() 192 | // FIXME: should we continue installing ? 193 | if err != nil { 194 | return err 195 | } 196 | 197 | return nil 198 | } 199 | 200 | func (i *InstallCommand) install(d *msg.Dependency) (*common.PackageWrapper, error) { 201 | 202 | signature := strings.Join([]string{d.Name, d.Version.Label}, "@") 203 | fmt.Println("Installing", signature) 204 | 205 | installer, err := vcs.CreateInstaller(d.Repository) 206 | if err != nil { 207 | i.Error(err) 208 | return nil, err 209 | } 210 | 211 | destination := i.vendorDir + string(filepath.Separator) + strings.Replace(d.Name, ".", string(filepath.Separator), -1) 212 | pkg, err := installer.Install(d.Repository, d.Version, destination) 213 | if err != nil { 214 | i.Error(err) 215 | return nil, err 216 | } 217 | 218 | return pkg, nil 219 | } 220 | 221 | func (i *InstallCommand) save(newDeps []*common.PackageWrapper) error { 222 | 223 | existingDeps := i.pkg.ParseDependencies() 224 | 225 | for _, d := range newDeps { 226 | existingVersion, exists := existingDeps[d.Name] 227 | if exists { 228 | if d.Version.Label != existingVersion { 229 | existingSignature := strings.Join([]string{d.Name, existingVersion}, "@") 230 | message := fmt.Sprint(existingSignature, " is already a dependency. Replacing with version ", d.Version.Label, ".") 231 | i.Warning(message) 232 | for n, e := range i.pkg.Dependencies { 233 | if existingSignature == e { 234 | i.pkg.Dependencies[n] = d.GetDependencySignature() 235 | break 236 | } 237 | } 238 | } 239 | } else { 240 | i.pkg.Dependencies = append(i.pkg.Dependencies, d.GetDependencySignature()) 241 | } 242 | } 243 | 244 | err := i.pkg.Save() 245 | if err != nil { 246 | i.Error(err) 247 | return err 248 | } 249 | 250 | return nil 251 | } 252 | 253 | func (i *InstallCommand) postInstall() error { 254 | if err := GenerateVendorPri(i.vendorDir, i.pkg); err != nil { 255 | i.Error(err) 256 | return err 257 | } 258 | return nil 259 | } 260 | 261 | // Generates a vendor.pri inside vendorDir using the information contained in the package file 262 | // and the dependencies 263 | func GenerateVendorPri(vendorDir string, pkg *common.PackageWrapper) error { 264 | depMap, err := common.LoadPackages(vendorDir) 265 | if err != nil { 266 | return err 267 | } 268 | 269 | var deps []*common.PackageWrapper 270 | for _, dep := range depMap { 271 | deps = append(deps, dep) 272 | } 273 | 274 | vendorPriFile := filepath.Join(vendorDir, core.Vendor+".pri") 275 | 276 | data := struct { 277 | VendorDir string 278 | Package *common.PackageWrapper 279 | Dependencies []*common.PackageWrapper 280 | }{ 281 | vendorDir, 282 | pkg, 283 | deps, 284 | } 285 | 286 | return core.WriteTemplate(vendorPriFile, vendorPri, data) 287 | } 288 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The Artistic License 2.0 2 | 3 | Copyright (c) 2000-2006, The Perl Foundation. 4 | 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | This license establishes the terms under which a given free software 11 | Package may be copied, modified, distributed, and/or redistributed. 12 | The intent is that the Copyright Holder maintains some artistic 13 | control over the development of that Package while still keeping the 14 | Package available as open source and free software. 15 | 16 | You are always permitted to make arrangements wholly outside of this 17 | license directly with the Copyright Holder of a given Package. If the 18 | terms of this license do not permit the full use that you propose to 19 | make of the Package, you should contact the Copyright Holder and seek 20 | a different licensing arrangement. 21 | 22 | Definitions 23 | 24 | "Copyright Holder" means the individual(s) or organization(s) 25 | named in the copyright notice for the entire Package. 26 | 27 | "Contributor" means any party that has contributed code or other 28 | material to the Package, in accordance with the Copyright Holder's 29 | procedures. 30 | 31 | "You" and "your" means any person who would like to copy, 32 | distribute, or modify the Package. 33 | 34 | "Package" means the collection of files distributed by the 35 | Copyright Holder, and derivatives of that collection and/or of 36 | those files. A given Package may consist of either the Standard 37 | Version, or a Modified Version. 38 | 39 | "Distribute" means providing a copy of the Package or making it 40 | accessible to anyone else, or in the case of a company or 41 | organization, to others outside of your company or organization. 42 | 43 | "Distributor Fee" means any fee that you charge for Distributing 44 | this Package or providing support for this Package to another 45 | party. It does not mean licensing fees. 46 | 47 | "Standard Version" refers to the Package if it has not been 48 | modified, or has been modified only in ways explicitly requested 49 | by the Copyright Holder. 50 | 51 | "Modified Version" means the Package, if it has been changed, and 52 | such changes were not explicitly requested by the Copyright 53 | Holder. 54 | 55 | "Original License" means this Artistic License as Distributed with 56 | the Standard Version of the Package, in its current version or as 57 | it may be modified by The Perl Foundation in the future. 58 | 59 | "Source" form means the source code, documentation source, and 60 | configuration files for the Package. 61 | 62 | "Compiled" form means the compiled bytecode, object code, binary, 63 | or any other form resulting from mechanical transformation or 64 | translation of the Source form. 65 | 66 | 67 | Permission for Use and Modification Without Distribution 68 | 69 | (1) You are permitted to use the Standard Version and create and use 70 | Modified Versions for any purpose without restriction, provided that 71 | you do not Distribute the Modified Version. 72 | 73 | 74 | Permissions for Redistribution of the Standard Version 75 | 76 | (2) You may Distribute verbatim copies of the Source form of the 77 | Standard Version of this Package in any medium without restriction, 78 | either gratis or for a Distributor Fee, provided that you duplicate 79 | all of the original copyright notices and associated disclaimers. At 80 | your discretion, such verbatim copies may or may not include a 81 | Compiled form of the Package. 82 | 83 | (3) You may apply any bug fixes, portability changes, and other 84 | modifications made available from the Copyright Holder. The resulting 85 | Package will still be considered the Standard Version, and as such 86 | will be subject to the Original License. 87 | 88 | 89 | Distribution of Modified Versions of the Package as Source 90 | 91 | (4) You may Distribute your Modified Version as Source (either gratis 92 | or for a Distributor Fee, and with or without a Compiled form of the 93 | Modified Version) provided that you clearly document how it differs 94 | from the Standard Version, including, but not limited to, documenting 95 | any non-standard features, executables, or modules, and provided that 96 | you do at least ONE of the following: 97 | 98 | (a) make the Modified Version available to the Copyright Holder 99 | of the Standard Version, under the Original License, so that the 100 | Copyright Holder may include your modifications in the Standard 101 | Version. 102 | 103 | (b) ensure that installation of your Modified Version does not 104 | prevent the user installing or running the Standard Version. In 105 | addition, the Modified Version must bear a name that is different 106 | from the name of the Standard Version. 107 | 108 | (c) allow anyone who receives a copy of the Modified Version to 109 | make the Source form of the Modified Version available to others 110 | under 111 | 112 | (i) the Original License or 113 | 114 | (ii) a license that permits the licensee to freely copy, 115 | modify and redistribute the Modified Version using the same 116 | licensing terms that apply to the copy that the licensee 117 | received, and requires that the Source form of the Modified 118 | Version, and of any works derived from it, be made freely 119 | available in that license fees are prohibited but Distributor 120 | Fees are allowed. 121 | 122 | 123 | Distribution of Compiled Forms of the Standard Version 124 | or Modified Versions without the Source 125 | 126 | (5) You may Distribute Compiled forms of the Standard Version without 127 | the Source, provided that you include complete instructions on how to 128 | get the Source of the Standard Version. Such instructions must be 129 | valid at the time of your distribution. If these instructions, at any 130 | time while you are carrying out such distribution, become invalid, you 131 | must provide new instructions on demand or cease further distribution. 132 | If you provide valid instructions or cease distribution within thirty 133 | days after you become aware that the instructions are invalid, then 134 | you do not forfeit any of your rights under this license. 135 | 136 | (6) You may Distribute a Modified Version in Compiled form without 137 | the Source, provided that you comply with Section 4 with respect to 138 | the Source of the Modified Version. 139 | 140 | 141 | Aggregating or Linking the Package 142 | 143 | (7) You may aggregate the Package (either the Standard Version or 144 | Modified Version) with other packages and Distribute the resulting 145 | aggregation provided that you do not charge a licensing fee for the 146 | Package. Distributor Fees are permitted, and licensing fees for other 147 | components in the aggregation are permitted. The terms of this license 148 | apply to the use and Distribution of the Standard or Modified Versions 149 | as included in the aggregation. 150 | 151 | (8) You are permitted to link Modified and Standard Versions with 152 | other works, to embed the Package in a larger work of your own, or to 153 | build stand-alone binary or bytecode versions of applications that 154 | include the Package, and Distribute the result without restriction, 155 | provided the result does not expose a direct interface to the Package. 156 | 157 | 158 | Items That are Not Considered Part of a Modified Version 159 | 160 | (9) Works (including, but not limited to, modules and scripts) that 161 | merely extend or make use of the Package, do not, by themselves, cause 162 | the Package to be a Modified Version. In addition, such works are not 163 | considered parts of the Package itself, and are not subject to the 164 | terms of this license. 165 | 166 | 167 | General Provisions 168 | 169 | (10) Any use, modification, and distribution of the Standard or 170 | Modified Versions is governed by this Artistic License. By using, 171 | modifying or distributing the Package, you accept this license. Do not 172 | use, modify, or distribute the Package, if you do not accept this 173 | license. 174 | 175 | (11) If your Modified Version has been derived from a Modified 176 | Version made by someone other than you, you are nevertheless required 177 | to ensure that your Modified Version complies with the requirements of 178 | this license. 179 | 180 | (12) This license does not grant you the right to use any trademark, 181 | service mark, tradename, or logo of the Copyright Holder. 182 | 183 | (13) This license includes the non-exclusive, worldwide, 184 | free-of-charge patent license to make, have made, use, offer to sell, 185 | sell, import and otherwise transfer the Package with respect to any 186 | patent claims licensable by the Copyright Holder that are necessarily 187 | infringed by the Package. If you institute patent litigation 188 | (including a cross-claim or counterclaim) against any party alleging 189 | that the Package constitutes direct or contributory patent 190 | infringement, then this Artistic License to you shall terminate on the 191 | date that such litigation is filed. 192 | 193 | (14) Disclaimer of Warranty: 194 | THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS 195 | IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED 196 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR 197 | NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL 198 | LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL 199 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 200 | DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF 201 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 202 | 203 | -------------------------- 204 | 205 | The following additional terms shall apply to use of the qpm software, the qpm.io 206 | website and repository: 207 | 208 | "qpm" and "qpm.io" are owned by Cutehacks AS. All rights reserved. 209 | 210 | Modules published on the qpm registry are not officially endorsed by Cutehacks AS. 211 | 212 | Data published to the qpm registry is not part of qpm itself, and is the sole 213 | property of the publisher. While every effort is made to ensure accountability, 214 | there is absolutely no guarantee, warranty, or assertion expressed or implied 215 | as to the quality, fitness for a specific purpose, or lack of malice in any 216 | given qpm package. Packages downloaded through qpm.io are independently licensed 217 | and are not covered by this license. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | [![Build Status](https://travis-ci.org/Cutehacks/qpm.svg?branch=master)](https://travis-ci.org/Cutehacks/qpm) 3 | 4 | [Roadmap](https://github.com/Cutehacks/qpm/projects/1) 5 | 6 | # Important 7 | 8 | We will be shutting down the qpm servers. We recommend that you use [Conan](https://conan.io/) instead. 9 | 10 | # Introduction 11 | 12 | > qpm is a command line tool for accessing the qpm.io Qt package registry and installing dependencies. 13 | 14 | qpm and the corresponding qpm.io service provide a way for Qt developers to search, install and 15 | publish source code (QML, JS, C++) components that can be compiled into Qt applications or libraries. 16 | 17 | # Content 18 | 19 | * [Goals](#goals) 20 | * [How it works](#how-it-works) 21 | * [Installing](#installing) 22 | * [Download a binary](#download-a-binary) 23 | * [Install from a package manager](#install-from-a-package-manager) 24 | * [Use Qt's Maintenance Tool](#use-qts-maintenance-tool) 25 | * [Compile from source](#compile-from-source) 26 | * [Usage for App Developers](#usage-for-app-developers) 27 | * [Usage for Package Authors](#usage-for-package-authors) 28 | * [Example Package](#example-package) 29 | * [Package Naming](#package-naming) 30 | * [A note on versioning](#a-note-on-versioning) 31 | * [Tips](#tips) 32 | * [Self-registering packages](#self-registering-packages) 33 | * [Contributing](#contributing) 34 | * [Code Style](#code-style) 35 | * [Prerequisites](#prerequisites) 36 | * [Generating Protocol Buffers](#generating-protocol-buffers) 37 | * [FAQ](#faq) 38 | 39 | ## Goals 40 | 41 | qpm aims to: 42 | 43 | * Help application and library developers. 44 | * Provide a central registry of re-usable Qt components. 45 | * Produce consistent dependencies for applications. 46 | 47 | qpm is **not**: 48 | 49 | * A binary package manager (like apt-get, yum, or brew). 50 | * A build system (like qmake or qbs). 51 | * A version control system (like Git or SVN). 52 | 53 | ## How it works 54 | 55 | Packages are published to the qpm.io registry by a package author. Only the meta data is stored in 56 | the registry, the actual source code resides in a public repository under the author's control 57 | (eg: Github). 58 | 59 | An application developer installs a package that he or she wishes to use and qpm will fetch the source 60 | code for that package and also fetch any *nested* dependencies that the package has. The application 61 | developer can then use those package dependencies in their application. 62 | 63 | A package in qpm terms consists of a `qpm.json` file which contains meta data about the package 64 | such as the name, maintainer, version, dependencies, etc. The name of the package must be unique as 65 | it is used later to avoid naming collisions. 66 | 67 | When an application developer installs a package for use in a Qt app, qpm will automatically create a 68 | qpm.json file to track the apps dependencies. Even though apps contain qpm.json files, they 69 | should not be published to the registry. The qpm.json file should be checked into your version 70 | control system so that the dependencies can be re-created later. Upon installing a package dependency, 71 | the qpm tool will create a directory called `vendor` which contains the source code of the package. 72 | This is included in the application (see below) and can used as normal. 73 | 74 | # Installing 75 | 76 | There are currently 4 ways to install the qpm client: 77 | 78 | ## Download a binary 79 | 80 | The pre-compiled binaries can be downloaded from http://www.qpm.io. 81 | 82 | ## Install from a package manager 83 | 84 | The goal is to make qpm available in the following desktop package managers: 85 | 86 | | Package Manager | Status | Command | 87 | | ------------- | ------------- | ------------- 88 | | Homebrew | Done | `brew install qpm` | 89 | | MacPorts | Not started | | 90 | | Chocolately | Not started | | 91 | | RPM | Not started | | 92 | | Debian | Not started | | 93 | | Pacman | [AUR](https://aur.archlinux.org/packages/?O=0&SeB=nd&K=qpm&outdated=&SB=n&SO=a&PP=50&do_Search=Go) |`yaourt -S qpm` | 94 | 95 | ## Use Qt's Maintenance Tool 96 | 97 | qpm is a available via a custom repository which can be added to Qt's Maintence Tool 98 | which is part of the SDK. 99 | 100 | * Open maintenancetool. 101 | * Select "Add or remove components" (do not click Continue yet). 102 | * Click "Settings" in the bottom left. 103 | * Go to the "Repositories" tab. 104 | * Scroll to the bottom and select "User defined repositories". 105 | * Click the "Add" button underneath. 106 | * Enter the following URL: `https://storage.googleapis.com/www.qpm.io/repository`.[1] 107 | * Leave username and password empty. 108 | * Clicked "Test" to ensure everything is working. 109 | * Click "Ok" to exit Settings. 110 | * Click "Continue". 111 | * Expand the `qpm` item and choose your platform. 112 | 113 | NOTE[1]: You can (and should) use HTTPS for the URL if it works, but at least on Mac OS X 114 | Yosemite, there is a problem with Qt's handing of Google's SSL certificate that prevents 115 | it from working. 116 | 117 | ## Compile from source 118 | 119 | The easiest way to build from source is to use the tools that ship with Go. If you 120 | already have Go installed and a workspace setup (GOPATH environment variable), then 121 | installing qpm is as simple as: 122 | 123 | ```bash 124 | go get qpm.io/qpm 125 | ``` 126 | 127 | If you don't want to use `go get` and would prefer to do it the hard way, then you can do the following: 128 | 129 | * Ensure you have [Go](http://golang.org/) installed (tested with 1.4.2 and 1.5) 130 | * Ensure you have a workspace setup (ie: define `GOPATH`) 131 | * Clone this repository into `$GOPATH/src/qpm.io` 132 | 133 | The qpm tool has its dependencies stored in the repo as Git submodules, so to initialize 134 | those you need to navigate to the root of the project and run: 135 | 136 | ``` 137 | git submodule init 138 | git submodule update 139 | ``` 140 | 141 | Compiling the command line tool is as simple as: 142 | 143 | ``` 144 | go build qpm.io/qpm 145 | ``` 146 | 147 | Although you should probably install it as well with (installing runs the build step): 148 | 149 | ``` 150 | go install qpm.io/qpm 151 | ``` 152 | 153 | If you are using Go 1.5, then cross compiling is simply a matter of setting ´GOARCH´ 154 | and ´GOOS´, for example to generate a 32-bit Windows build: 155 | 156 | ``` 157 | GOOS=windows GOARCH=386 go install qpm.io/qpm 158 | ``` 159 | 160 | The compiled binaries are placed in `$GOPATH/bin`. 161 | 162 | # Usage for App Developers 163 | 164 | Installing package dependencies with qpm requires no login or registration. You can search for packages 165 | using the following command: 166 | 167 | ``` 168 | qpm search 169 | ``` 170 | 171 | This will list a package and version number as well as the author. To get more information about the 172 | package, you can run: 173 | 174 | ``` 175 | qpm info 176 | ``` 177 | 178 | If the package sounds useful, you can install it: 179 | 180 | ``` 181 | qpm install 182 | ``` 183 | 184 | By default qpm will install the latest version of the component, however you can request a specific 185 | version using the following syntax: 186 | 187 | ``` 188 | qpm install package@1.0.1 189 | ``` 190 | 191 | Installing a package for the first time will create a new file called `qpm.json`. Subsequent 192 | installs will update this file with the new package. If you want to install all of the packages 193 | listed in your `qpm.json` file, then you use: 194 | 195 | ``` 196 | qpm install 197 | ``` 198 | 199 | With no arguments, this will install your dependent packages. Upon installing a new package, there 200 | will be a directory called `vendor` which contains the code for each package in its own 201 | subdirectory. The vendor directory will also contain a file called `vendor.pri` which should be 202 | included in your applications .pro file like so: 203 | 204 | ``` 205 | include(vendor/vendor.pri) 206 | ``` 207 | 208 | The vendor.pri takes care of including each package's .pri file which will expose the contents of the 209 | package to your project's build. Package .pri typically add files to `SOURCES`, `HEADERS` and 210 | `RESOURCES` so that they can be accessible to your app. 211 | 212 | If the package contains QML or Javascript code, then you need to register it with the QML engine, like 213 | so in your main.cpp (or wherever): 214 | 215 | ``` 216 | QQmlApplicationEngine engine; 217 | QPM_INIT(engine) 218 | engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); 219 | ``` 220 | 221 | It is important that you call `QPM_INIT` before calling `engine.load()` because otherwise you will 222 | errors about missing components. 223 | 224 | For packages that contain C++ code, these need to be manually exposed to QML for the time being, but 225 | perhaps we can find something clever here in the future. 226 | 227 | Uninstalling package can be done with: 228 | 229 | ``` 230 | qpm uninstall 231 | ``` 232 | 233 | # Usage for Package Authors 234 | 235 | If you have an idea for a Qt component that you would like to share, you can publish it on qpm.io. 236 | qpm can help you get started by running the following interactive command: 237 | 238 | ``` 239 | qpm init 240 | ``` 241 | 242 | This asks some basic questions to get you going and to generate a qpm.json file containing your 243 | package's meta data. If you are starting from scratch, you can let the `init` command generate some 244 | boilerplate code for you. This generates some basic files that you can extend as you create your 245 | module. The boilerplate currently generated is: 246 | 247 | * **qmldir**: QML module definition file 248 | * **package.pri**: Pri file for inclusion (indirectly) by apps 249 | * **package.qrc**: A Qt resource file for listing embedded source such as QML, JS, etc. 250 | 251 | To simplify deployment of applications that use your package, we recommend package authors to add 252 | as much as possible (QML, JS, PNG, etc.) to the resource file so everything gets compiled into 253 | the application binary. 254 | 255 | If you don't use the init command to generate the boilerplate, then you should use the following 256 | command to ensure that everything is in the right place with the right format: 257 | 258 | ``` 259 | qpm check 260 | ``` 261 | 262 | This command checks for common mistakes that will likely result in yor package not working correctly. 263 | 264 | Finally, when your package is ready to go, you can run the following command: 265 | 266 | ``` 267 | qpm publish 268 | ``` 269 | 270 | This command will prompt you to login or register and is required every time you publish a package. 271 | This is to prevent other people from publishing your package. In the future, it will be possible to 272 | have several contributors that can publish the same package. 273 | 274 | ## Example Package 275 | 276 | There is as example package which can be used as a template here: 277 | 278 | https://github.com/Cutehacks/qpm-example 279 | 280 | ## Package Naming 281 | 282 | Due to the nature of qpm and the way it compiles everything into the same compilation unit, we 283 | strongly recommend (enforce in fact) that packages be namespaced at several levels. The package 284 | name itself should use the same reverse DNS naming scheme that is used by Java (io.qpm.example). 285 | This is done to avoid popular package names (eg: components) being used exclusively by a single 286 | person or company. 287 | 288 | Package names should not include superfluous information such as "qt" or "qml". In the context 289 | of qpm, it is understood that a package is specific to Qt or QML so there is no need to state 290 | this explicitly. Note that the repository name can be completely independent of the package name. 291 | In that context, it most certainly can make sense to use "qt" or "qml" to differentiate the 292 | repository. 293 | 294 | ## A note on versioning 295 | 296 | qpm is somewhat strict on versioning. This is because one of the goals of qpm is to have consistent 297 | dependencies. If two applications use the same version of a package, they will contain the same code. 298 | 299 | A version consists of a label (eg: 0.0.1) and a revision (eg: Git SHA1 or tag). For package authors, 300 | this means that once a version has been published, it cannot be republished with a different revision. 301 | Publishing a new revision entails publishing a new version. Multiple labels can point to the same 302 | revision though. This is to handle the case where, for example, a release candidate (1.0.0-rc1) is 303 | identical to the final release (1.0.0). 304 | 305 | Publishing a new version of a package, is simply a matter of modifying the respective fields in the 306 | qpm.json file and running `qpm publish`. The meta data for a package is not versioned, so 307 | changing the description or author will affect all versions of a package. Nested dependencies for a 308 | package **are** versioned so publishing a new version with new dependencies will not affect previous 309 | versions. 310 | 311 | ## Tips 312 | 313 | ### Self-registering packages 314 | 315 | Package authors should aim to make their packages self-registering. This means that application developers 316 | should require very little boilerplate code to start using your package. Typical boilerplate code is 317 | registering various types with Qt's various classes. 318 | 319 | If you have QML items that are written in C++, your package can automatically register these types by using the 320 | [Q_COREAPP_STARTUP_FUNCTION](http://doc.qt.io/qt-5/qcoreapplication.html#Q_COREAPP_STARTUP_FUNCTION) macro. For 321 | example, in one of your `cpp` files, you can do the following: 322 | 323 | ```cpp 324 | 325 | static void registerTypes() { 326 | qmlRegisterType("io.qpm.MyPackage", 1, 0, "MyItem"); 327 | } 328 | 329 | Q_COREAPP_STARTUP_FUNCTION(registerTypes) 330 | ``` 331 | 332 | The above function will automatically be called after Q[Core|Gui]Application is constructed so the application developer 333 | needs to do nothing else in order to start using your item :ok_hand:. 334 | 335 | If you have items or types that are written in QML or Javascript, they get registered with the QML type system through 336 | a `qmldir` file. The pattern promoted by qpm involves putting your `qmldir` file inside a resource file to make it 337 | more self-contained, but this then creates the problem of how does the QML import engine find it? This is the reason 338 | that we added the `QML_INIT` macro which basically expands to this: 339 | 340 | `engine.addImportPath(QStringLiteral("qrc:/"));` 341 | 342 | This is somewhat suboptimal though and is the kind of boilerplate we would like to avoid when using qpm. In Qt 5.7 343 | there was a [change](https://github.com/qt/qtdeclarative/commit/ec5a886d4b92a18669d5bbd01b43a57f7d81b856) that updated 344 | the default import path with a path in the resource system. The path used was `qrc:/qt-project.org/imports`. This 345 | implies that it should now be possible for packages to write self-registering QML as well by adding the additional 346 | prefix to their `qrc` file. For example: 347 | 348 | ```xml 349 | 350 | 351 | qmldir 352 | MyItem.qml 353 | ... 354 | 355 | 356 | ``` 357 | 358 | # Contributing 359 | 360 | qpm is open source and we encourage other developers to contribute if they see an opportunity. The tool 361 | is written in Go so following the steps above to compile from source is a good place to start. 362 | 363 | qpm uses Google's Protocol Buffers to communicate between the command line tool and the server. More specifically, 364 | it uses the new "proto3" version which has support for Go and also gRPC which is also used. 365 | 366 | If you make change to the .proto files you need to follow the steps below. 367 | 368 | ## Code Style 369 | 370 | Since the app is written in Go, we request that all contributions be formatted with the `go fmt` tool. More info on this tool can be found [here](https://blog.golang.org/go-fmt-your-code). 371 | 372 | Additionally, if you edit the .proto file, you should follow the [Protocol Buffers Style Guide](https://developers.google.com/protocol-buffers/docs/style). 373 | 374 | ## Prerequisites 375 | 376 | Ensure the following component is installed and the `protoc` command is in your path somewhere. 377 | Note that repository is independent of Go so it should be cloned and installed globally. 378 | 379 | * https://github.com/google/protobuf (brew install --devel protobuf) or (port install protobuf3-cpp) 380 | 381 | ## Generating Protocol Buffers 382 | 383 | The generated code for the protocol buffers is checked in to the repo so the following 384 | steps are only needed if you modify the .proto file(s). 385 | 386 | In order to use the source generator you need to first build the `protoc-gen-go` tool: 387 | 388 | ``` 389 | cd src/github.com/golang/protobuf/protoc-gen-go 390 | go build 391 | go install 392 | ``` 393 | 394 | You can verify that the binary was built correctly in `$GOPATH/bin`. The above is only 395 | done once. 396 | 397 | Every time you make a modification to a .proto file, you need to regenerate some code 398 | by doing the following: 399 | 400 | ``` 401 | cd src/qpm.io/common/messages 402 | protoc --go_out=plugins=grpc:. *.proto 403 | ``` 404 | 405 | This will generate files of the format \*.pb.go in the same directory. You should 406 | check in the generated files for now. 407 | 408 | 409 | # FAQ 410 | 411 | ## How stable is it? 412 | 413 | This project is very young so your mileage may vary. At this point we are at the 414 | "Developer Preview" stage where feedback is the most important thing. We took 415 | a few shortcuts to get this out quickly, but we hope it scratches an itch for a 416 | few people at least. 417 | 418 | ## Why did you make qpm? 419 | 420 | Having now worked on frameworks outside of Qt, we saw that there was a big hole in 421 | Qt's developer offering. iOS has CocoaPods, Android has Maven/Gradle, Node has npm, 422 | Ruby has gem, Python has pip, Go has.. well Go. We wanted something that integrated 423 | well with Qt so we made something. We took inspiration from all of the above tools 424 | in some way or another. 425 | 426 | ## Why Go? 427 | 428 | The decision came down to Node.js, Go and Qt. Ideally we wanted to use a language 429 | that allowed us to share code between the client and server. Node and Go have 430 | stronger server side support than Qt/C++ so that eliminated Qt. There are plenty of 431 | cloud providers that support Go and Node but very few (any?) that support C++. 432 | 433 | The beautiful thing about Go is that it compiles to a single static binary which is 434 | exactly what we wanted for a command line tool. It also makes server deploys easy :) 435 | 436 | ## Why is the binary so big? 437 | 438 | This seems to be a result of using Go which presumably has a rather large runtime 439 | associated with it. 440 | 441 | ## How do I add a new FAQ? 442 | 443 | Submit a pull request to this file with your question and create an issue for us 444 | to answer it! 445 | -------------------------------------------------------------------------------- /common/messages/qpm.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. 2 | // source: qpm.proto 3 | // DO NOT EDIT! 4 | 5 | /* 6 | Package messages is a generated protocol buffer package. 7 | 8 | It is generated from these files: 9 | qpm.proto 10 | 11 | It has these top-level messages: 12 | DependencyMessage 13 | Package 14 | Dependency 15 | VersionInfo 16 | SearchResult 17 | InstallStats 18 | PingRequest 19 | PingResponse 20 | PublishRequest 21 | PublishResponse 22 | DependencyRequest 23 | DependencyResponse 24 | SearchRequest 25 | SearchResponse 26 | ListRequest 27 | ListResponse 28 | LoginRequest 29 | LoginResponse 30 | InfoRequest 31 | InfoResponse 32 | LicenseRequest 33 | LicenseResponse 34 | */ 35 | package messages 36 | 37 | import proto "github.com/golang/protobuf/proto" 38 | import fmt "fmt" 39 | import math "math" 40 | 41 | import ( 42 | context "golang.org/x/net/context" 43 | grpc "google.golang.org/grpc" 44 | ) 45 | 46 | // Reference imports to suppress errors if they are not otherwise used. 47 | var _ = proto.Marshal 48 | var _ = fmt.Errorf 49 | var _ = math.Inf 50 | 51 | // This is a compile-time assertion to ensure that this generated file 52 | // is compatible with the proto package it is being compiled against. 53 | // A compilation error at this line likely means your copy of the 54 | // proto package needs to be updated. 55 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 56 | 57 | type RepoType int32 58 | 59 | const ( 60 | RepoType_AUTO RepoType = 0 61 | RepoType_GITHUB RepoType = 1 62 | RepoType_GIT RepoType = 2 63 | RepoType_MERCURIAL RepoType = 3 64 | ) 65 | 66 | var RepoType_name = map[int32]string{ 67 | 0: "AUTO", 68 | 1: "GITHUB", 69 | 2: "GIT", 70 | 3: "MERCURIAL", 71 | } 72 | var RepoType_value = map[string]int32{ 73 | "AUTO": 0, 74 | "GITHUB": 1, 75 | "GIT": 2, 76 | "MERCURIAL": 3, 77 | } 78 | 79 | func (x RepoType) String() string { 80 | return proto.EnumName(RepoType_name, int32(x)) 81 | } 82 | func (RepoType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 83 | 84 | // The values in this enum should correspond to an SPDX identifier 85 | type LicenseType int32 86 | 87 | const ( 88 | LicenseType_NONE LicenseType = 0 89 | LicenseType_MIT LicenseType = 1 90 | LicenseType_AGPL_3_0 LicenseType = 2 91 | LicenseType_APACHE_2_0 LicenseType = 3 92 | LicenseType_ARTISTIC_2_0 LicenseType = 4 93 | LicenseType_BSD_2_CLAUSE LicenseType = 5 94 | LicenseType_BSD_3_CLAUSE LicenseType = 6 95 | LicenseType_CC0_1_0 LicenseType = 7 96 | LicenseType_EPL_1_0 LicenseType = 8 97 | LicenseType_GPL_2_0 LicenseType = 9 98 | LicenseType_GPL_3_0 LicenseType = 10 99 | LicenseType_ISC LicenseType = 11 100 | LicenseType_LGPL_2_1 LicenseType = 12 101 | LicenseType_LGPL_3_0 LicenseType = 13 102 | LicenseType_UNLICENSE LicenseType = 14 103 | LicenseType_MPL_2_0 LicenseType = 15 104 | ) 105 | 106 | var LicenseType_name = map[int32]string{ 107 | 0: "NONE", 108 | 1: "MIT", 109 | 2: "AGPL_3_0", 110 | 3: "APACHE_2_0", 111 | 4: "ARTISTIC_2_0", 112 | 5: "BSD_2_CLAUSE", 113 | 6: "BSD_3_CLAUSE", 114 | 7: "CC0_1_0", 115 | 8: "EPL_1_0", 116 | 9: "GPL_2_0", 117 | 10: "GPL_3_0", 118 | 11: "ISC", 119 | 12: "LGPL_2_1", 120 | 13: "LGPL_3_0", 121 | 14: "UNLICENSE", 122 | 15: "MPL_2_0", 123 | } 124 | var LicenseType_value = map[string]int32{ 125 | "NONE": 0, 126 | "MIT": 1, 127 | "AGPL_3_0": 2, 128 | "APACHE_2_0": 3, 129 | "ARTISTIC_2_0": 4, 130 | "BSD_2_CLAUSE": 5, 131 | "BSD_3_CLAUSE": 6, 132 | "CC0_1_0": 7, 133 | "EPL_1_0": 8, 134 | "GPL_2_0": 9, 135 | "GPL_3_0": 10, 136 | "ISC": 11, 137 | "LGPL_2_1": 12, 138 | "LGPL_3_0": 13, 139 | "UNLICENSE": 14, 140 | "MPL_2_0": 15, 141 | } 142 | 143 | func (x LicenseType) String() string { 144 | return proto.EnumName(LicenseType_name, int32(x)) 145 | } 146 | func (LicenseType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 147 | 148 | type MessageType int32 149 | 150 | const ( 151 | MessageType_INFO MessageType = 0 152 | MessageType_WARNING MessageType = 1 153 | MessageType_ERROR MessageType = 2 154 | ) 155 | 156 | var MessageType_name = map[int32]string{ 157 | 0: "INFO", 158 | 1: "WARNING", 159 | 2: "ERROR", 160 | } 161 | var MessageType_value = map[string]int32{ 162 | "INFO": 0, 163 | "WARNING": 1, 164 | "ERROR": 2, 165 | } 166 | 167 | func (x MessageType) String() string { 168 | return proto.EnumName(MessageType_name, int32(x)) 169 | } 170 | func (MessageType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } 171 | 172 | type DependencyMessage struct { 173 | Type MessageType `protobuf:"varint,1,opt,name=type,enum=messages.MessageType" json:"type,omitempty"` 174 | Title string `protobuf:"bytes,2,opt,name=title" json:"title,omitempty"` 175 | Body string `protobuf:"bytes,3,opt,name=body" json:"body,omitempty"` 176 | Prompt bool `protobuf:"varint,4,opt,name=prompt" json:"prompt,omitempty"` 177 | } 178 | 179 | func (m *DependencyMessage) Reset() { *m = DependencyMessage{} } 180 | func (m *DependencyMessage) String() string { return proto.CompactTextString(m) } 181 | func (*DependencyMessage) ProtoMessage() {} 182 | func (*DependencyMessage) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 183 | 184 | type Package struct { 185 | Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` 186 | Description string `protobuf:"bytes,2,opt,name=description" json:"description,omitempty"` 187 | Author *Package_Author `protobuf:"bytes,3,opt,name=author" json:"author,omitempty"` 188 | Repository *Package_Repository `protobuf:"bytes,4,opt,name=repository" json:"repository,omitempty"` 189 | Version *Package_Version `protobuf:"bytes,5,opt,name=version" json:"version,omitempty"` 190 | Dependencies []string `protobuf:"bytes,6,rep,name=dependencies" json:"dependencies,omitempty"` 191 | License LicenseType `protobuf:"varint,7,opt,name=license,enum=messages.LicenseType" json:"license,omitempty"` 192 | PriFilename string `protobuf:"bytes,8,opt,name=pri_filename,json=priFilename" json:"pri_filename,omitempty"` 193 | Webpage string `protobuf:"bytes,10,opt,name=webpage" json:"webpage,omitempty"` 194 | } 195 | 196 | func (m *Package) Reset() { *m = Package{} } 197 | func (m *Package) String() string { return proto.CompactTextString(m) } 198 | func (*Package) ProtoMessage() {} 199 | func (*Package) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 200 | 201 | func (m *Package) GetAuthor() *Package_Author { 202 | if m != nil { 203 | return m.Author 204 | } 205 | return nil 206 | } 207 | 208 | func (m *Package) GetRepository() *Package_Repository { 209 | if m != nil { 210 | return m.Repository 211 | } 212 | return nil 213 | } 214 | 215 | func (m *Package) GetVersion() *Package_Version { 216 | if m != nil { 217 | return m.Version 218 | } 219 | return nil 220 | } 221 | 222 | type Package_Repository struct { 223 | Type RepoType `protobuf:"varint,1,opt,name=type,enum=messages.RepoType" json:"type,omitempty"` 224 | Url string `protobuf:"bytes,2,opt,name=url" json:"url,omitempty"` 225 | } 226 | 227 | func (m *Package_Repository) Reset() { *m = Package_Repository{} } 228 | func (m *Package_Repository) String() string { return proto.CompactTextString(m) } 229 | func (*Package_Repository) ProtoMessage() {} 230 | func (*Package_Repository) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 0} } 231 | 232 | type Package_Version struct { 233 | Label string `protobuf:"bytes,1,opt,name=label" json:"label,omitempty"` 234 | Revision string `protobuf:"bytes,2,opt,name=revision" json:"revision,omitempty"` 235 | Fingerprint string `protobuf:"bytes,3,opt,name=fingerprint" json:"fingerprint,omitempty"` 236 | } 237 | 238 | func (m *Package_Version) Reset() { *m = Package_Version{} } 239 | func (m *Package_Version) String() string { return proto.CompactTextString(m) } 240 | func (*Package_Version) ProtoMessage() {} 241 | func (*Package_Version) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 1} } 242 | 243 | type Package_Author struct { 244 | Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` 245 | Email string `protobuf:"bytes,2,opt,name=email" json:"email,omitempty"` 246 | } 247 | 248 | func (m *Package_Author) Reset() { *m = Package_Author{} } 249 | func (m *Package_Author) String() string { return proto.CompactTextString(m) } 250 | func (*Package_Author) ProtoMessage() {} 251 | func (*Package_Author) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 2} } 252 | 253 | type Dependency struct { 254 | Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` 255 | Repository *Package_Repository `protobuf:"bytes,2,opt,name=repository" json:"repository,omitempty"` 256 | Version *Package_Version `protobuf:"bytes,3,opt,name=version" json:"version,omitempty"` 257 | } 258 | 259 | func (m *Dependency) Reset() { *m = Dependency{} } 260 | func (m *Dependency) String() string { return proto.CompactTextString(m) } 261 | func (*Dependency) ProtoMessage() {} 262 | func (*Dependency) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } 263 | 264 | func (m *Dependency) GetRepository() *Package_Repository { 265 | if m != nil { 266 | return m.Repository 267 | } 268 | return nil 269 | } 270 | 271 | func (m *Dependency) GetVersion() *Package_Version { 272 | if m != nil { 273 | return m.Version 274 | } 275 | return nil 276 | } 277 | 278 | type VersionInfo struct { 279 | Version *Package_Version `protobuf:"bytes,1,opt,name=version" json:"version,omitempty"` 280 | DatePublished string `protobuf:"bytes,2,opt,name=date_published,json=datePublished" json:"date_published,omitempty"` 281 | } 282 | 283 | func (m *VersionInfo) Reset() { *m = VersionInfo{} } 284 | func (m *VersionInfo) String() string { return proto.CompactTextString(m) } 285 | func (*VersionInfo) ProtoMessage() {} 286 | func (*VersionInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } 287 | 288 | func (m *VersionInfo) GetVersion() *Package_Version { 289 | if m != nil { 290 | return m.Version 291 | } 292 | return nil 293 | } 294 | 295 | type SearchResult struct { 296 | Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` 297 | Version string `protobuf:"bytes,2,opt,name=version" json:"version,omitempty"` 298 | Author *Package_Author `protobuf:"bytes,3,opt,name=author" json:"author,omitempty"` 299 | Description string `protobuf:"bytes,4,opt,name=description" json:"description,omitempty"` 300 | License LicenseType `protobuf:"varint,5,opt,name=license,enum=messages.LicenseType" json:"license,omitempty"` 301 | Webpage string `protobuf:"bytes,6,opt,name=webpage" json:"webpage,omitempty"` 302 | } 303 | 304 | func (m *SearchResult) Reset() { *m = SearchResult{} } 305 | func (m *SearchResult) String() string { return proto.CompactTextString(m) } 306 | func (*SearchResult) ProtoMessage() {} 307 | func (*SearchResult) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } 308 | 309 | func (m *SearchResult) GetAuthor() *Package_Author { 310 | if m != nil { 311 | return m.Author 312 | } 313 | return nil 314 | } 315 | 316 | type InstallStats struct { 317 | Daily uint32 `protobuf:"varint,1,opt,name=daily" json:"daily,omitempty"` 318 | Weekly uint32 `protobuf:"varint,2,opt,name=weekly" json:"weekly,omitempty"` 319 | Monthly uint32 `protobuf:"varint,3,opt,name=monthly" json:"monthly,omitempty"` 320 | Yearly uint32 `protobuf:"varint,4,opt,name=yearly" json:"yearly,omitempty"` 321 | Total uint32 `protobuf:"varint,5,opt,name=total" json:"total,omitempty"` 322 | } 323 | 324 | func (m *InstallStats) Reset() { *m = InstallStats{} } 325 | func (m *InstallStats) String() string { return proto.CompactTextString(m) } 326 | func (*InstallStats) ProtoMessage() {} 327 | func (*InstallStats) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } 328 | 329 | type PingRequest struct { 330 | } 331 | 332 | func (m *PingRequest) Reset() { *m = PingRequest{} } 333 | func (m *PingRequest) String() string { return proto.CompactTextString(m) } 334 | func (*PingRequest) ProtoMessage() {} 335 | func (*PingRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } 336 | 337 | type PingResponse struct { 338 | } 339 | 340 | func (m *PingResponse) Reset() { *m = PingResponse{} } 341 | func (m *PingResponse) String() string { return proto.CompactTextString(m) } 342 | func (*PingResponse) ProtoMessage() {} 343 | func (*PingResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } 344 | 345 | type PublishRequest struct { 346 | PackageDescription *Package `protobuf:"bytes,1,opt,name=package_description,json=packageDescription" json:"package_description,omitempty"` 347 | Token string `protobuf:"bytes,2,opt,name=token" json:"token,omitempty"` 348 | } 349 | 350 | func (m *PublishRequest) Reset() { *m = PublishRequest{} } 351 | func (m *PublishRequest) String() string { return proto.CompactTextString(m) } 352 | func (*PublishRequest) ProtoMessage() {} 353 | func (*PublishRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } 354 | 355 | func (m *PublishRequest) GetPackageDescription() *Package { 356 | if m != nil { 357 | return m.PackageDescription 358 | } 359 | return nil 360 | } 361 | 362 | type PublishResponse struct { 363 | } 364 | 365 | func (m *PublishResponse) Reset() { *m = PublishResponse{} } 366 | func (m *PublishResponse) String() string { return proto.CompactTextString(m) } 367 | func (*PublishResponse) ProtoMessage() {} 368 | func (*PublishResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } 369 | 370 | type DependencyRequest struct { 371 | PackageNames []string `protobuf:"bytes,1,rep,name=package_names,json=packageNames" json:"package_names,omitempty"` 372 | CompatLicense LicenseType `protobuf:"varint,4,opt,name=compat_license,json=compatLicense,enum=messages.LicenseType" json:"compat_license,omitempty"` 373 | } 374 | 375 | func (m *DependencyRequest) Reset() { *m = DependencyRequest{} } 376 | func (m *DependencyRequest) String() string { return proto.CompactTextString(m) } 377 | func (*DependencyRequest) ProtoMessage() {} 378 | func (*DependencyRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } 379 | 380 | type DependencyResponse struct { 381 | Dependencies []*Dependency `protobuf:"bytes,1,rep,name=dependencies" json:"dependencies,omitempty"` 382 | Messages []*DependencyMessage `protobuf:"bytes,2,rep,name=messages" json:"messages,omitempty"` 383 | } 384 | 385 | func (m *DependencyResponse) Reset() { *m = DependencyResponse{} } 386 | func (m *DependencyResponse) String() string { return proto.CompactTextString(m) } 387 | func (*DependencyResponse) ProtoMessage() {} 388 | func (*DependencyResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } 389 | 390 | func (m *DependencyResponse) GetDependencies() []*Dependency { 391 | if m != nil { 392 | return m.Dependencies 393 | } 394 | return nil 395 | } 396 | 397 | func (m *DependencyResponse) GetMessages() []*DependencyMessage { 398 | if m != nil { 399 | return m.Messages 400 | } 401 | return nil 402 | } 403 | 404 | type SearchRequest struct { 405 | PackageName string `protobuf:"bytes,1,opt,name=package_name,json=packageName" json:"package_name,omitempty"` 406 | } 407 | 408 | func (m *SearchRequest) Reset() { *m = SearchRequest{} } 409 | func (m *SearchRequest) String() string { return proto.CompactTextString(m) } 410 | func (*SearchRequest) ProtoMessage() {} 411 | func (*SearchRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } 412 | 413 | type SearchResponse struct { 414 | Results []*SearchResult `protobuf:"bytes,1,rep,name=results" json:"results,omitempty"` 415 | } 416 | 417 | func (m *SearchResponse) Reset() { *m = SearchResponse{} } 418 | func (m *SearchResponse) String() string { return proto.CompactTextString(m) } 419 | func (*SearchResponse) ProtoMessage() {} 420 | func (*SearchResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } 421 | 422 | func (m *SearchResponse) GetResults() []*SearchResult { 423 | if m != nil { 424 | return m.Results 425 | } 426 | return nil 427 | } 428 | 429 | type ListRequest struct { 430 | } 431 | 432 | func (m *ListRequest) Reset() { *m = ListRequest{} } 433 | func (m *ListRequest) String() string { return proto.CompactTextString(m) } 434 | func (*ListRequest) ProtoMessage() {} 435 | func (*ListRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } 436 | 437 | type ListResponse struct { 438 | Results []*SearchResult `protobuf:"bytes,1,rep,name=results" json:"results,omitempty"` 439 | } 440 | 441 | func (m *ListResponse) Reset() { *m = ListResponse{} } 442 | func (m *ListResponse) String() string { return proto.CompactTextString(m) } 443 | func (*ListResponse) ProtoMessage() {} 444 | func (*ListResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{15} } 445 | 446 | func (m *ListResponse) GetResults() []*SearchResult { 447 | if m != nil { 448 | return m.Results 449 | } 450 | return nil 451 | } 452 | 453 | type LoginRequest struct { 454 | Email string `protobuf:"bytes,1,opt,name=email" json:"email,omitempty"` 455 | Password string `protobuf:"bytes,2,opt,name=password" json:"password,omitempty"` 456 | Create bool `protobuf:"varint,3,opt,name=create" json:"create,omitempty"` 457 | } 458 | 459 | func (m *LoginRequest) Reset() { *m = LoginRequest{} } 460 | func (m *LoginRequest) String() string { return proto.CompactTextString(m) } 461 | func (*LoginRequest) ProtoMessage() {} 462 | func (*LoginRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{16} } 463 | 464 | type LoginResponse struct { 465 | Token string `protobuf:"bytes,1,opt,name=token" json:"token,omitempty"` 466 | } 467 | 468 | func (m *LoginResponse) Reset() { *m = LoginResponse{} } 469 | func (m *LoginResponse) String() string { return proto.CompactTextString(m) } 470 | func (*LoginResponse) ProtoMessage() {} 471 | func (*LoginResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{17} } 472 | 473 | type InfoRequest struct { 474 | PackageName string `protobuf:"bytes,1,opt,name=package_name,json=packageName" json:"package_name,omitempty"` 475 | } 476 | 477 | func (m *InfoRequest) Reset() { *m = InfoRequest{} } 478 | func (m *InfoRequest) String() string { return proto.CompactTextString(m) } 479 | func (*InfoRequest) ProtoMessage() {} 480 | func (*InfoRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{18} } 481 | 482 | type InfoResponse struct { 483 | Package *Package `protobuf:"bytes,1,opt,name=package" json:"package,omitempty"` 484 | Versions []*VersionInfo `protobuf:"bytes,2,rep,name=versions" json:"versions,omitempty"` 485 | Dependencies []*Dependency `protobuf:"bytes,3,rep,name=dependencies" json:"dependencies,omitempty"` 486 | InstallStats *InstallStats `protobuf:"bytes,4,opt,name=install_stats,json=installStats" json:"install_stats,omitempty"` 487 | } 488 | 489 | func (m *InfoResponse) Reset() { *m = InfoResponse{} } 490 | func (m *InfoResponse) String() string { return proto.CompactTextString(m) } 491 | func (*InfoResponse) ProtoMessage() {} 492 | func (*InfoResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{19} } 493 | 494 | func (m *InfoResponse) GetPackage() *Package { 495 | if m != nil { 496 | return m.Package 497 | } 498 | return nil 499 | } 500 | 501 | func (m *InfoResponse) GetVersions() []*VersionInfo { 502 | if m != nil { 503 | return m.Versions 504 | } 505 | return nil 506 | } 507 | 508 | func (m *InfoResponse) GetDependencies() []*Dependency { 509 | if m != nil { 510 | return m.Dependencies 511 | } 512 | return nil 513 | } 514 | 515 | func (m *InfoResponse) GetInstallStats() *InstallStats { 516 | if m != nil { 517 | return m.InstallStats 518 | } 519 | return nil 520 | } 521 | 522 | type LicenseRequest struct { 523 | Package *Package `protobuf:"bytes,1,opt,name=package" json:"package,omitempty"` 524 | } 525 | 526 | func (m *LicenseRequest) Reset() { *m = LicenseRequest{} } 527 | func (m *LicenseRequest) String() string { return proto.CompactTextString(m) } 528 | func (*LicenseRequest) ProtoMessage() {} 529 | func (*LicenseRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{20} } 530 | 531 | func (m *LicenseRequest) GetPackage() *Package { 532 | if m != nil { 533 | return m.Package 534 | } 535 | return nil 536 | } 537 | 538 | type LicenseResponse struct { 539 | Body string `protobuf:"bytes,1,opt,name=body" json:"body,omitempty"` 540 | } 541 | 542 | func (m *LicenseResponse) Reset() { *m = LicenseResponse{} } 543 | func (m *LicenseResponse) String() string { return proto.CompactTextString(m) } 544 | func (*LicenseResponse) ProtoMessage() {} 545 | func (*LicenseResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{21} } 546 | 547 | func init() { 548 | proto.RegisterType((*DependencyMessage)(nil), "messages.DependencyMessage") 549 | proto.RegisterType((*Package)(nil), "messages.Package") 550 | proto.RegisterType((*Package_Repository)(nil), "messages.Package.Repository") 551 | proto.RegisterType((*Package_Version)(nil), "messages.Package.Version") 552 | proto.RegisterType((*Package_Author)(nil), "messages.Package.Author") 553 | proto.RegisterType((*Dependency)(nil), "messages.Dependency") 554 | proto.RegisterType((*VersionInfo)(nil), "messages.VersionInfo") 555 | proto.RegisterType((*SearchResult)(nil), "messages.SearchResult") 556 | proto.RegisterType((*InstallStats)(nil), "messages.InstallStats") 557 | proto.RegisterType((*PingRequest)(nil), "messages.PingRequest") 558 | proto.RegisterType((*PingResponse)(nil), "messages.PingResponse") 559 | proto.RegisterType((*PublishRequest)(nil), "messages.PublishRequest") 560 | proto.RegisterType((*PublishResponse)(nil), "messages.PublishResponse") 561 | proto.RegisterType((*DependencyRequest)(nil), "messages.DependencyRequest") 562 | proto.RegisterType((*DependencyResponse)(nil), "messages.DependencyResponse") 563 | proto.RegisterType((*SearchRequest)(nil), "messages.SearchRequest") 564 | proto.RegisterType((*SearchResponse)(nil), "messages.SearchResponse") 565 | proto.RegisterType((*ListRequest)(nil), "messages.ListRequest") 566 | proto.RegisterType((*ListResponse)(nil), "messages.ListResponse") 567 | proto.RegisterType((*LoginRequest)(nil), "messages.LoginRequest") 568 | proto.RegisterType((*LoginResponse)(nil), "messages.LoginResponse") 569 | proto.RegisterType((*InfoRequest)(nil), "messages.InfoRequest") 570 | proto.RegisterType((*InfoResponse)(nil), "messages.InfoResponse") 571 | proto.RegisterType((*LicenseRequest)(nil), "messages.LicenseRequest") 572 | proto.RegisterType((*LicenseResponse)(nil), "messages.LicenseResponse") 573 | proto.RegisterEnum("messages.RepoType", RepoType_name, RepoType_value) 574 | proto.RegisterEnum("messages.LicenseType", LicenseType_name, LicenseType_value) 575 | proto.RegisterEnum("messages.MessageType", MessageType_name, MessageType_value) 576 | } 577 | 578 | // Reference imports to suppress errors if they are not otherwise used. 579 | var _ context.Context 580 | var _ grpc.ClientConn 581 | 582 | // This is a compile-time assertion to ensure that this generated file 583 | // is compatible with the grpc package it is being compiled against. 584 | const _ = grpc.SupportPackageIsVersion3 585 | 586 | // Client API for Qpm service 587 | 588 | type QpmClient interface { 589 | Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) 590 | Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*PublishResponse, error) 591 | GetDependencies(ctx context.Context, in *DependencyRequest, opts ...grpc.CallOption) (*DependencyResponse, error) 592 | Search(ctx context.Context, in *SearchRequest, opts ...grpc.CallOption) (*SearchResponse, error) 593 | List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) 594 | Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) 595 | Info(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*InfoResponse, error) 596 | GetLicense(ctx context.Context, in *LicenseRequest, opts ...grpc.CallOption) (*LicenseResponse, error) 597 | } 598 | 599 | type qpmClient struct { 600 | cc *grpc.ClientConn 601 | } 602 | 603 | func NewQpmClient(cc *grpc.ClientConn) QpmClient { 604 | return &qpmClient{cc} 605 | } 606 | 607 | func (c *qpmClient) Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) { 608 | out := new(PingResponse) 609 | err := grpc.Invoke(ctx, "/messages.Qpm/Ping", in, out, c.cc, opts...) 610 | if err != nil { 611 | return nil, err 612 | } 613 | return out, nil 614 | } 615 | 616 | func (c *qpmClient) Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*PublishResponse, error) { 617 | out := new(PublishResponse) 618 | err := grpc.Invoke(ctx, "/messages.Qpm/Publish", in, out, c.cc, opts...) 619 | if err != nil { 620 | return nil, err 621 | } 622 | return out, nil 623 | } 624 | 625 | func (c *qpmClient) GetDependencies(ctx context.Context, in *DependencyRequest, opts ...grpc.CallOption) (*DependencyResponse, error) { 626 | out := new(DependencyResponse) 627 | err := grpc.Invoke(ctx, "/messages.Qpm/GetDependencies", in, out, c.cc, opts...) 628 | if err != nil { 629 | return nil, err 630 | } 631 | return out, nil 632 | } 633 | 634 | func (c *qpmClient) Search(ctx context.Context, in *SearchRequest, opts ...grpc.CallOption) (*SearchResponse, error) { 635 | out := new(SearchResponse) 636 | err := grpc.Invoke(ctx, "/messages.Qpm/Search", in, out, c.cc, opts...) 637 | if err != nil { 638 | return nil, err 639 | } 640 | return out, nil 641 | } 642 | 643 | func (c *qpmClient) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) { 644 | out := new(ListResponse) 645 | err := grpc.Invoke(ctx, "/messages.Qpm/List", in, out, c.cc, opts...) 646 | if err != nil { 647 | return nil, err 648 | } 649 | return out, nil 650 | } 651 | 652 | func (c *qpmClient) Login(ctx context.Context, in *LoginRequest, opts ...grpc.CallOption) (*LoginResponse, error) { 653 | out := new(LoginResponse) 654 | err := grpc.Invoke(ctx, "/messages.Qpm/Login", in, out, c.cc, opts...) 655 | if err != nil { 656 | return nil, err 657 | } 658 | return out, nil 659 | } 660 | 661 | func (c *qpmClient) Info(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*InfoResponse, error) { 662 | out := new(InfoResponse) 663 | err := grpc.Invoke(ctx, "/messages.Qpm/Info", in, out, c.cc, opts...) 664 | if err != nil { 665 | return nil, err 666 | } 667 | return out, nil 668 | } 669 | 670 | func (c *qpmClient) GetLicense(ctx context.Context, in *LicenseRequest, opts ...grpc.CallOption) (*LicenseResponse, error) { 671 | out := new(LicenseResponse) 672 | err := grpc.Invoke(ctx, "/messages.Qpm/GetLicense", in, out, c.cc, opts...) 673 | if err != nil { 674 | return nil, err 675 | } 676 | return out, nil 677 | } 678 | 679 | // Server API for Qpm service 680 | 681 | type QpmServer interface { 682 | Ping(context.Context, *PingRequest) (*PingResponse, error) 683 | Publish(context.Context, *PublishRequest) (*PublishResponse, error) 684 | GetDependencies(context.Context, *DependencyRequest) (*DependencyResponse, error) 685 | Search(context.Context, *SearchRequest) (*SearchResponse, error) 686 | List(context.Context, *ListRequest) (*ListResponse, error) 687 | Login(context.Context, *LoginRequest) (*LoginResponse, error) 688 | Info(context.Context, *InfoRequest) (*InfoResponse, error) 689 | GetLicense(context.Context, *LicenseRequest) (*LicenseResponse, error) 690 | } 691 | 692 | func RegisterQpmServer(s *grpc.Server, srv QpmServer) { 693 | s.RegisterService(&_Qpm_serviceDesc, srv) 694 | } 695 | 696 | func _Qpm_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 697 | in := new(PingRequest) 698 | if err := dec(in); err != nil { 699 | return nil, err 700 | } 701 | if interceptor == nil { 702 | return srv.(QpmServer).Ping(ctx, in) 703 | } 704 | info := &grpc.UnaryServerInfo{ 705 | Server: srv, 706 | FullMethod: "/messages.Qpm/Ping", 707 | } 708 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 709 | return srv.(QpmServer).Ping(ctx, req.(*PingRequest)) 710 | } 711 | return interceptor(ctx, in, info, handler) 712 | } 713 | 714 | func _Qpm_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 715 | in := new(PublishRequest) 716 | if err := dec(in); err != nil { 717 | return nil, err 718 | } 719 | if interceptor == nil { 720 | return srv.(QpmServer).Publish(ctx, in) 721 | } 722 | info := &grpc.UnaryServerInfo{ 723 | Server: srv, 724 | FullMethod: "/messages.Qpm/Publish", 725 | } 726 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 727 | return srv.(QpmServer).Publish(ctx, req.(*PublishRequest)) 728 | } 729 | return interceptor(ctx, in, info, handler) 730 | } 731 | 732 | func _Qpm_GetDependencies_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 733 | in := new(DependencyRequest) 734 | if err := dec(in); err != nil { 735 | return nil, err 736 | } 737 | if interceptor == nil { 738 | return srv.(QpmServer).GetDependencies(ctx, in) 739 | } 740 | info := &grpc.UnaryServerInfo{ 741 | Server: srv, 742 | FullMethod: "/messages.Qpm/GetDependencies", 743 | } 744 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 745 | return srv.(QpmServer).GetDependencies(ctx, req.(*DependencyRequest)) 746 | } 747 | return interceptor(ctx, in, info, handler) 748 | } 749 | 750 | func _Qpm_Search_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 751 | in := new(SearchRequest) 752 | if err := dec(in); err != nil { 753 | return nil, err 754 | } 755 | if interceptor == nil { 756 | return srv.(QpmServer).Search(ctx, in) 757 | } 758 | info := &grpc.UnaryServerInfo{ 759 | Server: srv, 760 | FullMethod: "/messages.Qpm/Search", 761 | } 762 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 763 | return srv.(QpmServer).Search(ctx, req.(*SearchRequest)) 764 | } 765 | return interceptor(ctx, in, info, handler) 766 | } 767 | 768 | func _Qpm_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 769 | in := new(ListRequest) 770 | if err := dec(in); err != nil { 771 | return nil, err 772 | } 773 | if interceptor == nil { 774 | return srv.(QpmServer).List(ctx, in) 775 | } 776 | info := &grpc.UnaryServerInfo{ 777 | Server: srv, 778 | FullMethod: "/messages.Qpm/List", 779 | } 780 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 781 | return srv.(QpmServer).List(ctx, req.(*ListRequest)) 782 | } 783 | return interceptor(ctx, in, info, handler) 784 | } 785 | 786 | func _Qpm_Login_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 787 | in := new(LoginRequest) 788 | if err := dec(in); err != nil { 789 | return nil, err 790 | } 791 | if interceptor == nil { 792 | return srv.(QpmServer).Login(ctx, in) 793 | } 794 | info := &grpc.UnaryServerInfo{ 795 | Server: srv, 796 | FullMethod: "/messages.Qpm/Login", 797 | } 798 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 799 | return srv.(QpmServer).Login(ctx, req.(*LoginRequest)) 800 | } 801 | return interceptor(ctx, in, info, handler) 802 | } 803 | 804 | func _Qpm_Info_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 805 | in := new(InfoRequest) 806 | if err := dec(in); err != nil { 807 | return nil, err 808 | } 809 | if interceptor == nil { 810 | return srv.(QpmServer).Info(ctx, in) 811 | } 812 | info := &grpc.UnaryServerInfo{ 813 | Server: srv, 814 | FullMethod: "/messages.Qpm/Info", 815 | } 816 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 817 | return srv.(QpmServer).Info(ctx, req.(*InfoRequest)) 818 | } 819 | return interceptor(ctx, in, info, handler) 820 | } 821 | 822 | func _Qpm_GetLicense_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 823 | in := new(LicenseRequest) 824 | if err := dec(in); err != nil { 825 | return nil, err 826 | } 827 | if interceptor == nil { 828 | return srv.(QpmServer).GetLicense(ctx, in) 829 | } 830 | info := &grpc.UnaryServerInfo{ 831 | Server: srv, 832 | FullMethod: "/messages.Qpm/GetLicense", 833 | } 834 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 835 | return srv.(QpmServer).GetLicense(ctx, req.(*LicenseRequest)) 836 | } 837 | return interceptor(ctx, in, info, handler) 838 | } 839 | 840 | var _Qpm_serviceDesc = grpc.ServiceDesc{ 841 | ServiceName: "messages.Qpm", 842 | HandlerType: (*QpmServer)(nil), 843 | Methods: []grpc.MethodDesc{ 844 | { 845 | MethodName: "Ping", 846 | Handler: _Qpm_Ping_Handler, 847 | }, 848 | { 849 | MethodName: "Publish", 850 | Handler: _Qpm_Publish_Handler, 851 | }, 852 | { 853 | MethodName: "GetDependencies", 854 | Handler: _Qpm_GetDependencies_Handler, 855 | }, 856 | { 857 | MethodName: "Search", 858 | Handler: _Qpm_Search_Handler, 859 | }, 860 | { 861 | MethodName: "List", 862 | Handler: _Qpm_List_Handler, 863 | }, 864 | { 865 | MethodName: "Login", 866 | Handler: _Qpm_Login_Handler, 867 | }, 868 | { 869 | MethodName: "Info", 870 | Handler: _Qpm_Info_Handler, 871 | }, 872 | { 873 | MethodName: "GetLicense", 874 | Handler: _Qpm_GetLicense_Handler, 875 | }, 876 | }, 877 | Streams: []grpc.StreamDesc{}, 878 | Metadata: fileDescriptor0, 879 | } 880 | 881 | func init() { proto.RegisterFile("qpm.proto", fileDescriptor0) } 882 | 883 | var fileDescriptor0 = []byte{ 884 | // 1281 bytes of a gzipped FileDescriptorProto 885 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x57, 0x4b, 0x6f, 0xdb, 0xc6, 886 | 0x13, 0x37, 0xad, 0x07, 0xa5, 0xa1, 0x28, 0x33, 0xfb, 0x4f, 0x1c, 0x46, 0xff, 0x1c, 0x5c, 0x16, 887 | 0x29, 0xdc, 0x14, 0x70, 0x14, 0xf9, 0x90, 0xa0, 0x4d, 0x80, 0xc8, 0xb2, 0xe2, 0x10, 0x90, 0x15, 888 | 0x77, 0x25, 0xb7, 0xbd, 0x14, 0x04, 0x2d, 0x6d, 0x6c, 0x36, 0x14, 0xc9, 0x90, 0x74, 0x02, 0xdd, 889 | 0x8a, 0xa2, 0x40, 0x3f, 0x40, 0x3f, 0x59, 0xcf, 0x3d, 0xf7, 0xd0, 0x6f, 0x51, 0xec, 0x8b, 0x5a, 890 | 0x3d, 0x5a, 0x38, 0xb9, 0x69, 0x66, 0xe7, 0xb5, 0x33, 0xbf, 0xf9, 0x2d, 0x05, 0xf5, 0x77, 0xc9, 891 | 0xec, 0x20, 0x49, 0xe3, 0x3c, 0x46, 0xb5, 0x19, 0xc9, 0x32, 0xff, 0x92, 0x64, 0xce, 0xcf, 0x1a, 892 | 0xdc, 0x3a, 0x26, 0x09, 0x89, 0xa6, 0x24, 0x9a, 0xcc, 0x4f, 0xb9, 0x1a, 0x7d, 0x09, 0xe5, 0x7c, 893 | 0x9e, 0x10, 0x5b, 0xdb, 0xd3, 0xf6, 0x9b, 0x9d, 0x3b, 0x07, 0xd2, 0xfc, 0x40, 0x18, 0x8c, 0xe7, 894 | 0x09, 0xc1, 0xcc, 0x04, 0xdd, 0x86, 0x4a, 0x1e, 0xe4, 0x21, 0xb1, 0xb7, 0xf7, 0xb4, 0xfd, 0x3a, 895 | 0xe6, 0x02, 0x42, 0x50, 0xbe, 0x88, 0xa7, 0x73, 0xbb, 0xc4, 0x94, 0xec, 0x37, 0xda, 0x85, 0x6a, 896 | 0x92, 0xc6, 0xb3, 0x24, 0xb7, 0xcb, 0x7b, 0xda, 0x7e, 0x0d, 0x0b, 0xc9, 0xf9, 0xa3, 0x0c, 0xfa, 897 | 0x99, 0x3f, 0x79, 0x4b, 0x13, 0x23, 0x28, 0x47, 0xfe, 0x8c, 0x27, 0xae, 0x63, 0xf6, 0x1b, 0xed, 898 | 0x81, 0x31, 0x25, 0xd9, 0x24, 0x0d, 0x92, 0x3c, 0x88, 0x23, 0x91, 0x47, 0x55, 0xa1, 0x36, 0x54, 899 | 0xfd, 0xeb, 0xfc, 0x2a, 0x4e, 0x59, 0x3e, 0xa3, 0x63, 0x2f, 0x0a, 0x16, 0x81, 0x0f, 0xba, 0xec, 900 | 0x1c, 0x0b, 0x3b, 0xf4, 0x0c, 0x20, 0x25, 0x49, 0x9c, 0x05, 0x79, 0x9c, 0xce, 0x59, 0x3d, 0x46, 901 | 0xe7, 0xfe, 0xba, 0x17, 0x2e, 0x6c, 0xb0, 0x62, 0x8f, 0x0e, 0x41, 0x7f, 0x4f, 0xd2, 0x8c, 0x56, 902 | 0x53, 0x61, 0xae, 0xf7, 0xd6, 0x5d, 0xbf, 0xe3, 0x06, 0x58, 0x5a, 0x22, 0x07, 0x1a, 0x53, 0xd9, 903 | 0xe8, 0x80, 0x64, 0x76, 0x75, 0xaf, 0xb4, 0x5f, 0xc7, 0x4b, 0x3a, 0xf4, 0x08, 0xf4, 0x30, 0x98, 904 | 0x90, 0x28, 0x23, 0xb6, 0xbe, 0xda, 0xfa, 0x01, 0x3f, 0x60, 0xad, 0x97, 0x56, 0xe8, 0x33, 0x68, 905 | 0x24, 0x69, 0xe0, 0xbd, 0x09, 0x42, 0xc2, 0xfa, 0x56, 0xe3, 0xcd, 0x49, 0xd2, 0xe0, 0xa5, 0x50, 906 | 0x21, 0x1b, 0xf4, 0x0f, 0xe4, 0x22, 0xf1, 0x2f, 0x89, 0x0d, 0xec, 0x54, 0x8a, 0xad, 0x97, 0x00, 907 | 0x8b, 0x0b, 0xa2, 0x2f, 0x96, 0x66, 0x8e, 0x16, 0x89, 0xa9, 0x8d, 0x32, 0x70, 0x0b, 0x4a, 0xd7, 908 | 0x69, 0x28, 0xc6, 0x40, 0x7f, 0xb6, 0x7e, 0x04, 0x5d, 0xdc, 0x96, 0xa2, 0x21, 0xf4, 0x2f, 0x48, 909 | 0x28, 0x06, 0xc8, 0x05, 0xd4, 0x82, 0x5a, 0x4a, 0xde, 0x07, 0xd9, 0x62, 0x7c, 0x85, 0x4c, 0xa7, 910 | 0xfb, 0x26, 0x88, 0x2e, 0x49, 0x9a, 0xa4, 0x41, 0x94, 0x0b, 0xc0, 0xa8, 0xaa, 0x56, 0x07, 0xaa, 911 | 0x7c, 0x7a, 0x1b, 0xd1, 0x71, 0x1b, 0x2a, 0x64, 0xe6, 0x07, 0xb2, 0x20, 0x2e, 0x38, 0xbf, 0x6b, 912 | 0x00, 0x0b, 0x58, 0x6f, 0x74, 0x5c, 0x86, 0xc0, 0xf6, 0xa7, 0x43, 0xa0, 0x74, 0x53, 0x08, 0x38, 913 | 0x01, 0x18, 0x42, 0xe7, 0x46, 0x6f, 0x62, 0x35, 0x86, 0x76, 0x63, 0x18, 0x3d, 0x80, 0xe6, 0xd4, 914 | 0xcf, 0x89, 0x97, 0x5c, 0x5f, 0x84, 0x41, 0x76, 0x45, 0xa6, 0xe2, 0xe2, 0x26, 0xd5, 0x9e, 0x49, 915 | 0xa5, 0xf3, 0xa7, 0x06, 0x8d, 0x11, 0xf1, 0xd3, 0xc9, 0x15, 0x26, 0xd9, 0x75, 0x98, 0x6f, 0x6c, 916 | 0x81, 0xbd, 0x28, 0x80, 0x07, 0x29, 0xb2, 0x7c, 0xfc, 0x46, 0xad, 0x6c, 0x69, 0x79, 0x7d, 0x4b, 917 | 0x15, 0x70, 0x57, 0x6e, 0x04, 0x6e, 0x05, 0xb9, 0xd5, 0x25, 0xe4, 0x3a, 0xbf, 0x6a, 0xd0, 0x70, 918 | 0xa3, 0x2c, 0xf7, 0xc3, 0x70, 0x94, 0xfb, 0x79, 0x46, 0x51, 0x30, 0xf5, 0x83, 0x70, 0xce, 0xae, 919 | 0x67, 0x62, 0x2e, 0x50, 0xc6, 0xf9, 0x40, 0xc8, 0xdb, 0x90, 0x8f, 0xd7, 0xc4, 0x42, 0xa2, 0x81, 920 | 0x67, 0x71, 0x94, 0x5f, 0x85, 0x9c, 0xa0, 0x4c, 0x2c, 0x45, 0xea, 0x31, 0x27, 0x7e, 0x1a, 0x72, 921 | 0x4e, 0x30, 0xb1, 0x90, 0x18, 0xcb, 0xc5, 0xb9, 0x1f, 0xb2, 0xca, 0x4d, 0xcc, 0x05, 0xc7, 0x04, 922 | 0xe3, 0x2c, 0x88, 0x2e, 0x31, 0x79, 0x77, 0x4d, 0xb2, 0xdc, 0x69, 0x42, 0x83, 0x8b, 0x59, 0x12, 923 | 0x47, 0x19, 0x71, 0x7e, 0x82, 0xa6, 0x18, 0x88, 0xb0, 0x40, 0x47, 0xf0, 0xbf, 0x84, 0xb7, 0xcf, 924 | 0x53, 0x9b, 0xc5, 0xa7, 0x7f, 0x6b, 0xad, 0xc7, 0x18, 0x09, 0xeb, 0x63, 0xa5, 0x8d, 0xac, 0x94, 925 | 0xb7, 0x24, 0x2a, 0x08, 0x97, 0x0a, 0xce, 0x2d, 0xd8, 0x29, 0x72, 0x89, 0xf4, 0xef, 0x55, 0x66, 926 | 0x97, 0x15, 0x7c, 0x0e, 0xa6, 0xac, 0x80, 0x42, 0x20, 0xb3, 0x35, 0x4e, 0x43, 0x42, 0x39, 0xa4, 927 | 0x3a, 0xf4, 0x0c, 0x9a, 0x93, 0x78, 0x96, 0xf8, 0xb9, 0x27, 0x07, 0x56, 0xfe, 0xaf, 0x81, 0x99, 928 | 0xdc, 0x58, 0xa8, 0x9c, 0xdf, 0x34, 0x40, 0x6a, 0x62, 0x5e, 0x0e, 0x7a, 0xba, 0xc2, 0x7f, 0x34, 929 | 0xb1, 0xd1, 0xb9, 0xbd, 0x08, 0xa9, 0xf8, 0x2c, 0xb3, 0xe2, 0x13, 0x28, 0xde, 0x2b, 0x7b, 0x9b, 930 | 0x79, 0xfd, 0x7f, 0x93, 0x97, 0x78, 0x9b, 0xf0, 0xe2, 0x71, 0xeb, 0x80, 0x29, 0x77, 0x80, 0xdf, 931 | 0x9e, 0xd2, 0xa5, 0x72, 0x7b, 0xb1, 0x0c, 0x86, 0x72, 0x79, 0xe7, 0x08, 0x9a, 0xc5, 0xde, 0xf0, 932 | 0xc2, 0xdb, 0xa0, 0xa7, 0x6c, 0x87, 0x64, 0xcd, 0xbb, 0x8b, 0xec, 0xea, 0x8a, 0x61, 0x69, 0x46, 933 | 0x71, 0x31, 0x08, 0xb2, 0x5c, 0xe2, 0xe2, 0x05, 0x34, 0xb8, 0xf8, 0xc9, 0x01, 0x7f, 0x80, 0xc6, 934 | 0x20, 0xbe, 0x0c, 0x22, 0x79, 0x8f, 0x82, 0xf4, 0x34, 0x85, 0xf4, 0x28, 0xcd, 0x26, 0x7e, 0x96, 935 | 0x7d, 0x88, 0x53, 0x49, 0x0a, 0x85, 0x4c, 0x81, 0x3d, 0x49, 0x89, 0x9f, 0x13, 0x86, 0xf8, 0x1a, 936 | 0x16, 0x92, 0xf3, 0x00, 0x4c, 0x11, 0x59, 0x14, 0x57, 0xc0, 0x4b, 0x53, 0xe1, 0xd5, 0x06, 0x83, 937 | 0x52, 0xd6, 0x47, 0xf4, 0xf1, 0x2f, 0xb6, 0xa2, 0xd4, 0x45, 0x04, 0xfe, 0x0a, 0x74, 0x71, 0xfe, 938 | 0xef, 0x78, 0x97, 0x16, 0xe8, 0x31, 0xd4, 0x04, 0x15, 0xc9, 0x91, 0x2b, 0xd8, 0x53, 0x38, 0x14, 939 | 0x17, 0x66, 0x6b, 0xf8, 0x2a, 0xdd, 0x18, 0x5f, 0xdf, 0x80, 0x19, 0x70, 0x32, 0xf1, 0x32, 0xca, 940 | 0x26, 0xe2, 0x7b, 0x40, 0x99, 0x8a, 0xca, 0x35, 0xb8, 0x11, 0x28, 0x92, 0xf3, 0x1c, 0x9a, 0x02, 941 | 0xf8, 0xb2, 0x39, 0x1f, 0x73, 0x51, 0xe7, 0x01, 0xec, 0x14, 0xee, 0xa2, 0x51, 0xf2, 0xdb, 0x49, 942 | 0x5b, 0x7c, 0x3b, 0x3d, 0x7c, 0x0a, 0x35, 0xf9, 0x0c, 0xa3, 0x1a, 0x94, 0xbb, 0xe7, 0xe3, 0xd7, 943 | 0xd6, 0x16, 0x02, 0xa8, 0x9e, 0xb8, 0xe3, 0x57, 0xe7, 0x47, 0x96, 0x86, 0x74, 0x28, 0x9d, 0xb8, 944 | 0x63, 0x6b, 0x1b, 0x99, 0x50, 0x3f, 0xed, 0xe3, 0xde, 0x39, 0x76, 0xbb, 0x03, 0xab, 0xf4, 0xf0, 945 | 0x6f, 0x8d, 0x82, 0xb1, 0x58, 0x56, 0xea, 0x3d, 0x7c, 0x3d, 0xec, 0x5b, 0x5b, 0xd4, 0xe3, 0xd4, 946 | 0x1d, 0x5b, 0x1a, 0x6a, 0x40, 0xad, 0x7b, 0x72, 0x36, 0xf0, 0x0e, 0xbd, 0xb6, 0xb5, 0x8d, 0x9a, 947 | 0x00, 0xdd, 0xb3, 0x6e, 0xef, 0x55, 0xdf, 0xeb, 0x78, 0x6d, 0xab, 0x84, 0x2c, 0x68, 0x74, 0xf1, 948 | 0xd8, 0x1d, 0x8d, 0xdd, 0x1e, 0xd3, 0x94, 0xa9, 0xe6, 0x68, 0x74, 0xec, 0x75, 0xbc, 0xde, 0xa0, 949 | 0x7b, 0x3e, 0xea, 0x5b, 0x15, 0xa9, 0x39, 0x94, 0x9a, 0x2a, 0x32, 0x40, 0xef, 0xf5, 0xda, 0xde, 950 | 0x63, 0xaf, 0x6d, 0xe9, 0x54, 0xe8, 0x9f, 0x0d, 0x98, 0x50, 0xa3, 0x02, 0x4d, 0x46, 0x43, 0xd5, 951 | 0xa5, 0x40, 0x33, 0x03, 0x2d, 0xc8, 0x1d, 0xf5, 0x2c, 0x83, 0x16, 0x34, 0xe0, 0x36, 0x8f, 0xad, 952 | 0x46, 0x21, 0x51, 0x23, 0x93, 0x5e, 0xef, 0x7c, 0x38, 0x70, 0x7b, 0xfd, 0xe1, 0xa8, 0x6f, 0x35, 953 | 0x69, 0x80, 0x53, 0x11, 0x6d, 0xe7, 0xe1, 0x23, 0x30, 0x94, 0x0f, 0x54, 0x7a, 0x55, 0x77, 0xf8, 954 | 0x92, 0x36, 0xca, 0x00, 0xfd, 0xfb, 0x2e, 0x1e, 0xba, 0xc3, 0x13, 0x4b, 0x43, 0x75, 0xa8, 0xf4, 955 | 0x31, 0x7e, 0x8d, 0xad, 0xed, 0xce, 0x2f, 0x65, 0x28, 0x7d, 0x9b, 0xcc, 0xd0, 0x13, 0x28, 0x53, 956 | 0xe6, 0x46, 0x0a, 0xc8, 0x14, 0x62, 0x6f, 0xed, 0xae, 0xaa, 0x05, 0xc3, 0x6e, 0xa1, 0x17, 0xa0, 957 | 0x0b, 0xda, 0x45, 0xea, 0x13, 0xb9, 0xc4, 0xfa, 0xad, 0x7b, 0x1b, 0x4e, 0x8a, 0x08, 0x43, 0xd8, 958 | 0x39, 0x21, 0xf9, 0xb1, 0x8a, 0xc7, 0x8d, 0xec, 0x26, 0x83, 0xdd, 0xdf, 0x7c, 0x58, 0xc4, 0x7b, 959 | 0x0e, 0x55, 0xce, 0x21, 0xe8, 0xee, 0x3a, 0xab, 0xf0, 0x10, 0xf6, 0x06, 0xba, 0x91, 0xee, 0x4f, 960 | 0xa0, 0x4c, 0xb9, 0x0a, 0x2d, 0x51, 0x7d, 0x41, 0x65, 0x6a, 0x27, 0x54, 0x4a, 0x73, 0xb6, 0xd0, 961 | 0xd7, 0x50, 0x61, 0x44, 0x82, 0x54, 0x13, 0x85, 0xb3, 0x5a, 0x77, 0xd7, 0xf4, 0x6a, 0x52, 0xf6, 962 | 0x41, 0x74, 0x47, 0xdd, 0xb8, 0x82, 0x6d, 0x5a, 0xbb, 0xab, 0xea, 0xc2, 0xb1, 0x07, 0x70, 0x42, 963 | 0xe4, 0xc3, 0xa3, 0x4e, 0x60, 0x79, 0x25, 0xd5, 0x09, 0xac, 0x6c, 0x9b, 0xb3, 0x75, 0x51, 0x65, 964 | 0xff, 0x89, 0x0e, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0xe4, 0xca, 0xea, 0x7c, 0x20, 0x0d, 0x00, 965 | 0x00, 966 | } 967 | --------------------------------------------------------------------------------