From 14024f208c024ef29da8199b62efed2c81a9afb5 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Thu, 18 Jul 2024 11:53:10 +0200 Subject: [PATCH] Support properties in selectors --- internal/engine/selectors/mock/selectors.go | 13 +- internal/engine/selectors/selectors.go | 173 +++++++++++++++---- internal/engine/selectors/selectors_test.go | 178 ++++++++++++++++++-- internal/proto/internal.pb.go | 135 ++++++++------- internal/proto/internal.proto | 2 + 5 files changed, 394 insertions(+), 107 deletions(-) diff --git a/internal/engine/selectors/mock/selectors.go b/internal/engine/selectors/mock/selectors.go index 70a3f82a1c..65bcf293e3 100644 --- a/internal/engine/selectors/mock/selectors.go +++ b/internal/engine/selectors/mock/selectors.go @@ -80,16 +80,21 @@ func (m *MockSelection) EXPECT() *MockSelectionMockRecorder { } // Select mocks base method. -func (m *MockSelection) Select(arg0 *proto.SelectorEntity) (bool, error) { +func (m *MockSelection) Select(arg0 *proto.SelectorEntity, arg1 ...selectors.SelectOption) (bool, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Select", arg0) + varargs := []any{arg0} + for _, a := range arg1 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Select", varargs...) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // Select indicates an expected call of Select. -func (mr *MockSelectionMockRecorder) Select(arg0 any) *gomock.Call { +func (mr *MockSelectionMockRecorder) Select(arg0 any, arg1 ...any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Select", reflect.TypeOf((*MockSelection)(nil).Select), arg0) + varargs := append([]any{arg0}, arg1...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Select", reflect.TypeOf((*MockSelection)(nil).Select), varargs...) } diff --git a/internal/engine/selectors/selectors.go b/internal/engine/selectors/selectors.go index 70c25c0409..64f8166679 100644 --- a/internal/engine/selectors/selectors.go +++ b/internal/engine/selectors/selectors.go @@ -19,16 +19,27 @@ package selectors import ( + "errors" "fmt" + "strings" "sync" "github.com/google/cel-go/cel" "github.com/google/cel-go/checker/decls" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" + "github.com/google/cel-go/interpreter" internalpb "github.com/stacklok/minder/internal/proto" minderv1 "github.com/stacklok/minder/pkg/api/protobuf/go/minder/v1" ) +var ( + // ErrResultUnknown is returned when the result of a selector expression is unknown + // this tells the caller to try again with more information + ErrResultUnknown = errors.New("result is unknown") +) + // celEnvFactory is an interface for creating CEL environments // for an entity. Each entity must implement this interface to be // usable in selectors @@ -85,7 +96,36 @@ func newEnvForEntity(varName string, typ any, typName string) (*cel.Env, error) return env, nil } -type compiledSelector = cel.Program +type compiledSelector struct { + ast *cel.Ast + program cel.Program +} + +// compileSelectorForEntity compiles a selector expression for a given entity type into a CEL program +func compileSelectorForEntity(env *cel.Env, selector string) (*compiledSelector, error) { + ast, issues := env.Parse(selector) + if issues.Err() != nil { + return nil, fmt.Errorf("failed to parse expression %q: %w", selector, issues.Err()) + } + + checked, issues := env.Check(ast) + if issues.Err() != nil { + return nil, fmt.Errorf("failed to check expression %q: %w", selector, issues.Err()) + } + + program, err := env.Program(checked, + // OptPartialEval is needed to enable partial evaluation of the expression + // OptTrackState is needed to get the details about partial evaluation (aka what is missing) + cel.EvalOptions(cel.OptTrackState, cel.OptPartialEval)) + if err != nil { + return nil, fmt.Errorf("failed to create program for expression %q: %w", selector, err) + } + + return &compiledSelector{ + ast: checked, + program: program, + }, nil +} // SelectionBuilder is an interface for creating Selections (a collection of compiled CEL expressions) // for an entity type. This is what the user of this module uses. The interface makes it easier to pass @@ -137,7 +177,12 @@ func (e *Env) NewSelectionFromProfile( entityType minderv1.Entity, profileSelection []*minderv1.Profile_Selector, ) (Selection, error) { - selector := make([]cel.Program, 0, len(profileSelection)) + selector := make([]*compiledSelector, 0, len(profileSelection)) + + env, err := e.envForEntity(entityType) + if err != nil { + return nil, fmt.Errorf("failed to get environment for entity %v: %w", entityType, err) + } for _, sel := range profileSelection { ent := minderv1.EntityFromString(sel.GetEntity()) @@ -145,45 +190,21 @@ func (e *Env) NewSelectionFromProfile( continue } - program, err := e.compileSelectorForEntity(sel.Selector, ent) + compSel, err := compileSelectorForEntity(env, sel.Selector) if err != nil { return nil, fmt.Errorf("failed to compile selector %q: %w", sel.Selector, err) } - selector = append(selector, program) + selector = append(selector, compSel) } return &EntitySelection{ + env: env, selector: selector, entity: entityType, }, nil } -// compileSelectorForEntity compiles a selector expression for a given entity type into a CEL program -func (e *Env) compileSelectorForEntity(selector string, entityType minderv1.Entity) (compiledSelector, error) { - env, err := e.envForEntity(entityType) - if err != nil { - return nil, fmt.Errorf("failed to get environment for entity %v: %w", entityType, err) - } - - ast, issues := env.Parse(selector) - if issues.Err() != nil { - return nil, fmt.Errorf("failed to parse expression %q: %w", selector, issues.Err()) - } - - checked, issues := env.Check(ast) - if issues.Err() != nil { - return nil, fmt.Errorf("failed to check expression %q: %w", selector, issues.Err()) - } - - program, err := env.Program(checked) - if err != nil { - return nil, fmt.Errorf("failed to create program for expression %q: %w", selector, err) - } - - return program, nil -} - // envForEntity gets the CEL environment for a given entity type. If the environment is not cached, // it creates it using the factory for that entity type. func (e *Env) envForEntity(entity minderv1.Entity) (*cel.Env, error) { @@ -199,34 +220,65 @@ func (e *Env) envForEntity(entity minderv1.Entity) (*cel.Env, error) { return cache.env, cache.err } +// SelectOption is a functional option for the Select method +type SelectOption func(*selectionOptions) + +type selectionOptions struct { + unknownPaths []string +} + +// WithUnknownPaths sets the explicit unknown paths for the selection +func WithUnknownPaths(paths ...string) SelectOption { + return func(o *selectionOptions) { + o.unknownPaths = paths + } +} + // Selection is an interface for selecting entities based on a profile type Selection interface { - Select(*internalpb.SelectorEntity) (bool, error) + Select(*internalpb.SelectorEntity, ...SelectOption) (bool, error) } // EntitySelection is a struct that holds the compiled CEL expressions for a given entity type type EntitySelection struct { - selector []cel.Program + env *cel.Env + + selector []*compiledSelector entity minderv1.Entity } // Select return true if the entity matches all the compiled expressions and false otherwise -func (s *EntitySelection) Select(se *internalpb.SelectorEntity) (bool, error) { +func (s *EntitySelection) Select(se *internalpb.SelectorEntity, userOpts ...SelectOption) (bool, error) { if se == nil { return false, fmt.Errorf("input entity is nil") } + var opts selectionOptions + for _, opt := range userOpts { + opt(&opts) + } + for _, sel := range s.selector { entityMap, err := inputAsMap(se) if err != nil { return false, fmt.Errorf("failed to convert input to map: %w", err) } - out, _, err := sel.Eval(entityMap) + out, details, err := s.evalWithOpts(&opts, sel, entityMap) + // check unknowns /before/ an error. Maybe we should try to special-case the one + // error we get from the CEL library in this case and check for the rest? + if s.detailHasUnknowns(sel, details) { + return false, ErrResultUnknown + } + if err != nil { return false, fmt.Errorf("failed to evaluate Expression: %w", err) } + if types.IsUnknown(out) { + return false, ErrResultUnknown + } + if out.Type() != cel.BoolType { return false, fmt.Errorf("expression did not evaluate to a boolean: %v", out) } @@ -239,6 +291,61 @@ func (s *EntitySelection) Select(se *internalpb.SelectorEntity) (bool, error) { return true, nil } +func unknownAttributesFromOpts(unknownPaths []string) []*interpreter.AttributePattern { + unknowns := make([]*interpreter.AttributePattern, 0, len(unknownPaths)) + + for _, path := range unknownPaths { + frags := strings.Split(path, ".") + if len(frags) == 0 { + continue + } + + unknownAttr := interpreter.NewAttributePattern(frags[0]) + if len(frags) > 1 { + for _, frag := range frags[1:] { + unknownAttr = unknownAttr.QualString(frag) + } + } + unknowns = append(unknowns, unknownAttr) + } + + return unknowns +} + +func (_ *EntitySelection) evalWithOpts( + opts *selectionOptions, sel *compiledSelector, entityMap map[string]any, +) (ref.Val, *cel.EvalDetails, error) { + unknowns := unknownAttributesFromOpts(opts.unknownPaths) + if len(unknowns) > 0 { + partialMap, err := cel.PartialVars(entityMap, unknowns...) + if err != nil { + return types.NewErr("failed to create partial value"), nil, fmt.Errorf("failed to create partial vars: %w", err) + } + + return sel.program.Eval(partialMap) + } + + return sel.program.Eval(entityMap) +} + +func (s *EntitySelection) detailHasUnknowns(sel *compiledSelector, details *cel.EvalDetails) bool { + if details == nil { + return false + } + + residualAst, err := s.env.ResidualAst(sel.ast, details) + if err != nil { + return false + } + + checked, err := cel.AstToCheckedExpr(residualAst) + if err != nil { + return false + } + + return checked.GetExpr().GetConstExpr() == nil +} + func inputAsMap(se *internalpb.SelectorEntity) (map[string]any, error) { var value any diff --git a/internal/engine/selectors/selectors_test.go b/internal/engine/selectors/selectors_test.go index e883b7455d..765e9f521c 100644 --- a/internal/engine/selectors/selectors_test.go +++ b/internal/engine/selectors/selectors_test.go @@ -86,15 +86,23 @@ func withIsFork(isFork bool) testRepoOption { } } +func withProperties(properties map[string]string) testRepoOption { + return func(selRepo *internalpb.SelectorRepository) { + selRepo.Properties = properties + } +} + func TestSelectSelectorEntity(t *testing.T) { t.Parallel() scenarios := []struct { - name string - exprs []*minderv1.Profile_Selector - selectorEntityBld testSelectorEntityBuilder - expectedErr string - selected bool + name string + exprs []*minderv1.Profile_Selector + selectOptions []SelectOption + selectorEntityBld testSelectorEntityBuilder + expectedNewSelectionErr string + expectedSelectErr error + selected bool }{ { name: "No selectors", @@ -257,9 +265,9 @@ func TestSelectSelectorEntity(t *testing.T) { Selector: "artifact.name != 'testorg/testrepo'", }, }, - selectorEntityBld: newTestRepoSelectorEntity(), - expectedErr: "undeclared reference to 'artifact'", - selected: false, + selectorEntityBld: newTestRepoSelectorEntity(), + expectedNewSelectionErr: "undeclared reference to 'artifact'", + selected: false, }, { name: "Attempt to use a repo attribute that doesn't exist", @@ -269,8 +277,80 @@ func TestSelectSelectorEntity(t *testing.T) { Selector: "repository.iamnothere == 'value'", }, }, + selectorEntityBld: newTestRepoSelectorEntity(), + expectedNewSelectionErr: "undefined field 'iamnothere'", + selected: false, + }, + { + name: "Use a property that is defined and true result", + exprs: []*minderv1.Profile_Selector{ + { + Entity: minderv1.RepositoryEntity.String(), + Selector: "repository.properties['is_fork'] != 'true'", + }, + }, + selectorEntityBld: newTestRepoSelectorEntity(withProperties(map[string]string{"is_fork": "false"})), + selected: true, + }, + { + name: "Use a property that is defined and false result", + exprs: []*minderv1.Profile_Selector{ + { + Entity: minderv1.RepositoryEntity.String(), + Selector: "repository.properties['is_fork'] != 'true'", + }, + }, + selectorEntityBld: newTestRepoSelectorEntity(withProperties(map[string]string{"is_fork": "true"})), + selected: false, + }, + { + name: "Properties are non-nil but we use one that is not defined", + exprs: []*minderv1.Profile_Selector{ + { + Entity: minderv1.RepositoryEntity.String(), + Selector: "repository.properties['is_private'] != 'true'", + }, + }, + selectorEntityBld: newTestRepoSelectorEntity(withProperties(map[string]string{"is_fork": "true"})), + expectedSelectErr: ErrResultUnknown, + selected: false, + }, + { + name: "Attempt to use a property while having nil properties", + exprs: []*minderv1.Profile_Selector{ + { + Entity: minderv1.RepositoryEntity.String(), + Selector: "repository.properties['is_fork'] != 'true'", + }, + }, + selectorEntityBld: newTestRepoSelectorEntity(), + expectedSelectErr: ErrResultUnknown, + selected: false, + }, + { + name: "The selector shortcuts if evaluation is not needed for properties", + exprs: []*minderv1.Profile_Selector{ + { + Entity: minderv1.RepositoryEntity.String(), + Selector: "repository.name == 'testorg/testrepo' || repository.properties['is_fork'] != 'true'", + }, + }, selectorEntityBld: newTestRepoSelectorEntity(), - expectedErr: "undefined field 'iamnothere'", + selected: true, + }, + { + name: "Attempt to use a property but explicitly tell Select that it's not defined", + exprs: []*minderv1.Profile_Selector{ + { + Entity: minderv1.RepositoryEntity.String(), + Selector: "repository.properties['is_fork'] != 'true'", + }, + }, + selectOptions: []SelectOption{ + WithUnknownPaths("repository.properties"), + }, + selectorEntityBld: newTestRepoSelectorEntity(withProperties(map[string]string{"is_fork": "true"})), + expectedSelectErr: ErrResultUnknown, selected: false, }, } @@ -285,18 +365,92 @@ func TestSelectSelectorEntity(t *testing.T) { se := scenario.selectorEntityBld() sels, err := env.NewSelectionFromProfile(se.EntityType, scenario.exprs) - if scenario.expectedErr != "" { + if scenario.expectedNewSelectionErr != "" { require.Error(t, err) - require.Contains(t, err.Error(), scenario.expectedErr) + require.Contains(t, err.Error(), scenario.expectedNewSelectionErr) return } require.NoError(t, err) require.NotNil(t, sels) - selected, err := sels.Select(se) + selected, err := sels.Select(se, scenario.selectOptions...) + if scenario.expectedSelectErr != nil { + require.Error(t, err) + require.Equal(t, scenario.expectedSelectErr, err) + return + } + require.NoError(t, err) require.Equal(t, scenario.selected, selected) }) } } + +func TestSelectorEntityFillProperties(t *testing.T) { + t.Parallel() + + scenarios := []struct { + name string + exprs []*minderv1.Profile_Selector + mockFetch func(*internalpb.SelectorEntity) + secondSucceeds bool + }{ + { + name: "Fetch a property that exists", + exprs: []*minderv1.Profile_Selector{ + { + Entity: minderv1.RepositoryEntity.String(), + Selector: "repository.properties['is_fork'] == 'false'", + }, + }, + mockFetch: func(se *internalpb.SelectorEntity) { + se.Entity.(*internalpb.SelectorEntity_Repository).Repository.Properties = map[string]string{ + "is_fork": "false", + } + }, + secondSucceeds: true, + }, + { + name: "Fail to fetch a property", + exprs: []*minderv1.Profile_Selector{ + { + Entity: minderv1.RepositoryEntity.String(), + Selector: "repository.properties['is_private'] == 'false'", + }, + }, + mockFetch: func(se *internalpb.SelectorEntity) { + se.Entity.(*internalpb.SelectorEntity_Repository).Repository.Properties = map[string]string{ + "is_fork": "false", + } + }, + secondSucceeds: false, + }, + } + + for _, scenario := range scenarios { + env, err := NewEnv() + require.NoError(t, err) + + seBuilder := newTestRepoSelectorEntity() + se := seBuilder() + + sels, err := env.NewSelectionFromProfile(se.EntityType, scenario.exprs) + require.NoError(t, err) + require.NotNil(t, sels) + + _, err = sels.Select(se, WithUnknownPaths("repository.properties")) + require.ErrorIs(t, err, ErrResultUnknown) + + // simulate fetching properties + scenario.mockFetch(se) + + selected, err := sels.Select(se) + if scenario.secondSucceeds { + require.NoError(t, err) + require.True(t, selected) + } else { + require.ErrorIs(t, err, ErrResultUnknown) + } + } +} diff --git a/internal/proto/internal.pb.go b/internal/proto/internal.pb.go index f614914cc9..c623e1d02d 100644 --- a/internal/proto/internal.pb.go +++ b/internal/proto/internal.pb.go @@ -334,7 +334,8 @@ type SelectorRepository struct { IsFork *bool `protobuf:"varint,3,opt,name=is_fork,json=isFork,proto3,oneof" json:"is_fork,omitempty"` // is_private is true if the repository is private, nil if "don't know" or rather // not applicable to this provider - IsPrivate *bool `protobuf:"varint,4,opt,name=is_private,json=isPrivate,proto3,oneof" json:"is_private,omitempty"` + IsPrivate *bool `protobuf:"varint,4,opt,name=is_private,json=isPrivate,proto3,oneof" json:"is_private,omitempty"` + Properties map[string]string `protobuf:"bytes,5,rep,name=properties,proto3" json:"properties,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *SelectorRepository) Reset() { @@ -397,6 +398,13 @@ func (x *SelectorRepository) GetIsPrivate() bool { return false } +func (x *SelectorRepository) GetProperties() map[string]string { + if x != nil { + return x.Properties + } + return nil +} + type SelectorArtifact struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -856,7 +864,7 @@ var file_internal_proto_rawDesc = []byte{ 0x10, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0xbd, 0x01, 0x0a, 0x12, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0xca, 0x02, 0x0a, 0x12, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, @@ -867,43 +875,52 @@ var file_internal_proto_rawDesc = []byte{ 0x00, 0x52, 0x06, 0x69, 0x73, 0x46, 0x6f, 0x72, 0x6b, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x09, 0x69, 0x73, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x88, 0x01, 0x01, - 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x69, 0x73, 0x5f, 0x66, 0x6f, 0x72, 0x6b, 0x42, 0x0d, 0x0a, 0x0b, - 0x5f, 0x69, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x22, 0x72, 0x0a, 0x10, 0x53, - 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, - 0x94, 0x02, 0x0a, 0x0e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x12, 0x32, 0x0a, 0x0b, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x6d, 0x69, 0x6e, 0x64, 0x65, 0x72, - 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x0a, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x08, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x12, 0x3e, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x6f, 0x72, 0x79, 0x48, 0x00, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, - 0x72, 0x79, 0x12, 0x38, 0x0a, 0x08, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, - 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, - 0x48, 0x00, 0x52, 0x08, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x42, 0x08, 0x0a, 0x06, - 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x2a, 0x72, 0x0a, 0x0c, 0x44, 0x65, 0x70, 0x45, 0x63, 0x6f, - 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1d, 0x0a, 0x19, 0x44, 0x45, 0x50, 0x5f, 0x45, 0x43, - 0x4f, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, - 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x44, 0x45, 0x50, 0x5f, 0x45, 0x43, 0x4f, - 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x4e, 0x50, 0x4d, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, - 0x44, 0x45, 0x50, 0x5f, 0x45, 0x43, 0x4f, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x47, 0x4f, - 0x10, 0x02, 0x12, 0x16, 0x0a, 0x12, 0x44, 0x45, 0x50, 0x5f, 0x45, 0x43, 0x4f, 0x53, 0x59, 0x53, - 0x54, 0x45, 0x4d, 0x5f, 0x50, 0x59, 0x50, 0x49, 0x10, 0x03, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x6c, 0x6f, - 0x6b, 0x2f, 0x6d, 0x69, 0x6e, 0x64, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x12, 0x4c, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x05, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, + 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, + 0x72, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x1a, 0x3d, + 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0a, 0x0a, + 0x08, 0x5f, 0x69, 0x73, 0x5f, 0x66, 0x6f, 0x72, 0x6b, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x69, 0x73, + 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x22, 0x72, 0x0a, 0x10, 0x53, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x36, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x53, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x08, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x94, 0x02, 0x0a, + 0x0e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, + 0x32, 0x0a, 0x0b, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x6d, 0x69, 0x6e, 0x64, 0x65, 0x72, 0x2e, 0x76, 0x31, + 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x0a, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x36, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, + 0x3e, 0x0a, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x53, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, + 0x79, 0x48, 0x00, 0x52, 0x0a, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x12, + 0x38, 0x0a, 0x08, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x53, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x6f, 0x72, 0x41, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x48, 0x00, 0x52, + 0x08, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x42, 0x08, 0x0a, 0x06, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x2a, 0x72, 0x0a, 0x0c, 0x44, 0x65, 0x70, 0x45, 0x63, 0x6f, 0x73, 0x79, 0x73, + 0x74, 0x65, 0x6d, 0x12, 0x1d, 0x0a, 0x19, 0x44, 0x45, 0x50, 0x5f, 0x45, 0x43, 0x4f, 0x53, 0x59, + 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x44, 0x45, 0x50, 0x5f, 0x45, 0x43, 0x4f, 0x53, 0x59, 0x53, + 0x54, 0x45, 0x4d, 0x5f, 0x4e, 0x50, 0x4d, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x44, 0x45, 0x50, + 0x5f, 0x45, 0x43, 0x4f, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x47, 0x4f, 0x10, 0x02, 0x12, + 0x16, 0x0a, 0x12, 0x44, 0x45, 0x50, 0x5f, 0x45, 0x43, 0x4f, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, + 0x5f, 0x50, 0x59, 0x50, 0x49, 0x10, 0x03, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x74, 0x61, 0x63, 0x6b, 0x6c, 0x6f, 0x6b, 0x2f, 0x6d, + 0x69, 0x6e, 0x64, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -919,7 +936,7 @@ func file_internal_proto_rawDescGZIP() []byte { } var file_internal_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_internal_proto_msgTypes = make([]protoimpl.MessageInfo, 11) +var file_internal_proto_msgTypes = make([]protoimpl.MessageInfo, 12) var file_internal_proto_goTypes = []any{ (DepEcosystem)(0), // 0: internal.DepEcosystem (*Dependency)(nil), // 1: internal.Dependency @@ -933,29 +950,31 @@ var file_internal_proto_goTypes = []any{ (*PrDependencies_ContextualDependency_FilePatch)(nil), // 9: internal.PrDependencies.ContextualDependency.FilePatch (*PrContents_File)(nil), // 10: internal.PrContents.File (*PrContents_File_Line)(nil), // 11: internal.PrContents.File.Line - (*v1.PullRequest)(nil), // 12: minder.v1.PullRequest - (v1.Entity)(0), // 13: minder.v1.Entity + nil, // 12: internal.SelectorRepository.PropertiesEntry + (*v1.PullRequest)(nil), // 13: minder.v1.PullRequest + (v1.Entity)(0), // 14: minder.v1.Entity } var file_internal_proto_depIdxs = []int32{ 0, // 0: internal.Dependency.ecosystem:type_name -> internal.DepEcosystem - 12, // 1: internal.PrDependencies.pr:type_name -> minder.v1.PullRequest + 13, // 1: internal.PrDependencies.pr:type_name -> minder.v1.PullRequest 8, // 2: internal.PrDependencies.deps:type_name -> internal.PrDependencies.ContextualDependency - 12, // 3: internal.PrContents.pr:type_name -> minder.v1.PullRequest + 13, // 3: internal.PrContents.pr:type_name -> minder.v1.PullRequest 10, // 4: internal.PrContents.files:type_name -> internal.PrContents.File 4, // 5: internal.SelectorRepository.provider:type_name -> internal.SelectorProvider - 4, // 6: internal.SelectorArtifact.provider:type_name -> internal.SelectorProvider - 13, // 7: internal.SelectorEntity.entity_type:type_name -> minder.v1.Entity - 4, // 8: internal.SelectorEntity.provider:type_name -> internal.SelectorProvider - 5, // 9: internal.SelectorEntity.repository:type_name -> internal.SelectorRepository - 6, // 10: internal.SelectorEntity.artifact:type_name -> internal.SelectorArtifact - 1, // 11: internal.PrDependencies.ContextualDependency.dep:type_name -> internal.Dependency - 9, // 12: internal.PrDependencies.ContextualDependency.file:type_name -> internal.PrDependencies.ContextualDependency.FilePatch - 11, // 13: internal.PrContents.File.patch_lines:type_name -> internal.PrContents.File.Line - 14, // [14:14] is the sub-list for method output_type - 14, // [14:14] is the sub-list for method input_type - 14, // [14:14] is the sub-list for extension type_name - 14, // [14:14] is the sub-list for extension extendee - 0, // [0:14] is the sub-list for field type_name + 12, // 6: internal.SelectorRepository.properties:type_name -> internal.SelectorRepository.PropertiesEntry + 4, // 7: internal.SelectorArtifact.provider:type_name -> internal.SelectorProvider + 14, // 8: internal.SelectorEntity.entity_type:type_name -> minder.v1.Entity + 4, // 9: internal.SelectorEntity.provider:type_name -> internal.SelectorProvider + 5, // 10: internal.SelectorEntity.repository:type_name -> internal.SelectorRepository + 6, // 11: internal.SelectorEntity.artifact:type_name -> internal.SelectorArtifact + 1, // 12: internal.PrDependencies.ContextualDependency.dep:type_name -> internal.Dependency + 9, // 13: internal.PrDependencies.ContextualDependency.file:type_name -> internal.PrDependencies.ContextualDependency.FilePatch + 11, // 14: internal.PrContents.File.patch_lines:type_name -> internal.PrContents.File.Line + 15, // [15:15] is the sub-list for method output_type + 15, // [15:15] is the sub-list for method input_type + 15, // [15:15] is the sub-list for extension type_name + 15, // [15:15] is the sub-list for extension extendee + 0, // [0:15] is the sub-list for field type_name } func init() { file_internal_proto_init() } @@ -1108,7 +1127,7 @@ func file_internal_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_internal_proto_rawDesc, NumEnums: 1, - NumMessages: 11, + NumMessages: 12, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/proto/internal.proto b/internal/proto/internal.proto index fe314b398d..4d1c45c5aa 100644 --- a/internal/proto/internal.proto +++ b/internal/proto/internal.proto @@ -88,6 +88,8 @@ message SelectorRepository { // is_private is true if the repository is private, nil if "don't know" or rather // not applicable to this provider optional bool is_private = 4; + + map properties = 5; } message SelectorArtifact {