Commit 0b2469a4 authored by James Lopez's avatar James Lopez

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into fix/persistent-import-data

parents c607b1c9 9ca633eb
# Prefer single quotes
StringLiterals:
EnforcedStyle: single_quotes
Enabled: true
stage:
before:
- cp config/gitlab.teatro.yml config/gitlab.yml
- mkdir /apps/gitlab-satellites
- mkdir /apps/repositories
database:
- RAILS_ENV=development force=yes bundle exec rake db:create gitlab:setup
\ No newline at end of file
Please view this file on the master branch, on stable branches it's out of date.
v 8.10.0 (unreleased)
- Expose {should,force}_remove_source_branch (Ben Boeckel)
- Fix commit builds API, return all builds for all pipelines for given commit. !4849
- Replace Haml with Hamlit to make view rendering faster. !3666
- Expire the branch cache after `git gc` runs
- Refactor repository paths handling to allow multiple git mount points
- Optimize system note visibility checking by memoizing the visible reference count !5070
- Add Application Setting to configure default Repository Path for new projects
- Delete award emoji when deleting a user
- Remove pinTo from Flash and make inline flash messages look nicer !4854 (winniehell)
- Wrap code blocks on Activies and Todos page. !4783 (winniehell)
- Align flash messages with left side of page content !4959 (winniehell)
......@@ -30,7 +33,9 @@ v 8.10.0 (unreleased)
- Exclude email check from the standard health check
- Updated layout for Projects, Groups, Users on Admin area !4424
- Fix changing issue state columns in milestone view
- Update health_check gem to version 2.1.0
- Add notification settings dropdown for groups
- Render inline diffs for multiple changed lines following eachother
- Wildcards for protected branches. !4665
- Allow importing from Github using Personal Access Tokens. (Eric K Idema)
- API: Todos !3188 (Robert Schilling)
......@@ -38,6 +43,7 @@ v 8.10.0 (unreleased)
- Add "Enabled Git access protocols" to Application Settings
- Diffs will create button/diff form on demand no on server side
- Reduce size of HTML used by diff comment forms
- Protected branches have a "Developers can Merge" setting. !4892 (original implementation by Mathias Vestergaard)
- Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
- Only show New Snippet button to users that can create snippets.
- PipelinesFinder uses git cache data
......@@ -46,14 +52,17 @@ v 8.10.0 (unreleased)
- Collapse large diffs by default (!4990)
- Check for conflicts with existing Project's wiki path when creating a new project.
- Show last push widget in upstream after push to fork
- Cache todos pending/done dashboard query counts.
- Don't instantiate a git tree on Projects show default view
- Bump Rinku to 2.0.0
- Remove unused front-end variable -> default_issues_tracker
- ObjectRenderer retrieve renderer content using Rails.cache.read_multi
- Better caching of git calls on ProjectsController#show.
- Avoid to retrieve MR closes_issues as much as possible.
- Add API endpoint for a group issues !4520 (mahcsig)
- Add Bugzilla integration !4930 (iamtjg)
- Instrument Rinku usage
- Be explicit to define merge request discussion variables
- Metrics for Rouge::Plugins::Redcarpet and Rouge::Formatters::HTMLGitlab
- RailsCache metris now includes fetch_hit/fetch_miss and read_hit/read_miss info.
- Allow [ci skip] to be in any case and allow [skip ci]. !4785 (simon_w)
......@@ -67,12 +76,25 @@ v 8.10.0 (unreleased)
- Allow '?', or '&' for label names
- Fix importer for GitHub Pull Requests when a branch was reused across Pull Requests
- Add date when user joined the team on the member page
- Fix 404 redirect after validation fails importing a GitLab project
- Fix 404 redirect after validation fails importing a GitLab project
- Fix 404 redirect after validation fails importing a GitLab project
- Added setting to set new users by default as external !4545 (Dravere)
- Add min value for project limit field on user's form !3622 (jastkand)
- Reset project pushes_since_gc when we enqueue the git gc call
- Add reminder to not paste private SSH keys !4399 (Ingo Blechschmidt)
- Remove duplicate `description` field in `MergeRequest` entities (Ben Boeckel)
- Style of import project buttons were fixed in the new project page. !5183 (rdemirbay)
- Fix GitHub client requests when rate limit is disabled
- Optimistic locking for Issues and Merge Requests (Title and description overriding prevention)
- Redesign Builds and Pipelines pages
- Change status color and icon for running builds
v 8.9.6
- Fix importing of events under notes for GitLab projects. !5154
- Fix log statements in import/export. !5129
- Fix commit avatar alignment in compare view. !5128
- Fix broken migration in MySQL. !5005
- Overwrite Host and X-Forwarded-Host headers in NGINX !5213
v 8.9.7 (unreleased)
- Fix import_data wrongly saved as a result of an invalid import_url
......
......@@ -344,7 +344,7 @@ gem 'oauth2', '~> 1.2.0'
gem 'paranoia', '~> 2.0'
# Health check
gem 'health_check', '~> 1.5.1'
gem 'health_check', '~> 2.1.0'
# System information
gem 'vmstat', '~> 2.1.0'
......
......@@ -322,8 +322,8 @@ GEM
thor
tilt
hashie (3.4.3)
health_check (1.5.1)
rails (>= 2.3.0)
health_check (2.1.0)
rails (>= 4.0)
hipchat (1.5.2)
httparty
mimemagic
......@@ -870,7 +870,7 @@ DEPENDENCIES
grape (~> 0.13.0)
grape-entity (~> 0.4.2)
hamlit (~> 2.5)
health_check (~> 1.5.1)
health_check (~> 2.1.0)
hipchat (~> 1.5.0)
html-pipeline (~> 1.11.0)
httparty (~> 0.13.3)
......
class @Diff
UNFOLD_COUNT = 20
constructor: ->
$('.files .diff-file').singleFileDiff()
@filesCommentButton = $('.files .diff-file').filesCommentButton()
$(document).off('click', '.js-unfold')
......
$ ->
$(".protected-branches-list :checkbox").change (e) ->
name = $(this).attr("name")
if name == "developers_can_push"
if name == "developers_can_push" || name == "developers_can_merge"
id = $(this).val()
checked = $(this).is(":checked")
can_push = $(this).is(":checked")
url = $(this).data("url")
$.ajax
type: "PUT"
type: "PATCH"
url: url
dataType: "json"
data:
id: id
protected_branch:
developers_can_push: checked
"#{name}": can_push
success: ->
row = $(e.target)
......
......@@ -20,6 +20,7 @@
}
&.s16 { width: 16px; height: 16px; margin-right: 6px; }
&.s20 { width: 20px; height: 20px; margin-right: 7px; }
&.s24 { width: 24px; height: 24px; margin-right: 8px; }
&.s26 { width: 26px; height: 26px; margin-right: 8px; }
&.s32 { width: 32px; height: 32px; margin-right: 10px; }
......
......@@ -11,7 +11,6 @@
.toggle-nav-collapse,
.pin-nav-btn {
color: $color-light;
background: $color;
&:hover {
color: $white-light;
......
......@@ -17,7 +17,7 @@
height: 100%;
overflow: hidden;
transition: width $sidebar-transition-duration;
@include box-shadow(2px 0 16px 0 #bbb);
@include box-shadow(2px 0 16px 0 $black-transparent);
}
}
......@@ -76,7 +76,7 @@
}
a {
padding: 7px 15px 7px 12px;
padding: 7px 16px;
font-size: $gl-font-size;
line-height: 24px;
display: block;
......@@ -108,7 +108,7 @@
}
}
.toggle-nav-collapse {
.sidebar-action-buttons {
width: $sidebar_width;
position: absolute;
top: 0;
......@@ -117,12 +117,37 @@
padding: 5px 0;
font-size: 18px;
line-height: 30px;
.toggle-nav-collapse {
left: 0;
}
.pin-nav-btn {
right: 0;
display: none;
@media (min-width: $sidebar-breakpoint) {
display: block;
}
.fa {
transition: transform .15s;
}
&.is-active {
.fa {
transform: rotate(90deg);
}
}
}
}
.nav-header-btn {
padding: 10px 5px;
padding: 10px 16px;
color: inherit;
transition-duration: .3s;
position: absolute;
top: 0;
&:hover,
&:focus {
......@@ -131,30 +156,6 @@
}
}
.pin-nav-btn {
display: none;
position: absolute;
left: 0;
bottom: 0;
height: 50px;
width: $sidebar_width;
line-height: 30px;
@media (min-width: $sidebar-breakpoint) {
display: block;
}
.fa {
transition: transform .15s;
}
&.is-active {
.fa {
transform: rotate(90deg);
}
}
}
.page-sidebar-collapsed {
padding-left: 0;
......
......@@ -17,6 +17,7 @@ $focus-border-color: #3aabf0;
$table-border-color: #f0f0f0;
$background-color: #fafafa;
$dark-background-color: #f7f7f7;
$table-text-gray: #8f8f8f;
/*
* Text
......
......@@ -83,14 +83,6 @@
}
}
table.builds {
.build-link {
a {
color: $gl-dark-link-color;
}
}
}
.build-trace {
background: $ci-output-bg;
color: $ci-text-color;
......
......@@ -162,9 +162,15 @@
}
.filtered-labels {
font-size: 0;
padding: 12px 16px;
.label-row {
margin-top: 4px;
margin-bottom: 4px;
&:not(:last-child) {
margin-right: 5px;
margin-right: 8px;
}
}
......
......@@ -73,11 +73,14 @@
color: #888;
}
&.ci-pending,
&.ci-running {
&.ci-pending {
color: $gl-warning;
}
&.ci-running {
color: $blue-normal;
}
&.ci-failed,
&.ci-error {
color: $gl-danger;
......
.pipelines {
.stage {
max-width: 100px;
max-width: 80px;
width: 80px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.duration, .finished_at {
margin: 4px 0;
}
.commit-title {
margin: 0;
}
......@@ -22,3 +19,136 @@
margin: 4px;
}
}
.content-list {
&.pipelines,
&.builds-content-list {
width: 100%;
overflow: auto;
}
}
.table.builds {
min-width: 1100px;
tr {
th {
padding: 16px;
border: none;
}
}
tbody {
border-top-width: 1px;
}
.branch-commit {
.branch-name {
margin-left: 8px;
font-weight: bold;
max-width: 180px;
overflow: hidden;
display: inline-block;
white-space: nowrap;
vertical-align: top;
text-overflow: ellipsis;
}
svg {
margin: 0 6px;
height: 14px;
width: auto;
vertical-align: middle;
}
.commit-id {
color: $gl-link-color;
margin-right: 8px;
}
.commit-title {
margin-top: 4px;
max-width: 320px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.avatar {
margin-left: 0;
}
.label-container {
.label {
margin-top: 5px;
}
}
}
.duration,
.finished-at {
color: $table-text-gray;
margin: 4px 0;
.fa {
font-size: 12px;
}
svg {
height: 12px;
width: auto;
vertical-align: middle;
}
.fa,
svg {
margin-right: 5px;
}
}
.pipeline-actions {
.btn {
margin: 0;
color: $table-text-gray;
}
.dropdown-toggle,
.dropdown-menu {
color: $table-text-gray;
.fa {
color: $table-text-gray;
margin-right: 6px;
font-size: 14px;
}
}
.btn-remove {
color: $white-light;
}
.btn-group {
&.open {
.btn-default {
background-color: $white-normal;
border-color: $border-white-normal;
}
}
}
}
.build-link {
a {
color: $gl-dark-link-color;
}
}
.btn-group.open .dropdown-toggle {
box-shadow: none;
}
}
......@@ -340,23 +340,30 @@ a.deploy-project-label {
.project-import {
.form-group {
margin-bottom: 0;
margin-bottom: 5px;
}
.import-buttons {
padding-left: 0;
display: -webkit-flex;
display: flex;
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
.btn {
margin-right: 10px;
padding: 8px 12px;
margin: 0 10px 10px 0;
padding: 8px;
}
&> div {
margin-bottom: 14px;
> div {
padding-left: 0;
&:last-child {
margin-bottom: 0;
.btn {
margin-right: 0;
}
}
}
}
......
......@@ -32,11 +32,15 @@
border-color: $gl-gray;
}
&.ci-pending,
&.ci-running {
&.ci-pending {
color: $gl-warning;
border-color: $gl-warning;
}
&.ci-running {
color: $blue-normal;
border-color: $blue-normal;
}
}
.ci-status-icon-success {
......@@ -45,10 +49,12 @@
.ci-status-icon-failed {
color: $gl-danger;
}
.ci-status-icon-running,
.ci-status-icon-pending {
color: $gl-warning;
}
.ci-status-icon-running {
color: $blue-normal;
}
.ci-status-icon-canceled,
.ci-status-icon-disabled,
.ci-status-icon-not-found,
......
class Dashboard::TodosController < Dashboard::ApplicationController
include TodosHelper
before_action :find_todos, only: [:index, :destroy_all]
def index
......@@ -13,7 +11,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
respond_to do |format|
format.html { redirect_to dashboard_todos_path, notice: 'Todo was successfully marked as done.' }
format.js { head :ok }
format.json { render json: { count: todos_pending_count, done_count: todos_done_count } }
format.json { render json: todos_counts }
end
end
......@@ -23,7 +21,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
respond_to do |format|
format.html { redirect_to dashboard_todos_path, notice: 'All todos were marked as done.' }
format.js { head :ok }
format.json { render json: { count: todos_pending_count, done_count: todos_done_count } }
format.json { render json: todos_counts }
end
end
......@@ -36,4 +34,11 @@ class Dashboard::TodosController < Dashboard::ApplicationController
def find_todos
@todos ||= TodosFinder.new(current_user, params).execute
end
def todos_counts
{
count: TodosFinder.new(current_user, state: :pending).execute.count,
done_count: TodosFinder.new(current_user, state: :done).execute.count
}
end
end
......@@ -107,7 +107,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
# Only allow properly saved users to login.
if @user.persisted? && @user.valid?
log_audit_event(@user, with: oauth['provider'])
sign_in_and_redirect(@user)
if @user.two_factor_enabled?
prompt_for_two_factor(@user)
else
sign_in_and_redirect(@user)
end
else
error_message = @user.errors.full_messages.to_sentence
......
......@@ -119,6 +119,10 @@ class Projects::IssuesController < Projects::ApplicationController
render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } })
end
end
rescue ActiveRecord::StaleObjectError
@conflict = true
render :edit
end
def referenced_merge_requests
......@@ -216,7 +220,7 @@ class Projects::IssuesController < Projects::ApplicationController
def issue_params
params.require(:issue).permit(
:title, :assignee_id, :position, :description, :confidential,
:milestone_id, :due_date, :state_event, :task_num, label_ids: []
:milestone_id, :due_date, :state_event, :task_num, :lock_version, label_ids: []
)
end
......
......@@ -56,7 +56,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def show
respond_to do |format|
format.html
format.html { define_discussion_vars }
format.json do
render json: @merge_request
......@@ -82,7 +82,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request_diff = @merge_request.merge_request_diff
respond_to do |format|
format.html
format.html { define_discussion_vars }
format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } }
end
end
......@@ -108,7 +108,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def commits
respond_to do |format|
format.html { render 'show' }
format.html do
define_discussion_vars
render 'show'
end
format.json do
# Get commits from repository
# or from cache if already merged
......@@ -123,7 +127,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def builds
respond_to do |format|
format.html { render 'show' }
format.html do
define_discussion_vars
render 'show'
end
format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_builds') } }
end
end
......@@ -188,6 +196,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
else
render "edit"
end
rescue ActiveRecord::StaleObjectError
@conflict = true
render :edit
end
def remove_wip
......@@ -353,14 +364,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.unlock_mr
@merge_request.close
end
if request.format == :html || action_name == 'show'
define_show_html_vars
end
end
# Discussion tab data is only required on html requests
def define_show_html_vars
# Discussion tab data is rendered on html responses of actions
# :show, :diff, :commits, :builds. but not when request the data through AJAX
def define_discussion_vars
# Build a note object for comment form
@note = @project.notes.new(noteable: @noteable)
......@@ -419,7 +427,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
:title, :assignee_id, :source_project_id, :source_branch,
:target_project_id, :target_branch, :milestone_id,
:state_event, :description, :task_num, :force_remove_source_branch,
label_ids: []
:lock_version, label_ids: []
)
end
......
......@@ -50,6 +50,6 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
end
def protected_branch_params
params.require(:protected_branch).permit(:name, :developers_can_push)
params.require(:protected_branch).permit(:name, :developers_can_push, :developers_can_merge)
end
end
......@@ -5,7 +5,7 @@ class Projects::TodosController < Projects::ApplicationController
todo = TodoService.new.mark_todo(issuable, current_user)
render json: {
count: current_user.todos_pending_count,
count: TodosFinder.new(current_user, state: :pending).execute.count,
delete_path: dashboard_todo_path(todo)
}
end
......
......@@ -8,7 +8,7 @@
# action_id: integer
# author_id: integer
# project_id; integer
# state: 'pending' or 'done'
# state: 'pending' (default) or 'done'
# type: 'Issue' or 'MergeRequest'
#
......@@ -37,7 +37,7 @@ class TodosFinder
private
def action_id?
action_id.present? && [Todo::ASSIGNED, Todo::MENTIONED, Todo::BUILD_FAILED, Todo::MARKED].include?(action_id.to_i)
action_id.present? && Todo::ACTION_NAMES.has_key?(action_id.to_i)
end
def action_id
......
......@@ -31,7 +31,7 @@ module AppearancesHelper
end
end
def navbar_icon(icon_name, size: 16)
def custom_icon(icon_name, size: 16)
render "shared/icons/#{icon_name}.svg", size: size
end
end
......@@ -12,7 +12,7 @@ module BranchesHelper
def can_push_branch?(project, branch_name)
return false unless project.repository.branch_exists?(branch_name)
::Gitlab::GitAccess.new(current_user, project, 'web').can_push_to_branch?(branch_name)
::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(branch_name)
end
def project_branches
......
......@@ -29,8 +29,10 @@ module CiStatusHelper
'check'
when 'failed'
'close'
when 'running', 'pending'
when 'pending'
'clock-o'
when 'running'
'spinner'
else
'circle'
end
......
module TodosHelper
def todos_pending_count
TodosFinder.new(current_user, state: :pending).execute.count
@todos_pending_count ||= TodosFinder.new(current_user, state: :pending).execute.count
end
def todos_done_count
TodosFinder.new(current_user, state: :done).execute.count
@todos_done_count ||= TodosFinder.new(current_user, state: :done).execute.count
end
def todo_action_name(todo)
......@@ -13,6 +13,7 @@ module TodosHelper
when Todo::MENTIONED then 'mentioned you on'
when Todo::BUILD_FAILED then 'The build failed for your'
when Todo::MARKED then 'added a todo for'
when Todo::APPROVAL_REQUIRED then 'set you as an approver for'
end
end
......
......@@ -87,6 +87,12 @@ module Issuable
User.find(assignee_id_was).update_cache_counts if assignee_id_was
assignee.update_cache_counts if assignee
end
# We want to use optimistic lock for cases when only title or description are involved
# http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html
def locking_enabled?
title_changed? || description_changed?
end
end
module ClassMethods
......
......@@ -552,7 +552,13 @@ class MergeRequest < ActiveRecord::Base
end
def can_be_merged_by?(user)
::Gitlab::GitAccess.new(user, project, 'web').can_push_to_branch?(target_branch)
access = ::Gitlab::UserAccess.new(user, project: project)
access.can_push_to_branch?(target_branch) || access.can_merge_to_branch?(target_branch)
end
def can_be_merged_via_command_line_by?(user)
access = ::Gitlab::UserAccess.new(user, project: project)
access.can_push_to_branch?(target_branch)
end
def mergeable_ci_state?
......
......@@ -836,6 +836,10 @@ class Project < ActiveRecord::Base
protected_branches.matching(branch_name).any?(&:developers_can_push)
end
def developers_can_merge_to_protected_branch?(branch_name)
protected_branches.matching(branch_name).any?(&:developers_can_merge)
end
def forked?
!(forked_project_link.nil? || forked_project_link.forked_from_project.nil?)
end
......
......@@ -769,9 +769,9 @@ class Repository
end
end
def merge(user, source_sha, target_branch, options = {})
our_commit = rugged.branches[target_branch].target
their_commit = rugged.lookup(source_sha)
def merge(user, merge_request, options = {})
our_commit = rugged.branches[merge_request.target_branch].target
their_commit = rugged.lookup(merge_request.diff_head_sha)
raise "Invalid merge target" if our_commit.nil?
raise "Invalid merge source" if their_commit.nil?
......@@ -779,14 +779,16 @@ class Repository
merge_index = rugged.merge_commits(our_commit, their_commit)
return false if merge_index.conflicts?
commit_with_hooks(user, target_branch) do |ref|
commit_with_hooks(user, merge_request.target_branch) do |tmp_ref|
actual_options = options.merge(
parents: [our_commit, their_commit],
tree: merge_index.write_tree(rugged),
update_ref: ref
update_ref: tmp_ref
)
Rugged::Commit.create(rugged, actual_options)
commit_id = Rugged::Commit.create(rugged, actual_options)
merge_request.update(in_progress_merge_commit_sha: commit_id)
commit_id
end
end
......
class Todo < ActiveRecord::Base
ASSIGNED = 1
MENTIONED = 2
BUILD_FAILED = 3
MARKED = 4
ASSIGNED = 1
MENTIONED = 2
BUILD_FAILED = 3
MARKED = 4
APPROVAL_REQUIRED = 5 # This is an EE-only feature
ACTION_NAMES = {
ASSIGNED => :assigned,
MENTIONED => :mentioned,
BUILD_FAILED => :build_failed,
MARKED => :marked
MARKED => :marked,
APPROVAL_REQUIRED => :approval_required
}
belongs_to :author, class_name: "User"
......
......@@ -87,7 +87,7 @@ class User < ActiveRecord::Base
has_many :builds, dependent: :nullify, class_name: 'Ci::Build'
has_many :todos, dependent: :destroy
has_many :notification_settings, dependent: :destroy
has_many :award_emoji, as: :awardable, dependent: :destroy
has_many :award_emoji, dependent: :destroy
#
# Validations
......
......@@ -23,7 +23,7 @@ module Commits
private
def check_push_permissions
allowed = ::Gitlab::GitAccess.new(current_user, project, 'web').can_push_to_branch?(@target_branch)
allowed = ::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(@target_branch)
unless allowed
raise ValidationError.new('You are not allowed to push into this branch')
......@@ -31,7 +31,7 @@ module Commits
true
end
def create_target_branch(new_branch)
# Temporary branch exists and contains the change commit
return success if repository.find_branch(new_branch)
......
......@@ -42,7 +42,7 @@ module Files
end
def validate
allowed = ::Gitlab::GitAccess.new(current_user, project, 'web').can_push_to_branch?(@target_branch)
allowed = ::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(@target_branch)
unless allowed
raise_error("You are not allowed to push into this branch")
......
......@@ -89,7 +89,8 @@ class GitPushService < BaseService
# Set protection on the default branch if configured
if current_application_settings.default_branch_protection != PROTECTION_NONE
developers_can_push = current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_PUSH ? true : false
@project.protected_branches.create({ name: @project.default_branch, developers_can_push: developers_can_push })
developers_can_merge = current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_MERGE ? true : false
@project.protected_branches.create({ name: @project.default_branch, developers_can_push: developers_can_push, developers_can_merge: developers_can_merge })
end
end
......
......@@ -34,7 +34,7 @@ module MergeRequests
committer: committer
}
commit_id = repository.merge(current_user, merge_request.diff_head_sha, merge_request.target_branch, options)
commit_id = repository.merge(current_user, merge_request, options)
merge_request.update(merge_commit_sha: commit_id)
rescue GitHooksService::PreReceiveError => e
merge_request.update(merge_error: e.message)
......@@ -43,6 +43,8 @@ module MergeRequests
merge_request.update(merge_error: "Something went wrong during merge")
Rails.logger.error(e.message)
false
ensure
merge_request.update(in_progress_merge_commit_sha: nil)
end
def after_merge
......
......@@ -48,7 +48,7 @@ module MergeRequests
end
def force_push?
Gitlab::ForcePushCheck.force_push?(@project, @oldrev, @newrev)
Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev)
end
# Refresh merge request diff if we push to source or target branch of merge request
......
......@@ -7,8 +7,6 @@
#
module Projects
class HousekeepingService < BaseService
include Gitlab::ShellAdapter
LEASE_TIMEOUT = 3600
class LeaseTaken < StandardError
......@@ -24,11 +22,7 @@ module Projects
def execute
raise LeaseTaken unless try_obtain_lease
GitlabShellOneShotWorker.perform_async(:gc, @project.repository_storage_path, @project.path_with_namespace)
ensure
Gitlab::Metrics.measure(:reset_pushes_since_gc) do
update_pushes_since_gc(0)
end
execute_gitlab_shell_gc
end
def needed?
......@@ -36,19 +30,27 @@ module Projects
end
def increment!
Gitlab::Metrics.measure(:increment_pushes_since_gc) do
update_pushes_since_gc(@project.pushes_since_gc + 1)
if Gitlab::ExclusiveLease.new("project_housekeeping:increment!:#{@project.id}", timeout: 60).try_obtain
Gitlab::Metrics.measure(:increment_pushes_since_gc) do
update_pushes_since_gc(@project.pushes_since_gc + 1)
end
end
end
private
def update_pushes_since_gc(new_value)
if Gitlab::ExclusiveLease.new("project_housekeeping:update_pushes_since_gc:#{project.id}", timeout: 60).try_obtain
@project.update_column(:pushes_since_gc, new_value)
def execute_gitlab_shell_gc
GitGarbageCollectWorker.perform_async(@project.id)
ensure
Gitlab::Metrics.measure(:reset_pushes_since_gc) do
update_pushes_since_gc(0)
end
end
def update_pushes_since_gc(new_value)
@project.update_column(:pushes_since_gc, new_value)
end
def try_obtain_lease
Gitlab::Metrics.measure(:obtain_housekeeping_lease) do
lease = ::Gitlab::ExclusiveLease.new("project_housekeeping:#{@project.id}", timeout: LEASE_TIMEOUT)
......
- project = build.project
%tr.build
%tr.build.commit
%td.status
= ci_status_with_icon(build.status)
%td.build-link
- if can?(current_user, :read_build, build.project)
= link_to namespace_project_build_url(build.project.namespace, build.project, build) do
%strong Build ##{build.id}
- else
%strong Build ##{build.id}
%td
.branch-commit
- if can?(current_user, :read_build, build.project)
= link_to namespace_project_build_url(build.project.namespace, build.project, build) do
%span.build-link ##{build.id}
- else
%span.build-link ##{build.id}
- if build.stuck?
%i.fa.fa-warning.text-warning
- if build.stuck?
%i.fa.fa-warning.text-warning
%td
- if project
= link_to project.name_with_namespace, admin_namespace_project_path(project.namespace, project)
- if build.ref
= link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref), class: "monospace branch-name"
- else
.light none
= custom_icon("icon_commit")
%td
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace"
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace commit-id"
.label-container
- if build.tags.any?
- build.tags.each do |tag|
%span.label.label-primary
= tag
- if build.try(:trigger_request)
%span.label.label-info triggered
- if build.try(:allow_failure)
%span.label.label-danger allowed to fail
%td
- if build.ref
= link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref)
- else
.light none
- if project
= link_to project.name_with_namespace, admin_namespace_project_path(project.namespace, project)
%td
- if build.try(:runner)
......@@ -36,22 +46,15 @@
#{build.stage} / #{build.name}
%td
- if build.tags.any?
- build.tags.each do |tag|
%span.label.label-primary
= tag
- if build.try(:trigger_request)
%span.label.label-info triggered
- if build.try(:allow_failure)
%span.label.label-danger allowed to fail
%td.duration
- if build.duration
#{duration_in_words(build.finished_at, build.started_at)}
%p.duration
= custom_icon("icon_timer")
= duration_in_numbers(build.finished_at, build.started_at)
%td.timestamp
- if build.finished_at
%span #{time_ago_with_tooltip(build.finished_at)}
%p.finished-at
= icon("calendar")
%span #{time_ago_with_tooltip(build.finished_at)}
- if defined?(coverage) && coverage
%td.coverage
......
......@@ -27,7 +27,7 @@
.row-content-block.second-block
#{(@scope || 'all').capitalize} builds
%ul.content-list
%ul.content-list.builds-content-list
- if @builds.blank?
%li
.nothing-here-block No builds to show
......@@ -37,15 +37,11 @@
%thead
%tr
%th Status
%th Build ID
%th Project
%th Commit
%th Ref
%th Project
%th Runner
%th Name
%th Tags
%th Duration
%th Finished at
%th
%th
- @builds.each do |build|
......
......@@ -9,14 +9,14 @@
%span
To do
%span.badge
= todos_pending_count
= number_with_delimiter(todos_pending_count)
- todo_done_active = ('active' if params[:state] == 'done')
%li{class: "todos-done #{todo_done_active}"}
= link_to todos_filter_path(state: 'done') do
%span
Done
%span.badge
= todos_done_count
= number_with_delimiter(todos_done_count)
.nav-controls
- if @todos.any?(&:pending?)
......
......@@ -6,7 +6,6 @@
.nav-controls
- if can?(current_user, :admin_milestones, @group)
= link_to new_group_milestone_path(@group), class: "btn btn-new" do
= icon('plus')
New Milestone
.row-content-block
......
......@@ -7,7 +7,6 @@
- if can? current_user, :admin_group, @group
.controls
= link_to new_project_path(namespace_id: @group.id), class: "btn btn-sm btn-success" do
= icon('plus')
New Project
%ul.well-list
- @projects.each do |project|
......
......@@ -6,8 +6,7 @@
.cover-block.groups-cover-block
%div{ class: container_class }
= link_to group_icon(@group), target: '_blank' do
= image_tag group_icon(@group), class: "avatar group-avatar s70"
= image_tag group_icon(@group), class: "avatar group-avatar s70"
.group-info
.cover-title
%h1
......
= link_to '#', class: 'nav-header-btn text-center toggle-nav-collapse', title: "Open/Close" do
%span.sr-only Toggle navigation
= icon('bars')
.page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" }
.sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
= render partial: 'layouts/collapse_button'
.sidebar-action-buttons
= link_to '#', class: 'nav-header-btn toggle-nav-collapse', title: "Open/Close" do
%span.sr-only Toggle navigation
= icon('bars')
= link_to '#', class: "nav-header-btn pin-nav-btn has-tooltip #{'is-active' if pinned_nav?} js-nav-pin", title: pinned_nav? ? "Unpin navigation" : "Pin Navigation", data: {placement: 'right', container: 'body'} do
%span.sr-only Toggle navigation pinning
= icon('thumb-tack')
- if defined?(sidebar) && sidebar
= render "layouts/nav/#{sidebar}"
- elsif current_user
......@@ -8,9 +15,6 @@
- else
= render 'layouts/nav/explore'
= link_to '#', class: "nav-header-btn text-center pin-nav-btn has-tooltip #{'is-active' if pinned_nav?} js-nav-pin", title: pinned_nav? ? "Unpin navigation" : "Pin Navigation", data: {placement: 'right', container: 'body'} do
%span.sr-only Toggle navigation pinning
= icon('thumb-tack')
- if defined?(nav) && nav
.layout-nav
.container-fluid
......
%ul.nav.nav-sidebar
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do
= link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
.icon-container
= navbar_icon('project')
%span
Projects
= nav_link(controller: :todos) do
= link_to dashboard_todos_path, title: 'Todos' do
.icon-container
= icon('bell fw')
%span
Todos
%span.count= number_with_delimiter(todos_pending_count)
= nav_link(path: 'dashboard#activity') do
= link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
.icon-container
= navbar_icon('activity')
%span
Activity
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
= link_to dashboard_groups_path, title: 'Groups' do
.icon-container
= navbar_icon('group')
%span
Groups
= nav_link(controller: 'dashboard/milestones') do
= link_to dashboard_milestones_path, title: 'Milestones' do
.icon-container
= navbar_icon('milestones')
%span
Milestones
= nav_link(path: 'dashboard#issues') do
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
.icon-container
= navbar_icon('issues')
%span
Issues
%span.count= number_with_delimiter(current_user.assigned_issues.opened.count)
= nav_link(path: 'dashboard#merge_requests') do
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
.icon-container
= navbar_icon('mr')
%span
Merge Requests
%span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count)
= nav_link(controller: :snippets) do
= link_to dashboard_snippets_path, title: 'Snippets' do
.icon-container
= icon('clipboard fw')
%span
Snippets
= nav_link(controller: :help) do
= link_to help_path, title: 'Help' do
.icon-container
= icon('question-circle fw')
%span
Help
= nav_link(html_options: {class: profile_tab_class}) do
= link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do
.icon-container
= icon('user fw')
%span
Profile Settings
......@@ -18,9 +18,9 @@
%span
Applications
= nav_link(controller: :personal_access_tokens) do
= link_to profile_personal_access_tokens_path, title: 'Personal Access Tokens' do
= link_to profile_personal_access_tokens_path, title: 'Access Tokens' do
%span
Personal Access Tokens
Access Tokens
= nav_link(controller: :emails) do
= link_to profile_emails_path, title: 'Emails' do
%span
......
......@@ -36,7 +36,7 @@
= link_to ci_lint_path, class: 'btn btn-default' do
%span CI Lint
%ul.content-list
%ul.content-list.builds-content-list
- if @builds.blank?
%li
.nothing-here-block No builds to show
......@@ -46,14 +46,10 @@
%thead
%tr
%th Status
%th Build ID
%th Commit
%th Ref
%th Stage
%th Name
%th Tags
%th Duration
%th Finished at
%th
- if @project.build_coverage_enabled?
%th Coverage
%th
......
%tr.build
%tr.build.commit
%td.status
- if can?(current_user, :read_build, build)
= ci_status_with_icon(build.status, namespace_project_build_url(build.project.namespace, build.project, build))
- else
= ci_status_with_icon(build.status)
%td.build-link
- if can?(current_user, :read_build, build)
= link_to namespace_project_build_url(build.project.namespace, build.project, build) do
%strong ##{build.id}
- else
%strong ##{build.id}
%td
.branch-commit
- if can?(current_user, :read_build, build)
= link_to namespace_project_build_url(build.project.namespace, build.project, build) do
%span ##{build.id}
- else
%span ##{build.id}
- if build.stuck?
= icon('warning', class: 'text-warning has-tooltip', title: 'Build is stuck. Check runners.')
- if defined?(retried) && retried
= icon('warning', class: 'text-warning has-tooltip', title: 'Build was retried.')
- if build.stuck?
= icon('warning', class: 'text-warning has-tooltip', title: 'Build is stuck. Check runners.')
- if defined?(retried) && retried
= icon('warning', class: 'text-warning has-tooltip', title: 'Build was retried.')
- if defined?(commit_sha) && commit_sha
%td
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace"
- if defined?(ref) && ref
- if build.ref
= link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref), class: "monospace branch-name"
- else
.light none
= custom_icon("icon_commit")
- if defined?(commit_sha) && commit_sha
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "commit-id monospace"
.label-container
- if build.tags.any?
- build.tags.each do |tag|
%span.label.label-primary
= tag
- if build.try(:trigger_request)
%span.label.label-info triggered
- if build.try(:allow_failure)
%span.label.label-danger allowed to fail
- if defined?(retried) && retried
%span.label.label-warning retried
- if defined?(ref) && ref
%td
- if build.ref
= link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref)
- else
.light none
- if defined?(runner) && runner
%td
......@@ -43,25 +56,14 @@
= build.name
%td
.label-container
- if build.tags.any?
- build.tags.each do |tag|
%span.label.label-primary
= tag
- if build.try(:trigger_request)
%span.label.label-info triggered
- if build.try(:allow_failure)
%span.label.label-danger allowed to fail
- if defined?(retried) && retried
%span.label.label-warning retried
%td.duration
- if build.duration
#{duration_in_words(build.finished_at, build.started_at)}
%td.timestamp
%p.duration
= custom_icon("icon_timer")
= duration_in_numbers(build.finished_at, build.started_at)
- if build.finished_at
%span #{time_ago_with_tooltip(build.finished_at)}
%p.finished-at
= icon("calendar")
%span #{time_ago_with_tooltip(build.finished_at)}
- if defined?(coverage) && coverage
%td.coverage
......@@ -79,4 +81,4 @@
= icon('remove', class: 'cred')
- elsif defined?(allow_retry) && allow_retry && build.retryable?
= link_to retry_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Retry', class: 'btn btn-build' do
= icon('refresh')
= icon('repeat')
- status = pipeline.status
%tr.commit
%td.commit-link
= link_to namespace_project_pipeline_path(@project.namespace, @project, pipeline.id), class: "ci-status ci-#{status}" do
= ci_icon_for_status(status)
%strong ##{pipeline.id}
= link_to namespace_project_pipeline_path(@project.namespace, @project, pipeline.id) do
= ci_status_with_icon(status)
%td
%div.branch-commit
.branch-commit
= link_to namespace_project_pipeline_path(@project.namespace, @project, pipeline.id) do
%span ##{pipeline.id}
- if pipeline.ref
= link_to pipeline.ref, namespace_project_commits_path(@project.namespace, @project, pipeline.ref), class: "monospace"
&middot;
= link_to pipeline.ref, namespace_project_commits_path(@project.namespace, @project, pipeline.ref), class: "monospace branch-name"
= custom_icon("icon_commit")
= link_to pipeline.short_sha, namespace_project_commit_path(@project.namespace, @project, pipeline.sha), class: "commit-id monospace"
&nbsp;
- if pipeline.tag?
%span.label.label-primary tag
- elsif pipeline.latest?
......@@ -25,6 +26,7 @@
%p.commit-title
- if commit = pipeline.commit
= commit_author_avatar(commit, size: 20)
= link_to_gfm truncate(commit.title, length: 60), namespace_project_commit_path(@project.namespace, @project, commit.id), class: "commit-row-message"
- else
Cant find HEAD commit for this branch
......@@ -45,22 +47,37 @@
%td
- if pipeline.started_at && pipeline.finished_at
%p.duration
= custom_icon("icon_timer")
= duration_in_numbers(pipeline.finished_at, pipeline.started_at)
- if pipeline.finished_at
%p.finished-at
= icon("calendar")
#{time_ago_with_tooltip(pipeline.finished_at)}
%td
%td.pipeline-actions
.controls.hidden-xs.pull-right
- artifacts = pipeline.builds.latest.select { |b| b.artifacts? }
- if artifacts.present?
.dropdown.inline.build-artifacts
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
= icon('download')
%b.caret
%ul.dropdown-menu.dropdown-menu-align-right
- artifacts.each do |build|
.btn-group.inline
.btn-group
%a.dropdown-toggle.btn.btn-default{type: 'button', 'data-toggle' => 'dropdown'}
= icon("play")
%b.caret
%ul.dropdown-menu.dropdown-menu-align-right
%li
= link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, build), rel: 'nofollow' do
= icon("download")
%span Download '#{build.name}' artifacts
= link_to '#' do
= icon("play")
%span Deploy to production
.btn-group
%a.dropdown-toggle.btn.btn-default.build-artifacts{type: 'button', 'data-toggle' => 'dropdown'}
= icon("download")
%b.caret
%ul.dropdown-menu.dropdown-menu-align-right
- artifacts.each do |build|
%li
= link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, build), rel: 'nofollow' do
= icon("download")
%span Download '#{build.name}' artifacts
- if can?(current_user, :update_pipeline, @project)
- if pipeline.retryable?
......
......@@ -42,9 +42,7 @@
%th Status
%th Build ID
%th Name
%th Tags
%th Duration
%th Finished at
%th
- if pipeline.project.build_coverage_enabled?
%th Coverage
%th
......
......@@ -50,10 +50,12 @@
%td.duration
- if generic_commit_status.duration
= icon("clock-o")
#{duration_in_words(generic_commit_status.finished_at, generic_commit_status.started_at)}
%td.timestamp
- if generic_commit_status.finished_at
= icon("calendar")
%span #{time_ago_with_tooltip(generic_commit_status.finished_at)}
- if defined?(coverage) && coverage
......
......@@ -32,7 +32,7 @@
Code, test, and deploy together
.blank-state
.blank-state-icon
= navbar_icon("issues", size: 50)
= custom_icon("issues", size: 50)
%h3.blank-state-title
You don't have any issues right now.
%p.blank-state-text
......
......@@ -4,7 +4,7 @@
%p
Please resolve these conflicts or
- if @merge_request.can_be_merged_by?(current_user)
- if @merge_request.can_be_merged_via_command_line_by?(current_user)
#{link_to "merge this request manually", "#modal_merge_info", class: "how_to_merge_link vlink", "data-toggle" => "modal"}.
- else
ask someone with write access to this repository to merge this request manually.
......@@ -28,7 +28,7 @@
.nav-controls
- if can? current_user, :create_pipeline, @project
= link_to new_namespace_project_pipeline_path(@project.namespace, @project), class: 'btn btn-create' do
New pipeline
Run pipeline
- unless @repository.gitlab_ci_yml
= link_to 'Get started with Pipelines', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
......@@ -45,13 +45,13 @@
.table-holder
%table.table.builds
%tbody
%th ID
%th Status
%th Commit
- stages.each do |stage|
%th.stage
%span.has-tooltip{ title: "#{stage.titleize}" }
= stage.titleize
%th Duration
%th
%th
= render @pipelines, commit_sha: true, stage: true, allow_retry: true, stages: stages
......
......@@ -8,6 +8,7 @@
.table-responsive
%table.table.protected-branches-list
%colgroup
%col{ width: "20%" }
%col{ width: "30%" }
%col{ width: "25%" }
%col{ width: "25%" }
......@@ -18,6 +19,7 @@
%th Protected Branch
%th Commit
%th Developers Can Push
%th Developers Can Merge
- if can_admin_project
%th
%tbody
......
......@@ -16,6 +16,8 @@
(branch was removed from repository)
%td
= check_box_tag("developers_can_push", protected_branch.id, protected_branch.developers_can_push, data: { url: url })
%td
= check_box_tag("developers_can_merge", protected_branch.id, protected_branch.developers_can_merge, data: { url: url })
- if can_admin_project
%td
= link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-warning btn-sm pull-right"
......@@ -36,6 +36,14 @@
= f.label :developers_can_push, "Developers can push", class: "label-light append-bottom-0"
%p.light.append-bottom-0
Allow developers to push to this branch
.form-group
= f.check_box :developers_can_merge, class: "pull-left"
.prepend-left-20
= f.label :developers_can_merge, "Developers can merge", class: "label-light append-bottom-0"
%p.light.append-bottom-0
Allow developers to accept merge requests to this branch
= f.submit "Protect", class: "btn-create btn protect-branch-btn", disabled: true
%hr
= render "branches_list"
- page_title "Snippets"
.row-content-block.top-block
.sub-header-block
.pull-right
- if can?(current_user, :create_project_snippet, @project)
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do
......
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
<path fill="#8F8F8F" fill-rule="evenodd" d="M28.7769836,18 C27.8675252,13.9920226 24.2831748,11 20,11 C15.7168252,11 12.1324748,13.9920226 11.2230164,18 L4.0085302,18 C2.90195036,18 2,18.8954305 2,20 C2,21.1122704 2.8992496,22 4.0085302,22 L11.2230164,22 C12.1324748,26.0079774 15.7168252,29 20,29 C24.2831748,29 27.8675252,26.0079774 28.7769836,22 L35.9914698,22 C37.0980496,22 38,21.1045695 38,20 C38,18.8877296 37.1007504,18 35.9914698,18 L28.7769836,18 L28.7769836,18 Z M20,25 C22.7614237,25 25,22.7614237 25,20 C25,17.2385763 22.7614237,15 20,15 C17.2385763,15 15,17.2385763 15,20 C15,22.7614237 17.2385763,25 20,25 L20,25 Z"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><g fill="#8F8F8F" fill-rule="evenodd"><path d="M29.513 10.134A15.922 15.922 0 0 0 23 7.28V6h2.993C26.55 6 27 5.552 27 5V2a1 1 0 0 0-1.007-1H14.007C13.45 1 13 1.448 13 2v3a1 1 0 0 0 1.007 1H17v1.28C9.597 8.686 4 15.19 4 23c0 8.837 7.163 16 16 16s16-7.163 16-16c0-3.461-1.099-6.665-2.967-9.283l1.327-1.58a2.498 2.498 0 0 0-.303-3.53 2.499 2.499 0 0 0-3.528.315l-1.016 1.212zM20 34c6.075 0 11-4.925 11-11s-4.925-11-11-11S9 16.925 9 23s4.925 11 11 11z"/><path d="M19 21h-4.002c-.552 0-.998.452-.998 1.01v1.98c0 .567.447 1.01.998 1.01h7.004c.274 0 .521-.111.701-.291a.979.979 0 0 0 .297-.704v-8.01c0-.54-.452-.995-1.01-.995h-1.98a.997.997 0 0 0-1.01.995V21z"/></g></svg>
\ No newline at end of file
= form_errors(issuable)
- if @conflict
.alert.alert-danger
Someone edited the #{issuable.class.model_name.human.downcase} the same time you did.
Please check out
= link_to "the #{issuable.class.model_name.human.downcase}", polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), target: "_blank"
and make sure your changes will not unintentionally remove theirs
.form-group
= f.label :title, class: 'control-label'
.col-sm-10
......@@ -149,3 +156,5 @@
= link_to 'Delete', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), data: { confirm: "#{issuable.class.name.titleize} will be removed! Are you sure?" },
method: :delete, class: 'btn btn-danger btn-grouped'
= link_to 'Cancel', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), class: 'btn btn-grouped btn-cancel'
= f.hidden_field :lock_version
......@@ -26,7 +26,6 @@
%span.pull-right.tab-issues-buttons
- if project && can?(current_user, :create_issue, project)
= link_to new_namespace_project_issue_path(project.namespace, project, issue: { milestone_id: milestone.id }), class: "btn btn-grouped", title: "New Issue" do
%i.fa.fa-plus
New Issue
= link_to 'Browse Issues', milestones_browse_issuables_path(milestone, type: :issues), class: "btn btn-grouped"
%span.pull-right.tab-merge-requests-buttons.hidden
......
class GitGarbageCollectWorker
include Sidekiq::Worker
include Gitlab::ShellAdapter
sidekiq_options queue: :gitlab_shell, retry: false
def perform(project_id)
project = Project.find(project_id)
gitlab_shell.gc(project.repository_storage_path, project.path_with_namespace)
# Expire the branch cache in case garbage collection caused a ref lookup to fail
project.repository.after_create_branch
end
end
class GitlabShellOneShotWorker
include Sidekiq::Worker
include Gitlab::ShellAdapter
sidekiq_options queue: :gitlab_shell, retry: false
def perform(action, *arg)
gitlab_shell.send(action, *arg)
end
end
production: &base
gitlab:
host: localhost
port: 80
https: false
user: root
email_from: example@example.com
support_email: support@example.com
default_projects_features:
issues: true
merge_requests: true
wiki: true
snippets: false
visibility_level: "private" # can be "private" | "internal" | "public"
issues_tracker:
gravatar:
enabled: true # Use user avatar image from Gravatar.com (default: true)
ldap:
enabled: false
host: '_your_ldap_server'
port: 636
uid: 'sAMAccountName'
method: 'ssl' # "tls" or "ssl" or "plain"
bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
password: '_the_password_of_the_bind_user'
allow_username_or_email_login: true
base: ''
user_filter: ''
omniauth:
enabled: false
satellites:
# Relative paths are relative to Rails.root (default: tmp/repo_satellites/)
path: /apps/gitlab-satellites/
backup:
path: "tmp/backups" # Relative paths are relative to Rails.root (default: tmp/backups/)
repositories:
storages: # REPO PATHS MUST NOT BE A SYMLINK!!!
default: /apps/repositories/
gitlab_shell:
path: /apps/gitlab-shell/
hooks_path: /apps/gitlab-shell/hooks/
upload_pack: true
receive_pack: true
git:
bin_path: /usr/bin/git
max_size: 5242880 # 5.megabytes
timeout: 10
extra:
development:
<<: *base
test:
<<: *base
gravatar:
enabled: true
gitlab:
host: localhost
port: 80
issues_tracker:
redmine:
title: "Redmine"
project_url: "http://redmine/projects/:issues_tracker_id"
issues_url: "http://redmine/:project_id/:issues_tracker_id/:id"
new_issue_url: "http://redmine/projects/:issues_tracker_id/issues/new"
staging:
<<: *base
# Email forcibly included in the standard checks, but the email health check
# doesn't support the full range of SMTP options, which can result in failures
# for valid SMTP configurations.
# Overwrite the HealthCheck's detection of whether email is configured
# in order to avoid the email check during standard checks
module HealthCheck
class Utils
def self.mailer_configured?
false
end
end
end
HealthCheck.setup do |config|
config.standard_checks = ['database', 'migrations', 'cache']
config.full_checks = ['database', 'migrations', 'cache']
......
class AddDevelopersCanMergeToProtectedBranches < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
def change
add_column_with_default :protected_branches, :developers_can_merge, :boolean, default: false, allow_null: false
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddColumnInProgressMergeCommitShaToMergeRequests < ActiveRecord::Migration
def change
add_column :merge_requests, :in_progress_merge_commit_sha, :string
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddLockToIssuables < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
def up
add_column_with_default :issues, :lock_version, :integer, default: 0
add_column_with_default :merge_requests, :lock_version, :integer, default: 0
end
def down
remove_column :issues, :lock_version
remove_column :merge_requests, :lock_version
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class RemoveAwardEmojisWithNoUser < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# When using the methods "add_concurrent_index" or "add_column_with_default"
# you must disable the use of transactions as these methods can not run in an
# existing transaction. When using "add_concurrent_index" make sure that this
# method is the _only_ method called in the migration, any other changes
# should go in a separate migration. This ensures that upon failure _only_ the
# index creation fails and can be retried or reverted easily.
#
# To disable transactions uncomment the following line and remove these
# comments:
# disable_ddl_transaction!
def up
AwardEmoji.joins('LEFT JOIN users ON users.id = user_id').where('users.id IS NULL').destroy_all
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160705163108) do
ActiveRecord::Schema.define(version: 20160712171823) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -70,11 +70,11 @@ ActiveRecord::Schema.define(version: 20160705163108) do
t.string "recaptcha_site_key"
t.string "recaptcha_private_key"
t.integer "metrics_port", default: 8089
t.boolean "akismet_enabled", default: false
t.string "akismet_api_key"
t.integer "metrics_sample_interval", default: 15
t.boolean "sentry_enabled", default: false
t.string "sentry_dsn"
t.boolean "akismet_enabled", default: false
t.string "akismet_api_key"
t.boolean "email_author_in_body", default: false
t.integer "default_group_visibility"
t.boolean "repository_checks_enabled", default: false
......@@ -84,10 +84,10 @@ ActiveRecord::Schema.define(version: 20160705163108) do
t.string "health_check_access_token"
t.boolean "send_user_confirmation_email", default: false
t.integer "container_registry_token_expire_delay", default: 5
t.boolean "user_default_external", default: false, null: false
t.text "after_sign_up_text"
t.string "repository_storage", default: "default"
t.string "enabled_git_access_protocol"
t.boolean "user_default_external", default: false, null: false
end
create_table "audit_events", force: :cascade do |t|
......@@ -165,8 +165,8 @@ ActiveRecord::Schema.define(version: 20160705163108) do
t.text "artifacts_metadata"
t.integer "erased_by_id"
t.datetime "erased_at"
t.string "environment"
t.datetime "artifacts_expire_at"
t.string "environment"
t.integer "artifacts_size"
end
......@@ -481,10 +481,11 @@ ActiveRecord::Schema.define(version: 20160705163108) do
t.string "state"
t.integer "iid"
t.integer "updated_by_id"
t.integer "moved_to_id"
t.boolean "confidential", default: false
t.datetime "deleted_at"
t.date "due_date"
t.integer "moved_to_id"
t.integer "lock_version", default: 0, null: false
end
add_index "issues", ["assignee_id"], name: "index_issues_on_assignee_id", using: :btree
......@@ -624,6 +625,8 @@ ActiveRecord::Schema.define(version: 20160705163108) do
t.integer "merge_user_id"
t.string "merge_commit_sha"
t.datetime "deleted_at"
t.integer "lock_version", default: 0, null: false
t.string "in_progress_merge_commit_sha"
end
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
......@@ -773,10 +776,10 @@ ActiveRecord::Schema.define(version: 20160705163108) do
t.integer "user_id", null: false
t.string "token", null: false
t.string "name", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "revoked", default: false
t.datetime "expires_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "personal_access_tokens", ["token"], name: "index_personal_access_tokens_on_token", unique: true, using: :btree
......@@ -858,11 +861,12 @@ ActiveRecord::Schema.define(version: 20160705163108) do
add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree
create_table "protected_branches", force: :cascade do |t|
t.integer "project_id", null: false
t.string "name", null: false
t.integer "project_id", null: false
t.string "name", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "developers_can_push", default: false, null: false
t.boolean "developers_can_push", default: false, null: false
t.boolean "developers_can_merge", default: false, null: false
end
add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree
......@@ -896,9 +900,9 @@ ActiveRecord::Schema.define(version: 20160705163108) do
t.string "type"
t.string "title"
t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "active", default: false, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "active", null: false
t.text "properties"
t.boolean "template", default: false
t.boolean "push_events", default: true
......
......@@ -11,7 +11,7 @@
- [Importing and exporting projects between instances](user/project/settings/import_export.md).
- [Markdown](markdown/markdown.md) GitLab's advanced formatting system.
- [Migrating from SVN](workflow/importing/migrating_from_svn.md) Convert a SVN repository to Git and GitLab.
- [Permissions](permissions/permissions.md) Learn what each role in a project (external/guest/reporter/developer/master/owner) can do.
- [Permissions](user/permissions.md) Learn what each role in a project (external/guest/reporter/developer/master/owner) can do.
- [Profile Settings](profile/README.md)
- [Project Services](project_services/project_services.md) Integrate a project with external services, such as CI and chat.
- [Public access](public_access/public_access.md) Learn how you can allow public and internal access to projects.
......
......@@ -68,7 +68,9 @@ Parameters:
"merge_when_build_succeeds": true,
"merge_status": "can_be_merged",
"subscribed" : false,
"user_notes_count": 1
"user_notes_count": 1,
"should_remove_source_branch": true,
"force_remove_source_branch": false
}
]
```
......@@ -132,7 +134,9 @@ Parameters:
"merge_when_build_succeeds": true,
"merge_status": "can_be_merged",
"subscribed" : true,
"user_notes_count": 1
"user_notes_count": 1,
"should_remove_source_branch": true,
"force_remove_source_branch": false
}
```
......@@ -233,6 +237,8 @@ Parameters:
"merge_status": "can_be_merged",
"subscribed" : true,
"user_notes_count": 1,
"should_remove_source_branch": true,
"force_remove_source_branch": false,
"changes": [
{
"old_path": "VERSION",
......@@ -312,7 +318,9 @@ Parameters:
"merge_when_build_succeeds": true,
"merge_status": "can_be_merged",
"subscribed" : true,
"user_notes_count": 0
"user_notes_count": 0,
"should_remove_source_branch": true,
"force_remove_source_branch": false
}
```
......@@ -383,7 +391,9 @@ Parameters:
"merge_when_build_succeeds": true,
"merge_status": "can_be_merged",
"subscribed" : true,
"user_notes_count": 1
"user_notes_count": 1,
"should_remove_source_branch": true,
"force_remove_source_branch": false
}
```
......@@ -481,7 +491,9 @@ Parameters:
"merge_when_build_succeeds": true,
"merge_status": "can_be_merged",
"subscribed" : true,
"user_notes_count": 1
"user_notes_count": 1,
"should_remove_source_branch": true,
"force_remove_source_branch": false
}
```
......@@ -547,7 +559,9 @@ Parameters:
"merge_when_build_succeeds": true,
"merge_status": "can_be_merged",
"subscribed" : true,
"user_notes_count": 1
"user_notes_count": 1,
"should_remove_source_branch": true,
"force_remove_source_branch": false
}
```
......@@ -866,7 +880,9 @@ Example response:
"merge_when_build_succeeds": false,
"merge_status": "unchecked",
"subscribed": true,
"user_notes_count": 7
"user_notes_count": 7,
"should_remove_source_branch": true,
"force_remove_source_branch": false
},
"target_url": "https://gitlab.example.com/gitlab-org/gitlab-ci/merge_requests/7",
"body": "Et voluptas laudantium minus nihil recusandae ut accusamus earum aut non.",
......
......@@ -15,7 +15,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `action` | string | no | The action to be filtered. Can be `assigned`, `mentioned`, `build_failed`, or `marked`. |
| `action` | string | no | The action to be filtered. Can be `assigned`, `mentioned`, `build_failed`, `marked`, or `approval_required`. |
| `author_id` | integer | no | The ID of an author |
| `project_id` | integer | no | The ID of a project |
| `state` | string | no | The state of the todo. Can be either `pending` or `done` |
......
......@@ -15,6 +15,6 @@
- [Use SSH keys in your build environment](ssh_keys/README.md)
- [Trigger builds through the API](triggers/README.md)
- [Build artifacts](build_artifacts/README.md)
- [User permissions](permissions/README.md)
- [User permissions](../user/permissions.md#gitlab-ci)
- [API](../api/ci/README.md)
- [CI services (linked docker containers)](services/README.md)
# Users Permissions
GitLab CI relies on user's role on the GitLab. There are three permissions levels on GitLab CI: admin, master, developer, other.
Admin user can perform any actions on GitLab CI in scope of instance and project. Also user with admin permission can use admin interface.
| Action | Guest, Reporter | Developer | Master | Admin |
|---------------------------------------|-----------------|-------------|----------|--------|
| See commits and builds | ✓ | ✓ | ✓ | ✓ |
| Retry or cancel build | | ✓ | ✓ | ✓ |
| Remove project | | | ✓ | ✓ |
| Create project | | | ✓ | ✓ |
| Change project configuration | | | ✓ | ✓ |
| Add specific runners | | | ✓ | ✓ |
| Add shared runners | | | | ✓ |
| See events in the system | | | | ✓ |
| Admin interface | | | | ✓ |
This document was moved to [user/permissions.md](../../user/permissions.md#gitlab-ci).
......@@ -27,6 +27,8 @@ We try to keep the amount of tabs in the header navigation between 5 and 10 so t
tab should represent separate functionality. Everything related to the issue
tracker should be under the 'Issues' tab while everything related to the wiki should
be under 'Wiki' tab and so on and so forth.
When adding a new tab to the header don't use more than 2 words for text in the link.
We want to keep links short and easy to remember and fit all of them in the small screen.
## Mobile screen size
......
......@@ -138,7 +138,7 @@ This setting is only available on GitLab 8.7 and above.
SAML login includes support for external groups. You can define in the SAML
settings which groups, to which your users belong in your IdP, you wish to be
marked as [external](../permissions/permissions.md).
marked as [external](../user/permissions.md).
### Requirements
......@@ -306,4 +306,4 @@ For this you need take the following into account:
validators are optional
Make sure that one of the above described scenarios is valid, or the requests will
fail with one of the mentioned errors.
\ No newline at end of file
fail with one of the mentioned errors.
# Permissions
Users have different abilities depending on the access level they have in a particular group or project.
If a user is both in a project group and in the project itself, the highest permission level is used.
If a user is a GitLab administrator they receive all permissions.
On public and internal projects the Guest role is not enforced.
All users will be able to create issues, leave comments, and pull or download the project code.
To add or import a user, you can follow the [project users and members
documentation](../workflow/add-user/add-user.md).
## Project
| Action | Guest | Reporter | Developer | Master | Owner |
|---------------------------------------|---------|------------|-------------|----------|--------|
| Create new issue | ✓ | ✓ | ✓ | ✓ | ✓ |
| Leave comments | ✓ | ✓ | ✓ | ✓ | ✓ |
| See a list of builds | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
| See a build log | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
| Download and browse build artifacts | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
| Pull project code | | ✓ | ✓ | ✓ | ✓ |
| Download project | | ✓ | ✓ | ✓ | ✓ |
| Create code snippets | | ✓ | ✓ | ✓ | ✓ |
| Manage issue tracker | | ✓ | ✓ | ✓ | ✓ |
| Manage labels | | ✓ | ✓ | ✓ | ✓ |
| See a commit status | | ✓ | ✓ | ✓ | ✓ |
| See a container registry | | ✓ | ✓ | ✓ | ✓ |
| See environments | | ✓ | ✓ | ✓ | ✓ |
| Manage merge requests | | | ✓ | ✓ | ✓ |
| Create new merge request | | | ✓ | ✓ | ✓ |
| Create new branches | | | ✓ | ✓ | ✓ |
| Push to non-protected branches | | | ✓ | ✓ | ✓ |
| Force push to non-protected branches | | | ✓ | ✓ | ✓ |
| Remove non-protected branches | | | ✓ | ✓ | ✓ |
| Add tags | | | ✓ | ✓ | ✓ |
| Write a wiki | | | ✓ | ✓ | ✓ |
| Cancel and retry builds | | | ✓ | ✓ | ✓ |
| Create or update commit status | | | ✓ | ✓ | ✓ |
| Update a container registry | | | ✓ | ✓ | ✓ |
| Remove a container registry image | | | ✓ | ✓ | ✓ |
| Create new environments | | | ✓ | ✓ | ✓ |
| Create new milestones | | | | ✓ | ✓ |
| Add new team members | | | | ✓ | ✓ |
| Push to protected branches | | | | ✓ | ✓ |
| Enable/disable branch protection | | | | ✓ | ✓ |
| Turn on/off prot. branch push for devs| | | | ✓ | ✓ |
| Rewrite/remove git tags | | | | ✓ | ✓ |
| Edit project | | | | ✓ | ✓ |
| Add deploy keys to project | | | | ✓ | ✓ |
| Configure project hooks | | | | ✓ | ✓ |
| Manage runners | | | | ✓ | ✓ |
| Manage build triggers | | | | ✓ | ✓ |
| Manage variables | | | | ✓ | ✓ |
| Delete environments | | | | ✓ | ✓ |
| Switch visibility level | | | | | ✓ |
| Transfer project to another namespace | | | | | ✓ |
| Remove project | | | | | ✓ |
| Force push to protected branches [^2] | | | | | |
| Remove protected branches [^2] | | | | | |
[^1]: If **Allow guest to access builds** is enabled in CI settings
[^2]: Not allowed for Guest, Reporter, Developer, Master, or Owner
## Group
In order for a group to appear as public and be browsable, it must contain at
least one public project.
Any user can remove themselves from a group, unless they are the last Owner of the group.
| Action | Guest | Reporter | Developer | Master | Owner |
|-------------------------|-------|----------|-----------|--------|-------|
| Browse group | ✓ | ✓ | ✓ | ✓ | ✓ |
| Edit group | | | | | ✓ |
| Create project in group | | | | ✓ | ✓ |
| Manage group members | | | | | ✓ |
| Remove group | | | | | ✓ |
## External Users
In cases where it is desired that a user has access only to some internal or
private projects, there is the option of creating **External Users**. This
feature may be useful when for example a contractor is working on a given
project and should only have access to that project.
External users can only access projects to which they are explicitly granted
access, thus hiding all other internal or private ones from them. Access can be
granted by adding the user as member to the project or group.
They will, like usual users, receive a role in the project or group with all
the abilities that are mentioned in the table above. They cannot however create
groups or projects, and they have the same access as logged out users in all
other cases.
An administrator can flag a user as external [through the API](../api/users.md)
or by checking the checkbox on the admin panel. As an administrator, navigate
to **Admin > Users** to create a new user or edit an existing one. There, you
will find the option to flag the user as external.
By default new users are not set as external users. This behavior can be changed
by an administrator under **Admin > Application Settings**.
\ No newline at end of file
This document was moved to [user/permissions.md](../user/permissions.md).
......@@ -17,7 +17,7 @@ Public projects can be cloned **without any** authentication.
They will also be listed on the public access directory (`/public`).
**Any logged in user** will have [Guest](../permissions/permissions.md)
**Any logged in user** will have [Guest](../user/permissions.md)
permissions on the repository.
### Internal projects
......@@ -27,7 +27,7 @@ Internal projects can be cloned by any logged in user.
They will also be listed on the public access directory (`/public`) for logged
in users.
Any logged in user will have [Guest](../permissions/permissions.md) permissions
Any logged in user will have [Guest](../user/permissions.md) permissions
on the repository.
### How to change project visibility
......
# Permissions
Users have different abilities depending on the access level they have in a
particular group or project. If a user is both in a group's project and the
project itself, the highest permission level is used.
On public and internal projects the Guest role is not enforced. All users will
be able to create issues, leave comments, and pull or download the project code.
GitLab administrators receive all permissions.
To add or import a user, you can follow the [project users and members
documentation](../workflow/add-user/add-user.md).
## Project
The following table depicts the various user permission levels in a project.
| Action | Guest | Reporter | Developer | Master | Owner |
|---------------------------------------|---------|------------|-------------|----------|--------|
| Create new issue | ✓ | ✓ | ✓ | ✓ | ✓ |
| Leave comments | ✓ | ✓ | ✓ | ✓ | ✓ |
| See a list of builds | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
| See a build log | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
| Download and browse build artifacts | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
| Pull project code | | ✓ | ✓ | ✓ | ✓ |
| Download project | | ✓ | ✓ | ✓ | ✓ |
| Create code snippets | | ✓ | ✓ | ✓ | ✓ |
| Manage issue tracker | | ✓ | ✓ | ✓ | ✓ |
| Manage labels | | ✓ | ✓ | ✓ | ✓ |
| See a commit status | | ✓ | ✓ | ✓ | ✓ |
| See a container registry | | ✓ | ✓ | ✓ | ✓ |
| See environments | | ✓ | ✓ | ✓ | ✓ |
| Manage/Accept merge requests | | | ✓ | ✓ | ✓ |
| Create new merge request | | | ✓ | ✓ | ✓ |
| Create new branches | | | ✓ | ✓ | ✓ |
| Push to non-protected branches | | | ✓ | ✓ | ✓ |
| Force push to non-protected branches | | | ✓ | ✓ | ✓ |
| Remove non-protected branches | | | ✓ | ✓ | ✓ |
| Add tags | | | ✓ | ✓ | ✓ |
| Write a wiki | | | ✓ | ✓ | ✓ |
| Cancel and retry builds | | | ✓ | ✓ | ✓ |
| Create or update commit status | | | ✓ | ✓ | ✓ |
| Update a container registry | | | ✓ | ✓ | ✓ |
| Remove a container registry image | | | ✓ | ✓ | ✓ |
| Create new environments | | | ✓ | ✓ | ✓ |
| Create new milestones | | | | ✓ | ✓ |
| Add new team members | | | | ✓ | ✓ |
| Push to protected branches | | | | ✓ | ✓ |
| Enable/disable branch protection | | | | ✓ | ✓ |
| Turn on/off protected branch push for devs| | | | ✓ | ✓ |
| Rewrite/remove Git tags | | | | ✓ | ✓ |
| Edit project | | | | ✓ | ✓ |
| Add deploy keys to project | | | | ✓ | ✓ |
| Configure project hooks | | | | ✓ | ✓ |
| Manage runners | | | | ✓ | ✓ |
| Manage build triggers | | | | ✓ | ✓ |
| Manage variables | | | | ✓ | ✓ |
| Delete environments | | | | ✓ | ✓ |
| Switch visibility level | | | | | ✓ |
| Transfer project to another namespace | | | | | ✓ |
| Remove project | | | | | ✓ |
| Force push to protected branches [^2] | | | | | |
| Remove protected branches [^2] | | | | | |
[^1]: If **Allow guest to access builds** is enabled in CI settings
[^2]: Not allowed for Guest, Reporter, Developer, Master, or Owner
## Group
Any user can remove themselves from a group, unless they are the last Owner of
the group. The following table depicts the various user permission levels in a
group.
| Action | Guest | Reporter | Developer | Master | Owner |
|-------------------------|-------|----------|-----------|--------|-------|
| Browse group | ✓ | ✓ | ✓ | ✓ | ✓ |
| Edit group | | | | | ✓ |
| Create project in group | | | | ✓ | ✓ |
| Manage group members | | | | | ✓ |
| Remove group | | | | | ✓ |
## External Users
In cases where it is desired that a user has access only to some internal or
private projects, there is the option of creating **External Users**. This
feature may be useful when for example a contractor is working on a given
project and should only have access to that project.
External users can only access projects to which they are explicitly granted
access, thus hiding all other internal or private ones from them. Access can be
granted by adding the user as member to the project or group.
They will, like usual users, receive a role in the project or group with all
the abilities that are mentioned in the table above. They cannot however create
groups or projects, and they have the same access as logged out users in all
other cases.
An administrator can flag a user as external [through the API](../api/users.md)
or by checking the checkbox on the admin panel. As an administrator, navigate
to **Admin > Users** to create a new user or edit an existing one. There, you
will find the option to flag the user as external.
By default new users are not set as external users. This behavior can be changed
by an administrator under **Admin > Application Settings**.
## GitLab CI
GitLab CI permissions rely on the role the user has in GitLab. There are four
permission levels it total:
- admin
- master
- developer
- guest/reporter
The admin user can perform any action on GitLab CI in scope of the GitLab
instance and project. In addition, all admins can use the admin interface under
`/admin/runners`.
| Action | Guest, Reporter | Developer | Master | Admin |
|---------------------------------------|-----------------|-------------|----------|--------|
| See commits and builds | ✓ | ✓ | ✓ | ✓ |
| Retry or cancel build | | ✓ | ✓ | ✓ |
| Remove project | | | ✓ | ✓ |
| Create project | | | ✓ | ✓ |
| Change project configuration | | | ✓ | ✓ |
| Add specific runners | | | ✓ | ✓ |
| Add shared runners | | | | ✓ |
| See events in the system | | | | ✓ |
| Admin interface | | | | ✓ |
......@@ -23,7 +23,7 @@ want to add.
---
Select the user and the [permission level](../../permissions/permissions.md)
Select the user and the [permission level](../../user/permissions.md)
that you'd like to give the user. Note that you can select more than one user.
![Give user permissions](img/add_user_give_permissions.png)
......
......@@ -38,7 +38,7 @@ Forking a project is in most cases a two-step process.
---
After the forking is done, you can start working on the newly created
repository. There, you will have full [Owner](../permissions/permissions.md)
repository. There, you will have full [Owner](../user/permissions.md)
access, so you can set it up as you please.
## Merging upstream
......
......@@ -12,7 +12,7 @@ A protected branch does three simple things:
You can make any branch a protected branch. GitLab makes the master branch a protected branch by default.
To protect a branch, user needs to have at least a Master permission level, see [permissions document](../permissions/permissions.md).
To protect a branch, user needs to have at least a Master permission level, see [permissions document](../user/permissions.md).
![protected branches page](protected_branches/protected_branches1.png)
......
......@@ -89,7 +89,7 @@ Feature: Project Merge Requests
Then The list should be sorted by "Oldest updated"
@javascript
Scenario: Visiting Merge Requests from a differente Project after sorting
Scenario: Visiting Merge Requests from a different Project after sorting
Given I visit project "Shop" merge requests page
And I sort the list by "Oldest updated"
And I visit dashboard merge requests page
......
......@@ -207,6 +207,8 @@ module API
merge_request.subscribed?(options[:current_user])
end
expose :user_notes_count
expose :should_remove_source_branch?, as: :should_remove_source_branch
expose :force_remove_source_branch?, as: :force_remove_source_branch
end
class MergeRequestChanges < MergeRequest
......
......@@ -17,7 +17,7 @@ module API
def current_user
@current_user ||= (find_user_by_private_token || doorkeeper_guard)
unless @current_user && Gitlab::UserAccess.allowed?(@current_user)
unless @current_user && Gitlab::UserAccess.new(@current_user).allowed?
return nil
end
......
......@@ -3,6 +3,10 @@ module Banzai
Renderer.render(text, context)
end
def self.cache_collection_render(texts_and_contexts)
Renderer.cache_collection_render(texts_and_contexts)
end
def self.render_result(text, context = {})
Renderer.render_result(text, context)
end
......
......@@ -39,9 +39,7 @@ module Banzai
# Renders the attribute of every given object.
def render_objects(objects, attribute)
objects.map do |object|
render_attribute(object, attribute)
end
render_attributes(objects, attribute)
end
# Redacts the list of documents.
......@@ -64,16 +62,21 @@ module Banzai
context
end
# Renders the attribute of an object.
# Renders the attributes of a set of objects.
#
# Returns a `Nokogiri::HTML::Document`.
def render_attribute(object, attribute)
context = context_for(object, attribute)
# Returns an Array of `Nokogiri::HTML::Document`.
def render_attributes(objects, attribute)
strings_and_contexts = objects.map do |object|
context = context_for(object, attribute)
string = object.__send__(attribute)
string = object.__send__(attribute)
html = Banzai.render(string, context)
{ text: string, context: context }
end
Banzai::Pipeline[:relative_link].to_document(html, context)
Banzai.cache_collection_render(strings_and_contexts).each_with_index.map do |html, index|
Banzai::Pipeline[:relative_link].to_document(html, strings_and_contexts[index][:context])
end
end
def base_context
......
......@@ -10,7 +10,7 @@ module Banzai
# requiring XHTML, such as Atom feeds, need to call `post_process` on the
# result, providing the appropriate `pipeline` option.
#
# markdown - Markdown String
# text - Markdown String
# context - Hash of context options passed to our HTML Pipeline
#
# Returns an HTML-safe String
......@@ -29,6 +29,58 @@ module Banzai
end
end
# Perform multiple render from an Array of Markdown String into an
# Array of HTML-safe String of HTML.
#
# As the rendered Markdown String can be already cached read all the data
# from the cache using Rails.cache.read_multi operation. If the Markdown String
# is not in the cache or it's not cacheable (no cache_key entry is provided in
# the context) the Markdown String is rendered and stored in the cache so the
# next render call gets the rendered HTML-safe String from the cache.
#
# For further explanation see #render method comments.
#
# texts_and_contexts - An Array of Hashes that contains the Markdown String (:text)
# an options passed to our HTML Pipeline (:context)
#
# If on the :context you specify a :cache_key entry will be used to retrieve it
# and cache the result of rendering the Markdown String.
#
# Returns an Array containing HTML-safe String instances.
#
# Example:
# texts_and_contexts
# => [{ text: '### Hello',
# context: { cache_key: [note, :note] } }]
def self.cache_collection_render(texts_and_contexts)
items_collection = texts_and_contexts.each_with_index do |item, index|
context = item[:context]
cache_key = full_cache_multi_key(context.delete(:cache_key), context[:pipeline])
item[:cache_key] = cache_key if cache_key
end
cacheable_items, non_cacheable_items = items_collection.partition { |item| item.key?(:cache_key) }
items_in_cache = []
items_not_in_cache = []
unless cacheable_items.empty?
items_in_cache = Rails.cache.read_multi(*cacheable_items.map { |item| item[:cache_key] })
items_not_in_cache = cacheable_items.reject do |item|
item[:rendered] = items_in_cache[item[:cache_key]]
items_in_cache.key?(item[:cache_key])
end
end
(items_not_in_cache + non_cacheable_items).each do |item|
item[:rendered] = render(item[:text], item[:context])
Rails.cache.write(item[:cache_key], item[:rendered]) if item[:cache_key]
end
items_collection.map { |item| item[:rendered] }
end
def self.render_result(text, context = {})
text = Pipeline[:pre_process].to_html(text, context) if text
......@@ -78,5 +130,13 @@ module Banzai
return unless cache_key
["banzai", *cache_key, pipeline_name || :full]
end
# To map Rails.cache.read_multi results we need to know the Rails.cache.expanded_key.
# Other option will be to generate stringified keys on our side and don't delegate to Rails.cache.expanded_key
# method.
def self.full_cache_multi_key(cache_key, pipeline_name)
return unless cache_key
Rails.cache.send(:expanded_key, full_cache_key(cache_key, pipeline_name))
end
end
end
......@@ -14,9 +14,10 @@ module Gitlab
OWNER = 50
# Branch protection settings
PROTECTION_NONE = 0
PROTECTION_DEV_CAN_PUSH = 1
PROTECTION_FULL = 2
PROTECTION_NONE = 0
PROTECTION_DEV_CAN_PUSH = 1
PROTECTION_FULL = 2
PROTECTION_DEV_CAN_MERGE = 3
class << self
def values
......@@ -54,6 +55,7 @@ module Gitlab
def protection_options
{
"Not protected: Both developers and masters can push new commits, force push, or delete the branch." => PROTECTION_NONE,
"Protected against pushes: Developers cannot push new commits, but are allowed to accept merge requests to the branch." => PROTECTION_DEV_CAN_MERGE,
"Partially protected: Developers can push new commits, but cannot force push or delete the branch. Masters can do all of those." => PROTECTION_DEV_CAN_PUSH,
"Fully protected: Developers cannot push new commits, force push, or delete the branch. Only masters can do any of those." => PROTECTION_FULL,
}
......
module Gitlab
module Checks
class ChangeAccess
attr_reader :user_access, :project
def initialize(change, user_access:, project:)
@oldrev, @newrev, @ref = change.split(' ')
@branch_name = branch_name(@ref)
@user_access = user_access
@project = project
end
def exec
error = protected_branch_checks || tag_checks || push_checks
if error
GitAccessStatus.new(false, error)
else
GitAccessStatus.new(true)
end
end
protected
def protected_branch_checks
return unless project.protected_branch?(@branch_name)
if forced_push? && user_access.cannot_do_action?(:force_push_code_to_protected_branches)
return "You are not allowed to force push code to a protected branch on this project."
elsif Gitlab::Git.blank_ref?(@newrev) && user_access.cannot_do_action?(:remove_protected_branches)
return "You are not allowed to delete protected branches from this project."
end
if matching_merge_request?
if user_access.can_merge_to_branch?(@branch_name) || user_access.can_push_to_branch?(@branch_name)
return
else
"You are not allowed to merge code into protected branches on this project."
end
else
if user_access.can_push_to_branch?(@branch_name)
return
else
"You are not allowed to push code to protected branches on this project."
end
end
end
def tag_checks
tag_ref = tag_name(@ref)
if tag_ref && protected_tag?(tag_ref) && user_access.cannot_do_action?(:admin_project)
"You are not allowed to change existing tags on this project."
end
end
def push_checks
if user_access.cannot_do_action?(:push_code)
"You are not allowed to push code to this project."
end
end
private
def protected_tag?(tag_name)
project.repository.tag_exists?(tag_name)
end
def forced_push?
Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev)
end
def matching_merge_request?
Checks::MatchingMergeRequest.new(@newrev, @branch_name, @project).match?
end
def branch_name(ref)
ref = @ref.to_s
if Gitlab::Git.branch_ref?(ref)
Gitlab::Git.ref_name(ref)
else
nil
end
end
def tag_name(ref)
ref = @ref.to_s
if Gitlab::Git.tag_ref?(ref)
Gitlab::Git.ref_name(ref)
else
nil
end
end
end
end
end
module Gitlab
module Checks
class ForcePush
def self.force_push?(project, oldrev, newrev)
return false if project.empty_repo?
# Created or deleted branch
if Gitlab::Git.blank_ref?(oldrev) || Gitlab::Git.blank_ref?(newrev)
false
else
missed_refs, _ = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} --git-dir=#{project.repository.path_to_repo} rev-list #{oldrev} ^#{newrev}))
missed_refs.split("\n").size > 0
end
end
end
end
end
module Gitlab
module Checks
class MatchingMergeRequest
def initialize(newrev, branch_name, project)
@newrev = newrev
@branch_name = branch_name
@project = project
end
def match?
@project.merge_requests
.with_state(:locked)
.where(in_progress_merge_commit_sha: @newrev, target_branch: @branch_name)
.exists?
end
end
end
end
module Gitlab
module Diff
class InlineDiff
# Regex to find a run of deleted lines followed by the same number of added lines
LINE_PAIRS_PATTERN = %r{
# Runs start at the beginning of the string (the first line) or after a space (for an unchanged line)
(?:\A|\s)
# This matches a number of `-`s followed by the same number of `+`s through recursion
(?<del_ins>
-
\g<del_ins>?
\+
)
# Runs end at the end of the string (the last line) or before a space (for an unchanged line)
(?=\s|\z)
}x.freeze
attr_accessor :old_line, :new_line, :offset
def self.for_lines(lines)
local_edit_indexes = self.find_local_edits(lines)
changed_line_pairs = self.find_changed_line_pairs(lines)
inline_diffs = []
local_edit_indexes.each do |index|
old_index = index
new_index = index + 1
changed_line_pairs.each do |old_index, new_index|
old_line = lines[old_index]
new_line = lines[new_index]
......@@ -51,18 +65,28 @@ module Gitlab
private
def self.find_local_edits(lines)
line_prefixes = lines.map { |line| line.match(/\A([+-])/) ? $1 : ' ' }
joined_line_prefixes = " #{line_prefixes.join} "
offset = 0
local_edit_indexes = []
while index = joined_line_prefixes.index(" -+ ", offset)
local_edit_indexes << index
offset = index + 1
# Finds pairs of old/new line pairs that represent the same line that changed
def self.find_changed_line_pairs(lines)
# Prefixes of all diff lines, indicating their types
# For example: `" - + -+ ---+++ --+ -++"`
line_prefixes = lines.each_with_object("") { |line, s| s << line[0] }.gsub(/[^ +-]/, ' ')
changed_line_pairs = []
line_prefixes.scan(LINE_PAIRS_PATTERN) do
# For `"---+++"`, `begin_index == 0`, `end_index == 6`
begin_index, end_index = Regexp.last_match.offset(:del_ins)
# For `"---+++"`, `changed_line_count == 3`
changed_line_count = (end_index - begin_index) / 2
halfway_index = begin_index + changed_line_count
(begin_index...halfway_index).each do |i|
# For `"---+++"`, index 1 maps to 1 + 3 = 4
changed_line_pairs << [i, i + changed_line_count]
end
end
local_edit_indexes
changed_line_pairs
end
def longest_common_prefix(a, b)
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment