gitlab-labkit 0.39.0 → 0.41.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.
- checksums.yaml +4 -4
- data/.copier-answers.yml +2 -1
- data/.gitlab-ci-asdf-versions.yml +3 -3
- data/.gitlab-ci.yml +3 -9
- data/.pre-commit-config.yaml +2 -2
- data/.rubocop_todo.yml +1 -1
- data/.tool-versions +3 -3
- data/README.md +3 -1
- data/config/covered_experiences/schema.json +35 -0
- data/config/covered_experiences/testing_sample.yml +4 -0
- data/gitlab-labkit.gemspec +1 -0
- data/lib/gitlab-labkit.rb +2 -1
- data/lib/labkit/context.rb +1 -0
- data/lib/labkit/covered_experience/README.md +185 -0
- data/lib/labkit/covered_experience/current.rb +34 -0
- data/lib/labkit/covered_experience/error.rb +11 -0
- data/lib/labkit/covered_experience/experience.rb +287 -0
- data/lib/labkit/covered_experience/null.rb +29 -0
- data/lib/labkit/covered_experience/registry.rb +105 -0
- data/lib/labkit/covered_experience.rb +96 -0
- data/lib/labkit/logging/json_logger.rb +11 -0
- data/lib/labkit/middleware/rack.rb +23 -0
- data/lib/labkit/middleware/sidekiq/client.rb +1 -0
- data/lib/labkit/middleware/sidekiq/covered_experience/client.rb +27 -0
- data/lib/labkit/middleware/sidekiq/covered_experience/server.rb +25 -0
- data/lib/labkit/middleware/sidekiq/covered_experience.rb +14 -0
- data/lib/labkit/middleware/sidekiq/server.rb +1 -0
- data/lib/labkit/middleware/sidekiq.rb +1 -0
- data/lib/labkit/rspec/README.md +132 -0
- data/lib/labkit/rspec/matchers/covered_experience_matchers.rb +204 -0
- data/lib/labkit/rspec/matchers.rb +10 -0
- metadata +31 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 762df6d0e3348526c7b49929a3a6cdd82db05c80a9d83569a76bd10ddb131ca2
|
4
|
+
data.tar.gz: 0affeb965dd17cfc202a0b64419d46bb92419a051a70972cbab5d57e5844d027
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c703503bc9cf08155d72a0ffc61f7f8cc2c573353d67382e7b4d7b22ea554c2a14ee2e2d04f8804696fa95584843cd4a342cbcdb5e9cb8ac775bd0d36df006b3
|
7
|
+
data.tar.gz: 2a598ffe29d7382689637c605db8ba721c993e3fc4b033176b273b99515033577b16d82961007bb9e99da3feac5ec2419e58c7a45b354dfd820e4d9bfa9baa4b
|
data/.copier-answers.yml
CHANGED
@@ -3,10 +3,11 @@
|
|
3
3
|
# See the project for instructions on how to update the project
|
4
4
|
#
|
5
5
|
# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY
|
6
|
-
_commit: v1.
|
6
|
+
_commit: v1.35.1
|
7
7
|
_src_path: https://gitlab.com/gitlab-com/gl-infra/common-template-copier.git
|
8
8
|
ee_licensed: false
|
9
9
|
golang: false
|
10
|
+
helm: false
|
10
11
|
initial_codeowners: '@reprazent @andrewn @mkaeppler @ayufan'
|
11
12
|
jsonnet: false
|
12
13
|
project_name: labkit-ruby
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# DO NOT MANUALLY EDIT; Run ./scripts/update-asdf-version-variables.sh to update this
|
2
2
|
variables:
|
3
|
-
GL_ASDF_RUBY_VERSION: "3.4.
|
4
|
-
GL_ASDF_SHELLCHECK_VERSION: "0.
|
5
|
-
GL_ASDF_SHFMT_VERSION: "3.
|
3
|
+
GL_ASDF_RUBY_VERSION: "3.4.5"
|
4
|
+
GL_ASDF_SHELLCHECK_VERSION: "0.11"
|
5
|
+
GL_ASDF_SHFMT_VERSION: "3.12"
|
data/.gitlab-ci.yml
CHANGED
@@ -19,19 +19,13 @@ include:
|
|
19
19
|
# It includes standard checks, gitlab-scanners, validations and release processes
|
20
20
|
# common to all projects using this template library.
|
21
21
|
# see https://gitlab.com/gitlab-com/gl-infra/common-ci-tasks/-/blob/main/templates/standard.md
|
22
|
-
-
|
23
|
-
ref: v2.77 # renovate:managed
|
24
|
-
file: templates/standard.yml
|
22
|
+
- component: $CI_SERVER_FQDN/gitlab-com/gl-infra/common-ci-tasks/standard-build@v2.78
|
25
23
|
|
26
24
|
# Runs rspec tests and rubocop on the project
|
27
25
|
# see https://gitlab.com/gitlab-com/gl-infra/common-ci-tasks/-/blob/main/templates/ruby.md
|
28
|
-
-
|
29
|
-
ref: v2.77 # renovate:managed
|
30
|
-
file: templates/ruby.yml
|
26
|
+
- component: $CI_SERVER_FQDN/gitlab-com/gl-infra/common-ci-tasks/ruby-build@v2.78
|
31
27
|
|
32
|
-
-
|
33
|
-
ref: v2.77 # renovate:managed
|
34
|
-
file: "danger.yml"
|
28
|
+
- component: $CI_SERVER_FQDN/gitlab-com/gl-infra/common-ci-tasks/danger@v2.78
|
35
29
|
|
36
30
|
.test_template: &test_definition
|
37
31
|
extends: .with_bundle
|
data/.pre-commit-config.yaml
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
# exclude: '^fixtures/'
|
6
6
|
repos:
|
7
7
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
8
|
-
rev:
|
8
|
+
rev: v6.0.0 # renovate:managed
|
9
9
|
hooks:
|
10
10
|
- id: trailing-whitespace
|
11
11
|
- id: end-of-file-fixer
|
@@ -25,7 +25,7 @@ repos:
|
|
25
25
|
# Documentation available at
|
26
26
|
# https://gitlab.com/gitlab-com/gl-infra/common-ci-tasks/-/blob/main/docs/pre-commit.md
|
27
27
|
- repo: https://gitlab.com/gitlab-com/gl-infra/common-ci-tasks
|
28
|
-
rev: v2.
|
28
|
+
rev: v2.92 # renovate:managed
|
29
29
|
|
30
30
|
hooks:
|
31
31
|
- id: shellcheck # Run shellcheck for changed Shell files
|
data/.rubocop_todo.yml
CHANGED
data/.tool-versions
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
ruby 3.4.
|
2
|
-
shfmt 3.
|
3
|
-
shellcheck 0.
|
1
|
+
ruby 3.4.5
|
2
|
+
shfmt 3.12
|
3
|
+
shellcheck 0.11
|
data/README.md
CHANGED
@@ -19,10 +19,12 @@ The changelog is available via [**tagged release notes**](https://gitlab.com/git
|
|
19
19
|
LabKit-Ruby provides functionality in a number of areas:
|
20
20
|
|
21
21
|
1. `Labkit::Context` used for providing context information to log messages.
|
22
|
-
1. `Labkit::Correlation`
|
22
|
+
1. `Labkit::Correlation` for accessing the correlation id. (Generated and propagated by `Labkit::Context`)
|
23
|
+
1. `Labkit::CoveredExperience` for tracking covered experiences. More on the [README](./lib/labkit/covered_experience/README.md).
|
23
24
|
1. `Labkit::FIPS` for checking for FIPS mode and using FIPS-compliant algorithms.
|
24
25
|
1. `Labkit::Logging` for sanitizing log messages.
|
25
26
|
1. `Labkit::Metrics` for metrics. More on the [README](./lib/labkit/metrics/README.md).
|
27
|
+
1. `Labkit::RSpec` for RSpec matchers to test Labkit functionality (requires selective loading). More on the [README](./lib/labkit/rspec/README.md).
|
26
28
|
1. `Labkit::Tracing` for handling and propagating distributed traces.
|
27
29
|
|
28
30
|
## Developing
|
@@ -0,0 +1,35 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json-schema.org/draft-06/schema#",
|
3
|
+
"$id": "https://gitlab.com/gitlab-org/ruby/gems/labkit-ruby/-/raw/master/config/covered_experiences/schema.json",
|
4
|
+
"title": "Covered Experience Definition",
|
5
|
+
"description": "Schema for GitLab Covered Experience files",
|
6
|
+
"type": "object",
|
7
|
+
"properties": {
|
8
|
+
"description": {
|
9
|
+
"type": "string",
|
10
|
+
"minLength": 1,
|
11
|
+
"description": "Human-readable description of the covered experience"
|
12
|
+
},
|
13
|
+
"feature_category": {
|
14
|
+
"type": "string",
|
15
|
+
"minLength": 1,
|
16
|
+
"description": "GitLab feature category this experience belongs to"
|
17
|
+
},
|
18
|
+
"urgency": {
|
19
|
+
"type": "string",
|
20
|
+
"enum": [
|
21
|
+
"sync_fast",
|
22
|
+
"sync_slow",
|
23
|
+
"async_fast",
|
24
|
+
"async_slow"
|
25
|
+
],
|
26
|
+
"description": "Urgency level for this covered experience"
|
27
|
+
}
|
28
|
+
},
|
29
|
+
"required": [
|
30
|
+
"description",
|
31
|
+
"feature_category",
|
32
|
+
"urgency"
|
33
|
+
]
|
34
|
+
}
|
35
|
+
|
data/gitlab-labkit.gemspec
CHANGED
@@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.add_runtime_dependency "grpc", ">= 1.62" # Be sure to update the "grpc-tools" dev_dependency too
|
26
26
|
spec.add_runtime_dependency "google-protobuf", "~> 3" # Keep the major version to 3 until we update the `grpc` gem
|
27
27
|
spec.add_runtime_dependency "jaeger-client", "~> 1.1.0"
|
28
|
+
spec.add_runtime_dependency 'json-schema', '~> 5.1'
|
28
29
|
spec.add_runtime_dependency "opentracing", "~> 0.4"
|
29
30
|
spec.add_runtime_dependency "pg_query", ">= 6.1.0", "< 7.0"
|
30
31
|
spec.add_runtime_dependency "prometheus-client-mmap", "~> 1.2.9"
|
data/lib/gitlab-labkit.rb
CHANGED
@@ -7,8 +7,9 @@
|
|
7
7
|
module Labkit
|
8
8
|
autoload :System, "labkit/system"
|
9
9
|
|
10
|
-
autoload :Correlation, "labkit/correlation"
|
11
10
|
autoload :Context, "labkit/context"
|
11
|
+
autoload :Correlation, "labkit/correlation"
|
12
|
+
autoload :CoveredExperience, "labkit/covered_experience"
|
12
13
|
autoload :FIPS, "labkit/fips"
|
13
14
|
autoload :Tracing, "labkit/tracing"
|
14
15
|
autoload :Logging, "labkit/logging"
|
data/lib/labkit/context.rb
CHANGED
@@ -5,6 +5,7 @@ require "securerandom"
|
|
5
5
|
require "active_support/core_ext/module/delegation"
|
6
6
|
require "active_support/core_ext/string/starts_ends_with"
|
7
7
|
require "active_support/core_ext/string/inflections"
|
8
|
+
require "active_support/core_ext/object/blank"
|
8
9
|
|
9
10
|
module Labkit
|
10
11
|
# A context can be used to provide structured information on what resources
|
@@ -0,0 +1,185 @@
|
|
1
|
+
# Covered Experience
|
2
|
+
|
3
|
+
This module covers the definition for Covered Experiences, as described in the [blueprint](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/covered_experience_slis/#covered-experience-definition).
|
4
|
+
|
5
|
+
## Configuration
|
6
|
+
|
7
|
+
### Logger Configuration
|
8
|
+
|
9
|
+
By default, `Labkit::CoveredExperience` uses `Labkit::Logging::JsonLogger.new($stdout)` for logging. You can configure a custom logger:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
Labkit::CoveredExperience.configure do |config|
|
13
|
+
config.logger = Labkit::Logging::JsonLogger.new($stdout)
|
14
|
+
end
|
15
|
+
```
|
16
|
+
|
17
|
+
This configuration affects all Covered Experience instances and their logging output.
|
18
|
+
|
19
|
+
### Registry Path Configuration
|
20
|
+
|
21
|
+
By default, covered experience definitions are loaded from the `config/covered_experiences` directory. You can configure a custom registry path:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
Labkit::CoveredExperience.configure do |config|
|
25
|
+
config.registry_path = "my/custom/path"
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
This allows you to:
|
30
|
+
- Store covered experience definitions in a different directory structure
|
31
|
+
- Use different paths for different environments
|
32
|
+
- Organize definitions according to your application's needs
|
33
|
+
|
34
|
+
**Note:** The registry is automatically reset when the configuration changes, so the new path takes effect immediately.
|
35
|
+
|
36
|
+
### Covered Experience Definitions
|
37
|
+
|
38
|
+
Covered experience definitions will be lazy loaded from the default directory (`config/covered_experiences`).
|
39
|
+
|
40
|
+
Create a new covered experience file in the registry directory, e.g. config/covered_experiences/merge_request_creation.yaml
|
41
|
+
|
42
|
+
The basename of the file will be taken as the covered_experience_id.
|
43
|
+
|
44
|
+
The schema header is optional, but if you're using VSCode (or any other editor with support), you can get them validated
|
45
|
+
instantaneously in the editor via a [JSON schema plugin](https://marketplace.visualstudio.com/items?itemName=remcohaszing.schemastore).
|
46
|
+
|
47
|
+
```yaml
|
48
|
+
# yaml-language-server: $schema=https://gitlab.com/gitlab-org/ruby/gems/labkit-ruby/-/raw/master/config/covered_experiences/schema.json
|
49
|
+
description: "Creating a new merge request in a project"
|
50
|
+
feature_category: "source_code_management"
|
51
|
+
urgency: "sync_fast"
|
52
|
+
```
|
53
|
+
|
54
|
+
**Feature category**
|
55
|
+
|
56
|
+
https://docs.gitlab.com/development/feature_categorization/#feature-categorization.
|
57
|
+
|
58
|
+
**Urgency**
|
59
|
+
|
60
|
+
| Threshold | Description | Examples | Value |
|
61
|
+
|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------|-------|
|
62
|
+
| `sync_fast` | A user is awaiting a synchronous response which needs to be returned before they can continue with their action | A full-page render | 2s |
|
63
|
+
| `sync_slow` | A user is awaiting a synchronous response which needs to be returned before they can continue with their action, but which the user may accept a slower response | Displaying a full-text search response while displaying an amusement animation | 5s |
|
64
|
+
| `async_fast` | An async process which may block a user from continuing with their user journey | MR diff update after git push | 15s |
|
65
|
+
| `async_slow` | An async process which will not block a user and will not be immediately noticed as being slow | Notification following an assignment | 5m |
|
66
|
+
|
67
|
+
## Usage
|
68
|
+
|
69
|
+
The `Labkit::CoveredExperience` module provides a simple API for measuring and tracking covered experiences in your application.
|
70
|
+
|
71
|
+
|
72
|
+
#### Accessing a Covered Experience
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
# Get a covered experience by ID
|
76
|
+
experience = Labkit::CoveredExperience.get('merge_request_creation')
|
77
|
+
```
|
78
|
+
|
79
|
+
#### Using with a Block (Recommended)
|
80
|
+
|
81
|
+
The simplest way to use covered experiences is with a block, which automatically handles starting and completing the experience:
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
Labkit::CoveredExperience.start('merge_request_creation') do |experience|
|
85
|
+
# Your code here
|
86
|
+
create_merge_request
|
87
|
+
|
88
|
+
# Add checkpoints for important milestones
|
89
|
+
experience.checkpoint
|
90
|
+
|
91
|
+
validate_merge_request
|
92
|
+
experience.checkpoint
|
93
|
+
|
94
|
+
send_notifications
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
#### Manual Control
|
99
|
+
|
100
|
+
For more control, you can manually start, checkpoint, and complete experiences:
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
experience = Labkit::CoveredExperience.get('merge_request_creation')
|
104
|
+
experience.start
|
105
|
+
|
106
|
+
# Perform some work
|
107
|
+
create_merge_request
|
108
|
+
|
109
|
+
# Mark important milestones
|
110
|
+
experience.checkpoint
|
111
|
+
|
112
|
+
# Perform more work
|
113
|
+
validate_merge_request
|
114
|
+
experience.checkpoint
|
115
|
+
|
116
|
+
# Complete the experience
|
117
|
+
experience.complete
|
118
|
+
```
|
119
|
+
|
120
|
+
#### Resuming Experiences
|
121
|
+
|
122
|
+
You can resume a covered experience that was previously started and stored in the context. This is useful for distributed operations or when work spans multiple processes.
|
123
|
+
|
124
|
+
Just like the start method, we can use a block to automatically complete a covered experience:
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
# Resume an experience from context (with block)
|
128
|
+
Labkit::CoveredExperience.resume(:merge_request_creation) do |experience|
|
129
|
+
# Continue the work from where it left off
|
130
|
+
finalize_merge_request
|
131
|
+
|
132
|
+
# Add checkpoints as needed
|
133
|
+
experience.checkpoint
|
134
|
+
|
135
|
+
send_notifications
|
136
|
+
end
|
137
|
+
```
|
138
|
+
Or manually:
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
# Resume an experience from context (manual control)
|
142
|
+
experience = Labkit::CoveredExperience.resume(:merge_request_creation)
|
143
|
+
|
144
|
+
# Continue the work
|
145
|
+
finalize_merge_request
|
146
|
+
experience.checkpoint
|
147
|
+
|
148
|
+
# Complete the experience
|
149
|
+
experience.complete
|
150
|
+
```
|
151
|
+
|
152
|
+
**Note:** The `resume` method loads the start time from the Labkit context. If no covered experience data exists in the context, it behaves the same as calling methods on an unstarted experience (raises `UnstartedError` in development/test environments, or safely ignores in other environments).
|
153
|
+
|
154
|
+
### Error Handling
|
155
|
+
|
156
|
+
When using the block form, errors are automatically captured:
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
Labkit::CoveredExperience.start('merge_request_creation') do |experience|
|
160
|
+
# If this raises an exception, it will be captured automatically
|
161
|
+
risky_operation
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
For manual control, you can mark errors explicitly:
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
experience = Labkit::CoveredExperience.get('merge_request_creation')
|
169
|
+
experience.start
|
170
|
+
|
171
|
+
begin
|
172
|
+
risky_operation
|
173
|
+
rescue StandardError => e
|
174
|
+
experience.error!(e)
|
175
|
+
raise
|
176
|
+
ensure
|
177
|
+
experience.complete
|
178
|
+
end
|
179
|
+
```
|
180
|
+
|
181
|
+
### Error Behavior
|
182
|
+
|
183
|
+
- In `development` and `test` environments, accessing a non-existent covered experience will raise a `NotFoundError`
|
184
|
+
- In other environments, a null object is returned that safely ignores all method calls
|
185
|
+
- Attempting to checkpoint or complete an unstarted experience will raise an `UnstartedError` in `development` and `test` environments
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support"
|
4
|
+
require "labkit/covered_experience/experience"
|
5
|
+
|
6
|
+
module Labkit
|
7
|
+
module CoveredExperience
|
8
|
+
# The `Current` class represents a container for the current set
|
9
|
+
# of `Labkit::CoveredExperience::Experience` instances started and
|
10
|
+
# not yet completed.
|
11
|
+
#
|
12
|
+
# It uses `ActiveSupport::CurrentAttributes` to provide a thread-safe way to
|
13
|
+
# store and access experiences throughout the request and background job lifecycle.
|
14
|
+
#
|
15
|
+
# Example usage:
|
16
|
+
# Labkit::CoveredExperience::Current.active_experiences << my_experience
|
17
|
+
# Labkit::CoveredExperience::Current.rehydrate("create_merge_request", "start_time" => "2025-08-22T10:02:15.237Z")
|
18
|
+
class Current < ActiveSupport::CurrentAttributes
|
19
|
+
AGGREGATION_KEY = 'labkit_covered_experiences'
|
20
|
+
|
21
|
+
attribute :_active_experiences
|
22
|
+
|
23
|
+
def active_experiences
|
24
|
+
self._active_experiences ||= {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def rehydrate(experience_id, **data)
|
28
|
+
instance = Labkit::CoveredExperience.get(experience_id).rehydrate(data)
|
29
|
+
active_experiences[instance.id] = instance
|
30
|
+
instance
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Labkit
|
4
|
+
module CoveredExperience
|
5
|
+
CoveredExperienceError = Class.new(StandardError)
|
6
|
+
UnstartedError = Class.new(CoveredExperienceError)
|
7
|
+
CompletedError = Class.new(CoveredExperienceError)
|
8
|
+
NotFoundError = Class.new(CoveredExperienceError)
|
9
|
+
ReservedKeywordError = Class.new(CoveredExperienceError)
|
10
|
+
end
|
11
|
+
end
|