From 37224dc9c1ee80ba9030b616e2bc87bd96919e09 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 15 Feb 2012 22:02:33 +0200 Subject: [PATCH] ProtectedBranches model, Master permission for repo\n Allow push to protected branch for masters only --- .../protected_branches_controller.rb | 23 +++++++ app/models/project.rb | 41 ++++++++---- app/models/protected_branch.rb | 29 +++++++++ app/models/repository.rb | 4 +- app/views/protected_branches/index.html.haml | 39 ++++++++++++ .../repositories/_branches_head.html.haml | 9 +++ app/views/repositories/_head.html.haml | 2 +- app/views/repositories/branches.html.haml | 2 +- config/routes.rb | 1 + ...0120215182305_create_protected_branches.rb | 10 +++ db/schema.rb | 21 ++++++- lib/gitlabhq/gitolite.rb | 62 ++++++++++--------- spec/models/project_spec.rb | 23 ++++--- spec/models/protected_branch_spec.rb | 16 +++++ 14 files changed, 229 insertions(+), 53 deletions(-) create mode 100644 app/controllers/protected_branches_controller.rb create mode 100644 app/models/protected_branch.rb create mode 100644 app/views/protected_branches/index.html.haml create mode 100644 app/views/repositories/_branches_head.html.haml create mode 100644 db/migrate/20120215182305_create_protected_branches.rb create mode 100644 spec/models/protected_branch_spec.rb diff --git a/app/controllers/protected_branches_controller.rb b/app/controllers/protected_branches_controller.rb new file mode 100644 index 00000000..c91bd903 --- /dev/null +++ b/app/controllers/protected_branches_controller.rb @@ -0,0 +1,23 @@ +class ProtectedBranchesController < ApplicationController + before_filter :project + + # Authorize + before_filter :add_project_abilities + before_filter :authorize_read_project! + before_filter :require_non_empty_project + + layout "project" + + def index + @branches = @project.protected_branches.all + @protected_branch = @project.protected_branches.new + end + + def create + @project.protected_branches.create(params[:protected_branch]) + redirect_to project_protected_branches_path(@project) + end + + def destroy + end +end diff --git a/app/models/project.rb b/app/models/project.rb index 49311daa..ac70eedb 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -16,6 +16,7 @@ class Project < ActiveRecord::Base has_many :snippets, :dependent => :destroy has_many :deploy_keys, :dependent => :destroy, :foreign_key => "project_id", :class_name => "Key" has_many :web_hooks, :dependent => :destroy + has_many :protected_branches, :dependent => :destroy acts_as_taggable @@ -138,6 +139,15 @@ class Project < ActiveRecord::Base data end + def open_branches + if protected_branches.empty? + self.repo.heads + else + pnames = protected_branches.map(&:name) + self.repo.heads.reject { |h| pnames.include?(h.name) } + end.sort_by(&:name) + end + def team_member_by_name_or_email(email = nil, name = nil) user = users.where("email like ? or name like ?", email, name).first users_projects.find_by_user_id(user.id) if user @@ -210,6 +220,12 @@ class Project < ActiveRecord::Base keys.map(&:identifier) end + def repository_masters + keys = Key.joins({:user => :users_projects}). + where("users_projects.project_id = ? AND users_projects.repo_access = ?", id, Repository::REPO_MASTER) + keys.map(&:identifier) + end + def readers @readers ||= users_projects.includes(:user).where(:project_access => [PROJECT_R, PROJECT_RW, PROJECT_RWA]).map(&:user) end @@ -235,7 +251,7 @@ class Project < ActiveRecord::Base end def allow_pull_for?(user) - !users_projects.where(:user_id => user.id, :repo_access => [Repository::REPO_R, Repository::REPO_RW]).empty? + !users_projects.where(:user_id => user.id, :repo_access => [Repository::REPO_R, Repository::REPO_RW, Repository::REPO_MASTER]).empty? end def root_ref @@ -340,15 +356,18 @@ end # # Table name: projects # -# id :integer not null, primary key -# name :string(255) -# path :string(255) -# description :text -# created_at :datetime -# updated_at :datetime -# private_flag :boolean default(TRUE), not null -# code :string(255) -# owner_id :integer -# default_branch :string(255) default("master"), not null +# id :integer not null, primary key +# name :string(255) +# path :string(255) +# description :text +# created_at :datetime +# updated_at :datetime +# private_flag :boolean default(TRUE), not null +# code :string(255) +# owner_id :integer +# default_branch :string(255) default("master"), not null +# issues_enabled :boolean default(TRUE), not null +# wall_enabled :boolean default(TRUE), not null +# merge_requests_enabled :boolean default(TRUE), not null # diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb new file mode 100644 index 00000000..9c2d391d --- /dev/null +++ b/app/models/protected_branch.rb @@ -0,0 +1,29 @@ +class ProtectedBranch < ActiveRecord::Base + belongs_to :project + validates_presence_of :project_id + validates_presence_of :name + + after_save :update_repository + after_destroy :update_repository + + def update_repository + Gitlabhq::GitHost.system.new.configure do |c| + c.update_project(project.path, project) + end + end + + def commit + project.commit(self.name) + end +end +# == Schema Information +# +# Table name: protected_branches +# +# id :integer not null, primary key +# project_id :integer not null +# name :string(255) not null +# created_at :datetime not null +# updated_at :datetime not null +# + diff --git a/app/models/repository.rb b/app/models/repository.rb index 8f9f9746..3d9ad262 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -4,6 +4,7 @@ class Repository REPO_N = 0 REPO_R = 1 REPO_RW = 2 + REPO_MASTER = 3 attr_accessor :project @@ -15,7 +16,8 @@ class Repository { "Denied" => REPO_N, "Pull" => REPO_R, - "Pull & Push" => REPO_RW + "Pull & Push" => REPO_RW, + "Master" => REPO_MASTER } end diff --git a/app/views/protected_branches/index.html.haml b/app/views/protected_branches/index.html.haml new file mode 100644 index 00000000..055d9369 --- /dev/null +++ b/app/views/protected_branches/index.html.haml @@ -0,0 +1,39 @@ += render "repositories/branches_head" + += form_for [@project, @protected_branch] do |f| + -if @protected_branch.errors.any? + .alert-message.block-message.error + %ul + - @protected_branch.errors.full_messages.each do |msg| + %li= msg + + .clearfix + = f.label :name + .input= f.select(:name, @project.open_branches.map { |br| [br.name, br.name] } , { :include_blank => "Select branch" }, { :style => "width:300px" }) + .actions + = f.submit 'Add', :class => "primary btn" + + +- unless @branches.empty? + %table + %thead + %tr + %th Name + %th Last commit + %tbody + - @branches.each do |branch| + %tr + %td + = link_to project_commits_path(@project, :ref => branch.name) do + %strong= branch.name + - if branch.name == @project.root_ref + %span.label default + %td + = link_to project_commits_path(@project, branch.commit.id) do + = truncate branch.commit.id.to_s, :length => 10 + = time_ago_in_words(branch.commit.committed_date) + ago + + +:javascript + $('select#protected_branch_name').chosen(); diff --git a/app/views/repositories/_branches_head.html.haml b/app/views/repositories/_branches_head.html.haml new file mode 100644 index 00000000..d98dedc2 --- /dev/null +++ b/app/views/repositories/_branches_head.html.haml @@ -0,0 +1,9 @@ += render "repositories/head" +%ul.pills + %li{:class => ("active" if current_page?(branches_project_repository_path(@project)))} + = link_to branches_project_repository_path(@project) do + All + %li{:class => ("active" if current_page?(project_protected_branches_path(@project)))} + = link_to project_protected_branches_path(@project) do + Protected + diff --git a/app/views/repositories/_head.html.haml b/app/views/repositories/_head.html.haml index 37360d48..2d43d008 100644 --- a/app/views/repositories/_head.html.haml +++ b/app/views/repositories/_head.html.haml @@ -3,7 +3,7 @@ = link_to project_repository_path(@project) do %span Activities - %li{:class => "#{'active' if current_page?(branches_project_repository_path(@project)) }"} + %li{:class => "#{'active' if current_page?(branches_project_repository_path(@project)) || current_page?(project_protected_branches_path(@project)) }"} = link_to branches_project_repository_path(@project) do %span Branches diff --git a/app/views/repositories/branches.html.haml b/app/views/repositories/branches.html.haml index d817ab51..91a97207 100644 --- a/app/views/repositories/branches.html.haml +++ b/app/views/repositories/branches.html.haml @@ -1,4 +1,4 @@ -= render "head" += render "repositories/branches_head" - unless @branches.empty? %table %thead diff --git a/config/routes.rb b/config/routes.rb index 81bf93c7..e7a96fb4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -62,6 +62,7 @@ Gitlab::Application.routes.draw do end resources :deploy_keys + resources :protected_branches, :only => [:index, :create, :destroy] resources :refs, :only => [], :path => "/" do collection do diff --git a/db/migrate/20120215182305_create_protected_branches.rb b/db/migrate/20120215182305_create_protected_branches.rb new file mode 100644 index 00000000..841d08c3 --- /dev/null +++ b/db/migrate/20120215182305_create_protected_branches.rb @@ -0,0 +1,10 @@ +class CreateProtectedBranches < ActiveRecord::Migration + def change + create_table :protected_branches do |t| + t.integer :project_id, :null => false + t.string :name, :null => false + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index bb9f6387..f7006c4c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,19 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20120206170141) do +ActiveRecord::Schema.define(:version => 20120215182305) do + + create_table "features", :force => true do |t| + t.string "name" + t.string "branch_name" + t.integer "assignee_id" + t.integer "author_id" + t.integer "project_id" + t.datetime "created_at" + t.datetime "updated_at" + t.string "version" + t.integer "status", :default => 0, :null => false + end create_table "issues", :force => true do |t| t.string "title" @@ -82,6 +94,13 @@ ActiveRecord::Schema.define(:version => 20120206170141) do t.boolean "merge_requests_enabled", :default => true, :null => false end + create_table "protected_branches", :force => true do |t| + t.integer "project_id", :null => false + t.string "name", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + create_table "snippets", :force => true do |t| t.string "title" t.text "content" diff --git a/lib/gitlabhq/gitolite.rb b/lib/gitlabhq/gitolite.rb index e6eb8e51..4f911113 100644 --- a/lib/gitlabhq/gitolite.rb +++ b/lib/gitlabhq/gitolite.rb @@ -64,21 +64,9 @@ module Gitlabhq def update_project(repo_name, project) ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite')) conf = ga_repo.config - - repo = if conf.has_repo?(repo_name) - conf.get_repo(repo_name) - else - ::Gitolite::Config::Repo.new(repo_name) - end - - name_readers = project.repository_readers - name_writers = project.repository_writers - - repo.clean_permissions - repo.add_permission("R", "", name_readers) unless name_readers.blank? - repo.add_permission("RW+", "", name_writers) unless name_writers.blank? + repo = update_project_config(project, conf) conf.add_repo(repo, true) - + ga_repo.save end @@ -89,25 +77,43 @@ module Gitlabhq conf = ga_repo.config projects.each do |project| - repo_name = project.path - - repo = if conf.has_repo?(repo_name) - conf.get_repo(repo_name) - else - ::Gitolite::Config::Repo.new(repo_name) - end - - name_readers = project.repository_readers - name_writers = project.repository_writers - - repo.clean_permissions - repo.add_permission("R", "", name_readers) unless name_readers.blank? - repo.add_permission("RW+", "", name_writers) unless name_writers.blank? + repo = update_project_config(project, conf) conf.add_repo(repo, true) end ga_repo.save end + def update_project_config(project, conf) + repo_name = project.path + + repo = if conf.has_repo?(repo_name) + conf.get_repo(repo_name) + else + ::Gitolite::Config::Repo.new(repo_name) + end + + name_readers = project.repository_readers + name_writers = project.repository_writers + name_masters = project.repository_masters + + pr_br = project.protected_branches.map(&:name).join(" ") + + repo.clean_permissions + + # Deny access to protected branches for writers + unless name_writers.blank? || pr_br.blank? + repo.add_permission("-", pr_br, name_writers) + end + + # Add read permissions + repo.add_permission("R", "", name_readers) unless name_readers.blank? + + # Add write permissions + repo.add_permission("RW+", "", name_writers) unless name_writers.blank? + repo.add_permission("RW+", "", name_masters) unless name_masters.blank? + + repo + end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 68bc82de..437b1397 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -290,15 +290,18 @@ end # # Table name: projects # -# id :integer not null, primary key -# name :string(255) -# path :string(255) -# description :text -# created_at :datetime -# updated_at :datetime -# private_flag :boolean default(TRUE), not null -# code :string(255) -# owner_id :integer -# default_branch :string(255) default("master"), not null +# id :integer not null, primary key +# name :string(255) +# path :string(255) +# description :text +# created_at :datetime +# updated_at :datetime +# private_flag :boolean default(TRUE), not null +# code :string(255) +# owner_id :integer +# default_branch :string(255) default("master"), not null +# issues_enabled :boolean default(TRUE), not null +# wall_enabled :boolean default(TRUE), not null +# merge_requests_enabled :boolean default(TRUE), not null # diff --git a/spec/models/protected_branch_spec.rb b/spec/models/protected_branch_spec.rb new file mode 100644 index 00000000..a0b0032a --- /dev/null +++ b/spec/models/protected_branch_spec.rb @@ -0,0 +1,16 @@ +# == Schema Information +# +# Table name: protected_branches +# +# id :integer not null, primary key +# project_id :integer not null +# name :string(255) not null +# created_at :datetime not null +# updated_at :datetime not null +# + +require 'spec_helper' + +describe ProtectedBranch do + pending "add some examples to (or delete) #{__FILE__}" +end