Record the client version on backend analytics events

What does this MR do and why?

GitLab Rails resolves the client type and name of every request from the X-Gitlab-Client-Type and X-Gitlab-Client-Name headers (!257624 (merged)), but drops the X-Gitlab-Client-Version header the same clients send, although the gitlab_standard Snowplow context already has a client_version field and the AI Gateway and the Duo Workflow Service already fill it on their own events. Without it, analytics cannot tell the builds of a client apart, for example to cut a metric at the release that changed its headers.

This MR carries the version through the same path as the type and the name:

  • Gitlab::Tracking::ClientIdentity reads X-Gitlab-Client-Version for a client that declared its type or name, and keeps it only when it is shaped like a version (an optional v, a leading digit, then digits, letters, ., -, + and _, at most 64 characters), so no free text is stored. A bare version header without identity headers is ignored.
  • Gitlab::Middleware::RequestContext stores it on ApplicationContext as client_version, so Sidekiq jobs the request enqueues inherit it through Labkit and the structured logs carry it as meta.client_version.
  • Gitlab::Tracking::StandardContext emits client_version into the gitlab_standard context (schema 1-1-8 defines the field as a nullable string of up to 64 characters), and AI usage events carry it in their extras beside client_type and client_name.
  • Documentation: the client identity page gains a "Client version" section.
  • Side effect, regenerated in the second commit: the admin Sidekiq queues REST endpoint and the AdminSidekiqQueuesDeleteJobs mutation take one filter per ApplicationContext.allowed_job_keys entry, so client_version becomes a DELETE /admin/sidekiq/queues/:queue_name query parameter and a clientVersion mutation argument. doc/api/openapi/openapi_v3.yaml, the GraphQL reference and the introspection schemas are regenerated, and doc/api/admin_sidekiq_queues.md documents the parameter beside client_type and client_name.

Verification: RuboCop clean on the nine changed files. spec/lib/gitlab/tracking/client_identity_spec.rb, spec/lib/gitlab/tracking/standard_context_spec.rb and spec/lib/gitlab/middleware/request_context_spec.rb are green locally (106 examples). ee/spec/lib/gitlab/tracking/ai_tracking_spec.rb cannot run locally because it needs a ClickHouse configuration (as noted on !257624 (merged)); CI covers it.

References

Follows !257624 (merged) (client type and name) and !257626 (merged) (Workhorse forwarding). The mobile app sends the header from https://gitlab.com/mvanremmerden/gitlab-mobile; the IDE extensions and the Duo CLI already send it. Related: #630922.

How to set up and validate locally

  1. Send a request with curl -H 'X-Gitlab-Client-Name: gitlab-mobile-ios' -H 'X-Gitlab-Client-Version: 1.2.0' http://gdk.test:3000/api/v4/user with a token and check log/api_json.log for meta.client_version: "1.2.0".
  2. Send the same request with -H 'X-Gitlab-Client-Version: latest' and check that meta.client_version is absent.
  3. In a Rails console inside such a request context, Gitlab::Tracking::StandardContext.new.to_h.slice(:client_type, :client_name, :client_version) includes the version.

Reviewer notes

  • Needs an ~"analytics instrumentation" review; the label is applied.
  • No new Snowplow schema version: client_version has been part of gitlab_standard 1-1-8.
  • Labelled pipeline:run-as-if-foss because CE and EE both change.
  • No database migration, no feature flag: the change only reads one more header and copies it along the existing path.

MR acceptance checklist

Evaluated against the MR acceptance checklist.

🤖 Generated with Claude Code

Edited by Marcel van Remmerden

Merge request reports

Loading
Loading