gitlab-glaz 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Cargo.toml CHANGED
@@ -4,12 +4,14 @@ members = ["ext/*"]
4
4
  resolver = "2"
5
5
 
6
6
  [workspace.package]
7
- version = "1.0.0"
7
+ version = "1.2.0"
8
8
  edition = "2024"
9
9
 
10
10
  # Shared dependencies for all crates
11
11
  [workspace.dependencies]
12
12
  magnus = { version = "0.8.2", features = ["rb-sys"] }
13
13
  rb-sys = "0.9.128"
14
- glaz-module = { git = "https://gitlab.com/gitlab-org/auth/glaz.git", rev = "13a32ad3f0e6490384afb6ba6a3c3e2a6439901a" }
15
- glaz-roles = { git = "https://gitlab.com/gitlab-org/auth/glaz.git", rev = "13a32ad3f0e6490384afb6ba6a3c3e2a6439901a" }
14
+ prost = "0.14"
15
+ glaz-module = { git = "https://gitlab.com/gitlab-org/auth/glaz.git", rev = "af9019a186fbb42cede0ad5ba7496aff2f88cb41" }
16
+ glaz-roles = { git = "https://gitlab.com/gitlab-org/auth/glaz.git", rev = "af9019a186fbb42cede0ad5ba7496aff2f88cb41" }
17
+ glaz-proto = { git = "https://gitlab.com/gitlab-org/auth/glaz.git", rev = "af9019a186fbb42cede0ad5ba7496aff2f88cb41" }
data/README.md CHANGED
@@ -35,7 +35,7 @@ engine.load_policies(new_cedar_policies)
35
35
 
36
36
  The schema is loaded once at construction and cannot change; build a new engine to use a new schema.
37
37
 
38
- Schemas must declare `User` and `Resource` entities (checks build `User::"<subject_uuid>"` and `Resource::"<object_uuid>"` UIDs), and every action's context type must include `user_permissions: Set<Action>`, which the engine injects with the checked action.
38
+ Schemas must declare `User` and `Resource` entities (checks build `User::"<subject_uuid>"` and `Resource::"<object_uuid>"` UIDs), and every action's context type must include `permissions: Set<Action>`, which the engine injects with the checked action.
39
39
 
40
40
  ## Development
41
41
 
data/ext/glaz/Cargo.toml CHANGED
@@ -9,5 +9,7 @@ crate-type = ["cdylib"]
9
9
 
10
10
  [dependencies]
11
11
  glaz-module = { workspace = true }
12
+ glaz-proto = { workspace = true }
12
13
  glaz-roles = { workspace = true }
13
14
  magnus = { workspace = true }
15
+ prost = { workspace = true }
data/ext/glaz/src/lib.rs CHANGED
@@ -1,4 +1,5 @@
1
1
  use magnus::{Error, Ruby, prelude::*};
2
+ use prost::Message;
2
3
 
3
4
  /// Thin 1:1 wrapper around `glaz_module::PermissionCheckEngine`. The engine
4
5
  /// starts empty; `load_schema` must be called before `load_policies`, and
@@ -51,6 +52,93 @@ impl PermissionCheckEngine {
51
52
  }
52
53
  }
53
54
 
55
+ /// Thin 1:1 wrapper around `glaz_module::GovernPolicyCheckEngine`, the Rego
56
+ /// governance evaluator. Stateless between calls; `evaluate` raises on
57
+ /// failure (matching `PermissionCheckEngine`'s convention) and returns the
58
+ /// encoded `EvaluateGovernPolicyResponse` on success — `ArgumentError` for
59
+ /// bad policy/input, `RuntimeError` for an engine-side fault, see
60
+ /// `map_error`; `debug_evaluate` is infallible and carries failures in-band
61
+ /// in its JSON response instead, since it's a raw debugging escape hatch;
62
+ /// `validate` is also infallible — policy errors are returned as
63
+ /// `Ok(valid: false, errors: [...])` so the Policy Store can surface
64
+ /// actionable feedback without treating a user mistake as an exceptional
65
+ /// condition; a malformed protobuf payload raises `ArgumentError`, while
66
+ /// internal engine failures (`NotInitialized`, `Runtime`) raise
67
+ /// `RuntimeError` via `map_error`.
68
+ ///
69
+ /// Registered as `Glaz::Native::GovernPolicyEngine` (not
70
+ /// `Glaz::GovernPolicyEngine`) specifically to avoid colliding, in name,
71
+ /// with the polished `Gitlab::Glaz::GovernPolicyEngine` wrapper — this
72
+ /// class is raw FFI plumbing; prefer the wrapper unless you specifically
73
+ /// need `debug_evaluate`.
74
+ #[magnus::wrap(class = "Glaz::Native::GovernPolicyEngine")]
75
+ struct GovernPolicyEngine {
76
+ engine: glaz_module::GovernPolicyCheckEngine,
77
+ }
78
+
79
+ impl GovernPolicyEngine {
80
+ fn initialize() -> Self {
81
+ Self {
82
+ engine: glaz_module::GovernPolicyCheckEngine::new(),
83
+ }
84
+ }
85
+
86
+ /// Evaluate using the request's `query` field. `query` must be
87
+ /// non-blank; a blank query is reported in-band as an error rather than
88
+ /// falling back to discovery (use `evaluate` for that).
89
+ fn debug_evaluate(
90
+ ruby: &Ruby,
91
+ rb_self: &Self,
92
+ proto_bytes: magnus::RString,
93
+ ) -> Result<magnus::RString, Error> {
94
+ let bytes = unsafe { proto_bytes.as_slice() };
95
+ let response = rb_self.engine.debug_evaluate(bytes);
96
+ Ok(ruby.str_from_slice(&response))
97
+ }
98
+
99
+ /// Evaluate by auto-discovering the policy's `violation`, `deny`,
100
+ /// and/or `allow` rules. The request's `query` field is ignored.
101
+ ///
102
+ /// Raises on any evaluation failure rather than returning an in-band
103
+ /// error, matching `PermissionCheckEngine#check_action`'s convention.
104
+ /// `map_error` raises `ArgumentError` for malformed protobuf, invalid
105
+ /// policy, oversized documents, undefined results, or an unrecognized
106
+ /// result shape - all caller-fixable - and `RuntimeError` for an
107
+ /// engine-side fault (e.g. the Rego evaluation time budget was
108
+ /// exceeded), which is not.
109
+ fn evaluate(
110
+ ruby: &Ruby,
111
+ rb_self: &Self,
112
+ proto_bytes: magnus::RString,
113
+ ) -> Result<magnus::RString, Error> {
114
+ let bytes = unsafe { proto_bytes.as_slice() };
115
+ let response = rb_self
116
+ .engine
117
+ .evaluate(bytes)
118
+ .map_err(|e| map_error(ruby, e))?;
119
+ Ok(ruby.str_from_slice(&response.encode_to_vec()))
120
+ }
121
+
122
+ /// Parse and compile a Rego policy without evaluating it against input.
123
+ ///
124
+ /// Infallible for policy errors: parse failures and oversized input are
125
+ /// returned in-band as `{ valid: false, errors: [...] }` rather than
126
+ /// raised. Only a malformed protobuf payload raises `ArgumentError`; an
127
+ /// internal engine fault raises `RuntimeError` via `map_error`.
128
+ fn validate(
129
+ ruby: &Ruby,
130
+ rb_self: &Self,
131
+ proto_bytes: magnus::RString,
132
+ ) -> Result<magnus::RString, Error> {
133
+ let bytes = unsafe { proto_bytes.as_slice() };
134
+ let response = rb_self
135
+ .engine
136
+ .validate(bytes)
137
+ .map_err(|e| map_error(ruby, e))?;
138
+ Ok(ruby.str_from_slice(&response.encode_to_vec()))
139
+ }
140
+ }
141
+
54
142
  /// Return the default roles embedded in the extension at build time (parsed
55
143
  /// from glaz-roles' roles/**/*.yml) as an array of hashes shaped like
56
144
  /// `{ id: String, name: String, permissions: [String, ...] }`.
@@ -100,5 +188,14 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
100
188
  "check_action",
101
189
  magnus::method!(PermissionCheckEngine::check_action, 1),
102
190
  )?;
191
+ let native = glaz.define_module("Native")?;
192
+ let govern = native.define_class("GovernPolicyEngine", ruby.class_object())?;
193
+ govern.define_singleton_method("new", magnus::function!(GovernPolicyEngine::initialize, 0))?;
194
+ govern.define_method(
195
+ "debug_evaluate",
196
+ magnus::method!(GovernPolicyEngine::debug_evaluate, 1),
197
+ )?;
198
+ govern.define_method("evaluate", magnus::method!(GovernPolicyEngine::evaluate, 1))?;
199
+ govern.define_method("validate", magnus::method!(GovernPolicyEngine::validate, 1))?;
103
200
  Ok(())
104
201
  }
@@ -15,9 +15,9 @@ module Gitlab
15
15
  # each checkable action with `appliesTo { principal: [User], resource:
16
16
  # [Resource], ... }`, because the engine builds `User::"<subject_uuid>"` and
17
17
  # `Resource::"<object_uuid>"` entity UIDs for every check. The engine also
18
- # injects `user_permissions` (a Set<Action> containing the checked action)
18
+ # injects `permissions` (a Set<Action> containing the checked action)
19
19
  # into the Cedar context, so an action's declared context type must include
20
- # `user_permissions: Set<Action>` alongside any custom attributes.
20
+ # `permissions: Set<Action>` alongside any custom attributes.
21
21
  class Engine
22
22
  # @param schema [String] Cedar schema source (`.cedarschema` DSL).
23
23
  # @param policies [String] Cedar policy source, validated against the
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Gitlab
6
+ module Glaz
7
+ # Wraps a native Glaz::Native::GovernPolicyEngine instance - the Rego
8
+ # governance policy evaluator.
9
+ #
10
+ # The evaluator is a pure function over its arguments: it performs no
11
+ # I/O, holds no state between calls, and executes no actions. The caller
12
+ # supplies the policy's Rego source and the assembled context document,
13
+ # and is responsible for executing the returned actions.
14
+ #
15
+ # +reasons+ and +actions+ are independent, not 1:1: +reasons+ reports why
16
+ # the policy matched - one entry per `violation`/`deny` element the
17
+ # policy produced; empty means it did not match - while +actions+
18
+ # reports what to enforce as a result. Actions are conceptually owned by
19
+ # the policy's configuration in the future central Policy Store, so any
20
+ # match currently yields a single synthesized block action regardless of
21
+ # how many reasons fired. +matched+ is a convenience boolean mirroring
22
+ # "+actions+ is non-empty", for callers who don't want to infer that
23
+ # from array emptiness themselves.
24
+ #
25
+ # #evaluate is the primary path: it always auto-discovers the policy's
26
+ # `violation`/`deny`/`allow` rules (the native +evaluate+ path), returns
27
+ # the hardened Hash shape described below on success, and raises on any
28
+ # evaluation failure - the native call itself raises ArgumentError for
29
+ # bad policy/input and RuntimeError for an engine-side fault; see
30
+ # #evaluate's own doc comment.
31
+ # #debug_evaluate is a separate, secondary method for debugging/testing a Rego query
32
+ # directly: it performs no violation/deny/allow interpretation, does
33
+ # not raise, and returns whatever the query evaluated to (plus any
34
+ # error, in-band) with no guarantee about its shape.
35
+ class GovernPolicyEngine
36
+ def initialize
37
+ @native = ::Glaz::Native::GovernPolicyEngine.new
38
+ end
39
+
40
+ # Evaluate a Rego governance policy against context.
41
+ #
42
+ # @param policy_rego [String] Rego policy text (max 64 KiB).
43
+ # @param context [Hash] the host-assembled context, passed to the
44
+ # policy as the Rego `input` document (max 1 MiB as JSON).
45
+ # @param data [Hash] optional precomputed Rego `data` document (e.g.
46
+ # cached settings or scan results). Top-level keys must not collide
47
+ # with the policy's package path.
48
+ # @raise [ArgumentError] for a caller-fixable evaluation failure -
49
+ # malformed policy, oversized documents, an unrecognized
50
+ # violation/deny/allow shape, and so on.
51
+ # @raise [RuntimeError] for an engine-side fault, e.g. the Rego
52
+ # evaluation time budget was exceeded - not the caller's fault.
53
+ # Callers can rely on a successful return meaning the policy
54
+ # evaluated cleanly; there is no in-band error to check.
55
+ # @return [Hash] `{ matched: Boolean, actions: Array<Hash>, reasons:
56
+ # Array<Hash> }`. `matched` is `true` exactly when `actions`
57
+ # (equivalently `reasons`) is non-empty - a convenience so callers
58
+ # don't have to infer "did the policy fire" from array emptiness.
59
+ # It is not itself an allow/deny decision: today every action is
60
+ # `"block"`, so `matched` and "should deny" coincide, but a future
61
+ # Policy Store action type (e.g. `"log"`) could match without
62
+ # implying denial - check `actions` for enforcement decisions. Each
63
+ # action is `{ action_type: String, message: String | nil, params:
64
+ # Hash }`; each reason is `{ message: String | nil, details: Hash
65
+ # }`. Empty +actions+ means the policy allows the operation.
66
+ def evaluate(policy_rego:, context:, data: {})
67
+ encoded = ::Glaz::Govern::V1::EvaluateGovernPolicyRequest.encode(
68
+ ::Glaz::Govern::V1::EvaluateGovernPolicyRequest.new(
69
+ policy_rego: policy_rego,
70
+ input_json: context.to_json,
71
+ data_json: data.nil? || data.empty? ? "" : data.to_json
72
+ )
73
+ )
74
+
75
+ response = ::Glaz::Govern::V1::EvaluateGovernPolicyResponse.decode(
76
+ @native.evaluate(encoded)
77
+ )
78
+
79
+ {
80
+ matched: response.matched,
81
+ reasons: response.reasons.map { |reason| reason_hash(reason) },
82
+ actions: response.actions.map { |action| action_hash(action) }
83
+ }
84
+ end
85
+
86
+ # Validate a Rego policy at save time without evaluating it against input.
87
+ #
88
+ # Parses and compiles the policy source. A policy that fails to parse is
89
+ # not an error - it is returned as `{ valid: false, errors: [...] }` so
90
+ # the Policy Store can surface actionable feedback without treating a user
91
+ # mistake as an exceptional condition.
92
+ #
93
+ # @param policy_rego [String] Rego policy source text (UTF-8, max 64 KiB).
94
+ # Must be a valid UTF-8 string; raises `Encoding::InvalidByteSequenceError`
95
+ # if the string contains invalid byte sequences.
96
+ # @return [Hash] `{ valid: Boolean, errors: Array<Hash> }`.
97
+ # `valid` is `true` when the policy parsed and compiled successfully.
98
+ # `errors` is empty when valid; each entry is
99
+ # `{ message: String, location: String }` where `location` is a
100
+ # source-location hint in `"LINE:COL"` format (e.g. `"2:1"`) when the
101
+ # engine surfaces one, or `""` when no separate location is available.
102
+ def validate(policy_rego:)
103
+ encoded = ::Glaz::Govern::V1::ValidateGovernPolicyRequest.encode(
104
+ ::Glaz::Govern::V1::ValidateGovernPolicyRequest.new(
105
+ policy_rego: policy_rego
106
+ )
107
+ )
108
+
109
+ response = ::Glaz::Govern::V1::ValidateGovernPolicyResponse.decode(
110
+ @native.validate(encoded)
111
+ )
112
+
113
+ {
114
+ valid: response.valid,
115
+ errors: response.errors.map { |e| { message: e.message, location: e.location } }
116
+ }
117
+ end
118
+
119
+ # Evaluate a Rego query directly, with no `violation`/`deny`/`allow`
120
+ # interpretation. A debugging/testing escape hatch for exercising a
121
+ # policy's Rego directly; prefer #evaluate for the primary path.
122
+ # Unlike #evaluate, this does not raise on failure - +error+ stays
123
+ # in-band, since this method is meant for inspecting raw results
124
+ # (including failures) while developing a policy, not for production
125
+ # decision-making.
126
+ #
127
+ # @param policy_rego [String] Rego policy text (max 64 KiB).
128
+ # @param context [Hash] the host-assembled context, passed to the
129
+ # policy as the Rego `input` document (max 1 MiB as JSON).
130
+ # @param query [String] Rego query to evaluate (e.g.
131
+ # "data.mypackage.violation"); must not be blank.
132
+ # @param data [Hash] optional precomputed Rego `data` document.
133
+ # @return [Hash] `{ result: Object, error: String | nil }` - `result`
134
+ # is exactly whatever `query` evaluated to (parsed JSON), with no
135
+ # guarantee about its shape. A non-nil `error` means evaluation
136
+ # failed; `result` is then `nil`.
137
+ def debug_evaluate(policy_rego:, context:, query:, data: {})
138
+ encoded = ::Glaz::Govern::V1::EvaluateGovernPolicyDebugRequest.encode(
139
+ ::Glaz::Govern::V1::EvaluateGovernPolicyDebugRequest.new(
140
+ policy_rego: policy_rego,
141
+ query: query.to_s,
142
+ input_json: context.to_json,
143
+ data_json: data.nil? || data.empty? ? "" : data.to_json
144
+ )
145
+ )
146
+
147
+ response = JSON.parse(@native.debug_evaluate(encoded), symbolize_names: true)
148
+
149
+ {
150
+ result: response[:result],
151
+ error: response[:error].nil? || response[:error].empty? ? nil : response[:error]
152
+ }
153
+ end
154
+
155
+ private
156
+
157
+ def action_hash(action)
158
+ {
159
+ action_type: action.action_type,
160
+ message: action.message.empty? ? nil : action.message,
161
+ params: JSON.parse(action.params_json)
162
+ }
163
+ end
164
+
165
+ def reason_hash(reason)
166
+ {
167
+ message: reason.message.empty? ? nil : reason.message,
168
+ details: JSON.parse(reason.details_json)
169
+ }
170
+ end
171
+ end
172
+ end
173
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Gitlab
4
4
  module Glaz
5
- VERSION = "1.0.0"
5
+ VERSION = "1.2.0"
6
6
  end
7
7
  end
data/lib/gitlab/glaz.rb CHANGED
@@ -3,10 +3,12 @@
3
3
  require_relative "glaz/version"
4
4
  require_relative 'glaz/loader'
5
5
  require "proto/service_pb"
6
+ require "proto/govern_pb"
6
7
 
7
8
  load_rust_extension
8
9
 
9
10
  require_relative "glaz/engine"
11
+ require_relative "glaz/govern_policy_engine"
10
12
 
11
13
  module Gitlab
12
14
  module Glaz
@@ -21,5 +23,13 @@ module Gitlab
21
23
  role.freeze
22
24
  end.freeze
23
25
  end
26
+
27
+ # Returns the shared Gitlab::Glaz::GovernPolicyEngine instance, memoized
28
+ # across calls. Evaluate Rego governance policies via
29
+ # +govern_policy_engine.evaluate+; see GovernPolicyEngine#evaluate for
30
+ # the full contract.
31
+ def govern_policy_engine
32
+ @govern_policy_engine ||= GovernPolicyEngine.new
33
+ end
24
34
  end
25
35
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # source: proto/govern.proto
4
+
5
+ require 'google/protobuf'
6
+
7
+
8
+ descriptor_data = "\n\x12proto/govern.proto\x12\x0eglaz.govern.v1\"z\n\x1bEvaluateGovernPolicyRequest\x12\x1f\n\x0bpolicy_rego\x18\x01 \x01(\tR\npolicyRego\x12\x1d\n\ninput_json\x18\x02 \x01(\tR\tinputJson\x12\x1b\n\tdata_json\x18\x03 \x01(\tR\x08dataJson\"\x95\x01\n EvaluateGovernPolicyDebugRequest\x12\x1f\n\x0bpolicy_rego\x18\x01 \x01(\tR\npolicyRego\x12\x14\n\x05query\x18\x02 \x01(\tR\x05query\x12\x1d\n\ninput_json\x18\x03 \x01(\tR\tinputJson\x12\x1b\n\tdata_json\x18\x04 \x01(\tR\x08dataJson\"j\n\x0cGovernAction\x12\x1f\n\x0baction_type\x18\x01 \x01(\tR\nactionType\x12\x18\n\x07message\x18\x02 \x01(\tR\x07message\x12\x1f\n\x0bparams_json\x18\x03 \x01(\tR\nparamsJson\"K\n\x0cGovernReason\x12\x18\n\x07message\x18\x01 \x01(\tR\x07message\x12!\n\x0cdetails_json\x18\x02 \x01(\tR\x0bdetailsJson\"\xa8\x01\n\x1cEvaluateGovernPolicyResponse\x126\n\x07actions\x18\x01 \x03(\x0b2\x1c.glaz.govern.v1.GovernActionR\x07actions\x126\n\x07reasons\x18\x02 \x03(\x0b2\x1c.glaz.govern.v1.GovernReasonR\x07reasons\x12\x18\n\x07matched\x18\x03 \x01(\x08R\x07matched\">\n\x1bValidateGovernPolicyRequest\x12\x1f\n\x0bpolicy_rego\x18\x01 \x01(\tR\npolicyRego\"M\n\x15GovernValidationError\x12\x18\n\x07message\x18\x01 \x01(\tR\x07message\x12\x1a\n\x08location\x18\x02 \x01(\tR\x08location\"s\n\x1cValidateGovernPolicyResponse\x12\x14\n\x05valid\x18\x01 \x01(\x08R\x05valid\x12=\n\x06errors\x18\x02 \x03(\x0b2%.glaz.govern.v1.GovernValidationErrorR\x06errorsb\x06proto3"
9
+
10
+ pool = ::Google::Protobuf::DescriptorPool.generated_pool
11
+ pool.add_serialized_file(descriptor_data)
12
+
13
+ module Glaz
14
+ module Govern
15
+ module V1
16
+ EvaluateGovernPolicyRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("glaz.govern.v1.EvaluateGovernPolicyRequest").msgclass
17
+ EvaluateGovernPolicyDebugRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("glaz.govern.v1.EvaluateGovernPolicyDebugRequest").msgclass
18
+ GovernAction = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("glaz.govern.v1.GovernAction").msgclass
19
+ GovernReason = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("glaz.govern.v1.GovernReason").msgclass
20
+ EvaluateGovernPolicyResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("glaz.govern.v1.EvaluateGovernPolicyResponse").msgclass
21
+ ValidateGovernPolicyRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("glaz.govern.v1.ValidateGovernPolicyRequest").msgclass
22
+ GovernValidationError = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("glaz.govern.v1.GovernValidationError").msgclass
23
+ ValidateGovernPolicyResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("glaz.govern.v1.ValidateGovernPolicyResponse").msgclass
24
+ end
25
+ end
26
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitlab-glaz
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - group::authorization
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-07 00:00:00.000000000 Z
11
+ date: 2026-09-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: google-protobuf
@@ -98,8 +98,10 @@ files:
98
98
  - ext/glaz/src/lib.rs
99
99
  - lib/gitlab/glaz.rb
100
100
  - lib/gitlab/glaz/engine.rb
101
+ - lib/gitlab/glaz/govern_policy_engine.rb
101
102
  - lib/gitlab/glaz/loader.rb
102
103
  - lib/gitlab/glaz/version.rb
104
+ - lib/proto/govern_pb.rb
103
105
  - lib/proto/relationships/relationships_pb.rb
104
106
  - lib/proto/service_pb.rb
105
107
  homepage: https://gitlab.com/gitlab-org/ruby/gems/gitlab-glaz
@@ -114,7 +116,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
114
116
  requirements:
115
117
  - - ">="
116
118
  - !ruby/object:Gem::Version
117
- version: '3.1'
119
+ version: '3.3'
118
120
  required_rubygems_version: !ruby/object:Gem::Requirement
119
121
  requirements:
120
122
  - - ">="