diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..0ff09cb7a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +dist/ +vendor/ diff --git a/Dockerfile b/Dockerfile index a31da6846..08c12a584 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,31 @@ -FROM golang:1.13-alpine as build +# dynamic config +ARG BUILD_DATE +ARG VCS_REF +ARG VERSION + +# build +FROM golang:1.13-alpine as build RUN apk add --update --no-cache git gcc musl-dev make -ADD . /go/src/moul.io/depviz WORKDIR /go/src/moul.io/depviz -RUN GO111MODULE=on go get -v . -RUN GO111MODULE=on make install +ENV GO111MODULE=on +COPY go.* ./ +RUN go mod download +COPY . ./ +RUN make install -FROM alpine +# minimalist runtime +FROM alpine:3.10 +LABEL org.label-schema.build-date=$BUILD_DATE \ + org.label-schema.name="depviz" \ + org.label-schema.description="" \ + org.label-schema.url="https://moul.io/depviz/" \ + org.label-schema.vcs-ref=$VCS_REF \ + org.label-schema.vcs-url="https://github.com/moul/depviz" \ + org.label-schema.vendor="Manfred Touron" \ + org.label-schema.version=$VERSION \ + org.label-schema.schema-version="1.0" \ + org.label-schema.cmd="docker run -i -t --rm moul/depviz" \ + org.label-schema.help="docker exec -it $CONTAINER depviz --help" RUN apk add --update --no-cache ca-certificates COPY --from=build /go/bin/depviz /bin/ ENTRYPOINT ["depviz"] diff --git a/Makefile b/Makefile index c049f9068..ff99fba1d 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,64 @@ GOPKG ?= moul.io/depviz -GOBINS ?= . ./tools/opml-to-github-issues ./tools/sed-i-github-issues +GOBINS = ./cmd/depviz +#GOBINS += ./tools/opml-to-github-issues +#GOBINS += ./tools/sed-i-github-issues DOCKER_IMAGE ?= moul/depviz + all: test install + +PRE_INSTALL_STEPS += generate +PRE_TEST_STEPS += generate +PRE_BUILD_STEPS += generate +PRE_LING_STEPS += generate +PRE_BUMPDEPS_STEPS += generate include rules.mk + .PHONY: update_examples update_examples: for dir in $(sort $(dir $(wildcard examples/*/*))); do (cd $$dir && make); done @echo "now you can run:" @echo " git commit examples -m \"chore: update examples\"" + + +## +## generate +## + + +PROTOS_SRC := $(wildcard ./api/*.proto) $(wildcard ./api/internal/*.proto) +GEN_SRC := $(PROTOS_SRC) Makefile +.PHONY: generate +generate: gen.sum +gen.sum: $(GEN_SRC) + shasum $(GEN_SRC) | sort > gen.sum.tmp + diff -q gen.sum gen.sum.tmp || ( \ + set -e; \ + GO111MODULE=on go mod vendor; \ + docker run \ + --user=`id -u` \ + --volume="$(PWD):/go/src/moul.io/depviz" \ + --workdir="/go/src/moul.io/depviz" \ + --entrypoint="sh" \ + --rm \ + moul/depviz-protoc:1 \ + -xec 'make generate_local'; \ + make tidy \ + ) + + +.PHONY: generate_local +generate_local: + @set -e; for proto in $(PROTOS_SRC); do ( set -xe; \ + protoc -I ./api:./vendor:/protobuf --grpc-gateway_out=logtostderr=true:"$(GOPATH)/src" --gogofaster_out="plugins=grpc:$(GOPATH)/src" "$$proto" \ + ); done + goimports -w ./pkg ./cmd ./internal + shasum $(GEN_SRC) | sort > gen.sum.tmp + mv gen.sum.tmp gen.sum + + +.PHONY: clean +clean: + rm -f gen.sum $(wildcard */*/*.pb.go */*/*.pb.gw.go) $(wildcard out/*) diff --git a/airtable/cmd_airtable.go b/airtable/cmd_airtable.go deleted file mode 100644 index 86fabde39..000000000 --- a/airtable/cmd_airtable.go +++ /dev/null @@ -1,89 +0,0 @@ -package airtable // import "moul.io/depviz/airtable" - -import ( - "encoding/json" - - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/spf13/viper" - "go.uber.org/zap" - "moul.io/depviz/airtablemodel" - "moul.io/depviz/cli" -) - -// -// Options -// - -type Options struct { - IssuesTableName string `mapstructure:"airtable-issues-table-name"` - RepositoriesTableName string `mapstructure:"airtable-repositories-table-name"` - LabelsTableName string `mapstructure:"airtable-labels-table-name"` - MilestonesTableName string `mapstructure:"airtable-milestones-table-name"` - ProvidersTableName string `mapstructure:"airtable-providers-table-name"` - AccountsTableName string `mapstructure:"airtable-accounts-table-name"` - BaseID string `mapstructure:"airtable-base-id"` - Token string `mapstructure:"airtable-token"` - RateLimiter int `mapstructure:"airtable-ratelimiter"` -} - -func (opts Options) String() string { - out, _ := json.Marshal(opts) - return string(out) -} - -func (opts *Options) tableNames() []string { - tableNames := make([]string, airtablemodel.NumTables) - tableNames[airtablemodel.AccountIndex] = opts.AccountsTableName - tableNames[airtablemodel.IssueIndex] = opts.IssuesTableName - tableNames[airtablemodel.LabelIndex] = opts.LabelsTableName - tableNames[airtablemodel.MilestoneIndex] = opts.MilestonesTableName - tableNames[airtablemodel.ProviderIndex] = opts.ProvidersTableName - tableNames[airtablemodel.RepositoryIndex] = opts.RepositoriesTableName - return tableNames -} - -// -// Command -// - -func GetOptions(commands cli.Commands) Options { - return commands["airtable"].(*airtableCommand).opts -} - -func Commands() cli.Commands { - return cli.Commands{ - "airtable": &airtableCommand{}, - "airtable sync": &syncCommand{}, - "airtable info": &infoCommand{}, - } -} - -type airtableCommand struct{ opts Options } - -func (cmd *airtableCommand) LoadDefaultOptions() error { return viper.Unmarshal(&cmd.opts) } - -func (cmd *airtableCommand) ParseFlags(flags *pflag.FlagSet) { - flags.StringVarP(&cmd.opts.IssuesTableName, "airtable-issues-table-name", "", "Issues and PRs", "Airtable issues table name") - flags.StringVarP(&cmd.opts.RepositoriesTableName, "airtable-repositories-table-name", "", "Repositories", "Airtable repositories table name") - flags.StringVarP(&cmd.opts.AccountsTableName, "airtable-accounts-table-name", "", "Accounts", "Airtable accounts table name") - flags.StringVarP(&cmd.opts.LabelsTableName, "airtable-labels-table-name", "", "Labels", "Airtable labels table name") - flags.StringVarP(&cmd.opts.MilestonesTableName, "airtable-milestones-table-name", "", "Milestones", "Airtable milestones table nfame") - flags.StringVarP(&cmd.opts.ProvidersTableName, "airtable-providers-table-name", "", "Providers", "Airtable providers table name") - flags.StringVarP(&cmd.opts.BaseID, "airtable-base-id", "", "", "Airtable base ID") - flags.StringVarP(&cmd.opts.Token, "airtable-token", "", "", "Airtable token") - - if err := viper.BindPFlags(flags); err != nil { - zap.L().Warn("failed to bind viper flags", zap.Error(err)) - } -} - -func (cmd *airtableCommand) CobraCommand(commands cli.Commands) *cobra.Command { - command := &cobra.Command{ - Use: "airtable", - Short: "Manager airtable", - } - command.AddCommand(commands["airtable sync"].CobraCommand(commands)) - command.AddCommand(commands["airtable info"].CobraCommand(commands)) - return command -} diff --git a/airtable/cmd_airtable_info.go b/airtable/cmd_airtable_info.go deleted file mode 100644 index 048f7dd5e..000000000 --- a/airtable/cmd_airtable_info.go +++ /dev/null @@ -1,69 +0,0 @@ -package airtable - -import ( - "fmt" - - "github.com/brianloveswords/airtable" - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/spf13/viper" - "go.uber.org/zap" - "moul.io/depviz/airtablemodel" - "moul.io/depviz/cli" -) - -type InfoOptions struct { - Airtable Options `mapstructure:"airtable"` -} - -type infoCommand struct{ opts InfoOptions } - -func (cmd *infoCommand) CobraCommand(commands cli.Commands) *cobra.Command { - cc := &cobra.Command{ - Use: "info", - Short: "Print info about airtable", - RunE: func(_ *cobra.Command, args []string) error { - opts := cmd.opts - opts.Airtable = GetOptions(commands) - return Info(&opts) - }, - } - cmd.ParseFlags(cc.Flags()) - commands["airtable"].ParseFlags(cc.Flags()) - return cc -} - -func (cmd *infoCommand) LoadDefaultOptions() error { return viper.Unmarshal(&cmd.opts) } - -func (cmd *infoCommand) ParseFlags(flags *pflag.FlagSet) { - if err := viper.BindPFlags(flags); err != nil { - zap.L().Warn("failed to bind viper flags", zap.Error(err)) - } -} - -func Info(opts *InfoOptions) error { - if opts.Airtable.BaseID == "" || opts.Airtable.Token == "" { - return fmt.Errorf("missing token or baseid, check '-h'") - } - - if opts.Airtable.RateLimiter == 0 { - opts.Airtable.RateLimiter = 5 - } - client := airtable.Client{ - APIKey: opts.Airtable.Token, - BaseID: opts.Airtable.BaseID, - Limiter: airtable.RateLimiter(opts.Airtable.RateLimiter), - } - - cache := airtablemodel.NewDB() - - for tableKind, tableName := range opts.Airtable.tableNames() { - table := client.Table(tableName) - if err := cache.Tables[tableKind].Fetch(table); err != nil { - return err - } - fmt.Printf("- %s: %d\n", tableName, cache.Tables[tableKind].Len()) - } - - return nil -} diff --git a/airtable/cmd_airtable_sync.go b/airtable/cmd_airtable_sync.go deleted file mode 100644 index f3900ac87..000000000 --- a/airtable/cmd_airtable_sync.go +++ /dev/null @@ -1,237 +0,0 @@ -package airtable - -import ( - "fmt" - "log" - - "github.com/brianloveswords/airtable" - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/spf13/viper" - "go.uber.org/zap" - "moul.io/depviz/airtabledb" - "moul.io/depviz/airtablemodel" - "moul.io/depviz/cli" - "moul.io/depviz/compute" - "moul.io/depviz/model" - "moul.io/depviz/sql" - "moul.io/multipmuri" -) - -type SyncOptions struct { - Airtable Options `mapstructure:"airtable"` - SQL sql.Options `mapstructure:"sql"` // inherited with sql.GetOptions() - Targets []multipmuri.Entity `mapstructure:"targets"` // parsed from Args - DestroyInvalidRecords bool `mapstructure:"airtable-destroy-invalid-records"` -} - -type syncCommand struct{ opts SyncOptions } - -func (cmd *syncCommand) CobraCommand(commands cli.Commands) *cobra.Command { - cc := &cobra.Command{ - Use: "sync", - Short: "Upload issue info stored in database to airtable spreadsheets", - RunE: func(_ *cobra.Command, args []string) error { - opts := cmd.opts - targets, err := model.ParseTargets(args) - if err != nil { - return err - } - opts.Targets = targets - opts.SQL = sql.GetOptions(commands) - opts.Airtable = GetOptions(commands) - return Sync(&opts) - }, - } - cmd.ParseFlags(cc.Flags()) - commands["airtable"].ParseFlags(cc.Flags()) - commands["sql"].ParseFlags(cc.Flags()) - return cc -} - -func (cmd *syncCommand) LoadDefaultOptions() error { return viper.Unmarshal(&cmd.opts) } - -func (cmd *syncCommand) ParseFlags(flags *pflag.FlagSet) { - flags.BoolVarP(&cmd.opts.DestroyInvalidRecords, "airtable-destroy-invalid-records", "", false, "Destroy invalid records") - - if err := viper.BindPFlags(flags); err != nil { - zap.L().Warn("failed to bind viper flags", zap.Error(err)) - } -} - -// -// implementation -// - -// airtableSync pushes issue info to the airtable base specified in opts. -// Repository info is loaded from the targets specified in opts. -func Sync(opts *SyncOptions) error { - tableNames := make([]string, airtablemodel.NumTables) - tableNames[airtablemodel.AccountIndex] = opts.Airtable.AccountsTableName - tableNames[airtablemodel.IssueIndex] = opts.Airtable.IssuesTableName - tableNames[airtablemodel.LabelIndex] = opts.Airtable.LabelsTableName - tableNames[airtablemodel.MilestoneIndex] = opts.Airtable.MilestonesTableName - tableNames[airtablemodel.ProviderIndex] = opts.Airtable.ProvidersTableName - tableNames[airtablemodel.RepositoryIndex] = opts.Airtable.RepositoriesTableName - - if opts.Airtable.BaseID == "" || opts.Airtable.Token == "" { - return fmt.Errorf("missing token or baseid, check '-h'") - } - - // - // prepare - // - db, err := sql.FromOpts(&opts.SQL) - if err != nil { - return err - } - - loadedIssues, err := sql.LoadAllIssues(db) - if err != nil { - return errors.Wrap(err, "failed to load issues") - } - zap.L().Debug("fetch db entries", zap.Int("count", len(loadedIssues))) - - // compute and filter issues - computed := compute.Compute(loadedIssues) - computed.FilterByTargets(opts.Targets) - zap.L().Debug("fetch db entries", zap.Int("count", len(computed.Issues()))) - - issueFeatures := make([]map[string]model.Feature, airtablemodel.NumTables) - for i := range issueFeatures { - issueFeatures[i] = make(map[string]model.Feature) - } - - // Parse the loaded issues into the issueFeature map. - for _, issue := range computed.Issues() { - if issue.Hidden { - continue - } - // providers - issueFeatures[airtablemodel.ProviderIndex][issue.Repository.Provider.ID] = issue.Repository.Provider - - // labels - for _, label := range issue.Labels { - issueFeatures[airtablemodel.LabelIndex][label.ID] = label - } - - // accounts - if issue.Repository.Owner != nil { - issueFeatures[airtablemodel.AccountIndex][issue.Repository.Owner.ID] = issue.Repository.Owner - } - - issueFeatures[airtablemodel.AccountIndex][issue.Author.ID] = issue.Author - for _, assignee := range issue.Assignees { - issueFeatures[airtablemodel.AccountIndex][assignee.ID] = assignee - } - if issue.Milestone != nil && issue.Milestone.Creator != nil { - issueFeatures[airtablemodel.AccountIndex][issue.Milestone.Creator.ID] = issue.Milestone.Creator - } - - // repositories - issueFeatures[airtablemodel.RepositoryIndex][issue.Repository.ID] = issue.Repository - // FIXME: find external repositories based on depends-on links - - // milestones - if issue.Milestone != nil { - issueFeatures[airtablemodel.MilestoneIndex][issue.Milestone.ID] = issue.Milestone - } - - // issue - issueFeatures[airtablemodel.IssueIndex][issue.ID] = issue - // FIXME: find external issues based on depends-on links - } - - if opts.Airtable.RateLimiter == 0 { - opts.Airtable.RateLimiter = 5 - } - client := airtable.Client{ - APIKey: opts.Airtable.Token, - BaseID: opts.Airtable.BaseID, - Limiter: airtable.RateLimiter(opts.Airtable.RateLimiter), - } - - // cache stores issueFeatures inserted into the airtable base. - cache := airtablemodel.NewDB() - - // Store already existing issueFeatures into the cache. - for tableKind, tableName := range tableNames { - table := client.Table(tableName) - if err := cache.Tables[tableKind].Fetch(table); err != nil { - return err - } - } - - // unmatched stores new issueFeatures (exist in the loaded issues but not the airtable base). - unmatched := airtablemodel.NewDB() - - // Add new issueFeatures from unmatched to cache. - // Then, push new and altered issueFeatures from cache to airtable base. - for tableKind, tableName := range tableNames { - ut := unmatched.Tables[tableKind] - table := client.Table(tableName) - - for _, dbEntry := range issueFeatures[tableKind] { - matched := false - dbRecord := dbEntry.ToRecord(cache) - for idx := 0; idx < cache.Tables[tableKind].Len(); idx++ { - t := cache.Tables[tableKind] - if t.GetFieldID(idx) == dbEntry.GetID() { - if t.RecordsEqual(idx, dbRecord) { - t.SetState(idx, airtabledb.StateUnchanged) - } else { - t.CopyFields(idx, dbRecord) - t.SetState(idx, airtabledb.StateChanged) - } - matched = true - break - } - } - if !matched { - ut.Append(dbRecord) - } - } - - ct := cache.Tables[tableKind] - for i := 0; i < ut.Len(); i++ { - zap.L().Debug("create airtable entry", zap.String("type", tableName), zap.String("entry", ut.StringAt(i))) - if err := table.Create(ut.GetPtr(i)); err != nil { - return err - } - ut.SetState(i, airtabledb.StateNew) - ct.Append(ut.Get(i)) - } - for i := 0; i < ct.Len(); i++ { - var err error - switch ct.GetState(i) { - case airtabledb.StateUnknown: - if opts.DestroyInvalidRecords { - err = table.Delete(ct.GetPtr(i)) - zap.L().Debug("delete airtable entry", zap.String("type", tableName), zap.String("entry", ct.StringAt(i)), zap.Error(err)) - } else { - zap.L().Debug("unknown airtable entry, doing nothing", zap.String("type", tableName), zap.String("entry", ct.StringAt(i))) - } - case airtabledb.StateChanged: - err = table.Update(ct.GetPtr(i)) - zap.L().Debug("update airtable entry", zap.String("type", tableName), zap.String("entry", ct.StringAt(i)), zap.Error(err)) - case airtabledb.StateUnchanged: - zap.L().Debug("unchanged airtable entry", zap.String("type", tableName), zap.String("entry", ct.StringAt(i)), zap.Error(err)) - // do nothing - case airtabledb.StateNew: - zap.L().Debug("new airtable entry", zap.String("type", tableName), zap.String("entry", ct.StringAt(i)), zap.Error(err)) - // do nothing - } - } - } - - for tableKind, tableName := range tableNames { - ct := cache.Tables[tableKind] - log.Println(tableName) - for i := 0; i < ct.Len(); i++ { - log.Println("-", ct.GetID(i), airtabledb.StateString[ct.GetState(i)], ct.GetFieldID(i)) - } - } - - return nil -} diff --git a/airtabledb/airtabledb.go b/airtabledb/airtabledb.go deleted file mode 100644 index 692f9065e..000000000 --- a/airtabledb/airtabledb.go +++ /dev/null @@ -1,170 +0,0 @@ -package airtabledb // import "moul.io/depviz/airtabledb" - -import ( - "reflect" - "sort" - "time" - - "github.com/brianloveswords/airtable" -) - -type Record interface { - String() string -} - -func (t Table) RecordsEqual(idx int, b Record) bool { - sf, ok := reflect.TypeOf(t.Get(idx)).FieldByName("Fields") - if !ok { - panic("No struct field Fields in Record") - } - aTF := sf.Type - aVF := reflect.ValueOf(t.Get(idx)).FieldByName("Fields") - bVF := reflect.ValueOf(b).FieldByName("Fields") - - if aVF.NumField() != bVF.NumField() { - return false - } - for i := 0; i < aVF.NumField(); i++ { - aiSF := aTF.Field(i) - aiF := aVF.Field(i) - biF := bVF.FieldByName(aiSF.Name) - if aiF.Type() != biF.Type() { - return false - } - if aiF.Type().String() == "time.Time" { - if !isSameAirtableDate(aiF.Interface().(time.Time), biF.Interface().(time.Time)) { - return false - } - } else if aiF.Type().String() == "[]string" { - aS, bS := aiF.Interface().([]string), biF.Interface().([]string) - if aS == nil { - aS = []string{} - } - if bS == nil { - bS = []string{} - } - sort.Strings(aS) - sort.Strings(bS) - if !reflect.DeepEqual(aS, bS) { - return false - } - continue - } else { - if !reflect.DeepEqual(aiF.Interface(), biF.Interface()) { - return false - } - } - } - return true -} - -func isSameAirtableDate(a, b time.Time) bool { - return a.Truncate(time.Millisecond).UTC() == b.Truncate(time.Millisecond).UTC() -} - -type Table struct { - Elems interface{} -} - -func (t Table) SetState(idx int, state State) { - s := reflect.ValueOf(t.Elems).Elem().Index(idx).FieldByName("State") - s.SetInt(int64(state)) -} - -func (t Table) GetState(idx int) State { - return State(reflect.ValueOf(t.Elems).Elem().Index(idx).FieldByName("State").Int()) -} - -// CopyFields copies the 'Fields' struct from srcRecord into the Record at idx in the Tabel t. -// Will panic necessary fields do not exist. -func (t Table) CopyFields(idx int, srcRecord interface{}) { - dstF := reflect.ValueOf(t.Elems).Elem().Index(idx).FieldByName("Fields") - srcF := reflect.ValueOf(srcRecord).FieldByName("Fields") - dstF.Set(srcF) -} - -// GetFieldID returns the ID field of the Fields struct of the record at idx in the Table t. -// Will panic necessary fields do not exist. -func (t Table) GetFieldID(idx int) string { - return reflect.ValueOf(t.Elems).Elem().Index(idx).FieldByName("Fields").FieldByName("ID").String() -} - -// GetID returns the ID field of the record at idx in the Table t. -func (t Table) GetID(idx int) string { - return reflect.ValueOf(t.Elems).Elem().Index(idx).FieldByName("ID").String() -} - -// Len returns the number of records in the table. -func (t Table) Len() int { - return reflect.ValueOf(t.Elems).Elem().Len() -} - -// Append appends the given record to the table. Will panic if the given record is not of the right type. -func (t Table) Append(record interface{}) { - a := reflect.Append(reflect.ValueOf(t.Elems).Elem(), reflect.ValueOf(record)) - reflect.ValueOf(t.Elems).Elem().Set(a) -} - -// Fetch retrieves the airtable table records from at over the network and inserts the records into the table. -func (t Table) Fetch(at airtable.Table) error { - return at.List(t.Elems, &airtable.Options{}) -} - -// FindByID searches the table for a record with Fields.ID equal to id. -// Returns the record's ID if a match is found. Otherwise, returns the empty string. -func (t Table) FindByID(id string) string { - slice := reflect.ValueOf(t.Elems).Elem() - for i := 0; i < slice.Len(); i++ { - record := slice.Index(i) - fieldID := record.FieldByName("Fields").FieldByName("ID").String() - if fieldID == id { - return record.FieldByName("ID").String() - } - } - return "" -} - -// GetPtr returns an interface containing a pointer to the record in the table at index idx. -func (t Table) GetPtr(idx int) interface{} { - return reflect.ValueOf(t.Elems).Elem().Index(idx).Addr().Interface() -} - -// Get returns an interface to the record in the table at idx. -func (t Table) Get(idx int) interface{} { - return reflect.ValueOf(t.Elems).Elem().Index(idx).Interface() -} - -// StringAt returns a JSON string of the record in the table at idx. -func (t Table) StringAt(idx int) string { - out := reflect.ValueOf(t.Elems).Elem().Index(idx).MethodByName("String").Call(nil) - return out[0].String() -} - -type DB struct { - Tables []Table -} - -type Base struct { - ID string `json:"id"` - CreatedAt time.Time `json:"created-at"` - UpdatedAt time.Time `json:"updated-at"` - Errors string `json:"errors"` -} - -type State int - -const ( - StateUnknown State = iota - StateUnchanged - StateChanged - StateNew -) - -var ( - StateString = map[State]string{ - StateUnknown: "unknown", - StateUnchanged: "unchanged", - StateChanged: "changed", - StateNew: "new", - } -) diff --git a/airtablemodel/db.go b/airtablemodel/db.go deleted file mode 100644 index 3c656a908..000000000 --- a/airtablemodel/db.go +++ /dev/null @@ -1,42 +0,0 @@ -package airtablemodel // import "moul.io/depviz/airtablemodel" - -import "moul.io/depviz/airtabledb" - -// Unfortunately, the order matters here. -// We must first compute Records which are referenced by other Records... -const ( - ProviderIndex = iota - AccountIndex - RepositoryIndex - LabelIndex - MilestoneIndex - IssueIndex - NumTables -) - -var ( - TableNameToIndex = map[string]int{ - "provider": ProviderIndex, - "label": LabelIndex, - "account": AccountIndex, - "repository": RepositoryIndex, - "milestone": MilestoneIndex, - "issue": IssueIndex, - } -) - -func NewDB() airtabledb.DB { - db := airtabledb.DB{ - Tables: make([]airtabledb.Table, NumTables), - } - db.Tables[IssueIndex].Elems = &[]IssueRecord{} - db.Tables[RepositoryIndex].Elems = &[]RepositoryRecord{} - db.Tables[AccountIndex].Elems = &[]AccountRecord{} - db.Tables[LabelIndex].Elems = &[]LabelRecord{} - db.Tables[MilestoneIndex].Elems = &[]MilestoneRecord{} - db.Tables[ProviderIndex].Elems = &[]ProviderRecord{} - if len(db.Tables) != NumTables { - panic("missing an airtabledb Table") - } - return db -} diff --git a/airtablemodel/models.go b/airtablemodel/models.go deleted file mode 100644 index aadfbf06c..000000000 --- a/airtablemodel/models.go +++ /dev/null @@ -1,204 +0,0 @@ -package airtablemodel // import "moul.io/depviz/airtablemodel" - -import ( - "encoding/json" - "time" - - "github.com/brianloveswords/airtable" - "moul.io/depviz/airtabledb" -) - -// -// provider -// - -type ProviderRecord struct { - State airtabledb.State `json:"-"` // internal - - airtable.Record // provides ID, CreatedTime - Fields struct { - // base - airtabledb.Base - - // specific - URL string `json:"url"` - Driver string `json:"driver"` - - // relationship - // n/a - } `json:"fields,omitempty"` -} - -func (r ProviderRecord) String() string { - out, _ := json.Marshal(r) - return string(out) -} - -// -// label -// - -type LabelRecord struct { - State airtabledb.State `json:"-"` // internal - - airtable.Record // provides ID, CreatedTime - Fields struct { - // base - airtabledb.Base - - // specific - URL string `json:"url"` - Name string `json:"name"` - Color string `json:"color"` - Description string `json:"description"` - - // relationship - // n/a - } `json:"fields,omitempty"` -} - -func (r LabelRecord) String() string { - out, _ := json.Marshal(r) - return string(out) -} - -// -// account -// - -type AccountRecord struct { - State airtabledb.State `json:"-"` // internal - - airtable.Record // provides ID, CreatedTime - Fields struct { - // base - airtabledb.Base - - // specific - URL string `json:"url"` - Login string `json:"login"` - FullName string `json:"fullname"` - Type string `json:"type"` - Bio string `json:"bio"` - Location string `json:"location"` - Company string `json:"company"` - Blog string `json:"blog"` - Email string `json:"email"` - AvatarURL string `json:"avatar-url"` - - // relationships - Provider []string `json:"provider"` - } `json:"fields,omitempty"` -} - -func (r AccountRecord) String() string { - out, _ := json.Marshal(r) - return string(out) -} - -// -// repository -// - -type RepositoryRecord struct { - State airtabledb.State `json:"-"` // internal - - airtable.Record // provides ID, CreatedTime - Fields struct { - // base - airtabledb.Base - - // specific - URL string `json:"url"` - Title string `json:"title"` - Description string `json:"description"` - Homepage string `json:"homepage"` - PushedAt time.Time `json:"pushed-at"` - IsFork bool `json:"is-fork"` - - // relationships - Provider []string `json:"provider"` - Owner []string `json:"owner"` - } `json:"fields,omitempty"` -} - -func (r RepositoryRecord) String() string { - out, _ := json.Marshal(r) - return string(out) -} - -// -// milestone -// - -type MilestoneRecord struct { - State airtabledb.State `json:"-"` // internal - - airtable.Record // provides ID, CreatedTime - Fields struct { - // base - airtabledb.Base - - // specific - URL string `json:"url"` - Title string `json:"title"` - Description string `json:"description"` - ClosedAt time.Time `json:"closed-at"` - DueOn time.Time `json:"due-on"` - - // relationships - Creator []string `json:"creator"` - Repository []string `json:"repository"` - } `json:"fields,omitempty"` -} - -func (r MilestoneRecord) String() string { - out, _ := json.Marshal(r) - return string(out) -} - -// -// issue -// - -type IssueRecord struct { - State airtabledb.State `json:"-"` // internal - - airtable.Record // provides ID, CreatedTime - Fields struct { - // base - airtabledb.Base - - // specific - URL string `json:"url"` - CompletedAt time.Time `json:"completed-at"` - Title string `json:"title"` - State string `json:"state"` - Body string `json:"body"` - IsPR bool `json:"is-pr"` - IsLocked bool `json:"is-locked"` - NumComments int `json:"num-comments"` - NumUpvotes int `json:"num-upvotes"` - NumDownvotes int `json:"num-downvotes"` - IsOrphan bool `json:"is-orphan"` - IsHidden bool `json:"is-hidden"` - // Weight int `json:"weight"` - // IsEpic bool `json:"is-epic"` - // HasEpic bool `json:"has-epic"` - - // relationships - Repository []string `json:"repository"` - Milestone []string `json:"milestone"` - Author []string `json:"author"` - Labels []string `json:"labels"` - Assignees []string `json:"assignees"` - //Parents []string `json:"-"` - //Children []string `json:"-"` - //Duplicates []string `json:"-"` - } `json:"fields,omitempty"` -} - -func (r IssueRecord) String() string { - out, _ := json.Marshal(r) - return string(out) -} diff --git a/api/dvmodel.proto b/api/dvmodel.proto new file mode 100644 index 000000000..d0090530d --- /dev/null +++ b/api/dvmodel.proto @@ -0,0 +1,150 @@ +syntax = "proto3"; + +package depviz.model; + +import "github.com/golang/protobuf/ptypes/timestamp/timestamp.proto"; +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +option go_package = "moul.io/depviz/internal/dvmodel"; + +option (gogoproto.goproto_getters_all) = false; +option (gogoproto.marshaler_all) = true; +option (gogoproto.unmarshaler_all) = true; +option (gogoproto.sizer_all) = true; +option (gogoproto.goproto_registration) = true; +option (gogoproto.populate_all) = false; +option (gogoproto.equal_all) = false; + +// +// Owner +// + +// Owner is like a container of tasks or other containers. +// It's something that is rarely deleted and cannot really "closed" or "due". +// It's the entity used for Organizations, Teams, Groups, Users +// and for Projects, Workspaces, Repos, or a Provider. +message Owner { + enum Kind { + UnknownKind = 0; + User = 1; + Organization = 2; + Team = 3; + Repo = 4; + Provider = 5; + } + enum ForkStatus { + UnknownForkStatus = 0; + IsFork = 1; + IsSource = 2; + } + + string id = 1 [(gogoproto.casttype) = "github.com/cayleygraph/quad.IRI", (gogoproto.moretags) = "quad:\"@id\"", (gogoproto.customname) = "ID"]; // canonical URI + google.protobuf.Timestamp created_at = 3 [(gogoproto.moretags) = "quad:\"schema:createdAt,optional\"", (gogoproto.stdtime) = true, (gogoproto.nullable) = true]; + google.protobuf.Timestamp updated_at = 4 [(gogoproto.moretags) = "quad:\"schema:updatedAt,optional\"", (gogoproto.stdtime) = true, (gogoproto.nullable) = true]; + string local_id = 5 [(gogoproto.customname) = "LocalID", (gogoproto.moretags) = "quad:\"schema:localId,optional\""]; + + Kind kind = 10 [(gogoproto.moretags) = "quad:\"schema:kind,optional\""]; + string short_name = 11 [(gogoproto.moretags) = "quad:\"schema:shortName,optional\""]; + string full_name = 12 [(gogoproto.moretags) = "quad:\"schema:fullName,optional\""]; + Driver driver = 13 [(gogoproto.moretags) = "quad:\"schema:driver,optional\""]; + string homepage = 14 [(gogoproto.moretags) = "quad:\"schema:homepage,optional\""]; + string description = 15 [(gogoproto.moretags) = "quad:\"schema:description,optional\""]; + ForkStatus fork_status = 16 [(gogoproto.moretags) = "quad:\"schema:forkStatus,optional\""]; + string avatar_url = 17 [(gogoproto.moretags) = "quad:\"schema:avatarUrl,optional\"", (gogoproto.customname) = "AvatarURL"]; + + // relationships + string has_owner = 100 [(gogoproto.moretags) = "quad:\"hasOwner,optional\"", (gogoproto.casttype) = "github.com/cayleygraph/quad.IRI"]; +} + +// +// Task +// + +// Task defines a step or an action. +// It is owned by an owner and can link to other Owners. +// A task can contain other tasks (epic, story, milestone). +// It can be closed and can have due dates. +// It's the entity used for Issues, Pull Requests, Merge Requests, Cards, Epics, Milestones, Stories. +message Task { + enum Kind { + UnknownKind = 0; + Issue = 1; + MergeRequest = 2; + Milestone = 3; + Epic = 4; + Story = 5; + Card = 6; + } + enum State { + UnknownState = 0; + Open = 1; + Closed = 2; + } + + string id = 1 [(gogoproto.casttype) = "github.com/cayleygraph/quad.IRI", (gogoproto.moretags) = "quad:\"@id\"", (gogoproto.customname) = "ID"]; // canonical URI + google.protobuf.Timestamp created_at = 3 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false, (gogoproto.moretags) = "quad:\"schema:createdAt,optional\""]; + google.protobuf.Timestamp updated_at = 4 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false, (gogoproto.moretags) = "quad:\"schema:updatedAt,optional\""]; + string local_id = 5 [(gogoproto.customname) = "LocalID", (gogoproto.moretags) = "quad:\"schema:localId,optional\""]; + + Kind kind = 10 [(gogoproto.moretags) = "quad:\"schema:kind,optional\""]; + string title = 11 [(gogoproto.moretags) = "quad:\"schema:title,optional\""]; + string description = 12 [(gogoproto.moretags) = "quad:\"schema:description,optional\""]; + Driver driver = 13 [(gogoproto.moretags) = "quad:\"schema:driver,optional\""]; + google.protobuf.Timestamp due_on = 14 [(gogoproto.stdtime) = true, (gogoproto.nullable) = true, (gogoproto.moretags) = "quad:\"schema:dueOn,optional\""]; + google.protobuf.Timestamp completed_at = 15 [(gogoproto.stdtime) = true, (gogoproto.nullable) = true, (gogoproto.moretags) = "quad:\"schema:completedAt,optional\""]; + State state = 16 [(gogoproto.moretags) = "quad:\"schema:state,optional\""]; + bool is_locked = 17 [(gogoproto.nullable) = true, (gogoproto.moretags) = "quad:\"schema:isLocked,optional\""]; + int32 num_comments = 18 [(gogoproto.moretags) = "quad:\"schema:numComments,optional\""]; + int32 num_upvotes = 19 [(gogoproto.moretags) = "quad:\"schema:numUpvotes,optional\""]; + int32 num_downvotes = 20 [(gogoproto.moretags) = "quad:\"schema:numDownvotes,optional\""]; + + // relationships + string has_author = 100 [(gogoproto.moretags) = "quad:\"hasAuthor,optional\"", (gogoproto.casttype) = "github.com/cayleygraph/quad.IRI"]; + string has_owner = 101 [(gogoproto.moretags) = "quad:\"hasOwner,optional\"", (gogoproto.casttype) = "github.com/cayleygraph/quad.IRI"]; + string has_milestone = 102 [(gogoproto.moretags) = "quad:\"hasMilestone,optional\"", (gogoproto.casttype) = "github.com/cayleygraph/quad.IRI"]; + repeated string has_assignee = 103 [(gogoproto.moretags) = "quad:\"hasAssignee,optional\"", (gogoproto.casttype) = "github.com/cayleygraph/quad.IRI"]; + repeated string has_reviewer = 104 [(gogoproto.moretags) = "quad:\"hasReviewer,optional\"", (gogoproto.casttype) = "github.com/cayleygraph/quad.IRI"]; + repeated string has_label = 105 [(gogoproto.moretags) = "quad:\"hasLabel,optional\"", (gogoproto.casttype) = "github.com/cayleygraph/quad.IRI"]; + repeated string is_depending_on = 106 [(gogoproto.moretags) = "quad:\"isDependingOn,optional\"", (gogoproto.casttype) = "github.com/cayleygraph/quad.IRI"]; + repeated string is_blocking = 107 [(gogoproto.moretags) = "quad:\"isBlocking,optional\"", (gogoproto.casttype) = "github.com/cayleygraph/quad.IRI"]; + repeated string is_related_with = 108 [(gogoproto.moretags) = "quad:\"isRelatedWith,optional\"", (gogoproto.casttype) = "github.com/cayleygraph/quad.IRI"]; + repeated string is_part_of = 109 [(gogoproto.moretags) = "quad:\"isPartOf,optional\"", (gogoproto.casttype) = "github.com/cayleygraph/quad.IRI"]; + repeated string has_part = 110 [(gogoproto.moretags) = "quad:\"isPartOf,optional\"", (gogoproto.casttype) = "github.com/cayleygraph/quad.IRI"]; +} + +// +// Topic +// + +message Topic { + enum Kind { + UnknownKind = 0; + Label = 1; // standard GitHub/GitLab label + } + + string id = 1 [(gogoproto.casttype) = "github.com/cayleygraph/quad.IRI", (gogoproto.moretags) = "quad:\"@id\"", (gogoproto.customname) = "ID"]; // canonical URI + google.protobuf.Timestamp created_at = 3 [(gogoproto.stdtime) = true, (gogoproto.nullable) = true, (gogoproto.moretags) = "quad:\"schema:createdAt,optional\""]; + google.protobuf.Timestamp updated_at = 4 [(gogoproto.stdtime) = true, (gogoproto.nullable) = true, (gogoproto.moretags) = "quad:\"schema:updatedAt,optional\""]; + string local_id = 5 [(gogoproto.customname) = "LocalID", (gogoproto.moretags) = "quad:\"schema:localId,optional\""]; + + Kind kind = 10 [(gogoproto.moretags) = "quad:\"schema:kind,optional\""]; + string title = 11 [(gogoproto.moretags) = "quad:\"schema:title,optional\""]; + Driver driver = 12 [(gogoproto.moretags) = "quad:\"schema:driver,optional\""]; + string color = 13 [(gogoproto.moretags) = "quad:\"schema:color,optional\""]; + string description = 14 [(gogoproto.moretags) = "quad:\"schema:description,optional\""]; + + // relationships + string has_owner = 100 [(gogoproto.moretags) = "quad:\"hasOwner,optional\"", (gogoproto.casttype) = "github.com/cayleygraph/quad.IRI"]; +} + +// +// Constants +// + +enum Driver { + UnknownDriver = 0; + GitHub = 1; + GitLab = 2; + Trello = 3; + Jira = 4; +} diff --git a/cli/cobra.go b/cli/cobra.go deleted file mode 100644 index 432cd425f..000000000 --- a/cli/cobra.go +++ /dev/null @@ -1,14 +0,0 @@ -package cli - -import ( - "github.com/spf13/cobra" - "github.com/spf13/pflag" -) - -type Command interface { - CobraCommand(Commands) *cobra.Command - LoadDefaultOptions() error - ParseFlags(*pflag.FlagSet) -} - -type Commands map[string]Command diff --git a/cmd/depviz/main.go b/cmd/depviz/main.go new file mode 100644 index 000000000..030c6b753 --- /dev/null +++ b/cmd/depviz/main.go @@ -0,0 +1,274 @@ +package main // import "moul.io/depviz" + +import ( + "errors" + "flag" + "fmt" + "log" + "math/rand" + "os" + "time" + + "github.com/cayleygraph/cayley" + "github.com/cayleygraph/cayley/graph" + _ "github.com/cayleygraph/cayley/graph/kv/bolt" + "github.com/cayleygraph/cayley/schema" + "github.com/peterbourgon/ff" + "github.com/peterbourgon/ff/ffcli" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "moul.io/depviz/internal/dvcore" + "moul.io/depviz/internal/dvstore" +) + +var ( + logger *zap.Logger + schemaConfig *schema.Config + + globalFlags = flag.NewFlagSet("depviz", flag.ExitOnError) + globalStorePath = globalFlags.String("store-path", os.Getenv("HOME")+"/.depviz", "store path") + globalDebug = globalFlags.Bool("debug", false, "debug mode") + + airtableFlags = flag.NewFlagSet("airtable", flag.ExitOnError) + airtableToken = airtableFlags.String("token", "", "airtable token") + airtableBaseID = airtableFlags.String("base-id", "", "base ID") + airtableOwnersTab = airtableFlags.String("owners", "Owners", `"Owners" tab name`) + airtableTasksTab = airtableFlags.String("tasks", "Tasks", `"Tasks" tab name`) + airtableTopicsTab = airtableFlags.String("topics", "Topics", `"Topics" tab name`) + + serverFlags = flag.NewFlagSet("server", flag.ExitOnError) + serverBind = serverFlags.String("bind", ":8000", "server bind address") + serverGodmode = serverFlags.Bool("godmode", false, "enable dangerous API calls") + + runFlags = flag.NewFlagSet("run", flag.ExitOnError) + runNoPull = runFlags.Bool("no-pull", false, "don't pull providers (graph only)") + runNoGraph = runFlags.Bool("no-graph", false, "don't generate graph (pull only)") + runResync = runFlags.Bool("resync", false, "resync already synced content") + runGitHubToken = runFlags.String("github-token", "", "GitHub token") + runGitLabToken = runFlags.String("gitlab-token", "", "GitLab token") + runNoPert = runFlags.Bool("no-pert", false, "disable PERT computing") + runFormat = runFlags.String("format", "dot", "output format") + runVertical = runFlags.Bool("vertical", false, "vertical mode") + runHidePRs = runFlags.Bool("hide-prs", false, "hide PRs") + runHideExternalDeps = runFlags.Bool("hide-external-deps", false, "hide dependencies outside of the specified targets") + runHideIsolated = runFlags.Bool("hide-isolated", false, "hide isolated tasks") + runShowClosed = runFlags.Bool("show-closed", false, "show closed tasks") +) + +func main() { + log.SetFlags(0) + + defer func() { + if logger != nil { + _ = logger.Sync() + } + }() + + root := &ffcli.Command{ + Usage: "depviz [global flags] [flags] [args...]", + FlagSet: globalFlags, + Options: []ff.Option{ff.WithEnvVarNoPrefix()}, + LongHelp: "More info here: https://moul.io/depviz", + Subcommands: []*ffcli.Command{ + { + Name: "airtable", + ShortHelp: "manage airtable sync", + Usage: "airtable [flags] ", + FlagSet: airtableFlags, + Options: []ff.Option{ff.WithEnvVarNoPrefix()}, + Subcommands: []*ffcli.Command{ + {Name: "info", Exec: execAirtableInfo, ShortHelp: "get metrics"}, + {Name: "sync", Exec: execAirtableSync, ShortHelp: "sync store with Airtable"}, + }, + Exec: func([]string) error { return flag.ErrHelp }, + }, { + Name: "store", + ShortHelp: "manage the data store", + Subcommands: []*ffcli.Command{ + {Name: "dump-quads", Exec: execStoreDumpQuads}, + {Name: "dump-json", Exec: execStoreDumpJSON}, + {Name: "info", Exec: execStoreInfo}, + // restore-quads + // restore-json + }, + Exec: func([]string) error { return flag.ErrHelp }, + }, { + Name: "run", + ShortHelp: "sync target urls and draw a graph", + Usage: "run [flags] [url...]", + Exec: execRun, + FlagSet: runFlags, + Options: []ff.Option{ff.WithEnvVarNoPrefix()}, + }, { + Name: "server", + ShortHelp: "start a depviz server with depviz API", + FlagSet: serverFlags, + Options: []ff.Option{ff.WithEnvVarNoPrefix()}, + Exec: execServer, + }, + }, + Exec: func([]string) error { return flag.ErrHelp }, + } + + if err := root.Run(os.Args[1:]); err != nil { + if errors.Is(err, flag.ErrHelp) { + return + } + log.Fatalf("fatal: %+v", err) + } +} + +func globalPreRun() error { + rand.Seed(time.Now().UnixNano()) + + config := zap.NewDevelopmentConfig() + config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder + if *globalDebug { + config.Level.SetLevel(zap.DebugLevel) + config.DisableStacktrace = false + } else { + config.Level.SetLevel(zap.InfoLevel) + config.DisableStacktrace = true + } + var err error + logger, err = config.Build() + if err != nil { + return fmt.Errorf("init logger: %w", err) + } + logger.Debug("logger initialized") + + schemaConfig = dvstore.Schema() + return nil +} + +func execAirtableSync(args []string) error { + if err := globalPreRun(); err != nil { + return err + } + + store, err := storeFromArgs() + if err != nil { + return fmt.Errorf("init store: %w", err) + } + + opts := dvcore.AirtableOpts{ + Token: *airtableToken, + BaseID: *airtableBaseID, + OwnersTab: *airtableOwnersTab, + TasksTab: *airtableTasksTab, + TopicsTab: *airtableTopicsTab, + } + + return dvcore.AirtableSync(store, opts) +} + +func execAirtableInfo(args []string) error { + if err := globalPreRun(); err != nil { + return err + } + + opts := dvcore.AirtableOpts{ + Token: *airtableToken, + BaseID: *airtableBaseID, + OwnersTab: *airtableOwnersTab, + TasksTab: *airtableTasksTab, + TopicsTab: *airtableTopicsTab, + } + + return dvcore.AirtableInfo(opts) +} + +func execStoreDumpQuads(args []string) error { + if err := globalPreRun(); err != nil { + return err + } + + store, err := storeFromArgs() + if err != nil { + return fmt.Errorf("init store: %w", err) + } + + return dvcore.StoreDumpQuads(store) +} + +func execStoreDumpJSON(args []string) error { + if err := globalPreRun(); err != nil { + return err + } + + store, err := storeFromArgs() + if err != nil { + return fmt.Errorf("init store: %w", err) + } + + return dvcore.StoreDumpJSON(store, schemaConfig) +} + +func execStoreInfo(args []string) error { + if err := globalPreRun(); err != nil { + return err + } + + store, err := storeFromArgs() + if err != nil { + return fmt.Errorf("init store: %w", err) + } + + return dvcore.StoreInfo(store) +} + +func execRun(args []string) error { + if err := globalPreRun(); err != nil { + return err + } + + store, err := storeFromArgs() + if err != nil { + return fmt.Errorf("init store: %w", err) + } + + opts := dvcore.RunOpts{ + Logger: logger, + Schema: schemaConfig, + Vertical: *runVertical, + NoPert: *runNoPert, + NoGraph: *runNoGraph, + NoPull: *runNoPull, + Format: *runFormat, + GitHubToken: *runGitHubToken, + Resync: *runResync, + GitLabToken: *runGitLabToken, + ShowClosed: *runShowClosed, + HideIsolated: *runHideIsolated, + HidePRs: *runHidePRs, + HideExternalDeps: *runHideExternalDeps, + } + return dvcore.Run(store, args, opts) +} + +func execServer(args []string) error { + if err := globalPreRun(); err != nil { + return err + } + + store, err := storeFromArgs() + if err != nil { + return fmt.Errorf("init store: %w", err) + } + + return dvcore.Server(*serverBind, *serverGodmode, store, logger, schemaConfig) +} + +func storeFromArgs() (*cayley.Handle, error) { + if _, err := os.Stat(*globalStorePath); err != nil { + if err := graph.InitQuadStore("bolt", *globalStorePath, nil); err != nil { + return nil, fmt.Errorf("create quad store: %w", err) + } + } + store, err := cayley.NewGraph("bolt", *globalStorePath, nil) + if err != nil { + return nil, fmt.Errorf("load STORE: %w", err) + } + + return store, nil +} diff --git a/tools/opml-to-github-issues/main.go b/cmd/opml-to-github-issues/main.go similarity index 100% rename from tools/opml-to-github-issues/main.go rename to cmd/opml-to-github-issues/main.go diff --git a/tools/sed-i-github-issues/README.md b/cmd/sed-i-github-issues/README.md similarity index 100% rename from tools/sed-i-github-issues/README.md rename to cmd/sed-i-github-issues/README.md diff --git a/tools/sed-i-github-issues/main.go b/cmd/sed-i-github-issues/main.go similarity index 100% rename from tools/sed-i-github-issues/main.go rename to cmd/sed-i-github-issues/main.go diff --git a/compute/compute.go b/compute/compute.go deleted file mode 100644 index e9064ee60..000000000 --- a/compute/compute.go +++ /dev/null @@ -1,218 +0,0 @@ -package compute // import "moul.io/depviz/compute" - -import ( - "fmt" - "sort" - - "moul.io/depviz/model" - "moul.io/multipmuri" - "moul.io/multipmuri/pmbodyparser" -) - -// -// Computed -// - -type Computed struct { - AllIssues []*ComputedIssue - AllMilestones []*ComputedMilestone - AllRepos []*ComputedRepo - - // internal - mmap map[string]*ComputedMilestone - imap map[string]*ComputedIssue - rmap map[string]*ComputedRepo -} - -func Compute(input model.Issues) Computed { - computed := newComputed() - for _, issue := range input { - // issue - issue := newComputedIssue(issue) - issue.parseBody() - computed.imap[issue.URL] = issue - - // repo - repo := computed.getOrCreateRepo(issue.Repository) - repo.DependsOn = append(repo.DependsOn, issue.URL) - - // milestone - if issue.Milestone != nil { - milestone := computed.getOrCreateMilestone(issue.Milestone) - milestone.DependsOn = append(milestone.DependsOn, issue.URL) - // FIXME: a milestone belongs to a repo - } - } - for _, milestone := range computed.mmap { - repo := computed.getOrCreateRepo(milestone.Repository) - repo.DependsOn = append(repo.DependsOn, milestone.URL) - } - for _, issue := range computed.imap { - for _, relationship := range issue.Relationships { - switch relationship.Kind { - case pmbodyparser.Blocks, pmbodyparser.Fixes, pmbodyparser.Closes, pmbodyparser.Addresses, pmbodyparser.PartOf: - if relatedIssue, found := computed.imap[relationship.Target.String()]; found { - relatedIssue.DependsOn = append(relatedIssue.DependsOn, issue.URL) - } else { - issue.Errs = append(issue.Errs, fmt.Errorf("is dependent of a missing issue: %q", relationship.Target.String())) - // FIXME: create dummy issue? - } - case pmbodyparser.DependsOn, pmbodyparser.ParentOf: - issue.DependsOn = append(issue.DependsOn, relationship.Target.String()) - case pmbodyparser.RelatedWith: - // nothing to do (for now) - default: - panic(fmt.Errorf("unsupported pmbodyparser.Kind: %q", relationship.Kind)) - } - } - } - - computed.mapsToSlices() - return computed -} - -func (computed *Computed) FilterByTargets(targets []multipmuri.Entity) { - for _, issue := range computed.AllIssues { - issueEntity := issue.MultipmuriEntity() - for _, target := range targets { - if issueEntity.Equals(target) || target.Contains(issueEntity) { - issue.DirectMatchWithTarget = true - break - } - } - if !issue.DirectMatchWithTarget { - issue.Hidden = true - } - } - for _, milestone := range computed.AllMilestones { - milestoneEntity := milestone.MultipmuriEntity() - for _, target := range targets { - if milestoneEntity.Equals(target) || target.Contains(milestoneEntity) { - milestone.DirectMatchWithTarget = true - break - } - } - if !milestone.DirectMatchWithTarget { - milestone.Hidden = true - } - } - for _, repo := range computed.AllRepos { - repoEntity := repo.MultipmuriEntity() - for _, target := range targets { - if repoEntity.Equals(target) || target.Contains(repoEntity) { - repo.DirectMatchWithTarget = true - break - } - } - if !repo.DirectMatchWithTarget { - repo.Hidden = true - } - } - // FIXME: check for "indirect" matches too -} - -func (computed *Computed) IssueByURL(url string) *ComputedIssue { - for _, issue := range computed.AllIssues { - if issue.URL == url { - return issue - } - } - return nil -} - -func (computed *Computed) FilterClosed() { - for _, issue := range computed.AllIssues { - if issue.State == "closed" { - issue.Hidden = true - } - } - - for _, milestone := range computed.AllMilestones { - hasDeps := false - for _, dep := range milestone.DependsOn { - issue := computed.IssueByURL(dep) - if issue == nil { - // if we have at least one unknown dependency, we need to keep the whole object - hasDeps = true - break - } - if !issue.Hidden { - hasDeps = true - break - } - } - if !hasDeps { - milestone.Hidden = true - } - } -} - -func newComputed() Computed { - return Computed{ - AllIssues: make([]*ComputedIssue, 0), - AllMilestones: make([]*ComputedMilestone, 0), - AllRepos: make([]*ComputedRepo, 0), - - mmap: map[string]*ComputedMilestone{}, - imap: map[string]*ComputedIssue{}, - rmap: map[string]*ComputedRepo{}, - } -} - -func (computed *Computed) mapsToSlices() { - // generated sorted Computed object - // milestones - for _, milestone := range computed.mmap { - sort.Strings(milestone.DependsOn) - computed.AllMilestones = append(computed.AllMilestones, milestone) - } - sort.Slice(computed.AllMilestones, func(i, j int) bool { - return computed.AllMilestones[i].URL < computed.AllMilestones[j].URL - }) - // repos - for _, repo := range computed.rmap { - sort.Strings(repo.DependsOn) - computed.AllRepos = append(computed.AllRepos, repo) - } - sort.Slice(computed.AllRepos, func(i, j int) bool { - return computed.AllRepos[i].URL < computed.AllRepos[j].URL - }) - // issues - for _, issue := range computed.imap { - sort.Strings(issue.DependsOn) - computed.AllIssues = append(computed.AllIssues, issue) - } - sort.Slice(computed.AllIssues, func(i, j int) bool { - return computed.AllIssues[i].URL < computed.AllIssues[j].URL - }) -} - -func (computed *Computed) Repos() []*ComputedRepo { - enabled := []*ComputedRepo{} - for _, repo := range computed.AllRepos { - if !repo.Hidden { - enabled = append(enabled, repo) - } - } - return enabled -} - -func (computed *Computed) Milestones() []*ComputedMilestone { - enabled := []*ComputedMilestone{} - for _, milestone := range computed.AllMilestones { - if !milestone.Hidden { - enabled = append(enabled, milestone) - } - } - return enabled -} - -func (computed *Computed) Issues() []*ComputedIssue { - enabled := []*ComputedIssue{} - for _, issue := range computed.AllIssues { - if !issue.Hidden { - enabled = append(enabled, issue) - } - } - return enabled -} diff --git a/compute/db.go b/compute/db.go deleted file mode 100644 index 8f55f9dc4..000000000 --- a/compute/db.go +++ /dev/null @@ -1,64 +0,0 @@ -package compute - -import ( - "github.com/jinzhu/gorm" - "go.uber.org/zap" - "moul.io/depviz/sql" - "moul.io/multipmuri" -) - -type multipmuriRepo interface { - RepoEntity() multipmuri.Entity -} - -type multipmuriOwner interface { - OwnerEntity() multipmuri.Entity -} - -type multipmuriService interface { - ServiceEntity() multipmuri.Entity -} - -// FIXME: loadIssuesByAuthor -// FIXME: handle github search - -func LoadIssuesByTargets(db *gorm.DB, targets []multipmuri.Entity) (*Computed, error) { - byRepo := []string{} - byOwner := []string{} - byService := []string{} - useFilters := true - for _, target := range targets { - switch v := target.(type) { - case multipmuriRepo: - byRepo = append(byRepo, v.RepoEntity().String()) - case multipmuriOwner: - byOwner = append(byOwner, v.OwnerEntity().String()) - case multipmuriService: - byService = append(byService, v.ServiceEntity().String()) - default: - zap.L().Warn("unsupported target filter", zap.Any("target", target)) - useFilters = false - } - } - - // FIXME: add a owner field on issue - filteredDB := db - if useFilters { - filteredDB = filteredDB.Where( - "repository_id IN (?) OR repository_owner_id IN (?) OR service_id IN (?)", - byRepo, - byOwner, - byService, - ) - } - - allIssues, err := sql.LoadAllIssues(filteredDB) - if err != nil { - return nil, err - } - - computed := Compute(allIssues) - computed.FilterByTargets(targets) // in most cases, this step is optional as we are already filtering by targets when querying the database - - return &computed, nil -} diff --git a/compute/models.go b/compute/models.go deleted file mode 100644 index 5037fa277..000000000 --- a/compute/models.go +++ /dev/null @@ -1,116 +0,0 @@ -package compute - -import ( - "moul.io/depviz/model" - "moul.io/multipmuri" - "moul.io/multipmuri/pmbodyparser" -) - -// -// ComputedIssue -// - -type ComputedIssue struct { - model.Issue - DirectMatchWithTarget bool - Hidden bool - DependsOn []string - Relationships pmbodyparser.Relationships - Errs []error -} - -func (i ComputedIssue) MultipmuriEntity() multipmuri.Entity { - // FIXME: can be optimized by creating object directly - entity, err := multipmuri.DecodeString(i.URL) - if err != nil { - panic(err) - } - return entity -} - -func newComputedIssue(issue *model.Issue) *ComputedIssue { - return &ComputedIssue{ - Issue: *issue, - DependsOn: []string{}, - Errs: []error{}, - } -} - -func (i *ComputedIssue) parseBody() { - relationships, errs := pmbodyparser.RelParseString( - i.MultipmuriEntity(), - i.Body, - ) - if errs != nil && len(errs) > 0 { - i.Errs = append(i.Errs, errs...) - } - i.Relationships = relationships -} - -// -// ComputedMilestone -// - -type ComputedMilestone struct { - model.Milestone - DirectMatchWithTarget bool - Hidden bool - DependsOn []string -} - -func (m ComputedMilestone) MultipmuriEntity() multipmuri.Entity { - // FIXME: can be optimized by creating object directly - entity, err := multipmuri.DecodeString(m.URL) - if err != nil { - panic(err) - } - return entity -} - -func newComputedMilestone(milestone *model.Milestone) *ComputedMilestone { - return &ComputedMilestone{ - Milestone: *milestone, - DependsOn: []string{}, - } -} - -func (c *Computed) getOrCreateMilestone(input *model.Milestone) *ComputedMilestone { - if _, found := c.mmap[input.URL]; !found { - c.mmap[input.URL] = newComputedMilestone(input) - } - return c.mmap[input.URL] -} - -// -// ComputedRepo -// - -type ComputedRepo struct { - model.Repository - DirectMatchWithTarget bool - Hidden bool - DependsOn []string -} - -func (r ComputedRepo) MultipmuriEntity() multipmuri.Entity { - // FIXME: can be optimized by creating object directly - entity, err := multipmuri.DecodeString(r.URL) - if err != nil { - panic(err) - } - return entity -} - -func newComputedRepo(repo *model.Repository) *ComputedRepo { - return &ComputedRepo{ - Repository: *repo, - DependsOn: []string{}, - } -} - -func (c *Computed) getOrCreateRepo(input *model.Repository) *ComputedRepo { - if _, found := c.rmap[input.URL]; !found { - c.rmap[input.URL] = newComputedRepo(input) - } - return c.rmap[input.URL] -} diff --git a/gen.sum b/gen.sum new file mode 100644 index 000000000..436b78432 --- /dev/null +++ b/gen.sum @@ -0,0 +1,2 @@ +05b322877b51a94ad03f713faf16b1e526187fc9 Makefile +71d86eb1c5b188eae29633ae85c4e9795fd9d152 ./api/dvmodel.proto diff --git a/github/github.go b/github/github.go deleted file mode 100644 index 72deaa3e5..000000000 --- a/github/github.go +++ /dev/null @@ -1,71 +0,0 @@ -package github // import "moul.io/depviz/github" - -import ( - "context" - "fmt" - "log" - "sync" - - "github.com/google/go-github/v28/github" - "github.com/jinzhu/gorm" - "go.uber.org/zap" - "golang.org/x/oauth2" - "moul.io/depviz/model" - "moul.io/multipmuri" -) - -func Pull(input multipmuri.Entity, wg *sync.WaitGroup, token string, db *gorm.DB, out chan<- []*model.Issue) { - defer wg.Done() - type multipmuriMinimalInterface interface { - Repo() *multipmuri.GitHubRepo - } - target, ok := input.(multipmuriMinimalInterface) - if !ok { - zap.L().Warn("invalid input", zap.String("input", fmt.Sprintf("%v", input.String()))) - return - } - repo := target.Repo() - - // create client - ctx := context.Background() - ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}) - tc := oauth2.NewClient(ctx, ts) - client := github.NewClient(tc) - - // queries - totalIssues := 0 - callOpts := &github.IssueListByRepoOptions{State: "all"} - var lastEntry model.Issue - if err := db.Where("repository_id = ?", repo.String()).Order("updated_at desc").First(&lastEntry).Error; err == nil { - callOpts.Since = lastEntry.UpdatedAt - } else { - zap.L().Warn("failed to get last entry", zap.String("repo", repo.String()), zap.Error(err)) - } - - for { - issues, resp, err := client.Issues.ListByRepo(ctx, repo.OwnerID(), repo.RepoID(), callOpts) - if err != nil { - log.Fatal(err) - return - } - totalIssues += len(issues) - zap.L().Debug("paginate", - zap.String("provider", "github"), - zap.String("repo", repo.String()), - zap.Int("new-issues", len(issues)), - zap.Int("total-issues", totalIssues), - ) - normalizedIssues := []*model.Issue{} - for _, issue := range issues { - normalizedIssues = append(normalizedIssues, FromIssue(issue)) - } - out <- normalizedIssues - if resp.NextPage == 0 { - break - } - callOpts.Page = resp.NextPage - } - if rateLimits, _, err := client.RateLimits(ctx); err == nil { - zap.L().Debug("github API rate limiting", zap.Stringer("limit", rateLimits.GetCore())) - } -} diff --git a/github/model.go b/github/model.go deleted file mode 100644 index 1e4de3a9d..000000000 --- a/github/model.go +++ /dev/null @@ -1,156 +0,0 @@ -package github // import "moul.io/depviz/github" - -import ( - "strings" - - "github.com/google/go-github/v28/github" - "moul.io/depviz/model" - "moul.io/multipmuri" -) - -func FromUser(input *github.User) *model.Account { - entity, err := model.ParseTarget(input.GetHTMLURL()) - if err != nil { - panic(err) - } - - name := input.GetName() - if name == "" { - name = input.GetLogin() - } - return &model.Account{ - Base: model.Base{ - ID: input.GetLogin(), - CreatedAt: input.GetCreatedAt().Time, - UpdatedAt: input.GetUpdatedAt().Time, - URL: input.GetURL(), - }, - // Type: "user" - Provider: FromServiceURL(multipmuri.ServiceEntity(entity).String()), - Location: input.GetLocation(), - Company: input.GetCompany(), - Blog: input.GetBlog(), - Email: input.GetEmail(), - AvatarURL: input.GetAvatarURL(), - Login: input.GetLogin(), - FullName: name, - } -} - -func FromRepositoryURL(input string) *model.Repository { - entity, err := model.ParseTarget(input) - if err != nil { - panic(err) - } - owner := multipmuri.OwnerEntity(entity) - return &model.Repository{ - Base: model.Base{ - ID: input, - URL: input, - }, - Provider: FromServiceURL(multipmuri.ServiceEntity(entity).String()), - Owner: FromOwnerURL(owner.String()), - } -} - -func FromOwnerURL(input string) *model.Account { - entity, err := model.ParseTarget(input) - if err != nil { - panic(err) - } - return &model.Account{ - Base: model.Base{ - ID: input, - URL: input, - }, - Provider: FromServiceURL(multipmuri.ServiceEntity(entity).String()), - } -} - -func FromMilestone(input *github.Milestone) *model.Milestone { - if input == nil { - return nil - } - parts := strings.Split(input.GetHTMLURL(), "/") - return &model.Milestone{ - Base: model.Base{ - ID: input.GetHTMLURL(), // FIXME: make it smaller - CreatedAt: input.GetCreatedAt(), - UpdatedAt: input.GetUpdatedAt(), - URL: input.GetHTMLURL(), - }, - Title: input.GetTitle(), - Description: input.GetDescription(), - ClosedAt: input.GetClosedAt(), - DueOn: input.GetDueOn(), - Creator: FromUser(input.GetCreator()), - Repository: FromRepositoryURL(strings.Join(parts[0:len(parts)-2], "/")), - } -} - -func FromLabel(input *github.Label) *model.Label { - if input == nil { - return nil - } - return &model.Label{ - Base: model.Base{ - ID: input.GetURL(), // FIXME: make it smaller - URL: input.GetURL(), - }, - Name: input.GetName(), - Color: input.GetColor(), - Description: input.GetDescription(), - } -} - -func FromIssue(input *github.Issue) *model.Issue { - entity, err := model.ParseTarget(input.GetHTMLURL()) - if err != nil { - panic(err) - } - repo := multipmuri.RepoEntity(entity) - owner := multipmuri.OwnerEntity(entity) - service := multipmuri.ServiceEntity(entity) - - issue := &model.Issue{ - Base: model.Base{ - ID: entity.String(), - URL: entity.String(), - CreatedAt: input.GetCreatedAt(), - UpdatedAt: input.GetUpdatedAt(), - }, - CompletedAt: input.GetClosedAt(), - Repository: FromRepositoryURL(repo.String()), - RepositoryOwner: FromOwnerURL(owner.String()), - Service: FromServiceURL(service.String()), - Title: input.GetTitle(), - State: input.GetState(), - Body: input.GetBody(), - IsPR: input.PullRequestLinks != nil, - IsLocked: input.GetLocked(), - NumComments: input.GetComments(), - NumUpvotes: *input.Reactions.PlusOne, - NumDownvotes: *input.Reactions.MinusOne, - Labels: make([]*model.Label, 0), - Assignees: make([]*model.Account, 0), - Author: FromUser(input.User), - Milestone: FromMilestone(input.Milestone), - } - for _, label := range input.Labels { - issue.Labels = append(issue.Labels, FromLabel(&label)) - } - for _, assignee := range input.Assignees { - issue.Assignees = append(issue.Assignees, FromUser(assignee)) - } - return issue -} - -func FromServiceURL(input string) *model.Provider { - return &model.Provider{ - Base: model.Base{ - ID: input, - URL: input, - }, - Driver: string(model.GithubDriver), - } -} diff --git a/gitlab/gitlab.go b/gitlab/gitlab.go deleted file mode 100644 index 282a56f77..000000000 --- a/gitlab/gitlab.go +++ /dev/null @@ -1,72 +0,0 @@ -package gitlab // import "moul.io/depviz/gitlab" - -import ( - "fmt" - "sync" - - "github.com/jinzhu/gorm" - gitlab "github.com/xanzy/go-gitlab" - "go.uber.org/zap" - "moul.io/depviz/model" - "moul.io/multipmuri" -) - -func Pull(input multipmuri.Entity, wg *sync.WaitGroup, token string, db *gorm.DB, out chan<- []*model.Issue) { - defer wg.Done() - // parse input - type multipmuriMinimalInterface interface { - RepoEntity() *multipmuri.GitLabRepo - } - target, ok := input.(multipmuriMinimalInterface) - if !ok { - zap.L().Warn("invalid input", zap.String("input", fmt.Sprintf("%v", input))) - return - } - repo := target.RepoEntity() - - // create client - client := gitlab.NewClient(nil, token) - if err := client.SetBaseURL(fmt.Sprintf("%s/api/v4", repo.ServiceEntity().String())); err != nil { - zap.L().Error("failed to configure GitLab client", zap.Error(err)) - return - } - total := 0 - gitlabOpts := &gitlab.ListProjectIssuesOptions{ - ListOptions: gitlab.ListOptions{ - PerPage: 30, - Page: 1, - }, - } - - var lastEntry model.Issue - if err := db.Where("repository_id = ?", repo.String()).Order("updated_at desc").First(&lastEntry).Error; err == nil { - gitlabOpts.UpdatedAfter = &lastEntry.UpdatedAt - } - - // FIXME: fetch PRs - - for { - path := fmt.Sprintf("%s/%s", repo.Owner(), repo.Repo()) - issues, resp, err := client.Issues.ListProjectIssues(path, gitlabOpts) - if err != nil { - zap.L().Error("failed to pull issues", zap.Error(err)) - return - } - total += len(issues) - zap.L().Debug("paginate", - zap.String("provider", "gitlab"), - zap.String("repo", repo.String()), - zap.Int("new-issues", len(issues)), - zap.Int("total-issues", total), - ) - normalizedIssues := []*model.Issue{} - for _, issue := range issues { - normalizedIssues = append(normalizedIssues, FromIssue(issue)) - } - out <- normalizedIssues - if resp.NextPage == 0 { - break - } - gitlabOpts.ListOptions.Page = resp.NextPage - } -} diff --git a/gitlab/model.go b/gitlab/model.go deleted file mode 100644 index 0c4f5c7ea..000000000 --- a/gitlab/model.go +++ /dev/null @@ -1,165 +0,0 @@ -package gitlab // import "moul.io/depviz/gitlab" - -import ( - "fmt" - "net/url" - "strings" - "time" - - "moul.io/depviz/model" - - gitlab "github.com/xanzy/go-gitlab" - "go.uber.org/zap" -) - -func FromIssue(input *gitlab.Issue) *model.Issue { - repoURL := input.Links.Project - if repoURL == "" { - repoURL = strings.Replace(input.WebURL, fmt.Sprintf("/issues/%d", input.IID), "", -1) - } - - //out, _ := json.MarshalIndent(input, "", " ") - //fmt.Println(string(out)) - - repo := FromRepositoryURL(repoURL) - issue := &model.Issue{ - Base: model.Base{ - ID: input.WebURL, - CreatedAt: *input.CreatedAt, - UpdatedAt: *input.UpdatedAt, - URL: input.WebURL, - }, - Repository: repo, - Title: input.Title, - State: input.State, - Body: input.Description, - IsPR: false, - IsLocked: false, // not supported on GitLab - NumComments: 0, // not supported directly - NumUpvotes: input.Upvotes, - NumDownvotes: input.Downvotes, - Labels: make([]*model.Label, 0), - Assignees: make([]*model.Account, 0), - Author: FromIssueAuthor(repo.Provider, input.Author), - Milestone: FromMilestone(repo, input.Milestone), - /* - IsOrphan bool `json:"is-orphan"` - IsHidden bool `json:"is-hidden"` - BaseWeight int `json:"base-weight"` - Weight int `json:"weight"` - IsEpic bool `json:"is-epic"` - HasEpic bool `json:"has-epic"` - - // internal - Parents []*Issue `json:"-" gorm:"-"` - Children []*Issue `json:"-" gorm:"-"` - Duplicates []*Issue `json:"-" gorm:"-"` - */ - } - if input.ClosedAt != nil { - issue.CompletedAt = *input.ClosedAt - } - for _, label := range input.Labels { - issue.Labels = append(issue.Labels, FromLabelname(repo, label)) - } - //issue.Assignees = append(issue.Assignees, FromIssueAssignee(input.Assignee)) - for _, assignee := range input.Assignees { - issue.Assignees = append(issue.Assignees, FromIssueAssignee(repo.Provider, assignee)) - } - return issue -} - -func FromLabelname(repository *model.Repository, name string) *model.Label { - url := fmt.Sprintf("%s/labels/%s", repository.URL, name) - return &model.Label{ - Base: model.Base{ - ID: url, - URL: url, - }, - Name: name, - Color: "aaaacc", - // Description: - } -} - -func FromIssueAssignee(provider *model.Provider, input *gitlab.IssueAssignee) *model.Account { - author := gitlab.IssueAuthor(*input) - return FromIssueAuthor(provider, &author) -} - -func FromIssueAuthor(provider *model.Provider, input *gitlab.IssueAuthor) *model.Account { - name := input.Name - if name == "" { - name = input.Username - } - account := model.Account{ - Base: model.Base{ - ID: input.WebURL, - // UpdatedAt: - // CreatedAt: - URL: input.WebURL, - }, - Provider: &model.Provider{ - Base: model.Base{ - ID: "gitlab", // FIXME: support multiple gitlab instances - }, - Driver: string(model.GitlabDriver), - }, - // Email: - FullName: name, - Login: input.Username, - // State: // FIXME: investigate what to do with this - - // Location: - // Company: - // Blog: - AvatarURL: input.AvatarURL, - } - - return &account -} - -func FromRepositoryURL(input string) *model.Repository { - u, err := url.Parse(input) - if err != nil { - zap.L().Warn("invalid repository URL", zap.String("URL", input)) - return nil - } - providerURL := fmt.Sprintf("%s://%s", u.Scheme, u.Host) - return &model.Repository{ - Base: model.Base{ - ID: input, - URL: input, - }, - Provider: &model.Provider{ - Base: model.Base{ - ID: "gitlab", // FIXME: support multiple gitlab instances - URL: providerURL, - }, - Driver: string(model.GitlabDriver), - }, - } -} - -func FromMilestone(repository *model.Repository, input *gitlab.Milestone) *model.Milestone { - if input == nil { - return nil - } - url := fmt.Sprintf("%s/milestones/%d", repository.URL, input.ID) - milestone := model.Milestone{ - Base: model.Base{ - ID: url, - CreatedAt: *input.CreatedAt, - UpdatedAt: *input.UpdatedAt, - URL: url, - }, - Title: input.Title, - Description: input.Description, - } - if input.DueDate != nil { - milestone.DueOn = time.Time(*input.DueDate) - } - // startdate // FIXME: todo - // state // FIXME: todo - return &milestone -} diff --git a/go.mod b/go.mod index d58149d7a..6dc0d86ea 100644 --- a/go.mod +++ b/go.mod @@ -2,27 +2,36 @@ module moul.io/depviz go 1.13 +replace github.com/brianloveswords/airtable => github.com/moul/brianloveswords-airtable v0.0.0-20191014120838-8b07ee6d33b2 + require ( - github.com/brianloveswords/airtable v0.0.0-20180329193050-a39294038dd9 + github.com/cayleygraph/cayley v0.7.7 + github.com/cayleygraph/quad v1.1.0 + github.com/cespare/xxhash/v2 v2.1.1 // indirect + github.com/gilliek/go-opml v1.0.0 github.com/go-chi/chi v4.0.2+incompatible - github.com/go-chi/docgen v1.0.5 github.com/go-chi/render v1.0.1 + github.com/gobuffalo/packr/v2 v2.7.1 + github.com/gogo/protobuf v1.3.1 + github.com/golang/protobuf v1.3.2 github.com/google/go-github/v28 v28.1.1 - github.com/jinzhu/gorm v1.9.11 - github.com/lib/pq v1.2.0 - github.com/mattn/go-sqlite3 v1.11.0 - github.com/pkg/errors v0.8.1 - github.com/spf13/cobra v0.0.5 - github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.4.0 - github.com/xanzy/go-gitlab v0.20.1 - go.uber.org/zap v1.10.0 + github.com/gorilla/schema v1.1.0 + github.com/grpc-ecosystem/grpc-gateway v1.12.1 + github.com/peterbourgon/ff v1.6.1-0.20190916204019-6cd704ec2eeb + github.com/prometheus/client_golang v1.2.1 // indirect + github.com/prometheus/procfs v0.0.7 // indirect + go.uber.org/multierr v1.4.0 // indirect + go.uber.org/zap v1.13.0 + golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f // indirect + golang.org/x/net v0.0.0-20191116160921-f9c825593386 // indirect golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 - gopkg.in/yaml.v2 v2.2.4 + golang.org/x/sys v0.0.0-20191115151921-52ab43148777 // indirect + golang.org/x/tools v0.0.0-20191116214431-80313e1ba718 // indirect + google.golang.org/appengine v1.6.5 // indirect + gopkg.in/yaml.v2 v2.2.5 + gopkg.in/yaml.v3 v3.0.0-20191107175235-0b070bb63a18 // indirect + moul.io/godev v1.3.0 moul.io/graphman v1.5.0 moul.io/graphman/viz v0.0.0-20190925205035-97b8bdad4639 - moul.io/multipmuri v1.8.0 - moul.io/zapgorm v0.0.0-20190706070406-8138918b527b + moul.io/multipmuri v1.12.0 ) - -replace github.com/brianloveswords/airtable => github.com/moul/brianloveswords-airtable v0.0.0-20191014120838-8b07ee6d33b2 diff --git a/go.sum b/go.sum index ec1e8ffa5..49729a630 100644 --- a/go.sum +++ b/go.sum @@ -1,61 +1,127 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU= cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= +github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/awalterschulze/gographviz v0.0.0-20190522210029-fa59802746ab h1:+cdNqtOJWjvepyhxy23G7z7vmpYCoC65AP0nqi1f53s= github.com/awalterschulze/gographviz v0.0.0-20190522210029-fa59802746ab/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= +github.com/badgerodon/peg v0.0.0-20130729175151-9e5f7f4d07ca/go.mod h1:TWe0N2hv5qvpLHT+K16gYcGBllld4h65dQ/5CNuirmk= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/cayleygraph/cayley v0.7.7 h1:z+7xkAbg6bKiXJOtOkEG3zCm2K084sr/aGwFV7xcQNs= +github.com/cayleygraph/cayley v0.7.7/go.mod h1:VUd+PInYf94/VY41ePeFtFyP99BAs953kFT4N+6F7Ko= +github.com/cayleygraph/quad v1.1.0 h1:w1nXAmn+nz07+qlw89dke9LwWkYpeX+OcvfTvGQRBpM= +github.com/cayleygraph/quad v1.1.0/go.mod h1:maWODEekEhrO0mdc9h5n/oP7cH1h/OTgqQ2qWbuI9M4= +github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.0 h1:yTUvW7Vhb89inJ+8irsUqiWjh8iT6sQPZiQzI6ReGkA= +github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/cznic/mathutil v0.0.0-20170313102836-1447ad269d64 h1:oad14P7M0/ZAPSMH1nl1vC8zdKVkA3kfHLO59z1l8Eg= +github.com/cznic/mathutil v0.0.0-20170313102836-1447ad269d64/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= +github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U= +github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 h1:tkum0XDgfR0jcVVXuTsYv/erY2NnEDqwRojbxR1rBYA= -github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM= +github.com/dennwc/base v1.0.0 h1:xlBzvBNRvkQ1LFI/jom7rr0vZsvYDKtvMM6lIpjFb3M= +github.com/dennwc/base v1.0.0/go.mod h1:zaTDIiAcg2oKW9XhjIaRc1kJVteCFXSSW6jwmCedUaI= +github.com/dennwc/graphql v0.0.0-20180603144102-12cfed44bc5d/go.mod h1:lg9KQn0BgRCSCGNpcGvJp/0Ljf1Yxk8TZq9HSYc43fk= +github.com/dgraph-io/badger v1.5.4/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= +github.com/dgraph-io/badger v1.5.5/go.mod h1:QgCntgIUPsjnp7cMLhUybJHb7iIoQWAHT6tF8ngCjWk= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190416075124-e1214b5e05dc/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dlclark/regexp2 v1.1.4 h1:1udHhhGkIMplSrLeMJpPN7BHz1Iq2wVBUcb+3fxzhQM= +github.com/dlclark/regexp2 v1.1.4/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/docker/docker v0.7.3-0.20180412203414-a422774e593b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dop251/goja v0.0.0-20190105122144-6d5bf35058fa h1:cA2OMt2CQ2yq2WhQw16mHv6ej9YY07H4pzfR/z/y+1Q= +github.com/dop251/goja v0.0.0-20190105122144-6d5bf35058fa/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= -github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= +github.com/flimzy/diff v0.1.5/go.mod h1:lFJtC7SPsK0EroDmGTSrdtWKAxOk3rO+q+e04LL05Hs= +github.com/flimzy/diff v0.1.6/go.mod h1:lFJtC7SPsK0EroDmGTSrdtWKAxOk3rO+q+e04LL05Hs= +github.com/flimzy/kivik v1.8.1/go.mod h1:S2aPycbG0eDFll4wgXt9uacSNkXISPufutnc9sv+mdA= +github.com/flimzy/testy v0.1.16/go.mod h1:3szguN8NXqgq9bt9Gu8TQVj698PJWmyx/VY1frwwKrM= +github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsouza/go-dockerclient v1.2.2/go.mod h1:KpcjM623fQYE9MZiTGzKhjfxXAV9wbyX2C1cyRHfhl0= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-chi/chi v4.0.0+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/gilliek/go-opml v1.0.0 h1:X8xVjtySRXU/x6KvaiXkn7OV3a4DHqxY8Rpv6U/JvCY= +github.com/gilliek/go-opml v1.0.0/go.mod h1:fOxmtlzyBvUjU6bjpdjyxCGlWz+pgtAHrHf/xRZl3lk= github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-chi/docgen v1.0.5 h1:TiGvJAuVPZJ9zFSwoF52eORe0SztOYqf9C79LVw/xbY= -github.com/go-chi/docgen v1.0.5/go.mod h1:Nm4H4RaynSlvTexxWYWwXBzrwZKRE00MrkIIcJelhWM= github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8= github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kivik/couchdb v1.8.1/go.mod h1:5XJRkAMpBlEVA4q0ktIZjUPYBjoBmRoiWvwUBzP3BOQ= +github.com/go-kivik/kivik v1.8.1/go.mod h1:nIuJ8z4ikBrVUSk3Ua8NoDqYKULPNjuddjqRvlSUyyQ= +github.com/go-kivik/kiviktest v1.1.2/go.mod h1:JdhVyzixoYhoIDUt6hRf1yAfYyaDa5/u9SDOindDkfQ= +github.com/go-kivik/pouchdb v1.3.5/go.mod h1:U+siUrqLCVxeMU3QjQTYIC3/F/e6EUKm+o5buJb7vpw= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= +github.com/go-sourcemap/sourcemap v2.1.2+incompatible h1:0b/xya7BKGhXuqFESKM4oIiRo9WOt2ebz7KxfreD6ug= +github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.1 h1:OQl5ys5MBea7OGCdvPbBJWRgnhC/fGona6QKfvFeau8= +github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w= +github.com/gobuffalo/logger v1.0.1 h1:ZEgyRGgAm4ZAhAO45YXMs5Fp+bzGLESFewzAVBMKuTg= +github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= +github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4= +github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= +github.com/gobuffalo/packr/v2 v2.7.1 h1:n3CIW5T17T8v4GGK5sWXLVWJhCz7b5aNLSxW6gYim4o= +github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -63,73 +129,120 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo= github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f h1:KMlcu9X58lhTA/KrfX8Bi1LQSO4pzoVjTiL3h4Jk+Zk= +github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/jsbuiltin v0.0.0-20180426082241-50091555e127/go.mod h1:7X1acUyFRf+oVFTU6SWw9mnb57Vxn+Nbh8iPbKg95hs= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY= +github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0 h1:bM6ZAFZmc/wPFaRDi0d5L7hGEZEx/2u+Tmr2evNHDiI= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.12.1 h1:zCy2xE9ablevUOrUZc3Dl72Dt+ya2FNAvC2yLYMHzi4= +github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hidal-go/hidalgo v0.0.0-20190814174001-42e03f3b5eaa h1:hBE4LGxApbZiV/3YoEPv7uYlUMWOogG1hwtkpiU87zQ= +github.com/hidal-go/hidalgo v0.0.0-20190814174001-42e03f3b5eaa/go.mod h1:bPkrxDlroXxigw8BMWTEPTv4W5/rQwNgg2BECXsgyX0= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jinzhu/gorm v1.9.11 h1:gaHGvE+UnWGlbWG4Y3FUwY1EcZ5n6S9WtqBA/uySMLE= -github.com/jinzhu/gorm v1.9.11/go.mod h1:bu/pK8szGZ2puuErfU0RwyeNdsf3e6nCX/noXaVxkfw= -github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= -github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M= -github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= +github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= +github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= +github.com/linkeddata/gojsonld v0.0.0-20170418210642-4f5db6791326 h1:YP3lfXXYiQV5MKeUqVnxRP5uuMQTLPx+PGYm1UBoU98= +github.com/linkeddata/gojsonld v0.0.0-20170418210642-4f5db6791326/go.mod h1:nfqkuSNlsk1bvti/oa7TThx4KmRMBmSxf3okHI9wp3E= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q= -github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.0.0-20180730094502-03f2033d19d5/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/moul/brianloveswords-airtable v0.0.0-20191014120838-8b07ee6d33b2 h1:SX2d3PP5B0Xs3CoekgC5qRQlC9nHD38VlI6q66Lp12A= -github.com/moul/brianloveswords-airtable v0.0.0-20191014120838-8b07ee6d33b2/go.mod h1:nwbEOwACogJJxLEo0a4lnyieg8EKfq3uIzuZeEHMPI0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/selinux v1.0.0/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/ory/dockertest v3.3.4+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg= +github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= +github.com/peterbourgon/ff v1.6.1-0.20190916204019-6cd704ec2eeb h1:xax7BA50iaQoo6EnC7EhJlGsioTuo/e2FhMGq44iOHE= +github.com/peterbourgon/ff v1.6.1-0.20190916204019-6cd704ec2eeb/go.mod h1:7n3S/NbAF/XvqgI95LxtGcH7jMzS8mCsTaPjMSfJXwo= +github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= @@ -138,35 +251,67 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.2.1 h1:JnMpQc6ppsNgw9QPAGF6Dod479itz7lvlsMzzNayLOI= +github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.7 h1:RS5GAlMbnkWkhs4+bPocMTmGjYkuCY5djjqEDdXOhcQ= +github.com/prometheus/procfs v0.0.7/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.5.0 h1:Usqs0/lDK/NqTkvrmKSwA/3XkZAs7ZAW/eLeQ2MVBTw= +github.com/rogpeppe/go-internal v1.5.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w= +github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= +github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -179,55 +324,87 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tylertreat/BoomFilters v0.0.0-20181028192813-611b3dbe80e8 h1:7X4KYG3guI2mPQGxm/ZNNsiu4BjKnef0KG0TblMC+Z8= +github.com/tylertreat/BoomFilters v0.0.0-20181028192813-611b3dbe80e8/go.mod h1:OYRfF6eb5wY9VRFkXJH8FFBi3plw2v+giaIu7P054pM= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/xanzy/go-gitlab v0.20.1 h1:+1BWDry84G5PzsnzG9DI4YjPbHeWKyouM0q0gfDPKgY= -github.com/xanzy/go-gitlab v0.20.1/go.mod h1:LSfUQ9OPDnwRqulJk2HcWaAiFfCzaknyeGvjQI67MbE= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.mongodb.org/mongo-driver v1.0.4/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/ratelimit v0.1.0 h1:U2AruXqeTb4Eh9sYQSTrMhH8Cb7M0Ian2ibBOnBcnAw= -go.uber.org/ratelimit v0.1.0/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.4.0 h1:f3WCSC2KzAcBXGATIxAB1E2XuCpNU255wNKZ505qi3E= +go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc h1:c0o/qxkaO2LF5t6fQrT4b5hzyggAkLLlCUjqfRxd8Q4= +golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f h1:kz4KIr+xcPUsI3VMoqWfPMvtnJ6MGfiVwsWSVzphMO4= +golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190110200230-915654e7eabc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191116160921-f9c825593386 h1:ktbWvQrW08Txdxno1PiDpSxPXG6ndGsfnJjRRtkM0LQ= +golang.org/x/net v0.0.0-20191116160921-f9c825593386/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -235,53 +412,97 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190614160838-b47fdc937951/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191009170203-06d7bd2c5f4f h1:hjzMYz/7Ea1mNKfOnFOfktR0mlA5jqhvywClCMHM/qw= +golang.org/x/sys v0.0.0-20191009170203-06d7bd2c5f4f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777 h1:wejkGHRTr38uaKRqECZlsCsJ1/TGxIyFbH32x5zUdu4= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191010075000-0337d82405ff h1:XdBG6es/oFDr1HwaxkxgVve7NB281QhxgK/i4voubFs= +golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191116214431-80313e1ba718 h1:cWviR33VVbwok1/RNvFm9XHNcdJCsaSocBflkEXrIdo= +golang.org/x/tools v0.0.0-20191116214431-80313e1ba718/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/olivere/elastic.v5 v5.0.80/go.mod h1:uhHoB4o3bvX5sorxBU29rPcmBQdV2Qfg0FBrx5D6pV0= +gopkg.in/olivere/elastic.v5 v5.0.81/go.mod h1:uhHoB4o3bvX5sorxBU29rPcmBQdV2Qfg0FBrx5D6pV0= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22 h1:0efs3hwEZhFKsCoP8l6dDB1AZWMgnEl3yWXWRZTOaEA= +gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20191107175235-0b070bb63a18 h1:VaaR1yHVgJQaTGM1DXum4OU6He6gaZXAPII85hHgSzQ= +gopkg.in/yaml.v3 v3.0.0-20191107175235-0b070bb63a18/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +moul.io/godev v1.3.0 h1:+GGk+scZhZ4citKb/lLubV/QJsu7OvB5UZhSYNQRCx4= +moul.io/godev v1.3.0/go.mod h1:xsV3hXF1E9vy2T/1b1CLjSImJ1/nQeTfqTDhJHhJCWg= moul.io/graphman v1.5.0 h1:m2kVB06uI82UEEWSOtR58sKwB2yahhY4eDu3wmIDTCE= moul.io/graphman v1.5.0/go.mod h1:A34tMYPJRjkFBWPIsmrVFb481r+dsPyP2Ee83RuObwI= moul.io/graphman/viz v0.0.0-20190925205035-97b8bdad4639 h1:9gsAG7VeFpUVTVTx9aihJRtUnNSCkTq9rH9FRE31N1A= moul.io/graphman/viz v0.0.0-20190925205035-97b8bdad4639/go.mod h1:gIMudP/g+QhImUksKOueXPiDi5WRapt1N1wfVdxLMdo= -moul.io/multipmuri v1.8.0 h1:KOQ/Nu9pe+5HyjJ8XUO0wxcGvLgrOo4bkxYyb9G0ZnY= -moul.io/multipmuri v1.8.0/go.mod h1:xTE1AUMMTNRFyNRNwnM9E7UgOalny3SNb3B4h5CYv4Q= -moul.io/zapgorm v0.0.0-20190706070406-8138918b527b h1:7A2qUMck+Ikop+5+Ar6wyweFOHTwXEgHbNayY2mEB6Q= -moul.io/zapgorm v0.0.0-20190706070406-8138918b527b/go.mod h1:JDE3xz5BQ1ccnAijE5+T8Qin6T256Bw2Cpdi+qMfWgw= +moul.io/multipmuri v1.12.0 h1:ns0SF+HuqnudOIzzXJ41oONIbhSAZiojp9LCnUeQfZo= +moul.io/multipmuri v1.12.0/go.mod h1:7qiDMEzR2yz6XIDl0vldcW/EHpH/uj+TxgQ14+YO5oQ= diff --git a/graph/cmd_graph.go b/graph/cmd_graph.go deleted file mode 100644 index f07656a74..000000000 --- a/graph/cmd_graph.go +++ /dev/null @@ -1,70 +0,0 @@ -package graph // import "moul.io/depviz/graph" - -import ( - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/spf13/viper" - "go.uber.org/zap" - "moul.io/depviz/cli" - "moul.io/depviz/model" - "moul.io/depviz/sql" -) - -func GetOptions(commands cli.Commands) Options { - return commands["graph"].(*graphCommand).opts -} - -func Commands() cli.Commands { - return cli.Commands{"graph": &graphCommand{}} -} - -type graphCommand struct { - opts Options -} - -func (cmd *graphCommand) CobraCommand(commands cli.Commands) *cobra.Command { - cc := &cobra.Command{ - Use: "graph", - Short: "Output graph of relationships between all issues stored in database", - Args: func(c *cobra.Command, args []string) error { - // FIXME: if no args, then graph the whole database - if err := cobra.MinimumNArgs(1)(c, args); err != nil { - return err - } - return nil - }, - RunE: func(_ *cobra.Command, args []string) error { - opts := cmd.opts - opts.SQL = sql.GetOptions(commands) - targets, err := model.ParseTargets(args) - if err != nil { - return err - } - opts.Targets = targets - if err := opts.Validate(); err != nil { - return err - } - return PrintGraph(&opts) - }, - } - cmd.ParseFlags(cc.Flags()) - commands["sql"].ParseFlags(cc.Flags()) - return cc -} - -func (cmd *graphCommand) LoadDefaultOptions() error { - return viper.Unmarshal(&cmd.opts) -} - -func (cmd *graphCommand) ParseFlags(flags *pflag.FlagSet) { - flags.BoolVarP(&cmd.opts.ShowClosed, "show-closed", "", false, "show closed issues/PRs") - flags.BoolVarP(&cmd.opts.ShowOrphans, "show-orphans", "", false, "show orphans issues/PRs") - flags.BoolVarP(&cmd.opts.ShowPRs, "show-prs", "", false, "show PRs") - flags.BoolVarP(&cmd.opts.ShowAllRelated, "show-all-related", "", false, "show related from other repos") - flags.BoolVarP(&cmd.opts.Vertical, "vertical", "", false, "display graph vertically instead of horizontally") - flags.StringVarP(&cmd.opts.Format, "format", "f", "dot", "output format (dot, graphman-pert)") - flags.BoolVarP(&cmd.opts.NoPertEstimates, "no-pert-estimates", "", false, "do not compute PERT estimates") - if err := viper.BindPFlags(flags); err != nil { - zap.L().Warn("failed to bind viper flags", zap.Error(err)) - } -} diff --git a/graph/graph.go b/graph/graph.go deleted file mode 100644 index e8c4f6a54..000000000 --- a/graph/graph.go +++ /dev/null @@ -1,176 +0,0 @@ -package graph // import "moul.io/depviz/graph" - -import ( - "encoding/json" - "fmt" - - "go.uber.org/zap" - "gopkg.in/yaml.v2" - "moul.io/depviz/compute" - "moul.io/depviz/sql" - "moul.io/graphman" - "moul.io/graphman/viz" - "moul.io/multipmuri" -) - -type Options struct { - SQL sql.Options `mapstructure:"sql"` // inherited with sql.GetOptions() - Targets []multipmuri.Entity `mapstructure:"targets"` // parsed from Args - ShowClosed bool `mapstructure:"show-closed"` - ShowOrphans bool `mapstructure:"show-orphans"` - ShowPRs bool `mapstructure:"show-prs"` - ShowAllRelated bool `mapstructure:"show-all-related"` - NoPertEstimates bool `mapstructure:"no-pert-estimates"` - Vertical bool `mapstructure:"vertical"` - Format string `mapstructure:"format"` -} - -func (opts Options) Validate() error { - if err := opts.SQL.Validate(); err != nil { - return err - } - switch format := opts.Format; format { - case "dot", "graphman-pert": - default: - return fmt.Errorf("invalid format: %q", format) - } - return nil -} - -func (opts Options) String() string { - out, _ := json.Marshal(opts) - return string(out) -} - -func PrintGraph(opts *Options) error { - zap.L().Debug("PrintGraph", zap.Stringer("opts", *opts)) - - str, err := Graph(opts) - if err != nil { - return err - } - - fmt.Println(str) - return nil -} - -func Graph(opts *Options) (string, error) { - zap.L().Debug("Graph", zap.Stringer("opts", *opts)) - - db, err := sql.FromOpts(&opts.SQL) - if err != nil { - return "", err - } - - computed, err := compute.LoadIssuesByTargets(db, opts.Targets) - if err != nil { - return "", err - } - if !opts.ShowClosed { - computed.FilterClosed() - } - // FIXME: if !opts.ShowOrphans { computed.FilterOrphans() } - // FIXME: if !opts.ShowAllRelated { computed.FilterAllRelated() - // FIXME: if !opts.ShowPRs { computed.FilterPRs() - - // initialize graph config - config := graphman.PertConfig{ - Actions: []graphman.PertAction{}, - States: []graphman.PertState{}, - } - config.Opts.NoSimplify = false - - // process computed issues - for _, issue := range computed.Issues() { - // fmt.Println(issue.Hidden, issue.URL) - if issue.Hidden { - continue - } - config.Actions = append( - config.Actions, - graphman.PertAction{ - ID: issue.URL, - Title: issue.Title, - DependsOn: issue.DependsOn, - // Estimate - // FIXME: set style based on type, active, etc - }, - ) - } - for _, milestone := range computed.Milestones() { - if milestone.Hidden { - continue - } - config.States = append( - config.States, - graphman.PertState{ - ID: milestone.URL, - Title: milestone.Title, - DependsOn: milestone.DependsOn, - }, - ) - } - if len(computed.Repos()) > 1 { - // FIXME: alternative layout with repo a bordered subgraph - for _, repo := range computed.Repos() { - if repo.Hidden { - continue - } - config.States = append( - config.States, - graphman.PertState{ - ID: repo.URL, - Title: fmt.Sprintf("Repo %q", repo.URL), - DependsOn: repo.DependsOn, - }, - ) - } - } - - if opts.Format == "graphman-pert" { - out, err := yaml.Marshal(config) - if err != nil { - return "", err - } - return string(out), nil - } - - // initialize graph from config - graph := graphman.FromPertConfig(config) - if !opts.NoPertEstimates { - _ = graphman.ComputePert(graph) - //for _, e := range graph.Edges() {log.Println("*", e)} - shortestPath, _ := graph.FindShortestPath("Start", "Finish") - for _, edge := range shortestPath { - edge.Dst().SetColor("red") - edge.SetColor("red") - } - } - - // graph fine tuning - graph.GetVertex("Start").SetColor("blue") - graph.GetVertex("Finish").SetColor("blue") - if opts.Vertical { - graph.Attrs["rankdir"] = "TB" - } - //graph.Attrs["size"] = "\"11,11\"" - graph.Attrs["overlap"] = "false" - graph.Attrs["pack"] = "true" - graph.Attrs["splines"] = "true" - // graph.Attrs["layout"] = "neato" - graph.Attrs["sep"] = "0.1" - // graph.Attrs["start"] = "random" - // FIXME: hightlight critical paths - // FIXME: highlight other infos - // FIXME: highlight target - - // graphviz - s, err := viz.ToGraphviz(graph, &viz.Opts{ - CommentsInLabel: true, - }) - if err != nil { - return "", err - } - - return s, nil -} diff --git a/web/chi_util.go b/internal/chiutil/chi.go similarity index 97% rename from web/chi_util.go rename to internal/chiutil/chi.go index 9db193742..8f6d37913 100644 --- a/web/chi_util.go +++ b/internal/chiutil/chi.go @@ -1,4 +1,4 @@ -package web // import "moul.io/depviz/web" +package chiutil import ( "net/http" diff --git a/internal/chiutil/doc.go b/internal/chiutil/doc.go new file mode 100644 index 000000000..e15ead935 --- /dev/null +++ b/internal/chiutil/doc.go @@ -0,0 +1 @@ +package chiutil // import "moul.io/depviz/internal/chiutil" diff --git a/internal/dvcore/airtable.go b/internal/dvcore/airtable.go new file mode 100644 index 000000000..f9decf11e --- /dev/null +++ b/internal/dvcore/airtable.go @@ -0,0 +1,25 @@ +package dvcore + +import ( + "fmt" + + "github.com/cayleygraph/cayley" +) + +type AirtableOpts struct { + Token string + BaseID string + OwnersTab string + TasksTab string + TopicsTab string +} + +func AirtableSync(db *cayley.Handle, opts AirtableOpts) error { + fmt.Println(db, opts) + return fmt.Errorf("not implemented") +} + +func AirtableInfo(opts AirtableOpts) error { + fmt.Println(opts) + return fmt.Errorf("not implemented") +} diff --git a/internal/dvcore/doc.go b/internal/dvcore/doc.go new file mode 100644 index 000000000..5b465486f --- /dev/null +++ b/internal/dvcore/doc.go @@ -0,0 +1 @@ +package dvcore // import "moul.io/depviz/internal/dvcore" diff --git a/internal/dvcore/run.go b/internal/dvcore/run.go new file mode 100644 index 000000000..137492cef --- /dev/null +++ b/internal/dvcore/run.go @@ -0,0 +1,316 @@ +package dvcore + +import ( + "context" + "encoding/json" + "fmt" + "sync" + + "github.com/cayleygraph/cayley" + "github.com/cayleygraph/cayley/graph" + "github.com/cayleygraph/cayley/schema" + "go.uber.org/zap" + "gopkg.in/yaml.v2" + "moul.io/depviz/internal/dvmodel" + "moul.io/depviz/internal/dvparser" + "moul.io/depviz/internal/dvstore" + "moul.io/depviz/internal/githubprovider" + "moul.io/graphman" + "moul.io/graphman/viz" + "moul.io/multipmuri" +) + +type RunOpts struct { + // global + NoPull bool + NoGraph bool + Logger *zap.Logger + Schema *schema.Config + + // pull + GitHubToken string + GitLabToken string + // TrelloToken + // JiraToken + Resync bool + + // graph + Format string + Vertical bool + NoPert bool + ShowClosed bool + HideIsolated bool + HidePRs bool + HideExternalDeps bool +} + +func Run(h *cayley.Handle, args []string, opts RunOpts) error { + if opts.Logger == nil { + opts.Logger = zap.NewNop() + } + opts.Logger.Debug("Run called", zap.Strings("args", args), zap.Any("opts", opts)) + + targets, err := dvparser.ParseTargets(args) + if err != nil { + return fmt.Errorf("parse targets: %w", err) + } + + if !opts.NoPull { + batches, err := pullBatches(targets, h, opts) + if err != nil { + return fmt.Errorf("pull batches: %w", err) + } + err = saveBatches(h, opts.Schema, batches) + if err != nil { + return fmt.Errorf("save batches: %w", err) + } + } + + if !opts.NoGraph { + // load tasks + filters := dvstore.LoadTasksFilters{ + Targets: targets, + WithClosed: opts.ShowClosed, + WithoutIsolated: opts.HideIsolated, + WithoutPRs: opts.HidePRs, + WithoutExternalDeps: opts.HideExternalDeps, + } + tasks, err := dvstore.LoadTasks(h, opts.Schema, filters) + if err != nil { + return fmt.Errorf("load tasks: %w", err) + } + + // graph + pertConfig, err := graphmanPertConfig(tasks, opts) + if err != nil { + return fmt.Errorf("graph: %w", err) + } + + switch opts.Format { + case "json": + out, err := json.MarshalIndent(tasks, "", " ") + if err != nil { + return err + } + fmt.Println(string(out)) + return nil + case "graphman-pert": + out, err := yaml.Marshal(pertConfig) + if err != nil { + return err + } + fmt.Println(string(out)) + return nil + case "dot": + // graph from PERT config + graph := graphman.FromPertConfig(*pertConfig) + + // initialize graph from config + if !opts.NoPert { + result := graphman.ComputePert(graph) + shortestPath, distance := graph.FindShortestPath("Start", "Finish") + opts.Logger.Debug("pert result", zap.Any("result", result), zap.Int64("distance", distance)) + + for _, edge := range shortestPath { + edge.Dst().SetColor("red") + edge.SetColor("red") + } + } + + // graph fine tuning + graph.GetVertex("Start").SetColor("blue") + graph.GetVertex("Finish").SetColor("blue") + if opts.Vertical { + graph.Attrs["rankdir"] = "TB" + } + graph.Attrs["overlap"] = "false" + graph.Attrs["pack"] = "true" + graph.Attrs["splines"] = "true" + graph.Attrs["sep"] = "0.1" + // graph.Attrs["layout"] = "neato" + // graph.Attrs["size"] = "\"11,11\"" + // graph.Attrs["start"] = "random" + // FIXME: hightlight critical paths + // FIXME: highlight other infos + // FIXME: highlight target + + // graphviz + s, err := viz.ToGraphviz(graph, &viz.Opts{ + CommentsInLabel: true, + }) + if err != nil { + return fmt.Errorf("graphviz: %w", err) + } + + fmt.Println(s) + return nil + case "quads": + return fmt.Errorf("not implemented") + default: + return fmt.Errorf("unsupported graph format: %q", opts.Format) + } + } + + return nil +} + +func pullBatches(targets []multipmuri.Entity, h *cayley.Handle, opts RunOpts) ([]dvmodel.Batch, error) { + // FIXME: handle the special '@me' target + var ( + wg sync.WaitGroup + batches = []dvmodel.Batch{} + out = make(chan dvmodel.Batch) + ctx = context.Background() + ) + + // parallel fetches + wg.Add(len(targets)) + for _, target := range targets { + switch provider := target.Provider(); provider { + case multipmuri.GitHubProvider: + go func(repo multipmuri.Entity) { + defer wg.Done() + + ghOpts := githubprovider.Opts{ + // FIXME: Since: lastUpdated, + Logger: opts.Logger.Named("github"), + } + + if !opts.Resync { + since, err := dvstore.LastUpdatedIssueInRepo(ctx, h, repo) + if err != nil { + opts.Logger.Warn("failed to get last updated issue", zap.Error(err)) + + } + ghOpts.Since = &since + } + + githubprovider.FetchRepo(ctx, repo, opts.GitHubToken, out, ghOpts) + }(target) + //case multipmuri.GitLabProvider: + //go gitlab.Pull(target, &wg, opts.GitlabToken, db, out) + default: + // FIXME: clean context-based exit + panic(fmt.Sprintf("unsupported provider: %v", provider)) + } + } + go func() { + wg.Wait() + close(out) + }() + + for batch := range out { + batches = append(batches, batch) + } + + return batches, nil +} + +func saveBatches(h *cayley.Handle, schema *schema.Config, batches []dvmodel.Batch) error { + ctx := context.TODO() + + tx := cayley.NewTransaction() + dw := graph.NewTxWriter(tx, graph.Delete) + iw := graph.NewTxWriter(tx, graph.Add) + + // FIXME: append new owner to existing one (field by field), or just don't delete the source? + for _, batch := range batches { + for _, owner := range batch.Owners { + var working dvmodel.Owner + if err := schema.LoadTo(ctx, h, &working, owner.ID); err == nil { + _, _ = schema.WriteAsQuads(dw, working) + } + + working = owner + if _, err := schema.WriteAsQuads(iw, working); err != nil { + return fmt.Errorf("write as quads: %w", err) + } + } + for _, task := range batch.Tasks { + var working dvmodel.Task + if err := schema.LoadTo(ctx, h, &working, task.ID); err == nil { + _, _ = schema.WriteAsQuads(dw, working) + } + + working = task + if _, err := schema.WriteAsQuads(iw, working); err != nil { + return fmt.Errorf("write as quads: %w", err) + } + } + for _, topic := range batch.Topics { + var working dvmodel.Topic + if err := schema.LoadTo(ctx, h, &working, topic.ID); err == nil { + _, _ = schema.WriteAsQuads(dw, working) + } + + working = topic + if _, err := schema.WriteAsQuads(iw, working); err != nil { + return fmt.Errorf("write as quads: %w", err) + } + } + /* + for _, relationship := range batch.Relationships { + tx.AddQuad(quad.Make(quad.IRI(relationship.Subject), relationship.Predicate, quad.IRI(relationship.Object), nil)) + } + */ + } + + if err := h.ApplyTransaction(tx); err != nil { + return fmt.Errorf("apply tx: %w", err) + } + + //return Compute(db) + return nil +} + +func graphmanPertConfig(tasks []dvmodel.Task, opts RunOpts) (*graphman.PertConfig, error) { + opts.Logger.Debug("graphTargets", zap.Int("tasks", len(tasks)), zap.Any("opts", opts)) + + // initialize graph config + config := graphman.PertConfig{ + Actions: []graphman.PertAction{}, + States: []graphman.PertState{}, + } + config.Opts.NoSimplify = false + + // process tasks + for _, task := range tasks { + // compute dependsOn + dependsOn := []string{} + for _, dep := range task.IsDependingOn { + dependsOn = append(dependsOn, string(dep)) + } + // FIXME: compute reverse dependsOn + + switch task.Kind { + case dvmodel.Task_Issue, dvmodel.Task_MergeRequest: + config.Actions = append( + config.Actions, + graphman.PertAction{ + ID: string(task.ID), + Title: task.Title, + DependsOn: dependsOn, + // FIXME: Estimate + // FIXME: set style based on type, active, etc + }, + ) + case dvmodel.Task_Milestone: + config.States = append( + config.States, + graphman.PertState{ + ID: string(task.ID), + Title: task.Title, + DependsOn: dependsOn, + // FIXME: auto estimate (PERT) + // FIXME: DependsOn: milestone.DependsOn + // FIXME: styling + }, + ) + default: + opts.Logger.Warn("unsupported task kind", zap.Stringer("kind", task.Kind)) + } + } + // FIXME: if len(unique(repos)) > 1 -> add PertState for each repo with DependsOn + + return &config, nil +} diff --git a/internal/dvcore/server.go b/internal/dvcore/server.go new file mode 100644 index 000000000..aa6acea28 --- /dev/null +++ b/internal/dvcore/server.go @@ -0,0 +1,135 @@ +package dvcore + +import ( + "fmt" + "net/http" + "strings" + "time" + + "github.com/cayleygraph/cayley" + "github.com/cayleygraph/cayley/schema" + "github.com/go-chi/chi" + "github.com/go-chi/chi/middleware" + "github.com/go-chi/render" + "github.com/gobuffalo/packr/v2" + gschema "github.com/gorilla/schema" + "go.uber.org/zap" + "moul.io/depviz/internal/chiutil" + "moul.io/depviz/internal/dvparser" + "moul.io/depviz/internal/dvstore" +) + +func Server(bind string, godmode bool, h *cayley.Handle, logger *zap.Logger, schema *schema.Config) error { + logger.Debug("Server called", zap.String("bind", bind), zap.Bool("godmode", godmode)) + + r := chi.NewRouter() + + //r.Use(middleware.RequestID) + //r.Use(middleware.RealIP) + //r.Use(middleware.URLFormat) + r.Use(middleware.Logger) + r.Use(middleware.Recoverer) + r.Use(middleware.Timeout(5 * time.Second)) + // FIXME: add caching + + a := api{ + logger: logger, + h: h, + schema: schema, + } + + r.Route("/api", func(r chi.Router) { + r.Route("/", func(r chi.Router) { + r.Use(render.SetContentType(render.ContentTypeJSON)) + if godmode { + r.Get("/store/dump.json", a.storeDumpJSON) + } + r.Get("/graph.json", a.graphJSON) + }) + }) + + box := packr.New("static", "./static") + chiutil.FileServer(r, "/", box) + + { // print listeners and routes + logger.Info("HTTP API listening", zap.String("bind", bind)) + walkFunc := func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error { + logger.Debug(fmt.Sprintf(" %s %s", method, route)) + return nil + } + if err := chi.Walk(r, walkFunc); err != nil { + logger.Warn("chi walk", zap.Error(err)) + } + } + + return http.ListenAndServe(bind, r) +} + +type api struct { + logger *zap.Logger + h *cayley.Handle + schema *schema.Config +} + +func (api *api) storeDumpJSON(w http.ResponseWriter, r *http.Request) { + dump, err := getStoreDump(api.h, api.schema) + if err != nil { + _ = render.Render(w, r, chiutil.ErrRender(err)) + return + } + + if err := render.Render(w, r, dump); err != nil { + _ = render.Render(w, r, chiutil.ErrRender(err)) + return + } +} + +func (api *api) graphJSON(w http.ResponseWriter, r *http.Request) { + // parsing + var decoder = gschema.NewDecoder() + type Opts struct { + Targets string `schema:"targets"` + WithClosed bool `schema:"with-closed"` + WithoutIsolated bool `schema:"without-isolated"` + WithoutPRs bool `schema:"without-prs"` + WithoutExternalDeps bool `schema:"without-external-deps"` + } + opts := Opts{} + + err := decoder.Decode(&opts, r.URL.Query()) + if err != nil { + _ = render.Render(w, r, chiutil.ErrRender(err)) + return + } + + // validation + if opts.Targets == "" { + _ = render.Render(w, r, chiutil.ErrRender(fmt.Errorf("missing ?targets="))) + return + } + filters := dvstore.LoadTasksFilters{ + WithClosed: opts.WithClosed, + WithoutIsolated: opts.WithoutIsolated, + WithoutPRs: opts.WithoutPRs, + WithoutExternalDeps: opts.WithoutExternalDeps, + } + targets, err := dvparser.ParseTargets(strings.Split(opts.Targets, ",")) + if err != nil { + _ = render.Render(w, r, chiutil.ErrRender(err)) + return + } + filters.Targets = targets + + // load tasks + tasks, err := dvstore.LoadTasks(api.h, api.schema, filters) + if err != nil { + _ = render.Render(w, r, chiutil.ErrRender(err)) + return + } + + // return JSON + if err := render.Render(w, r, tasks); err != nil { + _ = render.Render(w, r, chiutil.ErrRender(err)) + return + } +} diff --git a/internal/dvcore/store.go b/internal/dvcore/store.go new file mode 100644 index 000000000..333459e23 --- /dev/null +++ b/internal/dvcore/store.go @@ -0,0 +1,60 @@ +package dvcore + +import ( + "context" + "fmt" + + "github.com/cayleygraph/cayley" + "github.com/cayleygraph/cayley/schema" + "moul.io/depviz/internal/dvmodel" + "moul.io/godev" +) + +func StoreDumpQuads(h *cayley.Handle) error { + fmt.Println("quads:") + ctx := context.Background() + it := h.QuadsAllIterator() + for it.Next(ctx) { + fmt.Println(h.Quad(it.Result())) + } + + return nil +} + +func getStoreDump(h *cayley.Handle, schema *schema.Config) (*dvmodel.Batch, error) { + dump := dvmodel.Batch{} + ctx := context.TODO() + + if err := schema.LoadTo(ctx, h, &dump.Owners); err != nil { + return nil, fmt.Errorf("load owners: %w", err) + } + if err := schema.LoadTo(ctx, h, &dump.Tasks); err != nil { + return nil, fmt.Errorf("load tasks: %w", err) + } + if err := schema.LoadTo(ctx, h, &dump.Topics); err != nil { + return nil, fmt.Errorf("load topics: %w", err) + } + + return &dump, nil +} + +func StoreDumpJSON(h *cayley.Handle, schema *schema.Config) error { + dump, err := getStoreDump(h, schema) + if err != nil { + return err + } + + fmt.Println(godev.PrettyJSON(dump)) + return nil +} + +func StoreInfo(h *cayley.Handle) error { + fmt.Println(h) + // FIXME: amount of quads + // FIXME: amount of owners, tasks, topics + // FIXME: amount of relationships + // FIXME: last refresh + // FIXME: db size + // FIXME: db location + return fmt.Errorf("not implemented") +} diff --git a/internal/dvmodel/doc.go b/internal/dvmodel/doc.go new file mode 100644 index 000000000..f40164002 --- /dev/null +++ b/internal/dvmodel/doc.go @@ -0,0 +1 @@ +package dvmodel // import "moul.io/depviz/internal/dvmodel" diff --git a/internal/dvmodel/dvmodel.pb.go b/internal/dvmodel/dvmodel.pb.go new file mode 100644 index 000000000..b589d153e --- /dev/null +++ b/internal/dvmodel/dvmodel.pb.go @@ -0,0 +1,2930 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: dvmodel.proto + +package dvmodel + +import ( + fmt "fmt" + io "io" + math "math" + math_bits "math/bits" + time "time" + + github_com_cayleygraph_quad "github.com/cayleygraph/quad" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + github_com_gogo_protobuf_types "github.com/gogo/protobuf/types" + golang_proto "github.com/golang/protobuf/proto" + _ "github.com/golang/protobuf/ptypes/timestamp" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = golang_proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf +var _ = time.Kitchen + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type Driver int32 + +const ( + Driver_UnknownDriver Driver = 0 + Driver_GitHub Driver = 1 + Driver_GitLab Driver = 2 + Driver_Trello Driver = 3 + Driver_Jira Driver = 4 +) + +var Driver_name = map[int32]string{ + 0: "UnknownDriver", + 1: "GitHub", + 2: "GitLab", + 3: "Trello", + 4: "Jira", +} + +var Driver_value = map[string]int32{ + "UnknownDriver": 0, + "GitHub": 1, + "GitLab": 2, + "Trello": 3, + "Jira": 4, +} + +func (x Driver) String() string { + return proto.EnumName(Driver_name, int32(x)) +} + +func (Driver) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_106647ce772da30c, []int{0} +} + +type Owner_Kind int32 + +const ( + Owner_UnknownKind Owner_Kind = 0 + Owner_User Owner_Kind = 1 + Owner_Organization Owner_Kind = 2 + Owner_Team Owner_Kind = 3 + Owner_Repo Owner_Kind = 4 + Owner_Provider Owner_Kind = 5 +) + +var Owner_Kind_name = map[int32]string{ + 0: "UnknownKind", + 1: "User", + 2: "Organization", + 3: "Team", + 4: "Repo", + 5: "Provider", +} + +var Owner_Kind_value = map[string]int32{ + "UnknownKind": 0, + "User": 1, + "Organization": 2, + "Team": 3, + "Repo": 4, + "Provider": 5, +} + +func (x Owner_Kind) String() string { + return proto.EnumName(Owner_Kind_name, int32(x)) +} + +func (Owner_Kind) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_106647ce772da30c, []int{0, 0} +} + +type Owner_ForkStatus int32 + +const ( + Owner_UnknownForkStatus Owner_ForkStatus = 0 + Owner_IsFork Owner_ForkStatus = 1 + Owner_IsSource Owner_ForkStatus = 2 +) + +var Owner_ForkStatus_name = map[int32]string{ + 0: "UnknownForkStatus", + 1: "IsFork", + 2: "IsSource", +} + +var Owner_ForkStatus_value = map[string]int32{ + "UnknownForkStatus": 0, + "IsFork": 1, + "IsSource": 2, +} + +func (x Owner_ForkStatus) String() string { + return proto.EnumName(Owner_ForkStatus_name, int32(x)) +} + +func (Owner_ForkStatus) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_106647ce772da30c, []int{0, 1} +} + +type Task_Kind int32 + +const ( + Task_UnknownKind Task_Kind = 0 + Task_Issue Task_Kind = 1 + Task_MergeRequest Task_Kind = 2 + Task_Milestone Task_Kind = 3 + Task_Epic Task_Kind = 4 + Task_Story Task_Kind = 5 + Task_Card Task_Kind = 6 +) + +var Task_Kind_name = map[int32]string{ + 0: "UnknownKind", + 1: "Issue", + 2: "MergeRequest", + 3: "Milestone", + 4: "Epic", + 5: "Story", + 6: "Card", +} + +var Task_Kind_value = map[string]int32{ + "UnknownKind": 0, + "Issue": 1, + "MergeRequest": 2, + "Milestone": 3, + "Epic": 4, + "Story": 5, + "Card": 6, +} + +func (x Task_Kind) String() string { + return proto.EnumName(Task_Kind_name, int32(x)) +} + +func (Task_Kind) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_106647ce772da30c, []int{1, 0} +} + +type Task_State int32 + +const ( + Task_UnknownState Task_State = 0 + Task_Open Task_State = 1 + Task_Closed Task_State = 2 +) + +var Task_State_name = map[int32]string{ + 0: "UnknownState", + 1: "Open", + 2: "Closed", +} + +var Task_State_value = map[string]int32{ + "UnknownState": 0, + "Open": 1, + "Closed": 2, +} + +func (x Task_State) String() string { + return proto.EnumName(Task_State_name, int32(x)) +} + +func (Task_State) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_106647ce772da30c, []int{1, 1} +} + +type Topic_Kind int32 + +const ( + Topic_UnknownKind Topic_Kind = 0 + Topic_Label Topic_Kind = 1 +) + +var Topic_Kind_name = map[int32]string{ + 0: "UnknownKind", + 1: "Label", +} + +var Topic_Kind_value = map[string]int32{ + "UnknownKind": 0, + "Label": 1, +} + +func (x Topic_Kind) String() string { + return proto.EnumName(Topic_Kind_name, int32(x)) +} + +func (Topic_Kind) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_106647ce772da30c, []int{2, 0} +} + +// Owner is like a container of tasks or other containers. +// It's something that is rarely deleted and cannot really "closed" or "due". +// It's the entity used for Organizations, Teams, Groups, Users +// and for Projects, Workspaces, Repos, or a Provider. +type Owner struct { + ID github_com_cayleygraph_quad.IRI `protobuf:"bytes,1,opt,name=id,proto3,casttype=github.com/cayleygraph/quad.IRI" json:"id,omitempty" quad:"@id"` + CreatedAt *time.Time `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3,stdtime" json:"created_at,omitempty" quad:"schema:createdAt,optional"` + UpdatedAt *time.Time `protobuf:"bytes,4,opt,name=updated_at,json=updatedAt,proto3,stdtime" json:"updated_at,omitempty" quad:"schema:updatedAt,optional"` + LocalID string `protobuf:"bytes,5,opt,name=local_id,json=localId,proto3" json:"local_id,omitempty" quad:"schema:localId,optional"` + Kind Owner_Kind `protobuf:"varint,10,opt,name=kind,proto3,enum=depviz.model.Owner_Kind" json:"kind,omitempty" quad:"schema:kind,optional"` + ShortName string `protobuf:"bytes,11,opt,name=short_name,json=shortName,proto3" json:"short_name,omitempty" quad:"schema:shortName,optional"` + FullName string `protobuf:"bytes,12,opt,name=full_name,json=fullName,proto3" json:"full_name,omitempty" quad:"schema:fullName,optional"` + Driver Driver `protobuf:"varint,13,opt,name=driver,proto3,enum=depviz.model.Driver" json:"driver,omitempty" quad:"schema:driver,optional"` + Homepage string `protobuf:"bytes,14,opt,name=homepage,proto3" json:"homepage,omitempty" quad:"schema:homepage,optional"` + Description string `protobuf:"bytes,15,opt,name=description,proto3" json:"description,omitempty" quad:"schema:description,optional"` + ForkStatus Owner_ForkStatus `protobuf:"varint,16,opt,name=fork_status,json=forkStatus,proto3,enum=depviz.model.Owner_ForkStatus" json:"fork_status,omitempty" quad:"schema:forkStatus,optional"` + AvatarURL string `protobuf:"bytes,17,opt,name=avatar_url,json=avatarUrl,proto3" json:"avatar_url,omitempty" quad:"schema:avatarUrl,optional"` + // relationships + HasOwner github_com_cayleygraph_quad.IRI `protobuf:"bytes,100,opt,name=has_owner,json=hasOwner,proto3,casttype=github.com/cayleygraph/quad.IRI" json:"has_owner,omitempty" quad:"hasOwner,optional"` +} + +func (m *Owner) Reset() { *m = Owner{} } +func (m *Owner) String() string { return proto.CompactTextString(m) } +func (*Owner) ProtoMessage() {} +func (*Owner) Descriptor() ([]byte, []int) { + return fileDescriptor_106647ce772da30c, []int{0} +} +func (m *Owner) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Owner) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Owner.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Owner) XXX_Merge(src proto.Message) { + xxx_messageInfo_Owner.Merge(m, src) +} +func (m *Owner) XXX_Size() int { + return m.Size() +} +func (m *Owner) XXX_DiscardUnknown() { + xxx_messageInfo_Owner.DiscardUnknown(m) +} + +var xxx_messageInfo_Owner proto.InternalMessageInfo + +// Task defines a step or an action. +// It is owned by an owner and can link to other Owners. +// A task can contain other tasks (epic, story, milestone). +// It can be closed and can have due dates. +// It's the entity used for Issues, Pull Requests, Merge Requests, Cards, Epics, Milestones, Stories. +type Task struct { + ID github_com_cayleygraph_quad.IRI `protobuf:"bytes,1,opt,name=id,proto3,casttype=github.com/cayleygraph/quad.IRI" json:"id,omitempty" quad:"@id"` + CreatedAt time.Time `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3,stdtime" json:"created_at" quad:"schema:createdAt,optional"` + UpdatedAt time.Time `protobuf:"bytes,4,opt,name=updated_at,json=updatedAt,proto3,stdtime" json:"updated_at" quad:"schema:updatedAt,optional"` + LocalID string `protobuf:"bytes,5,opt,name=local_id,json=localId,proto3" json:"local_id,omitempty" quad:"schema:localId,optional"` + Kind Task_Kind `protobuf:"varint,10,opt,name=kind,proto3,enum=depviz.model.Task_Kind" json:"kind,omitempty" quad:"schema:kind,optional"` + Title string `protobuf:"bytes,11,opt,name=title,proto3" json:"title,omitempty" quad:"schema:title,optional"` + Description string `protobuf:"bytes,12,opt,name=description,proto3" json:"description,omitempty" quad:"schema:description,optional"` + Driver Driver `protobuf:"varint,13,opt,name=driver,proto3,enum=depviz.model.Driver" json:"driver,omitempty" quad:"schema:driver,optional"` + DueOn *time.Time `protobuf:"bytes,14,opt,name=due_on,json=dueOn,proto3,stdtime" json:"due_on,omitempty" quad:"schema:dueOn,optional"` + CompletedAt *time.Time `protobuf:"bytes,15,opt,name=completed_at,json=completedAt,proto3,stdtime" json:"completed_at,omitempty" quad:"schema:completedAt,optional"` + State Task_State `protobuf:"varint,16,opt,name=state,proto3,enum=depviz.model.Task_State" json:"state,omitempty" quad:"schema:state,optional"` + IsLocked bool `protobuf:"varint,17,opt,name=is_locked,json=isLocked,proto3" json:"is_locked,omitempty" quad:"schema:isLocked,optional"` + NumComments int32 `protobuf:"varint,18,opt,name=num_comments,json=numComments,proto3" json:"num_comments,omitempty" quad:"schema:numComments,optional"` + NumUpvotes int32 `protobuf:"varint,19,opt,name=num_upvotes,json=numUpvotes,proto3" json:"num_upvotes,omitempty" quad:"schema:numUpvotes,optional"` + NumDownvotes int32 `protobuf:"varint,20,opt,name=num_downvotes,json=numDownvotes,proto3" json:"num_downvotes,omitempty" quad:"schema:numDownvotes,optional"` + // relationships + HasAuthor github_com_cayleygraph_quad.IRI `protobuf:"bytes,100,opt,name=has_author,json=hasAuthor,proto3,casttype=github.com/cayleygraph/quad.IRI" json:"has_author,omitempty" quad:"hasAuthor,optional"` + HasOwner github_com_cayleygraph_quad.IRI `protobuf:"bytes,101,opt,name=has_owner,json=hasOwner,proto3,casttype=github.com/cayleygraph/quad.IRI" json:"has_owner,omitempty" quad:"hasOwner,optional"` + HasMilestone github_com_cayleygraph_quad.IRI `protobuf:"bytes,102,opt,name=has_milestone,json=hasMilestone,proto3,casttype=github.com/cayleygraph/quad.IRI" json:"has_milestone,omitempty" quad:"hasMilestone,optional"` + HasAssignee []github_com_cayleygraph_quad.IRI `protobuf:"bytes,103,rep,name=has_assignee,json=hasAssignee,proto3,casttype=github.com/cayleygraph/quad.IRI" json:"has_assignee,omitempty" quad:"hasAssignee,optional"` + HasReviewer []github_com_cayleygraph_quad.IRI `protobuf:"bytes,104,rep,name=has_reviewer,json=hasReviewer,proto3,casttype=github.com/cayleygraph/quad.IRI" json:"has_reviewer,omitempty" quad:"hasReviewer,optional"` + HasLabel []github_com_cayleygraph_quad.IRI `protobuf:"bytes,105,rep,name=has_label,json=hasLabel,proto3,casttype=github.com/cayleygraph/quad.IRI" json:"has_label,omitempty" quad:"hasLabel,optional"` + IsDependingOn []github_com_cayleygraph_quad.IRI `protobuf:"bytes,106,rep,name=is_depending_on,json=isDependingOn,proto3,casttype=github.com/cayleygraph/quad.IRI" json:"is_depending_on,omitempty" quad:"isDependingOn,optional"` + IsBlocking []github_com_cayleygraph_quad.IRI `protobuf:"bytes,107,rep,name=is_blocking,json=isBlocking,proto3,casttype=github.com/cayleygraph/quad.IRI" json:"is_blocking,omitempty" quad:"isBlocking,optional"` + IsRelatedWith []github_com_cayleygraph_quad.IRI `protobuf:"bytes,108,rep,name=is_related_with,json=isRelatedWith,proto3,casttype=github.com/cayleygraph/quad.IRI" json:"is_related_with,omitempty" quad:"isRelatedWith,optional"` + IsPartOf []github_com_cayleygraph_quad.IRI `protobuf:"bytes,109,rep,name=is_part_of,json=isPartOf,proto3,casttype=github.com/cayleygraph/quad.IRI" json:"is_part_of,omitempty" quad:"isPartOf,optional"` + HasPart []github_com_cayleygraph_quad.IRI `protobuf:"bytes,110,rep,name=has_part,json=hasPart,proto3,casttype=github.com/cayleygraph/quad.IRI" json:"has_part,omitempty" quad:"isPartOf,optional"` +} + +func (m *Task) Reset() { *m = Task{} } +func (m *Task) String() string { return proto.CompactTextString(m) } +func (*Task) ProtoMessage() {} +func (*Task) Descriptor() ([]byte, []int) { + return fileDescriptor_106647ce772da30c, []int{1} +} +func (m *Task) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Task) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Task.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Task) XXX_Merge(src proto.Message) { + xxx_messageInfo_Task.Merge(m, src) +} +func (m *Task) XXX_Size() int { + return m.Size() +} +func (m *Task) XXX_DiscardUnknown() { + xxx_messageInfo_Task.DiscardUnknown(m) +} + +var xxx_messageInfo_Task proto.InternalMessageInfo + +type Topic struct { + ID github_com_cayleygraph_quad.IRI `protobuf:"bytes,1,opt,name=id,proto3,casttype=github.com/cayleygraph/quad.IRI" json:"id,omitempty" quad:"@id"` + CreatedAt *time.Time `protobuf:"bytes,3,opt,name=created_at,json=createdAt,proto3,stdtime" json:"created_at,omitempty" quad:"schema:createdAt,optional"` + UpdatedAt *time.Time `protobuf:"bytes,4,opt,name=updated_at,json=updatedAt,proto3,stdtime" json:"updated_at,omitempty" quad:"schema:updatedAt,optional"` + LocalID string `protobuf:"bytes,5,opt,name=local_id,json=localId,proto3" json:"local_id,omitempty" quad:"schema:localId,optional"` + Kind Topic_Kind `protobuf:"varint,10,opt,name=kind,proto3,enum=depviz.model.Topic_Kind" json:"kind,omitempty" quad:"schema:kind,optional"` + Title string `protobuf:"bytes,11,opt,name=title,proto3" json:"title,omitempty" quad:"schema:title,optional"` + Driver Driver `protobuf:"varint,12,opt,name=driver,proto3,enum=depviz.model.Driver" json:"driver,omitempty" quad:"schema:driver,optional"` + Color string `protobuf:"bytes,13,opt,name=color,proto3" json:"color,omitempty" quad:"schema:color,optional"` + Description string `protobuf:"bytes,14,opt,name=description,proto3" json:"description,omitempty" quad:"schema:description,optional"` + // relationships + HasOwner github_com_cayleygraph_quad.IRI `protobuf:"bytes,100,opt,name=has_owner,json=hasOwner,proto3,casttype=github.com/cayleygraph/quad.IRI" json:"has_owner,omitempty" quad:"hasOwner,optional"` +} + +func (m *Topic) Reset() { *m = Topic{} } +func (m *Topic) String() string { return proto.CompactTextString(m) } +func (*Topic) ProtoMessage() {} +func (*Topic) Descriptor() ([]byte, []int) { + return fileDescriptor_106647ce772da30c, []int{2} +} +func (m *Topic) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Topic) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Topic.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Topic) XXX_Merge(src proto.Message) { + xxx_messageInfo_Topic.Merge(m, src) +} +func (m *Topic) XXX_Size() int { + return m.Size() +} +func (m *Topic) XXX_DiscardUnknown() { + xxx_messageInfo_Topic.DiscardUnknown(m) +} + +var xxx_messageInfo_Topic proto.InternalMessageInfo + +func init() { + proto.RegisterEnum("depviz.model.Driver", Driver_name, Driver_value) + golang_proto.RegisterEnum("depviz.model.Driver", Driver_name, Driver_value) + proto.RegisterEnum("depviz.model.Owner_Kind", Owner_Kind_name, Owner_Kind_value) + golang_proto.RegisterEnum("depviz.model.Owner_Kind", Owner_Kind_name, Owner_Kind_value) + proto.RegisterEnum("depviz.model.Owner_ForkStatus", Owner_ForkStatus_name, Owner_ForkStatus_value) + golang_proto.RegisterEnum("depviz.model.Owner_ForkStatus", Owner_ForkStatus_name, Owner_ForkStatus_value) + proto.RegisterEnum("depviz.model.Task_Kind", Task_Kind_name, Task_Kind_value) + golang_proto.RegisterEnum("depviz.model.Task_Kind", Task_Kind_name, Task_Kind_value) + proto.RegisterEnum("depviz.model.Task_State", Task_State_name, Task_State_value) + golang_proto.RegisterEnum("depviz.model.Task_State", Task_State_name, Task_State_value) + proto.RegisterEnum("depviz.model.Topic_Kind", Topic_Kind_name, Topic_Kind_value) + golang_proto.RegisterEnum("depviz.model.Topic_Kind", Topic_Kind_name, Topic_Kind_value) + proto.RegisterType((*Owner)(nil), "depviz.model.Owner") + golang_proto.RegisterType((*Owner)(nil), "depviz.model.Owner") + proto.RegisterType((*Task)(nil), "depviz.model.Task") + golang_proto.RegisterType((*Task)(nil), "depviz.model.Task") + proto.RegisterType((*Topic)(nil), "depviz.model.Topic") + golang_proto.RegisterType((*Topic)(nil), "depviz.model.Topic") +} + +func init() { proto.RegisterFile("dvmodel.proto", fileDescriptor_106647ce772da30c) } +func init() { golang_proto.RegisterFile("dvmodel.proto", fileDescriptor_106647ce772da30c) } + +var fileDescriptor_106647ce772da30c = []byte{ + // 1448 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x58, 0x41, 0x6f, 0xdb, 0xc6, + 0x12, 0x16, 0x15, 0xc9, 0xb1, 0x56, 0x72, 0xcc, 0x6c, 0xf2, 0xf0, 0xf8, 0xfc, 0xde, 0x13, 0x1d, + 0xa5, 0x6d, 0x8c, 0x22, 0x91, 0xd0, 0x14, 0x48, 0x81, 0x14, 0x45, 0x62, 0xd9, 0x4d, 0xa2, 0xd6, + 0xa9, 0x0d, 0xc5, 0x46, 0x81, 0xb4, 0x05, 0xb1, 0x12, 0xd7, 0xd4, 0xd6, 0x24, 0x97, 0xd9, 0x5d, + 0xda, 0x70, 0x7e, 0x45, 0x7e, 0x46, 0x7f, 0x41, 0xcf, 0x3d, 0xfa, 0xd0, 0x43, 0x8e, 0x3d, 0xb1, + 0x8d, 0xfd, 0x0f, 0x74, 0x2a, 0x7a, 0x2a, 0x76, 0x49, 0x8a, 0xa4, 0xed, 0xa4, 0x51, 0xe0, 0x26, + 0x97, 0xde, 0xb4, 0x33, 0xf3, 0x7d, 0x33, 0xbb, 0xc3, 0xfd, 0x38, 0x14, 0x98, 0xb3, 0x77, 0x3d, + 0x6a, 0x63, 0xb7, 0x1d, 0x30, 0x2a, 0x28, 0x6c, 0xd8, 0x38, 0xd8, 0x25, 0x4f, 0xdb, 0xca, 0xb6, + 0xf0, 0xa9, 0x43, 0xc4, 0x28, 0x1c, 0xb4, 0x87, 0xd4, 0xeb, 0x38, 0xd4, 0x45, 0xbe, 0xd3, 0x51, + 0x61, 0x83, 0x70, 0xbb, 0x13, 0x88, 0xfd, 0x00, 0xf3, 0x8e, 0x20, 0x1e, 0xe6, 0x02, 0x79, 0x41, + 0xf6, 0x2b, 0xa6, 0x5a, 0xb8, 0x51, 0x00, 0x3b, 0x34, 0x83, 0xca, 0x95, 0x5a, 0xa8, 0x5f, 0x71, + 0x78, 0xeb, 0xe7, 0x1a, 0xa8, 0xae, 0xef, 0xf9, 0x98, 0xc1, 0xfb, 0xa0, 0x4c, 0x6c, 0x43, 0x5b, + 0xd4, 0x96, 0x6a, 0xdd, 0x4f, 0x0e, 0x23, 0xb3, 0xdc, 0x5b, 0x1d, 0x47, 0x26, 0x78, 0x12, 0x22, + 0xfb, 0x76, 0xeb, 0x2e, 0xb1, 0x5b, 0x7f, 0x44, 0xa6, 0x99, 0x23, 0x1f, 0xa2, 0x7d, 0x17, 0xef, + 0x3b, 0x0c, 0x05, 0xa3, 0x8e, 0x0c, 0x6a, 0xf7, 0xfa, 0xbd, 0x7e, 0x99, 0xd8, 0xd0, 0x01, 0x60, + 0xc8, 0x30, 0x12, 0xd8, 0xb6, 0x90, 0x30, 0xce, 0x2d, 0x6a, 0x4b, 0xf5, 0x9b, 0x0b, 0x6d, 0x87, + 0x52, 0xc7, 0xc5, 0xed, 0xb4, 0x9a, 0xf6, 0x66, 0x5a, 0x77, 0xf7, 0xfa, 0x41, 0x64, 0x6a, 0xe3, + 0xc8, 0x5c, 0x8c, 0x53, 0xf1, 0xe1, 0x08, 0x7b, 0xe8, 0x76, 0x42, 0xb1, 0x2c, 0xae, 0xd3, 0x40, + 0x10, 0xea, 0x23, 0xb7, 0xf5, 0xec, 0x57, 0x53, 0xeb, 0xd7, 0x26, 0x0e, 0x99, 0x28, 0x0c, 0xec, + 0x34, 0x51, 0xe5, 0x0d, 0x13, 0x25, 0x14, 0x27, 0x13, 0x4d, 0x1c, 0xf0, 0x01, 0x98, 0x75, 0xe9, + 0x10, 0xb9, 0x16, 0xb1, 0x8d, 0xaa, 0x3a, 0xa0, 0x1b, 0x87, 0x91, 0x79, 0x7e, 0x4d, 0xda, 0xd4, + 0x29, 0x35, 0x0b, 0x8c, 0x2a, 0xb6, 0x67, 0x67, 0x7c, 0xfd, 0xf3, 0x89, 0x09, 0x3e, 0x04, 0x95, + 0x1d, 0xe2, 0xdb, 0x06, 0x58, 0xd4, 0x96, 0x2e, 0xdc, 0x34, 0xda, 0xf9, 0xbe, 0xb7, 0x55, 0x1f, + 0xda, 0x5f, 0x12, 0xdf, 0xee, 0x9a, 0xe3, 0xc8, 0xfc, 0x6f, 0x81, 0x54, 0xc2, 0x72, 0x8c, 0x8a, + 0x06, 0xae, 0x00, 0xc0, 0x47, 0x94, 0x09, 0xcb, 0x47, 0x1e, 0x36, 0xea, 0xaa, 0xb4, 0xf7, 0x4e, + 0xec, 0x50, 0x85, 0x7c, 0x85, 0x3c, 0x9c, 0xc3, 0xd7, 0x26, 0x46, 0x78, 0x17, 0xd4, 0xb6, 0x43, + 0xd7, 0x8d, 0x39, 0x1a, 0x8a, 0xe3, 0xea, 0x38, 0x32, 0xcd, 0x02, 0x87, 0x8c, 0x38, 0x46, 0x31, + 0x9b, 0xda, 0xe0, 0x3a, 0x98, 0xb1, 0x19, 0xd9, 0xc5, 0xcc, 0x98, 0x53, 0xfb, 0xba, 0x5c, 0xdc, + 0xd7, 0xaa, 0xf2, 0x75, 0xaf, 0x8c, 0x23, 0xf3, 0xff, 0x05, 0xd2, 0x18, 0x94, 0xa3, 0x4c, 0x68, + 0xe0, 0x1d, 0x30, 0x3b, 0xa2, 0x1e, 0x0e, 0x90, 0x83, 0x8d, 0x0b, 0x2f, 0xa9, 0x28, 0x0d, 0xc8, + 0x57, 0x94, 0xda, 0xe0, 0x03, 0x50, 0xb7, 0x31, 0x1f, 0x32, 0xa2, 0x7c, 0xc6, 0xbc, 0xe2, 0xf8, + 0x60, 0x1c, 0x99, 0xad, 0x62, 0x01, 0x59, 0x4c, 0x8e, 0x26, 0x0f, 0x85, 0xdb, 0xa0, 0xbe, 0x4d, + 0xd9, 0x8e, 0xc5, 0x05, 0x12, 0x21, 0x37, 0x74, 0xb5, 0xc1, 0xe6, 0x69, 0x8d, 0xbb, 0x47, 0xd9, + 0xce, 0x23, 0x15, 0xd5, 0x7d, 0x7f, 0x1c, 0x99, 0x57, 0x8a, 0xe7, 0x37, 0x71, 0xe6, 0x12, 0x81, + 0xcc, 0x0a, 0x37, 0x00, 0x40, 0xbb, 0x48, 0x20, 0x66, 0x85, 0xcc, 0x35, 0x2e, 0xaa, 0x82, 0x3f, + 0x3a, 0x8c, 0xcc, 0xda, 0xb2, 0xb2, 0x6e, 0xf5, 0xd7, 0x4e, 0xf4, 0x35, 0x8e, 0xdf, 0x62, 0x6e, + 0xbe, 0xaf, 0x13, 0x23, 0xfc, 0x16, 0xd4, 0x46, 0x88, 0x5b, 0x54, 0x16, 0x67, 0xd8, 0x8a, 0xf0, + 0xce, 0x38, 0x32, 0x8d, 0x98, 0x63, 0x84, 0xb8, 0x2a, 0x3b, 0xc3, 0xbe, 0xce, 0xfd, 0x9e, 0x4d, + 0x61, 0xad, 0x2d, 0x50, 0x91, 0x4f, 0x2a, 0x9c, 0x07, 0xf5, 0x2d, 0x7f, 0xc7, 0xa7, 0x7b, 0xbe, + 0x5c, 0xea, 0x25, 0x38, 0x0b, 0x2a, 0x5b, 0x1c, 0x33, 0x5d, 0x83, 0x3a, 0x68, 0xac, 0x33, 0x07, + 0xf9, 0xe4, 0x29, 0x92, 0x29, 0xf4, 0xb2, 0xf4, 0x6d, 0x62, 0xe4, 0xe9, 0xe7, 0xe4, 0xaf, 0x3e, + 0x0e, 0xa8, 0x5e, 0x81, 0x0d, 0x30, 0xbb, 0xc1, 0xe8, 0x2e, 0xb1, 0x31, 0xd3, 0xab, 0xad, 0xcf, + 0x00, 0xc8, 0xce, 0x11, 0xfe, 0x0b, 0x5c, 0x4c, 0xc8, 0x33, 0xa3, 0x5e, 0x82, 0x00, 0xcc, 0xf4, + 0xb8, 0xb4, 0xe8, 0x9a, 0x84, 0xf7, 0xf8, 0x23, 0x1a, 0xb2, 0x21, 0xd6, 0xcb, 0xad, 0x1f, 0x2f, + 0x81, 0xca, 0x26, 0xe2, 0x3b, 0xef, 0x56, 0xcd, 0x4a, 0x6f, 0x4b, 0xcd, 0x4a, 0xef, 0x56, 0xcd, + 0xd6, 0x0a, 0x6a, 0xf6, 0xef, 0xe2, 0xa5, 0x90, 0x6d, 0x98, 0x4a, 0xcc, 0x6e, 0x81, 0xaa, 0x20, + 0xc2, 0x4d, 0x75, 0x6c, 0x71, 0x1c, 0x99, 0xff, 0x2b, 0xa0, 0x94, 0x37, 0x07, 0x8b, 0xc3, 0x8f, + 0xdf, 0xf5, 0xc6, 0x9b, 0xdf, 0xf5, 0x33, 0xd7, 0xb1, 0x6f, 0xc0, 0x8c, 0x1d, 0x62, 0x8b, 0xfa, + 0x4a, 0xc5, 0x5e, 0xdd, 0xcf, 0xa5, 0xe4, 0xed, 0x54, 0xdc, 0xb3, 0x1d, 0xe2, 0x75, 0xff, 0x58, + 0x2f, 0xab, 0xca, 0x08, 0x3d, 0xd0, 0x18, 0x52, 0x2f, 0x70, 0x71, 0xf2, 0xc8, 0xcc, 0xff, 0x65, + 0x8a, 0x76, 0x92, 0xa2, 0x78, 0x30, 0x13, 0x92, 0x13, 0x0f, 0x4d, 0x3d, 0xe7, 0x82, 0x1b, 0xa0, + 0x2a, 0x35, 0x10, 0x27, 0x12, 0x68, 0x9c, 0xd2, 0x6d, 0x79, 0x41, 0xf1, 0x29, 0x8d, 0x53, 0xb8, + 0x7c, 0xe3, 0x94, 0x01, 0xae, 0x82, 0x1a, 0xe1, 0x96, 0x4b, 0x87, 0x3b, 0xd8, 0x56, 0x8a, 0x37, + 0xdb, 0xbd, 0x96, 0x54, 0x58, 0x94, 0x7a, 0xc2, 0xd7, 0x54, 0x50, 0x5e, 0xea, 0x53, 0x1b, 0xec, + 0x81, 0x86, 0x1f, 0x7a, 0xd6, 0x90, 0x7a, 0x1e, 0xf6, 0x05, 0x37, 0xe0, 0xa2, 0xb6, 0x54, 0x3d, + 0xa5, 0xff, 0x7e, 0xe8, 0xad, 0x24, 0x31, 0xf9, 0xfe, 0xe7, 0xcc, 0xf0, 0x1e, 0x90, 0x4b, 0x2b, + 0x0c, 0x76, 0xa9, 0xc0, 0xdc, 0xb8, 0xa4, 0x98, 0x4e, 0x6a, 0xb9, 0x1f, 0x7a, 0x5b, 0x71, 0x48, + 0x5e, 0xcb, 0x33, 0x2b, 0x5c, 0x03, 0x73, 0x92, 0xc7, 0xa6, 0x7b, 0x7e, 0xcc, 0x74, 0x59, 0x31, + 0x5d, 0x1b, 0x47, 0xe6, 0xd5, 0xe3, 0x4c, 0xab, 0x69, 0x50, 0x8e, 0xab, 0x91, 0xb7, 0x43, 0x0b, + 0x00, 0xa9, 0xe3, 0x28, 0x14, 0x23, 0x9a, 0x0a, 0xf9, 0xdd, 0x71, 0x64, 0xfe, 0x67, 0x22, 0xe4, + 0xcb, 0xca, 0x35, 0x9d, 0x92, 0xd7, 0x26, 0xb8, 0xe2, 0x8b, 0x02, 0x9f, 0xf1, 0x8b, 0x02, 0x8e, + 0xc0, 0x9c, 0x64, 0xf7, 0x88, 0x8b, 0xb9, 0xa0, 0x3e, 0x36, 0xb6, 0x55, 0x86, 0x95, 0xec, 0x29, + 0x19, 0x21, 0xfe, 0x30, 0xf5, 0x4e, 0x97, 0xa5, 0x91, 0x87, 0x42, 0x0c, 0x1a, 0xea, 0xa0, 0x38, + 0x27, 0x8e, 0x8f, 0xb1, 0xe1, 0x2c, 0x9e, 0x5b, 0xaa, 0x75, 0xbb, 0x99, 0xfa, 0xc8, 0x2d, 0x27, + 0xce, 0xe9, 0xf2, 0xd4, 0x73, 0xc8, 0x34, 0x0d, 0xc3, 0xbb, 0x04, 0xef, 0x61, 0x66, 0x8c, 0x4e, + 0x49, 0xd3, 0x4f, 0x9c, 0xd3, 0xa7, 0x49, 0x91, 0x69, 0x57, 0x5c, 0x34, 0xc0, 0xae, 0x41, 0x54, + 0x8e, 0x62, 0x57, 0xd6, 0xa4, 0x67, 0xfa, 0xae, 0x28, 0x18, 0x74, 0xc1, 0x3c, 0xe1, 0x96, 0x8d, + 0x03, 0xec, 0xdb, 0xc4, 0x77, 0xa4, 0x44, 0x7d, 0xaf, 0x72, 0xac, 0x66, 0xea, 0x46, 0xf8, 0x6a, + 0xea, 0xcf, 0x6b, 0xd0, 0xeb, 0x24, 0x9a, 0x2b, 0x60, 0xe1, 0x00, 0xd4, 0x09, 0xb7, 0x06, 0xf2, + 0xaa, 0x13, 0xdf, 0x31, 0x76, 0x54, 0xa6, 0xe5, 0x71, 0x64, 0x2e, 0xa4, 0x99, 0xba, 0x89, 0x6f, + 0xba, 0x34, 0x20, 0x03, 0x26, 0x3b, 0x62, 0xd8, 0x55, 0xaf, 0xd0, 0x3d, 0x22, 0x46, 0x86, 0x7b, + 0x72, 0x47, 0xfd, 0xd8, 0xff, 0x35, 0x11, 0xa3, 0xa9, 0x77, 0x94, 0xc3, 0xc2, 0xef, 0x00, 0x20, + 0xdc, 0x0a, 0x10, 0x13, 0x16, 0xdd, 0x36, 0xbc, 0xe3, 0xed, 0x21, 0x7c, 0x03, 0x31, 0xb1, 0xbe, + 0x3d, 0x65, 0x7b, 0x52, 0x18, 0x7c, 0x0c, 0x64, 0xab, 0x14, 0xbf, 0xe1, 0x9f, 0x0d, 0xf9, 0xf9, + 0x11, 0x52, 0xb8, 0xd6, 0xe0, 0x65, 0x93, 0x5b, 0x0d, 0x54, 0x7b, 0x9c, 0x87, 0x38, 0x1e, 0xdd, + 0x1e, 0x62, 0xe6, 0xe0, 0x3e, 0x7e, 0x12, 0x62, 0x2e, 0xf4, 0x32, 0x9c, 0x03, 0xb5, 0xc9, 0x4d, + 0x8b, 0xe7, 0xb7, 0xcf, 0x03, 0x32, 0xd4, 0x2b, 0x12, 0xf5, 0x48, 0x50, 0xb6, 0xaf, 0x57, 0xa5, + 0x71, 0x05, 0x31, 0x5b, 0x9f, 0x69, 0x75, 0xa4, 0x51, 0x6a, 0xbc, 0x0e, 0x1a, 0x49, 0x12, 0xb5, + 0x8e, 0xe7, 0xc3, 0xf5, 0x00, 0xfb, 0xba, 0x26, 0xc7, 0xb8, 0x15, 0x97, 0x72, 0x6c, 0xeb, 0xe5, + 0xd6, 0xc1, 0x0c, 0xa8, 0x6e, 0xd2, 0x80, 0x0c, 0xff, 0xf9, 0x0e, 0x7d, 0xe7, 0xdf, 0xa1, 0xaa, + 0x0f, 0x6f, 0x65, 0x74, 0xcb, 0x06, 0xae, 0xc6, 0xd9, 0x0c, 0x5c, 0xb7, 0x40, 0x75, 0x48, 0x5d, + 0x1a, 0x0f, 0x70, 0xa7, 0x15, 0xa2, 0xbc, 0xf9, 0x42, 0x94, 0xe1, 0xf8, 0x0c, 0x79, 0xe1, 0xcd, + 0x67, 0xc8, 0xbf, 0xf7, 0xab, 0xab, 0xf5, 0x8a, 0xbb, 0xab, 0x84, 0x5d, 0xd7, 0x3e, 0xec, 0x81, + 0x99, 0xf8, 0xe0, 0xe0, 0x45, 0x30, 0x97, 0x44, 0xc5, 0x86, 0xf8, 0xd3, 0xe9, 0x3e, 0x11, 0x0f, + 0xc2, 0x41, 0x7c, 0xff, 0xee, 0x13, 0xb1, 0x86, 0x06, 0x7a, 0x59, 0xfe, 0xde, 0x64, 0xd8, 0x75, + 0x69, 0x7c, 0xb7, 0xbf, 0x20, 0x0c, 0xe9, 0x95, 0x6e, 0xef, 0xe0, 0x45, 0xb3, 0xf4, 0xfb, 0x8b, + 0x66, 0xe9, 0x87, 0xc3, 0x66, 0xe9, 0xe0, 0xb0, 0xa9, 0x3d, 0x3f, 0x6c, 0x6a, 0xbf, 0x1d, 0x36, + 0xb5, 0x67, 0x47, 0xcd, 0xd2, 0x4f, 0x47, 0x4d, 0xed, 0xf9, 0x51, 0xb3, 0xf4, 0xcb, 0x51, 0xb3, + 0xf4, 0xd8, 0xf4, 0x68, 0xe8, 0xb6, 0x09, 0xed, 0xc4, 0x0d, 0xec, 0x10, 0x5f, 0x60, 0xe6, 0x23, + 0xb7, 0x93, 0xfc, 0xd1, 0x35, 0x98, 0x51, 0x17, 0xe1, 0xe3, 0x3f, 0x03, 0x00, 0x00, 0xff, 0xff, + 0xbf, 0x23, 0x07, 0x76, 0xfa, 0x12, 0x00, 0x00, +} + +func (m *Owner) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Owner) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Owner) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.HasOwner) > 0 { + i -= len(m.HasOwner) + copy(dAtA[i:], m.HasOwner) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.HasOwner))) + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0xa2 + } + if len(m.AvatarURL) > 0 { + i -= len(m.AvatarURL) + copy(dAtA[i:], m.AvatarURL) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.AvatarURL))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x8a + } + if m.ForkStatus != 0 { + i = encodeVarintDvmodel(dAtA, i, uint64(m.ForkStatus)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x80 + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x7a + } + if len(m.Homepage) > 0 { + i -= len(m.Homepage) + copy(dAtA[i:], m.Homepage) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.Homepage))) + i-- + dAtA[i] = 0x72 + } + if m.Driver != 0 { + i = encodeVarintDvmodel(dAtA, i, uint64(m.Driver)) + i-- + dAtA[i] = 0x68 + } + if len(m.FullName) > 0 { + i -= len(m.FullName) + copy(dAtA[i:], m.FullName) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.FullName))) + i-- + dAtA[i] = 0x62 + } + if len(m.ShortName) > 0 { + i -= len(m.ShortName) + copy(dAtA[i:], m.ShortName) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.ShortName))) + i-- + dAtA[i] = 0x5a + } + if m.Kind != 0 { + i = encodeVarintDvmodel(dAtA, i, uint64(m.Kind)) + i-- + dAtA[i] = 0x50 + } + if len(m.LocalID) > 0 { + i -= len(m.LocalID) + copy(dAtA[i:], m.LocalID) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.LocalID))) + i-- + dAtA[i] = 0x2a + } + if m.UpdatedAt != nil { + n1, err1 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.UpdatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.UpdatedAt):]) + if err1 != nil { + return 0, err1 + } + i -= n1 + i = encodeVarintDvmodel(dAtA, i, uint64(n1)) + i-- + dAtA[i] = 0x22 + } + if m.CreatedAt != nil { + n2, err2 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.CreatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.CreatedAt):]) + if err2 != nil { + return 0, err2 + } + i -= n2 + i = encodeVarintDvmodel(dAtA, i, uint64(n2)) + i-- + dAtA[i] = 0x1a + } + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Task) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Task) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Task) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.HasPart) > 0 { + for iNdEx := len(m.HasPart) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.HasPart[iNdEx]) + copy(dAtA[i:], m.HasPart[iNdEx]) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.HasPart[iNdEx]))) + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0xf2 + } + } + if len(m.IsPartOf) > 0 { + for iNdEx := len(m.IsPartOf) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.IsPartOf[iNdEx]) + copy(dAtA[i:], m.IsPartOf[iNdEx]) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.IsPartOf[iNdEx]))) + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0xea + } + } + if len(m.IsRelatedWith) > 0 { + for iNdEx := len(m.IsRelatedWith) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.IsRelatedWith[iNdEx]) + copy(dAtA[i:], m.IsRelatedWith[iNdEx]) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.IsRelatedWith[iNdEx]))) + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0xe2 + } + } + if len(m.IsBlocking) > 0 { + for iNdEx := len(m.IsBlocking) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.IsBlocking[iNdEx]) + copy(dAtA[i:], m.IsBlocking[iNdEx]) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.IsBlocking[iNdEx]))) + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0xda + } + } + if len(m.IsDependingOn) > 0 { + for iNdEx := len(m.IsDependingOn) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.IsDependingOn[iNdEx]) + copy(dAtA[i:], m.IsDependingOn[iNdEx]) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.IsDependingOn[iNdEx]))) + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0xd2 + } + } + if len(m.HasLabel) > 0 { + for iNdEx := len(m.HasLabel) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.HasLabel[iNdEx]) + copy(dAtA[i:], m.HasLabel[iNdEx]) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.HasLabel[iNdEx]))) + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0xca + } + } + if len(m.HasReviewer) > 0 { + for iNdEx := len(m.HasReviewer) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.HasReviewer[iNdEx]) + copy(dAtA[i:], m.HasReviewer[iNdEx]) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.HasReviewer[iNdEx]))) + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0xc2 + } + } + if len(m.HasAssignee) > 0 { + for iNdEx := len(m.HasAssignee) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.HasAssignee[iNdEx]) + copy(dAtA[i:], m.HasAssignee[iNdEx]) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.HasAssignee[iNdEx]))) + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0xba + } + } + if len(m.HasMilestone) > 0 { + i -= len(m.HasMilestone) + copy(dAtA[i:], m.HasMilestone) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.HasMilestone))) + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0xb2 + } + if len(m.HasOwner) > 0 { + i -= len(m.HasOwner) + copy(dAtA[i:], m.HasOwner) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.HasOwner))) + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0xaa + } + if len(m.HasAuthor) > 0 { + i -= len(m.HasAuthor) + copy(dAtA[i:], m.HasAuthor) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.HasAuthor))) + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0xa2 + } + if m.NumDownvotes != 0 { + i = encodeVarintDvmodel(dAtA, i, uint64(m.NumDownvotes)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa0 + } + if m.NumUpvotes != 0 { + i = encodeVarintDvmodel(dAtA, i, uint64(m.NumUpvotes)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x98 + } + if m.NumComments != 0 { + i = encodeVarintDvmodel(dAtA, i, uint64(m.NumComments)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x90 + } + if m.IsLocked { + i-- + if m.IsLocked { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x88 + } + if m.State != 0 { + i = encodeVarintDvmodel(dAtA, i, uint64(m.State)) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x80 + } + if m.CompletedAt != nil { + n3, err3 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.CompletedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.CompletedAt):]) + if err3 != nil { + return 0, err3 + } + i -= n3 + i = encodeVarintDvmodel(dAtA, i, uint64(n3)) + i-- + dAtA[i] = 0x7a + } + if m.DueOn != nil { + n4, err4 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.DueOn, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.DueOn):]) + if err4 != nil { + return 0, err4 + } + i -= n4 + i = encodeVarintDvmodel(dAtA, i, uint64(n4)) + i-- + dAtA[i] = 0x72 + } + if m.Driver != 0 { + i = encodeVarintDvmodel(dAtA, i, uint64(m.Driver)) + i-- + dAtA[i] = 0x68 + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x62 + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0x5a + } + if m.Kind != 0 { + i = encodeVarintDvmodel(dAtA, i, uint64(m.Kind)) + i-- + dAtA[i] = 0x50 + } + if len(m.LocalID) > 0 { + i -= len(m.LocalID) + copy(dAtA[i:], m.LocalID) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.LocalID))) + i-- + dAtA[i] = 0x2a + } + n5, err5 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.UpdatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.UpdatedAt):]) + if err5 != nil { + return 0, err5 + } + i -= n5 + i = encodeVarintDvmodel(dAtA, i, uint64(n5)) + i-- + dAtA[i] = 0x22 + n6, err6 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.CreatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt):]) + if err6 != nil { + return 0, err6 + } + i -= n6 + i = encodeVarintDvmodel(dAtA, i, uint64(n6)) + i-- + dAtA[i] = 0x1a + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *Topic) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Topic) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Topic) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.HasOwner) > 0 { + i -= len(m.HasOwner) + copy(dAtA[i:], m.HasOwner) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.HasOwner))) + i-- + dAtA[i] = 0x6 + i-- + dAtA[i] = 0xa2 + } + if len(m.Description) > 0 { + i -= len(m.Description) + copy(dAtA[i:], m.Description) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.Description))) + i-- + dAtA[i] = 0x72 + } + if len(m.Color) > 0 { + i -= len(m.Color) + copy(dAtA[i:], m.Color) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.Color))) + i-- + dAtA[i] = 0x6a + } + if m.Driver != 0 { + i = encodeVarintDvmodel(dAtA, i, uint64(m.Driver)) + i-- + dAtA[i] = 0x60 + } + if len(m.Title) > 0 { + i -= len(m.Title) + copy(dAtA[i:], m.Title) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.Title))) + i-- + dAtA[i] = 0x5a + } + if m.Kind != 0 { + i = encodeVarintDvmodel(dAtA, i, uint64(m.Kind)) + i-- + dAtA[i] = 0x50 + } + if len(m.LocalID) > 0 { + i -= len(m.LocalID) + copy(dAtA[i:], m.LocalID) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.LocalID))) + i-- + dAtA[i] = 0x2a + } + if m.UpdatedAt != nil { + n7, err7 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.UpdatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.UpdatedAt):]) + if err7 != nil { + return 0, err7 + } + i -= n7 + i = encodeVarintDvmodel(dAtA, i, uint64(n7)) + i-- + dAtA[i] = 0x22 + } + if m.CreatedAt != nil { + n8, err8 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.CreatedAt, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.CreatedAt):]) + if err8 != nil { + return 0, err8 + } + i -= n8 + i = encodeVarintDvmodel(dAtA, i, uint64(n8)) + i-- + dAtA[i] = 0x1a + } + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintDvmodel(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintDvmodel(dAtA []byte, offset int, v uint64) int { + offset -= sovDvmodel(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Owner) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovDvmodel(uint64(l)) + } + if m.CreatedAt != nil { + l = github_com_gogo_protobuf_types.SizeOfStdTime(*m.CreatedAt) + n += 1 + l + sovDvmodel(uint64(l)) + } + if m.UpdatedAt != nil { + l = github_com_gogo_protobuf_types.SizeOfStdTime(*m.UpdatedAt) + n += 1 + l + sovDvmodel(uint64(l)) + } + l = len(m.LocalID) + if l > 0 { + n += 1 + l + sovDvmodel(uint64(l)) + } + if m.Kind != 0 { + n += 1 + sovDvmodel(uint64(m.Kind)) + } + l = len(m.ShortName) + if l > 0 { + n += 1 + l + sovDvmodel(uint64(l)) + } + l = len(m.FullName) + if l > 0 { + n += 1 + l + sovDvmodel(uint64(l)) + } + if m.Driver != 0 { + n += 1 + sovDvmodel(uint64(m.Driver)) + } + l = len(m.Homepage) + if l > 0 { + n += 1 + l + sovDvmodel(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovDvmodel(uint64(l)) + } + if m.ForkStatus != 0 { + n += 2 + sovDvmodel(uint64(m.ForkStatus)) + } + l = len(m.AvatarURL) + if l > 0 { + n += 2 + l + sovDvmodel(uint64(l)) + } + l = len(m.HasOwner) + if l > 0 { + n += 2 + l + sovDvmodel(uint64(l)) + } + return n +} + +func (m *Task) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovDvmodel(uint64(l)) + } + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.CreatedAt) + n += 1 + l + sovDvmodel(uint64(l)) + l = github_com_gogo_protobuf_types.SizeOfStdTime(m.UpdatedAt) + n += 1 + l + sovDvmodel(uint64(l)) + l = len(m.LocalID) + if l > 0 { + n += 1 + l + sovDvmodel(uint64(l)) + } + if m.Kind != 0 { + n += 1 + sovDvmodel(uint64(m.Kind)) + } + l = len(m.Title) + if l > 0 { + n += 1 + l + sovDvmodel(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovDvmodel(uint64(l)) + } + if m.Driver != 0 { + n += 1 + sovDvmodel(uint64(m.Driver)) + } + if m.DueOn != nil { + l = github_com_gogo_protobuf_types.SizeOfStdTime(*m.DueOn) + n += 1 + l + sovDvmodel(uint64(l)) + } + if m.CompletedAt != nil { + l = github_com_gogo_protobuf_types.SizeOfStdTime(*m.CompletedAt) + n += 1 + l + sovDvmodel(uint64(l)) + } + if m.State != 0 { + n += 2 + sovDvmodel(uint64(m.State)) + } + if m.IsLocked { + n += 3 + } + if m.NumComments != 0 { + n += 2 + sovDvmodel(uint64(m.NumComments)) + } + if m.NumUpvotes != 0 { + n += 2 + sovDvmodel(uint64(m.NumUpvotes)) + } + if m.NumDownvotes != 0 { + n += 2 + sovDvmodel(uint64(m.NumDownvotes)) + } + l = len(m.HasAuthor) + if l > 0 { + n += 2 + l + sovDvmodel(uint64(l)) + } + l = len(m.HasOwner) + if l > 0 { + n += 2 + l + sovDvmodel(uint64(l)) + } + l = len(m.HasMilestone) + if l > 0 { + n += 2 + l + sovDvmodel(uint64(l)) + } + if len(m.HasAssignee) > 0 { + for _, s := range m.HasAssignee { + l = len(s) + n += 2 + l + sovDvmodel(uint64(l)) + } + } + if len(m.HasReviewer) > 0 { + for _, s := range m.HasReviewer { + l = len(s) + n += 2 + l + sovDvmodel(uint64(l)) + } + } + if len(m.HasLabel) > 0 { + for _, s := range m.HasLabel { + l = len(s) + n += 2 + l + sovDvmodel(uint64(l)) + } + } + if len(m.IsDependingOn) > 0 { + for _, s := range m.IsDependingOn { + l = len(s) + n += 2 + l + sovDvmodel(uint64(l)) + } + } + if len(m.IsBlocking) > 0 { + for _, s := range m.IsBlocking { + l = len(s) + n += 2 + l + sovDvmodel(uint64(l)) + } + } + if len(m.IsRelatedWith) > 0 { + for _, s := range m.IsRelatedWith { + l = len(s) + n += 2 + l + sovDvmodel(uint64(l)) + } + } + if len(m.IsPartOf) > 0 { + for _, s := range m.IsPartOf { + l = len(s) + n += 2 + l + sovDvmodel(uint64(l)) + } + } + if len(m.HasPart) > 0 { + for _, s := range m.HasPart { + l = len(s) + n += 2 + l + sovDvmodel(uint64(l)) + } + } + return n +} + +func (m *Topic) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ID) + if l > 0 { + n += 1 + l + sovDvmodel(uint64(l)) + } + if m.CreatedAt != nil { + l = github_com_gogo_protobuf_types.SizeOfStdTime(*m.CreatedAt) + n += 1 + l + sovDvmodel(uint64(l)) + } + if m.UpdatedAt != nil { + l = github_com_gogo_protobuf_types.SizeOfStdTime(*m.UpdatedAt) + n += 1 + l + sovDvmodel(uint64(l)) + } + l = len(m.LocalID) + if l > 0 { + n += 1 + l + sovDvmodel(uint64(l)) + } + if m.Kind != 0 { + n += 1 + sovDvmodel(uint64(m.Kind)) + } + l = len(m.Title) + if l > 0 { + n += 1 + l + sovDvmodel(uint64(l)) + } + if m.Driver != 0 { + n += 1 + sovDvmodel(uint64(m.Driver)) + } + l = len(m.Color) + if l > 0 { + n += 1 + l + sovDvmodel(uint64(l)) + } + l = len(m.Description) + if l > 0 { + n += 1 + l + sovDvmodel(uint64(l)) + } + l = len(m.HasOwner) + if l > 0 { + n += 2 + l + sovDvmodel(uint64(l)) + } + return n +} + +func sovDvmodel(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozDvmodel(x uint64) (n int) { + return sovDvmodel(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Owner) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Owner: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Owner: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = github_com_cayleygraph_quad.IRI(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CreatedAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CreatedAt == nil { + m.CreatedAt = new(time.Time) + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(m.CreatedAt, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpdatedAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.UpdatedAt == nil { + m.UpdatedAt = new(time.Time) + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(m.UpdatedAt, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LocalID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LocalID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Kind", wireType) + } + m.Kind = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Kind |= Owner_Kind(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ShortName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ShortName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FullName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FullName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 13: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Driver", wireType) + } + m.Driver = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Driver |= Driver(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 14: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Homepage", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Homepage = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 15: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 16: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ForkStatus", wireType) + } + m.ForkStatus = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ForkStatus |= Owner_ForkStatus(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 17: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AvatarURL", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AvatarURL = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 100: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HasOwner", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.HasOwner = github_com_cayleygraph_quad.IRI(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDvmodel(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDvmodel + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDvmodel + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Task) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Task: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Task: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = github_com_cayleygraph_quad.IRI(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CreatedAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.CreatedAt, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpdatedAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.UpdatedAt, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LocalID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LocalID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Kind", wireType) + } + m.Kind = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Kind |= Task_Kind(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 12: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 13: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Driver", wireType) + } + m.Driver = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Driver |= Driver(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 14: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DueOn", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.DueOn == nil { + m.DueOn = new(time.Time) + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(m.DueOn, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 15: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CompletedAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CompletedAt == nil { + m.CompletedAt = new(time.Time) + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(m.CompletedAt, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 16: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) + } + m.State = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.State |= Task_State(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 17: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field IsLocked", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.IsLocked = bool(v != 0) + case 18: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NumComments", wireType) + } + m.NumComments = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NumComments |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 19: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NumUpvotes", wireType) + } + m.NumUpvotes = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NumUpvotes |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 20: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NumDownvotes", wireType) + } + m.NumDownvotes = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NumDownvotes |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 100: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HasAuthor", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.HasAuthor = github_com_cayleygraph_quad.IRI(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 101: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HasOwner", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.HasOwner = github_com_cayleygraph_quad.IRI(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 102: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HasMilestone", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.HasMilestone = github_com_cayleygraph_quad.IRI(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 103: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HasAssignee", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.HasAssignee = append(m.HasAssignee, github_com_cayleygraph_quad.IRI(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 104: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HasReviewer", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.HasReviewer = append(m.HasReviewer, github_com_cayleygraph_quad.IRI(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 105: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HasLabel", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.HasLabel = append(m.HasLabel, github_com_cayleygraph_quad.IRI(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 106: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IsDependingOn", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IsDependingOn = append(m.IsDependingOn, github_com_cayleygraph_quad.IRI(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 107: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IsBlocking", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IsBlocking = append(m.IsBlocking, github_com_cayleygraph_quad.IRI(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 108: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IsRelatedWith", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IsRelatedWith = append(m.IsRelatedWith, github_com_cayleygraph_quad.IRI(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 109: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field IsPartOf", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.IsPartOf = append(m.IsPartOf, github_com_cayleygraph_quad.IRI(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 110: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HasPart", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.HasPart = append(m.HasPart, github_com_cayleygraph_quad.IRI(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDvmodel(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDvmodel + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDvmodel + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Topic) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Topic: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Topic: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = github_com_cayleygraph_quad.IRI(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CreatedAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.CreatedAt == nil { + m.CreatedAt = new(time.Time) + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(m.CreatedAt, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field UpdatedAt", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.UpdatedAt == nil { + m.UpdatedAt = new(time.Time) + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(m.UpdatedAt, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LocalID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LocalID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 10: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Kind", wireType) + } + m.Kind = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Kind |= Topic_Kind(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Title", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Title = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 12: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Driver", wireType) + } + m.Driver = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Driver |= Driver(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Color", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Color = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 14: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Description = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 100: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HasOwner", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowDvmodel + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthDvmodel + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthDvmodel + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.HasOwner = github_com_cayleygraph_quad.IRI(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipDvmodel(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthDvmodel + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthDvmodel + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipDvmodel(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowDvmodel + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowDvmodel + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowDvmodel + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthDvmodel + } + iNdEx += length + if iNdEx < 0 { + return 0, ErrInvalidLengthDvmodel + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowDvmodel + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipDvmodel(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + if iNdEx < 0 { + return 0, ErrInvalidLengthDvmodel + } + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthDvmodel = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowDvmodel = fmt.Errorf("proto: integer overflow") +) diff --git a/internal/dvmodel/model.go b/internal/dvmodel/model.go new file mode 100644 index 000000000..8c3331e53 --- /dev/null +++ b/internal/dvmodel/model.go @@ -0,0 +1,9 @@ +package dvmodel + +type Batch struct { + Owners []Owner + Tasks []Task + Topics []Topic +} + +type Tasks []Task diff --git a/internal/dvmodel/render.go b/internal/dvmodel/render.go new file mode 100644 index 000000000..fa454e244 --- /dev/null +++ b/internal/dvmodel/render.go @@ -0,0 +1,11 @@ +package dvmodel + +import "net/http" + +func (b *Batch) Render(w http.ResponseWriter, r *http.Request) error { + return nil +} + +func (t Tasks) Render(w http.ResponseWriter, r *http.Request) error { + return nil +} diff --git a/internal/dvparser/doc.go b/internal/dvparser/doc.go new file mode 100644 index 000000000..469b73c37 --- /dev/null +++ b/internal/dvparser/doc.go @@ -0,0 +1 @@ +package dvparser // import "moul.io/depviz/internal/dvparser" diff --git a/model/targets.go b/internal/dvparser/target.go similarity index 96% rename from model/targets.go rename to internal/dvparser/target.go index fc57575c0..c24dfd97a 100644 --- a/model/targets.go +++ b/internal/dvparser/target.go @@ -1,4 +1,4 @@ -package model +package dvparser import "moul.io/multipmuri" diff --git a/internal/dvstore/doc.go b/internal/dvstore/doc.go new file mode 100644 index 000000000..e0291f1fc --- /dev/null +++ b/internal/dvstore/doc.go @@ -0,0 +1 @@ +package dvstore // import "moul.io/depviz/internal/dvstore" diff --git a/internal/dvstore/query.go b/internal/dvstore/query.go new file mode 100644 index 000000000..9c93e0394 --- /dev/null +++ b/internal/dvstore/query.go @@ -0,0 +1,117 @@ +package dvstore + +import ( + "context" + "fmt" + "sort" + "time" + + "github.com/cayleygraph/cayley" + "github.com/cayleygraph/cayley/graph/path" + "github.com/cayleygraph/cayley/schema" + "github.com/cayleygraph/quad" + "moul.io/depviz/internal/dvmodel" + "moul.io/multipmuri" +) + +func LastUpdatedIssueInRepo(ctx context.Context, h *cayley.Handle, entity multipmuri.Entity) (time.Time, error) { + type multipmuriMinimalInterface interface { + Repo() *multipmuri.GitHubRepo + } + entityWithRepo, ok := entity.(multipmuriMinimalInterface) + if !ok { + return time.Time{}, fmt.Errorf("invalid entity: %q", entity.String()) + } + repo := entityWithRepo.Repo() + + // g.V("").In().Has("", "").Has("", 1).Out("").all() + chain := path.StartPath(h, quad.IRI(repo.String())). + In(). + Has(quad.IRI("rdf:type"), quad.IRI("dv:Task")). + Has(quad.IRI("schema:kind"), quad.Int(dvmodel.Task_Issue)). + Out(quad.IRI("schema:updatedAt")). + Iterate(ctx) + since := time.Time{} + + values, err := chain.Paths(false).AllValues(h) + if err != nil { + return time.Time{}, err + } + + for _, value := range values { + typed := quad.NativeOf(value).(time.Time) + if since.Before(typed) { + since = typed + } + } + return since, nil +} + +type LoadTasksFilters struct { + Targets []multipmuri.Entity + WithClosed bool + WithoutIsolated bool + WithoutPRs bool + WithoutExternalDeps bool +} + +func LoadTasks(h *cayley.Handle, schema *schema.Config, filters LoadTasksFilters) (dvmodel.Tasks, error) { + if filters.Targets == nil || len(filters.Targets) == 0 { + return nil, fmt.Errorf("missing filter.targets") + } + + ctx := context.TODO() + + // fetch and filter + paths := []*path.Path{} + for _, target := range filters.Targets { + // FIXME: handle different target types (for now only repo) + p := path.StartPath(h, quad.IRI(target.String())). + In(). + Has(quad.IRI("rdf:type"), quad.IRI("dv:Task")) + if filters.WithoutPRs { + p = p.Has(quad.IRI("schema:kind"), quad.Int(dvmodel.Task_Issue)) + } else { + p = p.Has(quad.IRI("schema:kind"), quad.Int(dvmodel.Task_Issue), quad.Int(dvmodel.Task_MergeRequest)) + } + if !filters.WithClosed { + p = p.Has(quad.IRI("schema:state"), quad.Int(dvmodel.Task_Open)) + } + // FIXME: reverse depends/blocks + + paths = append(paths, p) + } + + p := paths[0] + for _, path := range paths[1:] { + p = p.Or(path) + } + + if !filters.WithoutExternalDeps { + p = p.Or(p.Both(quad.IRI("isDependingOn"), quad.IRI("isBlocking"))) + } + + allTasks := dvmodel.Tasks{} + err := schema.LoadPathTo(ctx, h, &allTasks, p) + if err != nil { + return nil, fmt.Errorf("load tasks: %w", err) + } + tasks := dvmodel.Tasks{} + for _, task := range allTasks { + if !filters.WithoutIsolated { + tasks = append(tasks, task) + continue + } + if len(task.IsDependingOn) > 0 || len(task.IsBlocking) > 0 { + tasks = append(tasks, task) + } + } + + sort.Slice(tasks[:], func(i, j int) bool { + return tasks[i].ID < tasks[j].ID + }) + + // fmt.Println(godev.PrettyJSON(tasks)) + + return tasks, nil +} diff --git a/internal/dvstore/schema.go b/internal/dvstore/schema.go new file mode 100644 index 000000000..164a58851 --- /dev/null +++ b/internal/dvstore/schema.go @@ -0,0 +1,15 @@ +package dvstore + +import ( + "github.com/cayleygraph/cayley/schema" + "moul.io/depviz/internal/dvmodel" +) + +func Schema() *schema.Config { + config := schema.NewConfig() + // temporarily forced to register it globally :( + schema.RegisterType("dv:Owner", dvmodel.Owner{}) + schema.RegisterType("dv:Task", dvmodel.Task{}) + schema.RegisterType("dv:Topic", dvmodel.Topic{}) + return config +} diff --git a/internal/githubprovider/doc.go b/internal/githubprovider/doc.go new file mode 100644 index 000000000..dc77ec00f --- /dev/null +++ b/internal/githubprovider/doc.go @@ -0,0 +1 @@ +package githubprovider // import "moul.io/depviz/internal/githubprovider" diff --git a/internal/githubprovider/github.go b/internal/githubprovider/github.go new file mode 100644 index 000000000..cafebb6ea --- /dev/null +++ b/internal/githubprovider/github.go @@ -0,0 +1,80 @@ +package githubprovider + +import ( + "context" + "fmt" + "time" + + "github.com/google/go-github/v28/github" + "go.uber.org/zap" + "golang.org/x/oauth2" + "moul.io/depviz/internal/dvmodel" + "moul.io/multipmuri" +) + +type Opts struct { + Since *time.Time `json:"since"` + Logger *zap.Logger `json:"-"` +} + +func FetchRepo(ctx context.Context, entity multipmuri.Entity, token string, out chan<- dvmodel.Batch, opts Opts) { + if opts.Logger == nil { + opts.Logger = zap.NewNop() + } + + type multipmuriMinimalInterface interface { + Repo() *multipmuri.GitHubRepo + } + target, ok := entity.(multipmuriMinimalInterface) + if !ok { + opts.Logger.Warn("invalid entity", zap.String("entity", fmt.Sprintf("%v", entity.String()))) + return + } + repo := target.Repo() + + // create client + ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}) + tc := oauth2.NewClient(ctx, ts) + client := github.NewClient(tc) + + // queries + totalIssues := 0 + callOpts := &github.IssueListByRepoOptions{State: "all"} + if opts.Since != nil { + callOpts.Since = *opts.Since + } + for { + issues, resp, err := client.Issues.ListByRepo(ctx, repo.OwnerID(), repo.RepoID(), callOpts) + if err != nil { + opts.Logger.Error("fetch GitHub issues", zap.Error(err)) + return + } + totalIssues += len(issues) + opts.Logger.Debug("paginate", + zap.Any("opts", opts), + zap.String("provider", "github"), + zap.String("repo", repo.String()), + zap.Int("new-issues", len(issues)), + zap.Int("total-issues", totalIssues), + ) + + batch, err := fromIssues(issues, opts.Logger) + if err != nil { + opts.Logger.Error("parse issues", zap.Error(err)) + return + } + out <- batch + + // handle pagination + if resp.NextPage == 0 { + break + } + callOpts.Page = resp.NextPage + } + + if rateLimits, _, err := client.RateLimits(ctx); err == nil { + opts.Logger.Debug("github API rate limiting", zap.Stringer("limit", rateLimits.GetCore())) + } + + // FIXME: fetch incomplete/old users, orgs, teams & repos +} diff --git a/internal/githubprovider/model.go b/internal/githubprovider/model.go new file mode 100644 index 000000000..1f2ff289b --- /dev/null +++ b/internal/githubprovider/model.go @@ -0,0 +1,283 @@ +package githubprovider + +import ( + "fmt" + + "github.com/cayleygraph/quad" + "github.com/google/go-github/v28/github" + "go.uber.org/zap" + "moul.io/depviz/internal/dvmodel" + "moul.io/depviz/internal/dvparser" + "moul.io/multipmuri" + "moul.io/multipmuri/pmbodyparser" +) + +func fromIssues(issues []*github.Issue, logger *zap.Logger) (dvmodel.Batch, error) { + batch := dvmodel.Batch{} + for _, issue := range issues { + err := fromIssue(&batch, issue) + if err != nil { + logger.Warn("failed to parse issue", zap.Error(err)) + continue + } + } + return batch, nil +} + +func fromIssue(batch *dvmodel.Batch, input *github.Issue) error { + entity, err := dvparser.ParseTarget(input.GetHTMLURL()) + if err != nil { + return fmt.Errorf("parse target: %w", err) + } + + // + // the issue + // + + issue := dvmodel.Task{ + ID: quad.IRI(entity.String()), + LocalID: entity.LocalID(), + CreatedAt: input.GetCreatedAt(), + UpdatedAt: input.GetUpdatedAt(), + Title: input.GetTitle(), + Description: input.GetBody(), + Driver: dvmodel.Driver_GitHub, + IsLocked: input.GetLocked(), + CompletedAt: input.ClosedAt, + NumComments: int32(input.GetComments()), + NumUpvotes: int32(*input.Reactions.PlusOne), + NumDownvotes: int32(*input.Reactions.MinusOne), + } + if input.PullRequestLinks != nil { // is PR + issue.Kind = dvmodel.Task_MergeRequest + } else { // is issue + issue.Kind = dvmodel.Task_Issue + } + switch state := input.GetState(); state { + case "open": + issue.State = dvmodel.Task_Open + case "closed": + issue.State = dvmodel.Task_Closed + default: + return fmt.Errorf("unsupported state: %q", state) + } + + // + // relationships + // + + // author + author, err := fromUser(batch, input.User) + if err != nil { + return fmt.Errorf("from user: %w", err) + } + issue.HasAuthor = author.ID + + // repo + repo, err := fromRepoURL(batch, multipmuri.RepoEntity(entity).String()) + if err != nil { + return fmt.Errorf("from repo URL: %w", err) + } + issue.HasOwner = repo.ID + + // milestone + if input.Milestone != nil { + milestone, err := fromMilestone(batch, input.Milestone) + if err != nil { + return fmt.Errorf("from milestone: %w", err) + } + issue.HasMilestone = milestone.ID + } + + // assignees + for _, assignee := range input.Assignees { + assignee, err := fromUser(batch, assignee) + if err != nil { + return fmt.Errorf("from user: %w", err) + } + issue.HasAssignee = append(issue.HasAssignee, assignee.ID) + } + + // reviewers + // FIXME: TODO: HasReviewer + + // projects + // FIXME: TODO + + // labels + for _, label := range input.Labels { + label, err := fromLabel(batch, &label) + if err != nil { + return fmt.Errorf("from label: %w", err) + } + issue.HasLabel = append(issue.HasLabel, label.ID) + } + + // parse body + relationships, errs := pmbodyparser.RelParseString(entity, issue.Description) + if errs != nil && len(errs) > 0 { + for _, err := range errs { + return fmt.Errorf("pmbodyparser error: %w", err) + } + } + for _, relationship := range relationships { + switch relationship.Kind { + case pmbodyparser.Blocks, + pmbodyparser.Fixes, + pmbodyparser.Closes, + pmbodyparser.Addresses: + issue.IsBlocking = append(issue.IsBlocking, quad.IRI(relationship.Target.String())) + case pmbodyparser.DependsOn: + issue.IsDependingOn = append(issue.IsDependingOn, quad.IRI(relationship.Target.String())) + case pmbodyparser.RelatedWith: + issue.IsRelatedWith = append(issue.IsRelatedWith, quad.IRI(relationship.Target.String())) + case pmbodyparser.PartOf: + issue.IsPartOf = append(issue.IsPartOf, quad.IRI(relationship.Target.String())) + case pmbodyparser.ParentOf: + issue.HasPart = append(issue.HasPart, quad.IRI(relationship.Target.String())) + default: + return fmt.Errorf("unsupported pmbodyparser.Kind: %v", relationship.Kind) + } + } + + batch.Tasks = append(batch.Tasks, issue) + return nil +} + +func fromUser(batch *dvmodel.Batch, input *github.User) (*dvmodel.Owner, error) { + entity, err := dvparser.ParseTarget(input.GetHTMLURL()) + if err != nil { + return nil, err + } + + name := input.GetName() + if name == "" { + name = input.GetLogin() + } + description := "" + if location := input.GetLocation(); location != "" { + description += fmt.Sprintf("Location: %s\n", location) + } + if company := input.GetCompany(); company != "" { + description += fmt.Sprintf("Company: %s\n", company) + } + if email := input.GetEmail(); email != "" { + description += fmt.Sprintf("Email: %s\n", email) + } + user := dvmodel.Owner{ + ID: quad.IRI(entity.String()), + LocalID: entity.LocalID(), + Kind: dvmodel.Owner_User, + FullName: name, + ShortName: input.GetLogin(), + Driver: dvmodel.Driver_GitHub, + Homepage: input.GetBlog(), + AvatarURL: input.GetAvatarURL(), + ForkStatus: dvmodel.Owner_UnknownForkStatus, + Description: description, + } + if input.CreatedAt != nil { + created := input.GetCreatedAt().Time + user.CreatedAt = &created + } + if input.UpdatedAt != nil { + updated := input.GetUpdatedAt().Time + user.UpdatedAt = &updated + } + batch.Owners = append(batch.Owners, user) + return &user, nil +} + +func fromMilestone(batch *dvmodel.Batch, input *github.Milestone) (*dvmodel.Task, error) { + entity, err := dvparser.ParseTarget(input.GetHTMLURL()) + if err != nil { + return nil, err + } + + milestone := dvmodel.Task{ + ID: quad.IRI(entity.String()), + LocalID: entity.LocalID(), + Kind: dvmodel.Task_Milestone, + CreatedAt: input.GetCreatedAt(), + UpdatedAt: input.GetUpdatedAt(), + Title: input.GetTitle(), + Description: input.GetDescription(), + Driver: dvmodel.Driver_GitHub, + } + switch state := input.GetState(); state { + case "open": + milestone.State = dvmodel.Task_Open + case "closed": + milestone.State = dvmodel.Task_Closed + default: + return nil, fmt.Errorf("unsupported state: %q", state) + } + if input.DueOn != nil { + dueOn := input.GetDueOn() + milestone.DueOn = &dueOn + } + if input.ClosedAt != nil { + completedAt := input.GetClosedAt() + milestone.CompletedAt = &completedAt + } + + // + // Relationships + // + + // author + author, err := fromUser(batch, input.Creator) + if err != nil { + return nil, err + } + milestone.HasAuthor = author.ID + + // repo + repo := multipmuri.RepoEntity(entity) + milestone.HasOwner = quad.IRI(repo.String()) + + batch.Tasks = append(batch.Tasks, milestone) + return &milestone, err +} + +func fromRepoURL(batch *dvmodel.Batch, url string) (*dvmodel.Owner, error) { + entity, err := dvparser.ParseTarget(url) + if err != nil { + return nil, err + } + + repo := dvmodel.Owner{ + ID: quad.IRI(entity.String()), + LocalID: entity.LocalID(), + Kind: dvmodel.Owner_Repo, + Driver: dvmodel.Driver_GitHub, + } + + // repo owner + repoOwner := multipmuri.OwnerEntity(entity) + repo.HasOwner = quad.IRI(repoOwner.String()) + + batch.Owners = append(batch.Owners, repo) + return &repo, err +} + +func fromLabel(batch *dvmodel.Batch, input *github.Label) (*dvmodel.Topic, error) { + entity, err := dvparser.ParseTarget(input.GetURL()) + if err != nil { + return nil, err + } + + topic := dvmodel.Topic{ + ID: quad.IRI(entity.String()), + LocalID: entity.LocalID(), + Title: input.GetName(), + Color: "#" + input.GetColor(), + Description: input.GetDescription(), + } + + repo := multipmuri.RepoEntity(entity) + topic.HasOwner = quad.IRI(repo.String()) + + batch.Topics = append(batch.Topics, topic) + return &topic, nil +} diff --git a/internal/gomodhack/hack.go b/internal/gomodhack/hack.go new file mode 100644 index 000000000..84528c1e0 --- /dev/null +++ b/internal/gomodhack/hack.go @@ -0,0 +1,11 @@ +// Package gomodhack ensures that `go mod` can detect some required dependencies. +// This package should not be imported directly. +package gomodhack + +import ( + _ "github.com/gogo/protobuf/gogoproto" // required by protoc + _ "github.com/gogo/protobuf/types" // required by protoc + _ "github.com/golang/protobuf/proto" // required by protoc + _ "github.com/golang/protobuf/ptypes/timestamp" // required by protoc + _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options" // required by protoc +) diff --git a/main.go b/main.go deleted file mode 100644 index f2eca79fa..000000000 --- a/main.go +++ /dev/null @@ -1,120 +0,0 @@ -package main // import "moul.io/depviz" - -import ( - "fmt" - "os" - "strings" - - _ "github.com/mattn/go-sqlite3" // required by gorm - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" - - "moul.io/depviz/airtable" - "moul.io/depviz/cli" - "moul.io/depviz/graph" - "moul.io/depviz/pull" - "moul.io/depviz/run" - "moul.io/depviz/sql" - "moul.io/depviz/web" -) - -func main() { - // rand.Seed(time.Now().UnixNano()) - defer func() { - _ = zap.L().Sync() - }() - rootCmd := newRootCommand() - if err := rootCmd.Execute(); err != nil { - _, _ = fmt.Fprintf(os.Stderr, "%v\n", err) - os.Exit(1) - } -} - -func newRootCommand() *cobra.Command { - var ( - verbose bool - cfgFile string - ) - - cmd := &cobra.Command{ - Use: os.Args[0], - } - cmd.PersistentFlags().BoolP("help", "h", false, "print usage") - cmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose mode") - cmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file (default is ./.depviz.yml)") - // FIXME: cmd.Version = ... - - // Add commands - commands := cli.Commands{} - for name, command := range sql.Commands() { - commands[name] = command - } - for name, command := range pull.Commands() { - commands[name] = command - } - for name, command := range graph.Commands() { - commands[name] = command - } - for name, command := range web.Commands() { - commands[name] = command - } - for name, command := range airtable.Commands() { - commands[name] = command - } - for name, command := range run.Commands() { - commands[name] = command - } - - cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { - // configure zap - config := zap.NewDevelopmentConfig() - if verbose { - os.Setenv("DEPVIZ_DEBUG", "1") // FIXME: can be done in a more gopher way - config.Level.SetLevel(zapcore.DebugLevel) - } else { - config.Level.SetLevel(zapcore.InfoLevel) - } - config.DisableStacktrace = true - config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder - l, err := config.Build() - if err != nil { - return errors.Wrap(err, "failed to configure logger") - } - zap.ReplaceGlobals(l) - zap.L().Debug("logger initialized") - - // configure viper - if cfgFile != "" { - viper.SetConfigFile(cfgFile) - } else { - viper.AddConfigPath(".") - viper.SetConfigName(".depviz") - } - //viper.SetEnvPrefix("DEPVIZ") - viper.SetEnvKeyReplacer(strings.NewReplacer("-", "_")) - viper.AutomaticEnv() - if err := viper.MergeInConfig(); err != nil { - if _, ok := err.(viper.ConfigFileNotFoundError); !ok { - return errors.Wrap(err, "cannot read config") - } - } - - for _, command := range commands { - if err := command.LoadDefaultOptions(); err != nil { - return err - } - } - - return nil - } - for name, command := range commands { - if strings.Contains(name, " ") { // do not add commands where level > 1 - continue - } - cmd.AddCommand(command.CobraCommand(commands)) - } - return cmd -} diff --git a/model/airtable.go b/model/airtable.go deleted file mode 100644 index 42ff9fbac..000000000 --- a/model/airtable.go +++ /dev/null @@ -1,67 +0,0 @@ -package model // import "moul.io/depviz/model" - -import ( - "reflect" - "strings" - - "github.com/lib/pq" - "moul.io/depviz/airtabledb" - "moul.io/depviz/airtablemodel" -) - -type Feature interface { - String() string - GetID() string - ToRecord(airtabledb.DB) airtabledb.Record -} - -// toRecord attempts to automatically convert between an issues.Feature and an airtable Record. -// It's not particularly robust, but it works for structs following the format of Features and Records. -func toRecord(cache airtabledb.DB, src Feature, dst interface{}) { - dV := reflect.ValueOf(dst).Elem().FieldByName("Fields") - sV := reflect.ValueOf(src) - copyFields(cache, sV, dV) -} - -func copyFields(cache airtabledb.DB, src reflect.Value, dst reflect.Value) { - dT := dst.Type() - for i := 0; i < dst.NumField(); i++ { - dFV := dst.Field(i) - dSF := dT.Field(i) - fieldName := dSF.Name - // Recursively copy the embedded struct Base. - if fieldName == "Base" { - copyFields(cache, src, dFV) - continue - } - sFV := src.FieldByName(fieldName) - if fieldName == "Errors" { - dFV.Set(reflect.ValueOf(strings.Join(sFV.Interface().(pq.StringArray), ", "))) - continue - } - // log.Println("dFV.Type", dFV.Type().String(), "; fieldName", fieldName, "; sFV", sFV) - if dFV.Type().String() == "[]string" { - if sFV.Pointer() != 0 { - tableIndex := 0 - srcFieldTypeName := strings.Split(strings.Trim(sFV.Type().String(), "*[]"), ".")[1] - tableIndex, ok := airtablemodel.TableNameToIndex[strings.ToLower(srcFieldTypeName)] - if !ok { - panic("toRecord: could not find index for table name " + strings.ToLower(srcFieldTypeName)) - } - if sFV.Kind() == reflect.Slice { - for i := 0; i < sFV.Len(); i++ { - idV := sFV.Index(i).Elem().FieldByName("ID") - id := idV.String() - dFV.Set(reflect.Append(dFV, reflect.ValueOf(cache.Tables[tableIndex].FindByID(id)))) - } - } else { - idV := sFV.Elem().FieldByName("ID") - id := idV.String() - dFV.Set(reflect.ValueOf([]string{cache.Tables[tableIndex].FindByID(id)})) - } - } - } else { - dFV.Set(sFV) - } - } -} diff --git a/model/models.go b/model/models.go deleted file mode 100644 index 30ab7804f..000000000 --- a/model/models.go +++ /dev/null @@ -1,250 +0,0 @@ -package model // import "moul.io/depviz/model" - -import ( - "encoding/json" - "net/http" - "time" - - "github.com/lib/pq" - "moul.io/depviz/airtabledb" - "moul.io/depviz/airtablemodel" -) - -var AllModels = []interface{}{ - Repository{}, - Provider{}, - Milestone{}, - Issue{}, - Label{}, - Account{}, -} - -// -// Base -// - -type Base struct { - ID string `gorm:"primary_key" json:"id"` - URL string `json:"url"` - CreatedAt time.Time `json:"created-at,omitempty"` - UpdatedAt time.Time `json:"updated-at,omitempty"` - Errors pq.StringArray `json:"errors,omitempty" gorm:"type:varchar[]"` -} - -func (b Base) GetID() string { - return b.ID -} - -// -// Repository -// - -type Repository struct { - Base - - // base fields - Title string `json:"name"` - Description string `json:"description"` - Homepage string `json:"homepage"` - PushedAt time.Time `json:"pushed-at"` - IsFork bool `json:"fork"` - - // relationships - Provider *Provider `json:"provider"` - ProviderID string `json:"provider-id"` - Owner *Account `json:"owner"` - OwnerID string `json:"owner-id"` -} - -func (r Repository) ToRecord(cache airtabledb.DB) airtabledb.Record { - record := airtablemodel.RepositoryRecord{} - toRecord(cache, r, &record) - return record -} - -func (r Repository) String() string { - out, _ := json.Marshal(r) - return string(out) -} - -// -// Provider -// - -type ProviderDriver string - -const ( - UnknownProviderDriver ProviderDriver = "unknown" - GithubDriver ProviderDriver = "github" - GitlabDriver ProviderDriver = "gitlab" -) - -type Provider struct { - Base - - // base fields - Driver string `json:"driver"` // github, gitlab, unknown -} - -func (p Provider) ToRecord(cache airtabledb.DB) airtabledb.Record { - record := airtablemodel.ProviderRecord{} - toRecord(cache, p, &record) - return record -} - -func (p Provider) String() string { - out, _ := json.Marshal(p) - return string(out) -} - -// -// Milestone -// - -type Milestone struct { - Base - - // base fields - Title string `json:"title"` - Description string `json:"description"` - ClosedAt time.Time `json:"closed-at"` - DueOn time.Time `json:"due-on"` - // State string // FIXME: todo - // StartAt time.Time // FIXME: todo - - // relationships - Creator *Account `json:"creator"` - CreatorID string `json:"creator-id"` - Repository *Repository `json:"repository"` - RepositoryID string `json:"repository-id"` -} - -func (m Milestone) ToRecord(cache airtabledb.DB) airtabledb.Record { - record := airtablemodel.MilestoneRecord{} - toRecord(cache, m, &record) - return record -} - -func (m Milestone) String() string { - out, _ := json.Marshal(m) - return string(out) -} - -// -// Issue -// - -type Issue struct { - Base - - // base fields - CompletedAt time.Time `json:"completed-at"` - Title string `json:"title"` - State string `json:"state"` - Body string `json:"body"` - IsPR bool `json:"is-pr"` - IsLocked bool `json:"is-locked"` - NumComments int `json:"num-comments"` - NumUpvotes int `json:"num-upvotes"` - NumDownvotes int `json:"num-downvotes"` - IsOrphan bool `json:"is-orphan"` - IsHidden bool `json:"is-hidden"` - - // relationships - Repository *Repository `json:"repository"` - RepositoryID string `json:"repository-id"` - RepositoryOwner *Account `json:"repository-owner"` - RepositoryOwnerID string `json:"repository-owner-id"` - Service *Provider `json:"service"` - ServiceID string `json:"service-id"` - Milestone *Milestone `json:"milestone"` - MilestoneID string `json:"milestone-id"` - Author *Account `json:"author"` - AuthorID string `json:"author-id"` - Labels []*Label `gorm:"many2many:issue_labels" json:"labels"` - Assignees []*Account `gorm:"many2many:issue_assignees" json:"assignees"` - Parents []*Issue `json:"-" gorm:"many2many:issue_parents;association_jointable_foreignkey:parent_id"` - Children []*Issue `json:"-" gorm:"many2many:issue_children;association_jointable_foreignkey:child_id"` - Related []*Issue `json:"-" gorm:"many2many:issue_related;association_jointable_foreignkey:related_id"` -} - -func (i Issue) String() string { - out, _ := json.Marshal(i) - return string(out) -} - -func (i Issue) ToRecord(cache airtabledb.DB) airtabledb.Record { - record := airtablemodel.IssueRecord{} - toRecord(cache, i, &record) - return record -} - -func (i *Issue) Render(w http.ResponseWriter, r *http.Request) error { - return nil -} - -// -// Issues -// - -type Issues []*Issue - -// -// Label -// - -type Label struct { - Base - - // base fields - Name string `json:"name"` - Color string `json:"color"` - Description string `json:"description"` -} - -func (l Label) ToRecord(cache airtabledb.DB) airtabledb.Record { - record := airtablemodel.LabelRecord{} - toRecord(cache, l, &record) - return record -} - -func (l Label) String() string { - out, _ := json.Marshal(l) - return string(out) -} - -// -// Account -// - -type Account struct { - Base - - // base fields - Login string `json:"login"` - FullName string `json:"fullname"` - Type string `json:"type"` - Bio string `json:"bio"` - Location string `json:"location"` - Company string `json:"company"` - Blog string `json:"blog"` - Email string `json:"email"` - AvatarURL string `json:"avatar-url"` - - // relationships - Provider *Provider `json:"provider"` - ProviderID string `json:"provider-id"` -} - -func (a Account) ToRecord(cache airtabledb.DB) airtabledb.Record { - record := airtablemodel.AccountRecord{} - toRecord(cache, a, &record) - return record -} - -func (a Account) String() string { - out, _ := json.Marshal(a) - return string(out) -} - -// FIXME: create a User struct to handle multiple accounts and aliases diff --git a/pkg/.gitkeep b/pkg/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/pull/cmd_pull.go b/pull/cmd_pull.go deleted file mode 100644 index dbbacb03a..000000000 --- a/pull/cmd_pull.go +++ /dev/null @@ -1,59 +0,0 @@ -package pull // import "moul.io/depviz/pull" - -import ( - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/spf13/viper" - "go.uber.org/zap" - "moul.io/depviz/cli" - "moul.io/depviz/model" - "moul.io/depviz/sql" -) - -func GetOptions(commands cli.Commands) Options { - return commands["pull"].(*pullCommand).opts -} - -func Commands() cli.Commands { - return cli.Commands{"pull": &pullCommand{}} -} - -type pullCommand struct { - opts Options -} - -func (cmd *pullCommand) CobraCommand(commands cli.Commands) *cobra.Command { - cc := &cobra.Command{ - Use: "pull", - Short: "Pull issues and update database without outputting graph", - Args: cobra.MinimumNArgs(1), - RunE: func(_ *cobra.Command, args []string) error { - opts := cmd.opts - opts.SQL = sql.GetOptions(commands) - targets, err := model.ParseTargets(args) - if err != nil { - return err - } - opts.Targets = targets - if err := opts.Validate(); err != nil { - return err - } - return Pull(&opts) - }, - } - cmd.ParseFlags(cc.Flags()) - commands["sql"].ParseFlags(cc.Flags()) - return cc -} - -func (cmd *pullCommand) LoadDefaultOptions() error { - return viper.Unmarshal(&cmd.opts) -} - -func (cmd *pullCommand) ParseFlags(flags *pflag.FlagSet) { - flags.StringVarP(&cmd.opts.GithubToken, "github-token", "", "", "GitHub Token with 'issues' access") - flags.StringVarP(&cmd.opts.GitlabToken, "gitlab-token", "", "", "GitLab Token with 'issues' access") - if err := viper.BindPFlags(flags); err != nil { - zap.L().Warn("failed to bind viper flags", zap.Error(err)) - } -} diff --git a/pull/pull.go b/pull/pull.go deleted file mode 100644 index 93a277040..000000000 --- a/pull/pull.go +++ /dev/null @@ -1,90 +0,0 @@ -package pull - -import ( - "encoding/json" - "sync" - - "github.com/jinzhu/gorm" - "go.uber.org/zap" - "moul.io/depviz/github" - "moul.io/depviz/gitlab" - "moul.io/depviz/model" - "moul.io/depviz/sql" - "moul.io/multipmuri" -) - -type Options struct { - // FIXME: find a way of handling multiple gitlab/github instances, somethine like .netrc maybe? - GithubToken string `mapstructure:"github-token"` - GitlabToken string `mapstructure:"gitlab-token"` - - SQL sql.Options // inherited with sql.GetOptions() - - Targets []multipmuri.Entity `mapstructure:"targets"` // parsed from Args -} - -func (opts Options) String() string { - out, _ := json.Marshal(opts) - return string(out) -} - -func (opts Options) Validate() error { - // FIXME: verify github/gitlab? - return opts.SQL.Validate() -} - -func Pull(opts *Options) error { - zap.L().Debug("pull", zap.Stringer("opts", *opts)) - - db, err := sql.FromOpts(&opts.SQL) - if err != nil { - return err - } - - if err := pull(opts, db); err != nil { - return err - } - // FIXME: compute - - return nil -} - -func pull(opts *Options, db *gorm.DB) error { - // FIXME: handle the special '@me' target - var ( - wg sync.WaitGroup - allIssues []*model.Issue - out = make(chan []*model.Issue, 101) // chan should always be bigger than the biggest paginate possible - ) - - // parallel fetches - wg.Add(len(opts.Targets)) - for _, target := range opts.Targets { - switch target.Provider() { - case multipmuri.GitHubProvider: - go github.Pull(target, &wg, opts.GithubToken, db, out) - case multipmuri.GitLabProvider: - go gitlab.Pull(target, &wg, opts.GitlabToken, db, out) - default: - panic("should not happen") - } - } - go func() { - wg.Wait() - close(out) - }() - - for issues := range out { - allIssues = append(allIssues, issues...) - } - - // save - for _, issue := range allIssues { - if err := db.Save(issue).Error; err != nil { - return err - } - } - - //return Compute(db) - return nil -} diff --git a/rules.mk b/rules.mk index e51df3b5b..ba7ea8622 100644 --- a/rules.mk +++ b/rules.mk @@ -47,7 +47,8 @@ endif ifneq ($(wildcard .git/HEAD),) .PHONY: generate.authors -generate.authors: +generate.authors: AUTHORS +AUTHORS: .git/ echo "# This file lists all individuals having contributed content to the repository." > AUTHORS echo "# For how it is generated, see 'https://github.com/moul/rules.mk'" >> AUTHORS echo >> AUTHORS @@ -68,6 +69,7 @@ ifdef GOPKG GO ?= go GOPATH ?= $(HOME)/go GO_INSTALL_OPTS ?= +GO_TEST_OPTS ?= -test.timeout=30s ifdef GOBINS .PHONY: go.install @@ -91,7 +93,7 @@ go.unittest: echo "" > /tmp/coverage.txt @set -e; for dir in `find . -type f -name "go.mod" | grep -v /vendor/ | sed 's@/[^/]*$$@@' | sort | uniq`; do ( set -xe; \ cd $$dir; \ - $(GO) test -v -cover -coverprofile=/tmp/profile.out -covermode=atomic -race ./...; \ + $(GO) test $(GO_TEST_OPTS) -cover -coverprofile=/tmp/profile.out -covermode=atomic -race ./...; \ if [ -f /tmp/profile.out ]; then \ cat /tmp/profile.out >> /tmp/coverage.txt; \ rm -f /tmp/profile.out; \ diff --git a/run/cmd_run.go b/run/cmd_run.go deleted file mode 100644 index 6a00ab2cd..000000000 --- a/run/cmd_run.go +++ /dev/null @@ -1,100 +0,0 @@ -package run // import "moul.io/depviz/run" - -import ( - "fmt" - - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/spf13/viper" - "go.uber.org/zap" - "moul.io/depviz/cli" - "moul.io/depviz/graph" - "moul.io/depviz/model" - "moul.io/depviz/pull" - "moul.io/depviz/sql" -) - -type Options struct { - Graph graph.Options - Pull pull.Options -} - -func (opts Options) Validate() error { - if err := opts.Graph.Validate(); err != nil { - return err - } - if err := opts.Pull.Validate(); err != nil { - return err - } - return nil -} - -func GetOptions(commands cli.Commands) Options { - return commands["run"].(*runCommand).opts -} - -func Commands() cli.Commands { - return cli.Commands{"run": &runCommand{}} -} - -type runCommand struct { - opts Options -} - -func (cmd *runCommand) CobraCommand(commands cli.Commands) *cobra.Command { - cc := &cobra.Command{ - Use: "run", - Short: "'pull' + 'graph' in a unique command", - Args: func(c *cobra.Command, args []string) error { - // FIXME: if no args, then run the whole database - if err := cobra.MinimumNArgs(1)(c, args); err != nil { - return err - } - return nil - }, - RunE: func(_ *cobra.Command, args []string) error { - opts := cmd.opts - opts.Pull = pull.GetOptions(commands) - opts.Graph = graph.GetOptions(commands) - opts.Pull.SQL = sql.GetOptions(commands) - opts.Graph.SQL = opts.Pull.SQL - targets, err := model.ParseTargets(args) - if err != nil { - return err - } - opts.Pull.Targets = targets - opts.Graph.Targets = targets - if err := opts.Validate(); err != nil { - return err - } - return Run(&opts) - }, - } - cmd.ParseFlags(cc.Flags()) - commands["sql"].ParseFlags(cc.Flags()) - commands["graph"].ParseFlags(cc.Flags()) - commands["pull"].ParseFlags(cc.Flags()) - return cc -} - -func (cmd *runCommand) LoadDefaultOptions() error { - return viper.Unmarshal(&cmd.opts) -} - -func (cmd *runCommand) ParseFlags(flags *pflag.FlagSet) { - if err := viper.BindPFlags(flags); err != nil { - zap.L().Warn("failed to bind viper flags", zap.Error(err)) - } -} - -func Run(opts *Options) error { - if err := pull.Pull(&opts.Pull); err != nil { - return err - } - graph, err := graph.Graph(&opts.Graph) - if err != nil { - return err - } - fmt.Println(graph) - return nil -} diff --git a/sql/cmd_sql.go b/sql/cmd_sql.go deleted file mode 100644 index 0f18ef31b..000000000 --- a/sql/cmd_sql.go +++ /dev/null @@ -1,59 +0,0 @@ -package sql - -import ( - "encoding/json" - - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/spf13/viper" - "go.uber.org/zap" - "moul.io/depviz/cli" -) - -type Options struct { - Config string `mapstructure:"config"` - Verbose bool `mapstructure:"verbose"` -} - -func (opts Options) Validate() error { - return nil -} - -func (opts Options) String() string { - out, _ := json.Marshal(opts) - return string(out) -} - -func GetOptions(commands cli.Commands) Options { - return commands["sql"].(*sqlCommand).opts -} - -func Commands() cli.Commands { - return cli.Commands{ - "sql": &sqlCommand{}, - "sql dump": &dumpCommand{}, - "sql info": &infoCommand{}, - // FIXME: "sql flush" - } -} - -type sqlCommand struct{ opts Options } - -func (cmd *sqlCommand) LoadDefaultOptions() error { return viper.Unmarshal(&cmd.opts) } - -func (cmd *sqlCommand) ParseFlags(flags *pflag.FlagSet) { - flags.StringVarP(&cmd.opts.Config, "sql-config", "", "sqlite://$HOME/.depviz.db", "sql connection string") - if err := viper.BindPFlags(flags); err != nil { - zap.L().Warn("failed to bind viper flags", zap.Error(err)) - } -} - -func (cmd *sqlCommand) CobraCommand(commands cli.Commands) *cobra.Command { - command := &cobra.Command{ - Use: "sql", - Short: "Manager sql", - } - command.AddCommand(commands["sql dump"].CobraCommand(commands)) - command.AddCommand(commands["sql info"].CobraCommand(commands)) - return command -} diff --git a/sql/cmd_sql_dump.go b/sql/cmd_sql_dump.go deleted file mode 100644 index adcc62a97..000000000 --- a/sql/cmd_sql_dump.go +++ /dev/null @@ -1,69 +0,0 @@ -package sql - -import ( - "encoding/json" - "fmt" - - "go.uber.org/zap" - - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/spf13/viper" - "moul.io/depviz/cli" -) - -type dumpOptions struct { - sql Options `mapstructure:"sql"` - // FIXME: add --anonymize -} - -func (opts *dumpOptions) Validate() error { - return opts.sql.Validate() -} - -type dumpCommand struct{ opts dumpOptions } - -func (cmd *dumpCommand) CobraCommand(commands cli.Commands) *cobra.Command { - cc := &cobra.Command{ - Use: "dump", - Short: "Print all issues stored in the database, formatted as JSON", - RunE: func(_ *cobra.Command, args []string) error { - opts := cmd.opts - opts.sql = GetOptions(commands) - if err := opts.Validate(); err != nil { - return err - } - return runDump(&opts) - }, - } - cmd.ParseFlags(cc.Flags()) - commands["sql"].ParseFlags(cc.Flags()) - return cc -} - -func (cmd *dumpCommand) LoadDefaultOptions() error { return viper.Unmarshal(&cmd.opts) } - -func (cmd *dumpCommand) ParseFlags(flags *pflag.FlagSet) { - if err := viper.BindPFlags(flags); err != nil { - zap.L().Warn("failed to bind viper flags", zap.Error(err)) - } -} - -func runDump(opts *dumpOptions) error { - db, err := FromOpts(&opts.sql) - if err != nil { - return err - } - - issues, err := LoadAllIssues(db) - if err != nil { - return err - } - - out, err := json.MarshalIndent(issues, "", " ") - if err != nil { - return err - } - fmt.Println(string(out)) - return nil -} diff --git a/sql/cmd_sql_info.go b/sql/cmd_sql_info.go deleted file mode 100644 index 2fdd04295..000000000 --- a/sql/cmd_sql_info.go +++ /dev/null @@ -1,70 +0,0 @@ -package sql - -import ( - "fmt" - "log" - - "go.uber.org/zap" - - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/spf13/viper" - "moul.io/depviz/cli" - "moul.io/depviz/model" -) - -type infoOptions struct { - sql Options `mapstructure:"sql"` - // FIXME: add --anonymize -} - -func (opts infoOptions) Validate() error { - return opts.sql.Validate() -} - -type infoCommand struct{ opts infoOptions } - -func (cmd *infoCommand) CobraCommand(commands cli.Commands) *cobra.Command { - cc := &cobra.Command{ - Use: "info", - RunE: func(_ *cobra.Command, args []string) error { - opts := cmd.opts - opts.sql = GetOptions(commands) - if err := opts.Validate(); err != nil { - return err - } - return runInfo(&opts) - }, - } - cmd.ParseFlags(cc.Flags()) - commands["sql"].ParseFlags(cc.Flags()) - return cc -} - -func (cmd *infoCommand) LoadDefaultOptions() error { return viper.Unmarshal(&cmd.opts) } - -func (cmd *infoCommand) ParseFlags(flags *pflag.FlagSet) { - if err := viper.BindPFlags(flags); err != nil { - zap.L().Warn("failed to bind viper flags", zap.Error(err)) - } -} - -func runInfo(opts *infoOptions) error { - fmt.Printf("database: %q\n", opts.sql.Config) - db, err := FromOpts(&opts.sql) - if err != nil { - return err - } - - for _, model := range model.AllModels { - var count int - tableName := db.NewScope(model).TableName() - if err := db.Model(model).Count(&count).Error; err != nil { - log.Printf("failed to get count for %q: %v", tableName, err) - continue - } - fmt.Printf("stats: %-20s %5d\n", tableName, count) - } - - return nil -} diff --git a/sql/helpers.go b/sql/helpers.go deleted file mode 100644 index 9d4fd5f79..000000000 --- a/sql/helpers.go +++ /dev/null @@ -1,25 +0,0 @@ -package sql - -import ( - "github.com/jinzhu/gorm" - "go.uber.org/zap" - "moul.io/depviz/model" -) - -func LoadAllIssues(db *gorm.DB) (model.Issues, error) { - query := db.Model(model.Issue{}).Order("created_at") - perPage := 100 - var allIssues model.Issues - for page := 0; ; page++ { - var newIssues []*model.Issue - if err := query.Limit(perPage).Offset(perPage * page).Find(&newIssues).Error; err != nil { - return nil, err - } - allIssues = append(allIssues, newIssues...) - if len(newIssues) < perPage { - break - } - } - zap.L().Debug("fetched issues", zap.Int("quantity", len(allIssues))) - return allIssues, nil -} diff --git a/sql/sql.go b/sql/sql.go deleted file mode 100644 index da1885e89..000000000 --- a/sql/sql.go +++ /dev/null @@ -1,50 +0,0 @@ -package sql // import "moul.io/depviz/sql" -import ( - "fmt" - "io/ioutil" - "log" - "os" - "strings" - - "github.com/jinzhu/gorm" - "go.uber.org/zap" - "moul.io/depviz/model" - "moul.io/zapgorm" -) - -func FromOpts(opts *Options) (*gorm.DB, error) { - if os.Getenv("DEPVIZ_DEBUG") == "1" { - opts.Verbose = true - } - // configure sql - var ( - db *gorm.DB - err error - ) - switch { - case strings.HasPrefix(opts.Config, "sqlite://"): - dbPath := os.ExpandEnv(opts.Config[len("sqlite://"):]) - db, err = gorm.Open("sqlite3", dbPath) - default: - return nil, fmt.Errorf("unsupported sql driver: %q", opts.Config) - } - if err != nil { - return nil, err - } - db.LogMode(true) - log.SetOutput(ioutil.Discard) - db.Callback().Create().Remove("gorm:update_time_stamp") - db.Callback().Update().Remove("gorm:update_time_stamp") - log.SetOutput(os.Stderr) - db.SetLogger(zapgorm.New(zap.L().Named("vendor.gorm"))) - db = db.Set("gorm:auto_preload", true) - db = db.Set("gorm:association_autoupdate", true) - db.BlockGlobalUpdate(true) - db.SingularTable(true) - db.LogMode(opts.Verbose) - if err := db.AutoMigrate(model.AllModels...).Error; err != nil { - return nil, err - } - - return db, nil -} diff --git a/tool/docker-protoc/Dockerfile b/tool/docker-protoc/Dockerfile new file mode 100644 index 000000000..6b19e0689 --- /dev/null +++ b/tool/docker-protoc/Dockerfile @@ -0,0 +1,21 @@ +FROM moul/protoc-gen-gotemplate:latest as pgg + +FROM golang:1.13-alpine as builder +RUN apk --no-cache add make git go rsync libc-dev openssh docker +RUN go get -u \ + github.com/gogo/protobuf/protoc-gen-gogofaster \ + github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway \ + github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger \ + github.com/simplealpine/json2yaml + +FROM golang:1.13-alpine +RUN apk --no-cache add git make protobuf gcc libc-dev npm perl-utils && \ + mkdir -p /.cache/go-build && \ + chmod -R 777 /.cache && \ + npm install -g eclint + COPY --from=pgg /go/bin/* /go/bin/ + COPY --from=builder /go/bin/* /go/bin/ + COPY --from=pgg /protobuf /protobuf + ENV GOPATH=/go \ + PATH=/go/bin:${PATH} \ + GOROOT=/usr/local/go diff --git a/tool/docker-protoc/Makefile b/tool/docker-protoc/Makefile new file mode 100644 index 000000000..c3191c33a --- /dev/null +++ b/tool/docker-protoc/Makefile @@ -0,0 +1,10 @@ +IMAGE ?= moul/depviz-protoc +VERSION = 1 + +build: + docker build -t $(IMAGE):$(VERSION) . + +publish: build + docker tag $(IMAGE):$(VERSION) $(IMAGE):latest + docker push $(IMAGE):$(VERSION) + docker push $(IMAGE):latest diff --git a/tools/go.mod b/tools/go.mod deleted file mode 100644 index 1dd7cd9ec..000000000 --- a/tools/go.mod +++ /dev/null @@ -1,9 +0,0 @@ -module moul.io/depviz/tools - -go 1.13 - -require ( - github.com/gilliek/go-opml v1.0.0 - github.com/google/go-github/v28 v28.1.1 - golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 -) diff --git a/tools/go.sum b/tools/go.sum deleted file mode 100644 index 149718d08..000000000 --- a/tools/go.sum +++ /dev/null @@ -1,28 +0,0 @@ -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/gilliek/go-opml v1.0.0 h1:X8xVjtySRXU/x6KvaiXkn7OV3a4DHqxY8Rpv6U/JvCY= -github.com/gilliek/go-opml v1.0.0/go.mod h1:fOxmtlzyBvUjU6bjpdjyxCGlWz+pgtAHrHf/xRZl3lk= -github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo= -github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= diff --git a/web/cmd_web.go b/web/cmd_web.go deleted file mode 100644 index 75e19d639..000000000 --- a/web/cmd_web.go +++ /dev/null @@ -1,50 +0,0 @@ -package web // import "moul.io/depviz/web" - -import ( - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/spf13/viper" - "go.uber.org/zap" - "moul.io/depviz/cli" - "moul.io/depviz/sql" -) - -func GetOptions(commands cli.Commands) Options { - return commands["web"].(*webCommand).opts -} - -func Commands() cli.Commands { - return cli.Commands{"web": &webCommand{}} -} - -type webCommand struct { - opts Options -} - -func (cmd *webCommand) CobraCommand(commands cli.Commands) *cobra.Command { - cc := &cobra.Command{ - Use: "web", - Short: "Output web of relationships between all issues stored in database", - Args: cobra.MaximumNArgs(0), - RunE: func(_ *cobra.Command, args []string) error { - opts := cmd.opts - opts.SQL = sql.GetOptions(commands) - return Web(&opts) - }, - } - cmd.ParseFlags(cc.Flags()) - commands["sql"].ParseFlags(cc.Flags()) - return cc -} - -func (cmd *webCommand) LoadDefaultOptions() error { - return viper.Unmarshal(&cmd.opts) -} - -func (cmd *webCommand) ParseFlags(flags *pflag.FlagSet) { - flags.StringVarP(&cmd.opts.Bind, "bind", "b", ":2020", "HTTP server bind address") - flags.BoolVarP(&cmd.opts.GenDoc, "gendoc", "", false, "generate Markdown documentation and exit") - if err := viper.BindPFlags(flags); err != nil { - zap.L().Warn("failed to bind viper flags", zap.Error(err)) - } -} diff --git a/web/web.go b/web/web.go deleted file mode 100644 index 8e4bd5a77..000000000 --- a/web/web.go +++ /dev/null @@ -1,149 +0,0 @@ -package web - -import ( - "bytes" - "fmt" - "log" - "net/http" - "os" - "os/exec" - "path/filepath" - "strings" - "time" - - "github.com/go-chi/chi" - "github.com/go-chi/chi/middleware" - "github.com/go-chi/docgen" - "github.com/go-chi/render" - "moul.io/depviz/graph" - "moul.io/depviz/model" - "moul.io/depviz/sql" -) - -type Options struct { - SQL sql.Options `mapstructure:"sql"` // inherited with sql.GetOptions() - Bind string `mapstructure:"bind"` - GenDoc bool `mapstructure:"gendoc"` - // Targets []multipmuri.Entity `mapstructure:"targets"` // parsed from Args -} - -func Web(opts *Options) error { - r := chi.NewRouter() - - //r.Use(middleware.RequestID) - //r.Use(middleware.RealIP) - r.Use(middleware.Logger) - r.Use(middleware.Recoverer) - //r.Use(middleware.URLFormat) - r.Use(middleware.Timeout(5 * time.Second)) - - h := handler{opts: opts} - - r.Route("/api", func(r chi.Router) { - r.Route("/", func(r chi.Router) { - r.Use(render.SetContentType(render.ContentTypeJSON)) - r.Get("/issues.json", h.webListIssues) - }) - r.Get("/graph/dot", h.webDotIssues) - r.Get("/graph/image", h.webImageIssues) - }) - - workDir, _ := os.Getwd() - filesDir := filepath.Join(workDir, "static") - FileServer(r, "/", http.Dir(filesDir)) - - if opts.GenDoc { - fmt.Println(docgen.MarkdownRoutesDoc(r, docgen.MarkdownOpts{ - ProjectPath: "moul.io/depviz", - Intro: "Welcome to depviz generated docs.", - })) - return nil - } - - log.Printf("Listening on %s", opts.Bind) - return http.ListenAndServe(opts.Bind, r) -} - -type handler struct { - opts *Options -} - -// webListIssues loads the issues stored in the database and writes them to the http response. -func (h *handler) webListIssues(w http.ResponseWriter, r *http.Request) { - db, err := sql.FromOpts(&h.opts.SQL) - if err != nil { - _ = render.Render(w, r, ErrRender(err)) - return - } - - issues, err := sql.LoadAllIssues(db) - if err != nil { - _ = render.Render(w, r, ErrRender(err)) - return - } - - if err != nil { - _ = render.Render(w, r, ErrRender(err)) - return - } - - list := []render.Renderer{} - for _, issue := range issues { - if issue.IsHidden { - continue - } - list = append(list, issue) - } - - if err := render.RenderList(w, r, list); err != nil { - _ = render.Render(w, r, ErrRender(err)) - return - } -} - -func (h *handler) webGraphviz(r *http.Request) (string, error) { - args := strings.Split(r.URL.Query().Get("targets"), ",") - targets, err := model.ParseTargets(args) - if err != nil { - return "", err - } - opts := graph.Options{ - SQL: h.opts.SQL, - Targets: targets, - // FIXME: add more options - } - return graph.Graph(&opts) -} - -func (h *handler) webDotIssues(w http.ResponseWriter, r *http.Request) { - out, err := h.webGraphviz(r) - if err != nil { - _ = render.Render(w, r, ErrRender(err)) - return - } - - _, _ = w.Write([]byte(out)) -} - -func (h *handler) webImageIssues(w http.ResponseWriter, r *http.Request) { - out, err := h.webGraphviz(r) - if err != nil { - _ = render.Render(w, r, ErrRender(err)) - return - } - - binary, err := exec.LookPath("dot") - if err != nil { - _ = render.Render(w, r, ErrRender(err)) - return - } - - cmd := exec.Command(binary, "-Tsvg") // guardrails-disable-line - cmd.Stdin = bytes.NewBuffer([]byte(out)) - cmd.Stdout = w - - if err := cmd.Run(); err != nil { - _ = render.Render(w, r, ErrRender(err)) - return - } -}