Skip to content
Snippets Groups Projects
Unverified Commit d74a8900 authored by Yorick Peterse's avatar Yorick Peterse
Browse files

Added simple feature flag system

This can be used to enable/disable certain features using the
ApplicationSetting model.

Features can be toggled from the application settings UI, or via a Ruby
script of sorts. Since this re-uses the ApplicationSetting model the
cached settings are flushed automatically.
parent cea3cf17
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -61,6 +61,7 @@ v 8.9.0 (unreleased)
- Markdown editor now correctly resets the input value on edit cancellation !4175
- Toggling a task list item in a issue/mr description does not creates a Todo for mentions
- Improved UX of date pickers on issue & milestone forms
- Added a simple feature flag system to allow enabling/disabling of certain features
 
v 8.8.5 (unreleased)
- Ensure branch cleanup regardless of whether the GitHub import process succeeds
Loading
Loading
Loading
Loading
@@ -109,6 +109,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:metrics_packet_size,
:send_user_confirmation_email,
:container_registry_token_expire_delay,
*Gitlab::Feature.column_names,
restricted_visibility_levels: [],
import_sources: [],
disabled_oauth_sign_in_sources: []
Loading
Loading
Loading
Loading
@@ -328,6 +328,23 @@
.help-block
If you got a lot of false alarms from repository checks you can choose to clear all repository check information from the database.
 
%fieldset
%legend Enabled Features
%p
Enabling or disabling these features can be useful to those wanting more
fine grained control over the behaviour of their GitLab instance.
Disabling certain features can also be useful when doing a deploy. For
example, should a deploy modify the data used for displaying/creating
comments it can be useful to disable the feature
<code>creating_notes</code> to prevent users from creating new notes while
the deploy is running.
.form-group
.col-sm-offset-2.col-sm-10
- Gitlab::Feature.features_with_columns.each do |feature, column|
.checkbox
= f.label(column) do
= f.check_box(column)
<code>#{feature}</code>
 
.form-actions
= f.submit 'Save', class: 'btn btn-save'
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddInitialFeatureFlags < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
def change
columns = [
:enable_toggling_award_emoji,
:enable_creating_notes,
:enable_updating_notes,
:enable_removing_notes,
:enable_removing_note_attachments
]
columns.each do |column|
add_column :application_settings, column, :boolean, default: true
end
end
end
Loading
Loading
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
 
ActiveRecord::Schema.define(version: 20160608155312) do
ActiveRecord::Schema.define(version: 20160609133359) do
 
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Loading
Loading
@@ -85,6 +85,11 @@ ActiveRecord::Schema.define(version: 20160608155312) do
t.boolean "send_user_confirmation_email", default: false
t.integer "container_registry_token_expire_delay", default: 5
t.text "after_sign_up_text"
t.boolean "enable_toggling_award_emoji", default: true
t.boolean "enable_creating_notes", default: true
t.boolean "enable_updating_notes", default: true
t.boolean "enable_removing_notes", default: true
t.boolean "enable_removing_note_attachments", default: true
end
 
create_table "audit_events", force: :cascade do |t|
Loading
Loading
module Gitlab
# Module for checking if certain features are enabled or not.
#
# To check for a feature you can use an "enabled?" method generated for each
# registered feature:
#
# if Gitlab::Feature.updating_notes_enabled?
# ...
# end
#
# ## Adding Features
#
# Adding features works in two simple steps:
#
# 1. Add the feature name to the FEATURES array below
# 2. Add a migration that adds a column called "enable_FEATURE" (where
# "FEATURE" is the name of your feature) to "application_settings". The
# type should be a boolean with a default value of true.
module Feature
extend CurrentSettings
FEATURES = [
:toggling_award_emoji,
:creating_notes,
:updating_notes,
:removing_notes,
:removing_note_attachments
].freeze
class << self
FEATURES.each do |feature|
define_method("#{feature}_enabled?") do
feature_enabled?(feature)
end
end
# Returns true if the given feature is enabled.
def feature_enabled?(feature)
settings = current_application_settings
method = column_name(feature)
# If a feature column doesn't exist we still want to enable the
# feature. This for example allows the use of a fake application
# settings object without having to duplicate any feature related
# logic there.
return true unless settings.respond_to?(method)
settings.__send__(method)
end
# Returns a Hash containing all features and the corresponding column
# names.
def features_with_columns
FEATURES.each_with_object({}) do |feature, hash|
hash[feature] = column_name(feature)
end
end
# Returns the names of the columns.
def column_names
FEATURES.map { |f| column_name(f) }
end
# Returns the column name for a feature.
def column_name(feature)
:"enable_#{feature}"
end
end
end
end
require 'spec_helper'
describe Gitlab::Feature do
Gitlab::Feature::FEATURES.each do |feature|
describe ".#{feature}_enabled?" do
it 'returns a boolean' do
expect(described_class.__send__("#{feature}_enabled?")).
to be_in([true, false])
end
end
end
describe '.feature_enabled?' do
it 'returns true when the column does not exist' do
settings = double(:settings)
expect(described_class).to receive(:current_application_settings).
and_return(settings)
expect(described_class.feature_enabled?(:foo)).to eq(true)
end
end
describe '.features_with_columns' do
it 'returns a Hash mapping feature names to their columns' do
map = described_class.features_with_columns
expect(map).to be_an_instance_of(Hash)
expect(map[:creating_notes]).to eq(:enable_creating_notes)
end
end
describe '.column_names' do
it 'returns an Array of column names' do
names = described_class.column_names
expect(names).to be_an_instance_of(Array)
expect(names).to include(:enable_creating_notes)
end
end
describe '.column_name' do
it 'returns the column name of a feature flag' do
expect(described_class.column_name(:foo)).to eq(:enable_foo)
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment