From 6384c757b7ce6d1c0c3e2a3828b0cfac26dfb7f9 Mon Sep 17 00:00:00 2001
From: Kamil Trzcinski <ayufan@ayufan.eu>
Date: Mon, 9 Nov 2015 16:48:03 +0100
Subject: [PATCH] Expose CI enable option in project features

- Enable CI by default for all new projects
---
 .../projects/application_controller.rb        |  2 +-
 app/controllers/projects_controller.rb        |  3 ++-
 app/helpers/projects_helper.rb                |  2 +-
 app/models/project.rb                         | 23 ++++++++++++++-----
 app/services/git_push_service.rb              |  2 +-
 app/services/projects/fork_service.rb         |  2 +-
 .../layouts/nav/_project_settings.html.haml   |  2 +-
 app/views/projects/edit.html.haml             | 11 ++++++++-
 app/views/projects/graphs/_head.html.haml     |  2 +-
 config/gitlab.yml.example                     |  1 +
 config/initializers/1_settings.rb             |  1 +
 doc/api/projects.md                           |  6 +++++
 lib/api/entities.rb                           |  2 +-
 lib/api/projects.rb                           |  6 +++++
 spec/factories/ci/projects.rb                 | 12 ++++++----
 spec/models/project_spec.rb                   |  9 +++++---
 spec/requests/api/projects_spec.rb            |  7 ++++--
 spec/requests/api/services_spec.rb            |  1 +
 spec/services/projects/create_service_spec.rb | 22 ++++++++++++++++++
 spec/services/projects/fork_service_spec.rb   |  2 +-
 20 files changed, 93 insertions(+), 25 deletions(-)

diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index 519d6d6127e..d3f926b62bc 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -29,7 +29,7 @@ class Projects::ApplicationController < ApplicationController
   private
 
   def ci_enabled
-    return render_404 unless @project.gitlab_ci?
+    return render_404 unless @project.builds_enabled?
   end
 
   def ci_project
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 00d13a83ce8..30b166334a9 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -213,7 +213,8 @@ class ProjectsController < ApplicationController
     params.require(:project).permit(
       :name, :path, :description, :issues_tracker, :tag_list,
       :issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch,
-      :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar
+      :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar,
+      :builds_enabled
     )
   end
 
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 5301c2ccf76..690ae2090db 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -117,7 +117,7 @@ module ProjectsHelper
       nav_tabs << :merge_requests
     end
 
-    if project.gitlab_ci? && can?(current_user, :read_build, project)
+    if project.builds_enabled? && can?(current_user, :read_build, project)
       nav_tabs << :builds
     end
 
diff --git a/app/models/project.rb b/app/models/project.rb
index bdb22e49bb5..3e72a9a46a0 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -52,6 +52,7 @@ class Project < ActiveRecord::Base
   default_value_for :visibility_level, gitlab_config_features.visibility_level
   default_value_for :issues_enabled, gitlab_config_features.issues
   default_value_for :merge_requests_enabled, gitlab_config_features.merge_requests
+  default_value_for :builds_enabled, gitlab_config_features.builds
   default_value_for :wiki_enabled, gitlab_config_features.wiki
   default_value_for :wall_enabled, false
   default_value_for :snippets_enabled, gitlab_config_features.snippets
@@ -457,10 +458,6 @@ class Project < ActiveRecord::Base
     list.find { |service| service.to_param == name }
   end
 
-  def gitlab_ci?
-    gitlab_ci_service && gitlab_ci_service.active && gitlab_ci_project.present?
-  end
-
   def ci_services
     services.select { |service| service.category == :ci }
   end
@@ -782,9 +779,23 @@ class Project < ActiveRecord::Base
     )
   end
 
-  def enable_ci
+  # TODO: this should be migrated to Project table,
+  # the same as issues_enabled
+  def builds_enabled
+    gitlab_ci_service && gitlab_ci_service.active
+  end
+
+  def builds_enabled?
+    builds_enabled
+  end
+
+  def builds_enabled=(value)
     service = gitlab_ci_service || create_gitlab_ci_service
-    service.active = true
+    service.active = value
     service.save
   end
+
+  def enable_ci
+    self.builds_enabled = true
+  end
 end
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index 3de7bb9dcaa..ccb6b97858c 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -60,7 +60,7 @@ class GitPushService
 
     # If CI was disabled but .gitlab-ci.yml file was pushed
     # we enable CI automatically
-    if !project.gitlab_ci? && gitlab_ci_yaml?(newrev)
+    if !project.builds_enabled? && gitlab_ci_yaml?(newrev)
       project.enable_ci
     end
 
diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb
index 46374a3909a..5da1c7afd92 100644
--- a/app/services/projects/fork_service.rb
+++ b/app/services/projects/fork_service.rb
@@ -17,7 +17,7 @@ module Projects
       new_project = CreateService.new(current_user, new_params).execute
 
       if new_project.persisted?
-        if @project.gitlab_ci?
+        if @project.builds_enabled?
           new_project.enable_ci
 
           settings = @project.gitlab_ci_project.attributes.select do |attr_name, value|
diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml
index a59939ccd31..377a99e719a 100644
--- a/app/views/layouts/nav/_project_settings.html.haml
+++ b/app/views/layouts/nav/_project_settings.html.haml
@@ -34,7 +34,7 @@
         %span
           Protected Branches
 
-    - if @project.gitlab_ci?
+    - if @project.builds_enabled?
       = nav_link(controller: :runners) do
         = link_to namespace_project_runners_path(@project.namespace, @project), title: 'Runners', data: {placement: 'right'} do
           = icon('cog fw')
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index afbf88b5507..3ebc175648e 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -57,7 +57,16 @@
                     = f.check_box :merge_requests_enabled
                     %strong Merge Requests
                     %br
-                    %span.descr Submit changes to be merged upstream.
+                    %span.descr Submit changes to be merged upstream
+
+            .form-group
+              .col-sm-offset-2.col-sm-10
+                .checkbox
+                  = f.label :builds_enabled do
+                    = f.check_box :builds_enabled
+                    %strong Builds
+                    %br
+                    %span.descr Test and deploy your changes before merge
 
             .form-group
               .col-sm-offset-2.col-sm-10
diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml
index e0d06a14bf4..03d0733f913 100644
--- a/app/views/projects/graphs/_head.html.haml
+++ b/app/views/projects/graphs/_head.html.haml
@@ -3,7 +3,7 @@
     = link_to 'Contributors', namespace_project_graph_path
   = nav_link(action: :commits) do
     = link_to 'Commits', commits_namespace_project_graph_path
-  - if @project.gitlab_ci?
+  - if @project.builds_enabled?
     = nav_link(action: :ci) do
       = link_to ci_namespace_project_graph_path do
         Continuous Integration
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 20894ebcdc9..5bd98e3e42d 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -84,6 +84,7 @@ production: &base
       merge_requests: true
       wiki: true
       snippets: false
+      builds: true
 
     ## Webhook settings
     # Number of seconds to wait for HTTP response after sending webhook HTTP POST request (default: 10)
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 302124bd977..847d9f21898 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -171,6 +171,7 @@ Settings.gitlab.default_projects_features['issues']         = true if Settings.g
 Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.gitlab.default_projects_features['merge_requests'].nil?
 Settings.gitlab.default_projects_features['wiki']           = true if Settings.gitlab.default_projects_features['wiki'].nil?
 Settings.gitlab.default_projects_features['snippets']       = false if Settings.gitlab.default_projects_features['snippets'].nil?
+Settings.gitlab.default_projects_features['builds']         = true if Settings.gitlab.default_projects_features['builds'].nil?
 Settings.gitlab.default_projects_features['visibility_level']    = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
 Settings.gitlab['repository_downloads_path'] = File.join(Settings.shared['path'], 'cache/archive') if Settings.gitlab['repository_downloads_path'].nil?
 Settings.gitlab['restricted_signup_domains'] ||= []
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 96485857035..755cc6525c2 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -60,6 +60,7 @@ Parameters:
     "path_with_namespace": "diaspora/diaspora-client",
     "issues_enabled": true,
     "merge_requests_enabled": true,
+    "builds_enabled": true,
     "wiki_enabled": true,
     "snippets_enabled": false,
     "created_at": "2013-09-30T13: 46: 02Z",
@@ -101,6 +102,7 @@ Parameters:
     "path_with_namespace": "brightbox/puppet",
     "issues_enabled": true,
     "merge_requests_enabled": true,
+    "builds_enabled": true,
     "wiki_enabled": true,
     "snippets_enabled": false,
     "created_at": "2013-09-30T13:46:02Z",
@@ -191,6 +193,7 @@ Parameters:
   "path_with_namespace": "diaspora/diaspora-project-site",
   "issues_enabled": true,
   "merge_requests_enabled": true,
+  "builds_enabled": true,
   "wiki_enabled": true,
   "snippets_enabled": false,
   "created_at": "2013-09-30T13: 46: 02Z",
@@ -312,6 +315,7 @@ Parameters:
 - `description` (optional) - short project description
 - `issues_enabled` (optional)
 - `merge_requests_enabled` (optional)
+- `builds_enabled` (optional)
 - `wiki_enabled` (optional)
 - `snippets_enabled` (optional)
 - `public` (optional) - if `true` same as setting visibility_level = 20
@@ -334,6 +338,7 @@ Parameters:
 - `default_branch` (optional) - 'master' by default
 - `issues_enabled` (optional)
 - `merge_requests_enabled` (optional)
+- `builds_enabled` (optional)
 - `wiki_enabled` (optional)
 - `snippets_enabled` (optional)
 - `public` (optional) - if `true` same as setting visibility_level = 20
@@ -357,6 +362,7 @@ Parameters:
 - `default_branch` (optional)
 - `issues_enabled` (optional)
 - `merge_requests_enabled` (optional)
+- `builds_enabled` (optional)
 - `wiki_enabled` (optional)
 - `snippets_enabled` (optional)
 - `public` (optional) - if `true` same as setting visibility_level = 20
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 20cadae2291..73acf66935a 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -62,7 +62,7 @@ module API
       expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group }
       expose :name, :name_with_namespace
       expose :path, :path_with_namespace
-      expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :snippets_enabled, :created_at, :last_activity_at
+      expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :builds_enabled, :snippets_enabled, :created_at, :last_activity_at
       expose :creator_id
       expose :namespace
       expose :forked_from_project, using: Entities::ForkedFromProject, if: lambda{ | project, options | project.forked? }
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 67ee66a2058..2b4ada6e2eb 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -75,6 +75,7 @@ module API
       #   description (optional) - short project description
       #   issues_enabled (optional)
       #   merge_requests_enabled (optional)
+      #   builds_enabled (optional)
       #   wiki_enabled (optional)
       #   snippets_enabled (optional)
       #   namespace_id (optional) - defaults to user namespace
@@ -90,6 +91,7 @@ module API
                                      :description,
                                      :issues_enabled,
                                      :merge_requests_enabled,
+                                     :builds_enabled,
                                      :wiki_enabled,
                                      :snippets_enabled,
                                      :namespace_id,
@@ -117,6 +119,7 @@ module API
       #   default_branch (optional) - 'master' by default
       #   issues_enabled (optional)
       #   merge_requests_enabled (optional)
+      #   builds_enabled (optional)
       #   wiki_enabled (optional)
       #   snippets_enabled (optional)
       #   public (optional) - if true same as setting visibility_level = 20
@@ -132,6 +135,7 @@ module API
                                      :default_branch,
                                      :issues_enabled,
                                      :merge_requests_enabled,
+                                     :builds_enabled,
                                      :wiki_enabled,
                                      :snippets_enabled,
                                      :public,
@@ -172,6 +176,7 @@ module API
       #   description (optional) - short project description
       #   issues_enabled (optional)
       #   merge_requests_enabled (optional)
+      #   builds_enabled (optional)
       #   wiki_enabled (optional)
       #   snippets_enabled (optional)
       #   public (optional) - if true same as setting visibility_level = 20
@@ -185,6 +190,7 @@ module API
                                      :default_branch,
                                      :issues_enabled,
                                      :merge_requests_enabled,
+                                     :builds_enabled,
                                      :wiki_enabled,
                                      :snippets_enabled,
                                      :public,
diff --git a/spec/factories/ci/projects.rb b/spec/factories/ci/projects.rb
index 1183a190353..11cb8c9eeaa 100644
--- a/spec/factories/ci/projects.rb
+++ b/spec/factories/ci/projects.rb
@@ -31,16 +31,20 @@ FactoryGirl.define do
   factory :ci_project_without_token, class: Ci::Project do
     default_ref 'master'
 
-    gl_project factory: :empty_project
-
     shared_runners_enabled false
 
     factory :ci_project do
       token 'iPWx6WM4lhHNedGfBpPJNP'
     end
 
-    factory :ci_public_project do
-      public true
+    initialize_with do
+      # TODO:
+      # this is required, because builds_enabled is initialized when Project is created
+      # and this create gitlab_ci_project if builds is set to true
+      # here we take created gitlab_ci_project and update it's attributes
+      ci_project = create(:empty_project).ensure_gitlab_ci_project
+      ci_project.update_attributes(attributes)
+      ci_project
     end
   end
 end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index f93935ebe3b..8d7e6e76766 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -415,12 +415,15 @@ describe Project do
     it { expect(project.ci_commit(commit.sha)).to eq(commit) }
   end
 
-  describe :enable_ci do
+  describe :builds_enabled do
     let(:project) { create :project }
 
-    before { project.enable_ci }
+    before { project.builds_enabled = true }
 
-    it { expect(project.gitlab_ci?).to be_truthy }
+    subject { project.builds_enabled }
+
+    it { is_expected.to eq(project.gitlab_ci_service.active) }
+    it { expect(project.builds_enabled?).to be_truthy }
     it { expect(project.gitlab_ci_project).to be_a(Ci::Project) }
   end
 
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index e9de9e0826d..9fc294118ae 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -88,8 +88,11 @@ describe API::API, api: true  do
         end
 
         it 'returns projects in the correct order when ci_enabled_first parameter is passed' do
-          [project, project2, project3].each{ |project| project.build_missing_services }
-          project2.gitlab_ci_service.update(active: true)
+          [project, project2, project3].each do |project|
+            project.builds_enabled = false
+            project.build_missing_services
+          end
+          project2.builds_enabled = true
           get api('/projects', user), { ci_enabled_first: 'true' }
           expect(response.status).to eq(200)
           expect(json_response).to be_an Array
diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb
index c0226605a23..b180d2fec77 100644
--- a/spec/requests/api/services_spec.rb
+++ b/spec/requests/api/services_spec.rb
@@ -46,6 +46,7 @@ describe API::API, api: true  do
         delete api("/projects/#{project.id}/services/#{dashed_service}", user)
 
         expect(response.status).to eq(200)
+        project.send(service_method).reload
         expect(project.send(service_method).activated?).to be_falsey
       end
     end
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index 25277f07482..e81c4edb7d8 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -70,6 +70,28 @@ describe Projects::CreateService do
       end
     end
 
+    context 'builds_enabled global setting' do
+      let(:project) { create_project(@user, @opts) }
+
+      subject { project.builds_enabled? }
+
+      context 'global builds_enabled false does not enable CI by default' do
+        before do
+          @opts.merge!(builds_enabled: false)
+        end
+
+        it { is_expected.to be_falsey }
+      end
+
+      context 'global builds_enabled true does enable CI by default' do
+        before do
+          @opts.merge!(builds_enabled: true)
+        end
+
+        it { is_expected.to be_truthy }
+      end
+    end
+
     context 'restricted visibility level' do
       before do
         stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index 65a8c81204d..e397b2b9b4a 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -46,7 +46,7 @@ describe Projects::ForkService do
       it "fork and enable CI for fork" do
         @from_project.enable_ci
         @to_project = fork_project(@from_project, @to_user)
-        expect(@to_project.gitlab_ci?).to be_truthy
+        expect(@to_project.builds_enabled?).to be_truthy
       end
     end
   end
-- 
GitLab