summaryrefslogtreecommitdiff
path: root/config/initializers/gitlab_experiment.rb
blob: a201a075f62c0d6debe9a08aaa0fbb55c9ac8bd4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# frozen_string_literal: true

Gitlab::Experiment.configure do |config|
  # The base experiment class that will be instantiated when using the
  # `experiment` DSL, is our ApplicationExperiment. If a custom experiment
  # class is resolvable by the experiment name, that will be instantiated
  # instead -- which can then inherit from whatever else it wants to.
  #
  # Custom experiment classes can be defined in /app/experiments.
  #
  config.base_class = 'ApplicationExperiment'

  # Customize the logic of our default rollout, which shouldn't include
  # assigning the control yet -- we specifically set it to false for now.
  #
  config.default_rollout = Gitlab::Experiment::Rollout.resolve(:feature)

  # Mount the engine and middleware at a gitlab friendly style path.
  #
  # The middleware currently focuses only on handling redirection logic, which
  # is used for instrumenting urls in places where urls are otherwise not
  # possible to instrument. Emails, and markdown content being among the top
  # places where this can be useful.
  #
  config.mount_at = '/-/experiment'

  # We use a long lived redis cache to increase the performance of experiments.
  #
  # Experiments can implement exclusionary and segmentation logic that can be
  # expensive, and so to better handle these cases, once a variant is assigned
  # to a given context, it's "sticky" to that context. This cache check is one
  # of the first things in the process of variant resolution, and so if one is
  # cached, no further logic is executed in resolving variant assignment.
  #
  # This means that there's no easy way to currently move a context from one
  # variant to another. Future tooling will make this easier, but implementing
  # a custom cache for your experiment may be required in edge cases.
  #
  config.cache = Gitlab::Experiment::Cache::RedisHashStore.new(
    pool: ->(&block) { Gitlab::Redis::SharedState.with(&block) }
  )

  # The middleware instruments and redirects urls, but we don't want this to be
  # exploited or used to send people from a trusted site to a nefarious one. So
  # we validate urls before redirecting them.
  #
  # This behavior doesn't make perfect sense for self managed installs either,
  # so we don't think we should redirect in those cases.
  #
  valid_domains = %w[about.gitlab.com docs.gitlab.com gitlab.com gdk.test localhost]
  config.redirect_url_validator = lambda do |url|
    Gitlab.com? && (url = URI.parse(url)) && valid_domains.include?(url.host)
  rescue URI::InvalidURIError
    false
  end

  # Experiments are instrumented using an event based system by default. This
  # can be overridden in your experiment by specifying a `#track` method.
  #
  # The basic behavior though, is to accept any details and pass them along to
  # snowplow, with an included gitlab_experiment schema, that has various
  # details about the experiment, like name and variant assignment.
  #
  # This uses the Gitlab::Tracking interface, so arbitrary event properties are
  # permitted, and will be sent along using Gitlab::Tracking::StandardContext.
  #
  config.tracking_behavior = lambda do |action, event_args|
    Gitlab::Tracking.event(name, action.to_s, **event_args.merge(
      context: (event_args[:context] || []) << SnowplowTracker::SelfDescribingJson.new(
        'iglu:com.gitlab/gitlab_experiment/jsonschema/1-0-0', signature
      )
    ))
  end

  # Deprecation warnings resolution for 0.7.0
  #
  # We're working through deprecation warnings one by one in:
  # https://gitlab.com/gitlab-org/gitlab/-/issues/350944
  #
  config.singleton_class.prepend(Module.new do
    # Disable all deprecations in non dev/test environments.
    #
    def deprecated(*args, version:, stack: 0)
      super if Gitlab.dev_or_test_env?
    end

    # Maintain a list of resolved deprecations to ensure that no new uses appear.
    #
    # Once a resolved deprecation warning has been added here, any future use will
    # raise an exception.
    #
    ActiveSupport::Deprecation.disallowed_warnings += [
      # 'Gitlab::Experiment 0.8 (instead use `control`)', # don't use `use`
      # 'Gitlab::Experiment 0.8 (instead use `candidate`)', # don't use `try`
      # 'Gitlab::Experiment 0.8 (instead use `variant(:variant_name)`)', # don't use `try(:variant_name)`
      # 'Gitlab::Experiment 0.8 (instead use `assigned(:candidate)`)', # don't use variant(:variant_name) to assign
      # 'Gitlab::Experiment 0.8 (instead use `assigned`)', # don't use variant.name to get the assigned variant
      # 'Gitlab::Experiment 0.8, instead register variants using:', # don't use public `*_behavior` methods
    ]
  end)
end