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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 639ab73c3db48ed9078b0ae3743b5031d3d3e89dfde6aff9c65818b9aece82cb
4
- data.tar.gz: ab65ac7320604017406ea64242c9dba98665ed12ad0f1268820e75031c301452
3
+ metadata.gz: 762df6d0e3348526c7b49929a3a6cdd82db05c80a9d83569a76bd10ddb131ca2
4
+ data.tar.gz: 0affeb965dd17cfc202a0b64419d46bb92419a051a70972cbab5d57e5844d027
5
5
  SHA512:
6
- metadata.gz: 14feead5381e4d99a08cac69768dace5080d394c7267cc4c90fc44d0b5839b24838a16469e627f086a2731ac972f38729cb28cec96e84c2ac0ba5d1c27cc4eeb
7
- data.tar.gz: aaae5ce7dbd8454862ce64a93a73280931802575213cfe18c35dee3a7edb410a240937da508b30c2cabe5dbefb0e3e534cbbd2c5a1428dcc2b6b8e90110392d8
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.31.0
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"
4
- GL_ASDF_SHELLCHECK_VERSION: "0.10.0"
5
- GL_ASDF_SHFMT_VERSION: "3.11.0"
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
- - project: "gitlab-com/gl-infra/common-ci-tasks"
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
- - project: "gitlab-com/gl-infra/common-ci-tasks"
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
- - project: "gitlab-com/gl-infra/common-ci-tasks"
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
@@ -5,7 +5,7 @@
5
5
  # exclude: '^fixtures/'
6
6
  repos:
7
7
  - repo: https://github.com/pre-commit/pre-commit-hooks
8
- rev: v5.0.0 # renovate:managed
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.77 # renovate:managed
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
@@ -257,7 +257,7 @@ RSpec/LetBeforeExamples:
257
257
  # Offense count: 20
258
258
  # Configuration parameters: AllowSubject.
259
259
  RSpec/MultipleMemoizedHelpers:
260
- Max: 7
260
+ Max: 8
261
261
 
262
262
  # Offense count: 24
263
263
  # Configuration parameters: EnforcedStyle, IgnoreSharedExamples.
data/.tool-versions CHANGED
@@ -1,3 +1,3 @@
1
- ruby 3.4.4
2
- shfmt 3.11.0
3
- shellcheck 0.10.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` For accessing the correlation id. (Generated and propagated by `Labkit::Context`)
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
+
@@ -0,0 +1,4 @@
1
+ # yaml-language-server: $schema=./schema.json
2
+ description: "Creating a new merge request in a project"
3
+ feature_category: "source_code_management"
4
+ urgency: "sync_fast"
@@ -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"
@@ -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