Commit 05b518aa authored by Jacob Vosmaer's avatar Jacob Vosmaer

Merge branch 'master' of dev.gitlab.org:gitlab/gitlabhq into backup-archive-permissions

parents 097a8952 8bda4337
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 7.14.0 (unreleased) v 7.14.0 (unreleased)
- Upgrade gitlab_git to version 7.2.6 to fix Error 500 when creating network graphs (Stan Hu)
- Fix URL used for refreshing notes if relative_url is present (Bartłomiej Święcki)
- Fix commit data retrieval when branch name has single quotes (Stan Hu)
- Fix Error 500 when browsing projects with no HEAD (Stan Hu)
- Add rake task 'gitlab:update_commit_count' (Daniel Gerhardt)
- Fix full screen mode for snippet comments (Daniel Gerhardt) - Fix full screen mode for snippet comments (Daniel Gerhardt)
- Fix 404 error in files view after deleting the last file in a repository (Stan Hu) - Fix 404 error in files view after deleting the last file in a repository (Stan Hu)
- Fix the "Reload with full diff" URL button (Stan Hu)
- Fix label read access for unauthenticated users (Daniel Gerhardt) - Fix label read access for unauthenticated users (Daniel Gerhardt)
- Fix access to disabled features for unauthenticated users (Daniel Gerhardt) - Fix access to disabled features for unauthenticated users (Daniel Gerhardt)
- Fix OAuth provider bug where GitLab would not go return to the redirect_uri after sign-in (Stan Hu) - Fix OAuth provider bug where GitLab would not go return to the redirect_uri after sign-in (Stan Hu)
...@@ -14,7 +20,22 @@ v 7.14.0 (unreleased) ...@@ -14,7 +20,22 @@ v 7.14.0 (unreleased)
- Allow custom backup archive permissions - Allow custom backup archive permissions
v 7.13.0 (unreleased) v 7.13.0 (unreleased)
- Add fetch command to the MR page
- Fix bug causing Bitbucket importer to crash when OAuth application had been removed.
v 7.13.1
- Fix: Label modifications are not reflected in existing notes and in the issue list
- Fix: Label not shown in the Issue list, although it's set through web interface
- Fix: Group/project references are linked incorrectly
- Improve documentation
- Fix of migration: Check if session_expire_delay column exists before adding the column
- Fix: ActionView::Template::Error
- Fix: "Create Merge Request" isn't always shown in event for newly pushed branch
- Fix bug causing "Remove source-branch" option not to work for merge requests from the same project.
v 7.13.0
- Remove repository graph log to fix slow cache updates after push event (Stan Hu) - Remove repository graph log to fix slow cache updates after push event (Stan Hu)
- Return comments in created order in merge request API (Stan Hu)
- Only enable HSTS header for HTTPS and port 443 (Stan Hu) - Only enable HSTS header for HTTPS and port 443 (Stan Hu)
- Fix user autocomplete for unauthenticated users accessing public projects (Stan Hu) - Fix user autocomplete for unauthenticated users accessing public projects (Stan Hu)
- Fix redirection to home page URL for unauthorized users (Daniel Gerhardt) - Fix redirection to home page URL for unauthorized users (Daniel Gerhardt)
...@@ -68,6 +89,7 @@ v 7.12.2 ...@@ -68,6 +89,7 @@ v 7.12.2
- Faster automerge check and merge itself when source and target branches are in same repository - Faster automerge check and merge itself when source and target branches are in same repository
- Audit log for user authentication - Audit log for user authentication
- Fix transferring of project to another group using the API. - Fix transferring of project to another group using the API.
- Allow custom label to be set for authentication providers.
v 7.12.1 v 7.12.1
- Fix error when deleting a user who has projects (Stan Hu) - Fix error when deleting a user who has projects (Stan Hu)
......
...@@ -38,7 +38,7 @@ gem "browser", '~> 0.8.0' ...@@ -38,7 +38,7 @@ gem "browser", '~> 0.8.0'
# Extracting information from a git repository # Extracting information from a git repository
# Provide access to Gitlab::Git library # Provide access to Gitlab::Git library
gem "gitlab_git", '~> 7.2.5' gem "gitlab_git", '~> 7.2.6'
# Ruby/Rack Git Smart-HTTP Server Handler # Ruby/Rack Git Smart-HTTP Server Handler
# GitLab fork with a lot of changes (improved thread-safety, better memory usage etc) # GitLab fork with a lot of changes (improved thread-safety, better memory usage etc)
...@@ -272,4 +272,3 @@ end ...@@ -272,4 +272,3 @@ end
gem "newrelic_rpm" gem "newrelic_rpm"
gem 'octokit', '3.7.0' gem 'octokit', '3.7.0'
gem "rugments", "~> 1.0.0.beta8"
...@@ -271,7 +271,7 @@ GEM ...@@ -271,7 +271,7 @@ GEM
mime-types (~> 1.19) mime-types (~> 1.19)
gitlab_emoji (0.1.0) gitlab_emoji (0.1.0)
gemojione (~> 2.0) gemojione (~> 2.0)
gitlab_git (7.2.5) gitlab_git (7.2.6)
activesupport (~> 4.0) activesupport (~> 4.0)
charlock_holmes (~> 0.6) charlock_holmes (~> 0.6)
gitlab-linguist (~> 3.0) gitlab-linguist (~> 3.0)
...@@ -288,7 +288,7 @@ GEM ...@@ -288,7 +288,7 @@ GEM
github-markup (~> 1.3.1) github-markup (~> 1.3.1)
gollum-grit_adapter (~> 0.1, >= 0.1.1) gollum-grit_adapter (~> 0.1, >= 0.1.1)
nokogiri (~> 1.6.4) nokogiri (~> 1.6.4)
rouge (~> 1.7.4) rouge (~> 1.9)
sanitize (~> 2.1.0) sanitize (~> 2.1.0)
stringex (~> 2.5.1) stringex (~> 2.5.1)
gon (5.0.1) gon (5.0.1)
...@@ -536,7 +536,7 @@ GEM ...@@ -536,7 +536,7 @@ GEM
netrc (~> 0.7) netrc (~> 0.7)
rinku (1.7.3) rinku (1.7.3)
rotp (1.6.1) rotp (1.6.1)
rouge (1.7.7) rouge (1.9.1)
rqrcode (0.4.2) rqrcode (0.4.2)
rqrcode-rails3 (0.1.7) rqrcode-rails3 (0.1.7)
rqrcode (>= 0.4.2) rqrcode (>= 0.4.2)
...@@ -579,7 +579,6 @@ GEM ...@@ -579,7 +579,6 @@ GEM
rubyntlm (0.5.0) rubyntlm (0.5.0)
rubypants (0.2.0) rubypants (0.2.0)
rugged (0.22.2) rugged (0.22.2)
rugments (1.0.0.beta8)
safe_yaml (1.0.4) safe_yaml (1.0.4)
sanitize (2.1.0) sanitize (2.1.0)
nokogiri (>= 1.4.4) nokogiri (>= 1.4.4)
...@@ -784,7 +783,7 @@ DEPENDENCIES ...@@ -784,7 +783,7 @@ DEPENDENCIES
gitlab-grack (~> 2.0.2) gitlab-grack (~> 2.0.2)
gitlab-linguist (~> 3.0.1) gitlab-linguist (~> 3.0.1)
gitlab_emoji (~> 0.1) gitlab_emoji (~> 0.1)
gitlab_git (~> 7.2.5) gitlab_git (~> 7.2.6)
gitlab_meta (= 7.0) gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.2.1) gitlab_omniauth-ldap (= 1.2.1)
gollum-lib (~> 4.0.2) gollum-lib (~> 4.0.2)
...@@ -836,7 +835,6 @@ DEPENDENCIES ...@@ -836,7 +835,6 @@ DEPENDENCIES
rqrcode-rails3 rqrcode-rails3
rspec-rails (~> 3.3.0) rspec-rails (~> 3.3.0)
rubocop (= 0.28.0) rubocop (= 0.28.0)
rugments (~> 1.0.0.beta8)
sanitize (~> 2.0) sanitize (~> 2.0)
sass-rails (~> 4.0.5) sass-rails (~> 4.0.5)
sdoc sdoc
......
...@@ -164,9 +164,10 @@ $ -> ...@@ -164,9 +164,10 @@ $ ->
$('.account-box').hover -> $(@).toggleClass('hover') $('.account-box').hover -> $(@).toggleClass('hover')
# Commit show suppressed diff # Commit show suppressed diff
$(".diff-content").on "click", ".supp_diff_link", -> $(document).on 'click', '.diff-content .js-show-suppressed-diff', ->
$(@).next('table').show() $container = $(@).parent()
$(@).remove() $container.next('table').show()
$container.remove()
$('.navbar-toggle').on 'click', -> $('.navbar-toggle').on 'click', ->
$('.header-content .title').toggle() $('.header-content .title').toggle()
......
...@@ -31,6 +31,10 @@ class @Diff ...@@ -31,6 +31,10 @@ class @Diff
bottom: unfoldBottom bottom: unfoldBottom
offset: offset offset: offset
unfold: unfold unfold: unfold
# indent is used to compensate for single space indent to fit
# '+' and '-' prepended to diff lines,
# see https://gitlab.com/gitlab-org/gitlab-ce/issues/707
indent: 1
$.get(link, params, (response) => $.get(link, params, (response) =>
target.parent().replaceWith(response) target.parent().replaceWith(response)
......
...@@ -49,12 +49,6 @@ class @MergeRequestTabs ...@@ -49,12 +49,6 @@ class @MergeRequestTabs
# Store the `location` object, allowing for easier stubbing in tests # Store the `location` object, allowing for easier stubbing in tests
@_location = location @_location = location
switch @opts.action
when 'commits'
@commitsLoaded = true
when 'diffs'
@diffsLoaded = true
@bindEvents() @bindEvents()
@activateTab(@opts.action) @activateTab(@opts.action)
...@@ -102,7 +96,7 @@ class @MergeRequestTabs ...@@ -102,7 +96,7 @@ class @MergeRequestTabs
action = 'notes' if action == 'show' action = 'notes' if action == 'show'
# Remove a trailing '/commits' or '/diffs' # Remove a trailing '/commits' or '/diffs'
new_state = @_location.pathname.replace(/\/(commits|diffs)\/?$/, '') new_state = @_location.pathname.replace(/\/(commits|diffs)(\.html)?\/?$/, '')
# Append the new action if we're on a tab other than 'notes' # Append the new action if we're on a tab other than 'notes'
unless action == 'notes' unless action == 'notes'
...@@ -133,7 +127,7 @@ class @MergeRequestTabs ...@@ -133,7 +127,7 @@ class @MergeRequestTabs
return if @diffsLoaded return if @diffsLoaded
@_get @_get
url: "#{source}.json" url: "#{source}.json" + @_location.search
success: (data) => success: (data) =>
document.getElementById('diffs').innerHTML = data.html document.getElementById('diffs').innerHTML = data.html
@diffsLoaded = true @diffsLoaded = true
......
...@@ -10,7 +10,6 @@ class @Notes ...@@ -10,7 +10,6 @@ class @Notes
constructor: (notes_url, note_ids, last_fetched_at, view) -> constructor: (notes_url, note_ids, last_fetched_at, view) ->
@notes_url = notes_url @notes_url = notes_url
@notes_url = gon.relative_url_root + @notes_url if gon.relative_url_root?
@note_ids = note_ids @note_ids = note_ids
@last_fetched_at = last_fetched_at @last_fetched_at = last_fetched_at
@view = view @view = view
......
...@@ -184,7 +184,7 @@ li.note { ...@@ -184,7 +184,7 @@ li.note {
} }
} }
.supp_diff_link, .show-suppressed-diff,
.show-all-commits { .show-all-commits {
cursor: pointer; cursor: pointer;
} }
......
...@@ -65,6 +65,17 @@ ...@@ -65,6 +65,17 @@
color: #777; color: #777;
} }
.suppressed-container {
padding: ($padding-base-vertical + 5px) $padding-base-horizontal;
text-align: center;
// "Changes suppressed. Click to show." link
.show-suppressed-diff {
font-size: 110%;
font-weight: bold;
}
}
table { table {
width: 100%; width: 100%;
font-family: $monospace_font; font-family: $monospace_font;
......
...@@ -37,7 +37,7 @@ ul.notes { ...@@ -37,7 +37,7 @@ ul.notes {
font-size: 13px; font-size: 13px;
a { a {
@extend .cgray; @extend .cgray;
&:hover { &:hover {
text-decoration: underline; text-decoration: underline;
...@@ -105,6 +105,8 @@ ul.notes { ...@@ -105,6 +105,8 @@ ul.notes {
} }
hr { hr {
// Darken 'whitesmoke' a bit to make it more visible in note bodies
border-color: darken(#F5F5F5, 8%);
margin: 10px 0; margin: 10px 0;
} }
} }
......
...@@ -299,14 +299,14 @@ class ApplicationController < ActionController::Base ...@@ -299,14 +299,14 @@ class ApplicationController < ActionController::Base
end end
def github_import_enabled? def github_import_enabled?
OauthHelper.enabled_oauth_providers.include?(:github) Gitlab::OAuth::Provider.enabled?(:github)
end end
def gitlab_import_enabled? def gitlab_import_enabled?
OauthHelper.enabled_oauth_providers.include?(:gitlab) Gitlab::OAuth::Provider.enabled?(:gitlab)
end end
def bitbucket_import_enabled? def bitbucket_import_enabled?
OauthHelper.enabled_oauth_providers.include?(:bitbucket) && Gitlab::BitbucketImport.public_key.present? Gitlab::OAuth::Provider.enabled?(:bitbucket) && Gitlab::BitbucketImport.public_key.present?
end end
end end
...@@ -3,6 +3,7 @@ class Import::BitbucketController < Import::BaseController ...@@ -3,6 +3,7 @@ class Import::BitbucketController < Import::BaseController
before_action :bitbucket_auth, except: :callback before_action :bitbucket_auth, except: :callback
rescue_from OAuth::Error, with: :bitbucket_unauthorized rescue_from OAuth::Error, with: :bitbucket_unauthorized
rescue_from Gitlab::BitbucketImport::Client::Unauthorized, with: :bitbucket_unauthorized
def callback def callback
request_token = session.delete(:oauth_request_token) request_token = session.delete(:oauth_request_token)
......
...@@ -72,10 +72,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -72,10 +72,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
end end
end end
rescue Gitlab::OAuth::SignupDisabledError => e rescue Gitlab::OAuth::SignupDisabledError => e
message = "Signing in using your #{oauth['provider']} account without a pre-existing GitLab account is not allowed." label = Gitlab::OAuth::Provider.label_for(oauth['provider'])
message = "Signing in using your #{label} account without a pre-existing GitLab account is not allowed."
if current_application_settings.signup_enabled? if current_application_settings.signup_enabled?
message << " Create a GitLab account first, and then connect it to your #{oauth['provider']} account." message << " Create a GitLab account first, and then connect it to your #{label} account."
end end
flash[:notice] = message flash[:notice] = message
......
class Projects::RefsController < Projects::ApplicationController class Projects::RefsController < Projects::ApplicationController
include ExtractsPath include ExtractsPath
include TreeHelper
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :assign_ref_vars before_action :assign_ref_vars
...@@ -60,6 +61,11 @@ class Projects::RefsController < Projects::ApplicationController ...@@ -60,6 +61,11 @@ class Projects::RefsController < Projects::ApplicationController
} }
end end
if @logs.present?
@log_url = namespace_project_tree_url(@project.namespace, @project, tree_join(@ref, @path || '/'))
@more_log_url = logs_file_namespace_project_ref_path(@project.namespace, @project, @ref, @path || '', offset: (@offset + @limit))
end
respond_to do |format| respond_to do |format|
format.html { render_404 } format.html { render_404 }
format.js format.js
......
...@@ -90,7 +90,7 @@ class SessionsController < Devise::SessionsController ...@@ -90,7 +90,7 @@ class SessionsController < Devise::SessionsController
# Prevent alert from popping up on the first page shown after authentication. # Prevent alert from popping up on the first page shown after authentication.
flash[:alert] = nil flash[:alert] = nil
redirect_to omniauth_authorize_path(:user, provider.to_sym) redirect_to user_omniauth_authorize_path(provider.to_sym)
end end
def valid_otp_attempt?(user) def valid_otp_attempt?(user)
......
module AuthHelper
PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2).freeze
FORM_BASED_PROVIDERS = [/\Aldap/, 'kerberos'].freeze
def ldap_enabled?
Gitlab.config.ldap.enabled
end
def provider_has_icon?(name)
PROVIDERS_WITH_ICONS.include?(name.to_s)
end
def auth_providers
Gitlab::OAuth::Provider.providers
end
def label_for_provider(name)
Gitlab::OAuth::Provider.label_for(name)
end
def form_based_provider?(name)
FORM_BASED_PROVIDERS.any? { |pattern| pattern === name.to_s }
end
def form_based_providers
auth_providers.select { |provider| form_based_provider?(provider) }
end
def button_based_providers
auth_providers.reject { |provider| form_based_provider?(provider) }
end
def provider_image_tag(provider, size = 64)
label = label_for_provider(provider)
if provider_has_icon?(provider)
file_name = "#{provider.to_s.split('_').first}_#{size}.png"
image_tag(image_path("auth_buttons/#{file_name}"), alt: label, title: "Sign in with #{label}")
else
label
end
end
def auth_active?(provider)
current_user.identities.exists?(provider: provider.to_s)
end
extend self
end
module BlobHelper module BlobHelper
def highlight(blob_name, blob_content, nowrap: false, continue: false) def highlight(blob_name, blob_content, nowrap: false, continue: false)
@formatter ||= Rugments::Formatters::HTML.new( @formatter ||= Rouge::Formatters::HTMLGitlab.new(
nowrap: nowrap, nowrap: nowrap,
cssclass: 'code highlight', cssclass: 'code highlight',
lineanchors: true, lineanchors: true,
...@@ -8,11 +8,11 @@ module BlobHelper ...@@ -8,11 +8,11 @@ module BlobHelper
) )
begin begin
@lexer ||= Rugments::Lexer.guess(filename: blob_name, source: blob_content).new @lexer ||= Rouge::Lexer.guess(filename: blob_name, source: blob_content).new
result = @formatter.format(@lexer.lex(blob_content, continue: continue)).html_safe result = @formatter.format(@lexer.lex(blob_content, continue: continue)).html_safe
rescue rescue
lexer = Rugments::Lexers::PlainText @lexer = Rouge::Lexers::PlainText
result = @formatter.format(lexer.lex(blob_content)).html_safe result = @formatter.format(@lexer.lex(blob_content)).html_safe
end end
result result
......
...@@ -31,8 +31,8 @@ module EmailsHelper ...@@ -31,8 +31,8 @@ module EmailsHelper
end end
def color_email_diff(diffcontent) def color_email_diff(diffcontent)
formatter = Rugments::Formatters::HTML.new(cssclass: "highlight", inline_theme: :github) formatter = Rouge::Formatters::HTML.new(css_class: 'highlight', inline_theme: 'github')
lexer = Rugments::Lexers::Diff.new lexer = Rouge::Lexers::Diff
raw formatter.format(lexer.lex(diffcontent)) raw formatter.format(lexer.lex(diffcontent))
end end
......
module OauthHelper
def ldap_enabled?
Gitlab.config.ldap.enabled
end
def default_providers
[:twitter, :github, :gitlab, :bitbucket, :google_oauth2, :ldap]
end
def enabled_oauth_providers
Devise.omniauth_providers
end
def enabled_social_providers
enabled_oauth_providers.select do |name|
[:saml, :twitter, :gitlab, :github, :bitbucket, :google_oauth2].include?(name.to_sym)
end
end
def additional_providers
enabled_oauth_providers.reject{|provider| provider.to_s.starts_with?('ldap')}
end
def oauth_image_tag(provider, size = 64)
file_name = "#{provider.to_s.split('_').first}_#{size}.png"
image_tag(image_path("authbuttons/#{file_name}"), alt: "Sign in with #{provider.to_s.titleize}")
end
def oauth_active?(provider)
current_user.identities.exists?(provider: provider.to_s)
end
extend self
end
module ProfileHelper
def show_profile_username_tab?
current_user.can_change_username?
end
def show_profile_social_tab?
enabled_social_providers.any?
end
def show_profile_remove_tab?
signup_enabled?
end
end
...@@ -278,7 +278,8 @@ module ProjectsHelper ...@@ -278,7 +278,8 @@ module ProjectsHelper
end end
def readme_cache_key def readme_cache_key
[@project.id, @project.commit.sha, "readme"].join('-') sha = @project.commit.try(:sha) || 'nil'
[@project.id, sha, "readme"].join('-')
end end
def round_commit_count(project) def round_commit_count(project)
......
...@@ -140,12 +140,13 @@ class Ability ...@@ -140,12 +140,13 @@ class Ability
:create_project_snippet, :create_project_snippet,
:update_issue, :update_issue,
:admin_issue, :admin_issue,
:admin_label, :admin_label
] ]
end end
def project_dev_rules def project_dev_rules
project_report_rules + [ project_report_rules + [
:admin_merge_request,
:create_merge_request, :create_merge_request,
:create_wiki, :create_wiki,
:push_code :push_code
......
...@@ -159,6 +159,16 @@ module Issuable ...@@ -159,6 +159,16 @@ module Issuable
end end
end end
# Convert this Issuable class name to a format usable by Ability definitions
#
# Examples:
#
# issuable.class # => MergeRequest
# issuable.to_ability_name # => "merge_request"
def to_ability_name
self.class.to_s.underscore
end
private private
def filter_superceded_votes(votes, notes) def filter_superceded_votes(votes, notes)
......
...@@ -235,6 +235,10 @@ class MergeRequest < ActiveRecord::Base ...@@ -235,6 +235,10 @@ class MergeRequest < ActiveRecord::Base
execute(self, commit_message) execute(self, commit_message)
end end
def remove_source_branch?
self.should_remove_source_branch && !self.source_project.root_ref?(self.source_branch) && !self.for_fork?
end
def open? def open?
opened? || reopened? opened? || reopened?
end end
......
...@@ -274,6 +274,10 @@ class User < ActiveRecord::Base ...@@ -274,6 +274,10 @@ class User < ActiveRecord::Base
value: login.to_s.downcase).first value: login.to_s.downcase).first
end end
def find_by_username!(username)
find_by!('lower(username) = ?', username.downcase)
end
def by_username_or_id(name_or_id) def by_username_or_id(name_or_id)
where('users.username = ? OR users.id = ?', name_or_id.to_s, name_or_id.to_i).first where('users.username = ? OR users.id = ?', name_or_id.to_s, name_or_id.to_i).first
end end
......
...@@ -39,7 +39,7 @@ module Files ...@@ -39,7 +39,7 @@ module Files
def after_commit(sha, branch) def after_commit(sha, branch)
commit = repository.commit(sha) commit = repository.commit(sha)
full_ref = 'refs/heads/' + branch full_ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{branch}"
old_sha = commit.parent_id || Gitlab::Git::BLANK_SHA old_sha = commit.parent_id || Gitlab::Git::BLANK_SHA
GitPushService.new.execute(project, current_user, old_sha, sha, full_ref) GitPushService.new.execute(project, current_user, old_sha, sha, full_ref)
end end
......
...@@ -27,8 +27,10 @@ class IssuableBaseService < BaseService ...@@ -27,8 +27,10 @@ class IssuableBaseService < BaseService
old_branch, new_branch) old_branch, new_branch)
end end
def filter_params def filter_params(issuable_ability_name = :issue)
unless can?(current_user, :admin_issue, project) ability = :"admin_#{issuable_ability_name}"
unless can?(current_user, ability, project)
params.delete(:milestone_id) params.delete(:milestone_id)
params.delete(:label_ids) params.delete(:label_ids)
params.delete(:assignee_id) params.delete(:assignee_id)
......
...@@ -10,6 +10,10 @@ module Issues ...@@ -10,6 +10,10 @@ module Issues
private private
def filter_params
super(:issue)
end
def execute_hooks(issue, action = 'open') def execute_hooks(issue, action = 'open')
issue_data = hook_data(issue, action) issue_data = hook_data(issue, action)
issue.project.execute_hooks(issue_data, :issue_hooks) issue.project.execute_hooks(issue_data, :issue_hooks)
......
...@@ -37,6 +37,14 @@ module MergeRequests ...@@ -37,6 +37,14 @@ module MergeRequests
# Merge local branches using rugged instead of satellites # Merge local branches using rugged instead of satellites
if sha = commit if sha = commit
after_commit(sha, merge_request.target_branch) after_commit(sha, merge_request.target_branch)
if merge_request.remove_source_branch?
DeleteBranchService.new(merge_request.source_project, current_user).execute(merge_request.source_branch)
end
true
else
false
end end
end end
end end
...@@ -55,7 +63,7 @@ module MergeRequests ...@@ -55,7 +63,7 @@ module MergeRequests
def after_commit(sha, branch) def after_commit(sha, branch)
commit = repository.commit(sha) commit = repository.commit(sha)
full_ref = 'refs/heads/' + branch full_ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{branch}"
old_sha = commit.parent_id || Gitlab::Git::BLANK_SHA old_sha = commit.parent_id || Gitlab::Git::BLANK_SHA
GitPushService.new.execute(project, current_user, old_sha, sha, full_ref) GitPushService.new.execute(project, current_user, old_sha, sha, full_ref)
end end
......
...@@ -20,5 +20,11 @@ module MergeRequests ...@@ -20,5 +20,11 @@ module MergeRequests
merge_request.project.execute_services(merge_data, :merge_request_hooks) merge_request.project.execute_services(merge_data, :merge_request_hooks)
end end
end end
private
def filter_params
super(:merge_request)
end
end end
end end
...@@ -85,6 +85,8 @@ module Projects ...@@ -85,6 +85,8 @@ module Projects
@project.create_wiki if @project.wiki_enabled? @project.create_wiki if @project.wiki_enabled?
@project.build_missing_services
event_service.create_project(@project, current_user) event_service.create_project(@project, current_user)
system_hook_service.execute_hooks_for(@project, :create) system_hook_service.execute_hooks_for(@project, :create)
......
...@@ -8,7 +8,8 @@ ...@@ -8,7 +8,8 @@
.form-group .form-group
= f.label :provider, class: 'control-label' = f.label :provider, class: 'control-label'
.col-sm-10 .col-sm-10
= f.select :provider, Gitlab::OAuth::Provider.names, { allow_blank: false }, class: 'form-control' - values = Gitlab::OAuth::Provider.providers.map { |name| ["#{Gitlab::OAuth::Provider.label_for(name)} (#{name})", name] }
= f.select :provider, values, { allow_blank: false }, class: 'form-control'
.form-group .form-group
= f.label :extern_uid, "Identifier", class: 'control-label' = f.label :extern_uid, "Identifier", class: 'control-label'
.col-sm-10 .col-sm-10
......
%tr %tr
%td %td
= identity.provider = "#{Gitlab::OAuth::Provider.label_for(identity.provider)} (#{identity.provider})"
%td %td
= identity.extern_uid = identity.extern_uid
%td %td
......
...@@ -6,4 +6,4 @@ ...@@ -6,4 +6,4 @@
%label{for: "remember_me"} %label{for: "remember_me"}
= check_box_tag :remember_me, '1', false, id: 'remember_me' = check_box_tag :remember_me, '1', false, id: 'remember_me'
%span Remember me %span Remember me
= button_tag "#{server['label']} Sign in", class: "btn-save btn" = button_tag "Sign in", class: "btn-save btn"
%p %p
%span.light %span.light
Sign in with &nbsp; Sign in with &nbsp;
- providers = additional_providers - providers = button_based_providers
- providers.each do |provider| - providers.each do |provider|
%span.light %span.light
- if default_providers.include?(provider) - has_icon = provider_has_icon?(provider)
= link_to oauth_image_tag(provider), omniauth_authorize_path(resource_name, provider), method: :post, class: 'oauth-image-link' = link_to provider_image_tag(provider), user_omniauth_authorize_path(provider), method: :post, class: (has_icon ? 'oauth-image-link' : 'btn'), "data-no-turbolink" => "true"
- else
= link_to provider.to_s.titleize, omniauth_authorize_path(resource_name, provider), method: :post, class: "btn", "data-no-turbolink" => "true"
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
.login-heading .login-heading
%h3 Sign in %h3 Sign in
.login-body .login-body
- if ldap_enabled? - if form_based_providers.any?
%ul.nav.nav-tabs %ul.nav.nav-tabs
- @ldap_servers.each_with_index do |server, i| - @ldap_servers.each_with_index do |server, i|
%li{class: (:active if i.zero?)} %li{class: (:active if i.zero?)}
......
...@@ -8,11 +8,10 @@ ...@@ -8,11 +8,10 @@
= image_tag avatar_icon(event.author_email, 24), class: "avatar s24", alt:'' = image_tag avatar_icon(event.author_email, 24), class: "avatar s24", alt:''
= render "events/event/created_project", event: event = render "events/event/created_project", event: event
- else - else
= cache event do = image_tag avatar_icon(event.author_email, 24), class: "avatar s24", alt:''
= image_tag avatar_icon(event.author_email, 24), class: "avatar s24", alt:'' - if event.push?
- if event.push? = render "events/event/push", event: event
= render "events/event/push", event: event - elsif event.commented?
- elsif event.commented? = render "events/event/note", event: event
= render "events/event/note", event: event - else
- else = render "events/event/common", event: event
= render "events/event/common", event: event
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
= yield = yield
%div.footer{style: "margin-top: 10px;"} %div.footer{style: "margin-top: 10px;"}
%p %p
\— &mdash;
%br %br
- if @target_url - if @target_url
#{link_to "View it on GitLab", @target_url} #{link_to "View it on GitLab", @target_url}
......
...@@ -59,22 +59,22 @@ ...@@ -59,22 +59,22 @@
%div %div
= link_to 'Enable Two-factor Authentication', new_profile_two_factor_auth_path, class: 'btn btn-success' = link_to 'Enable Two-factor Authentication', new_profile_two_factor_auth_path, class: 'btn btn-success'
- if show_profile_social_tab? - if button_based_providers.any?
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
Connected Accounts Connected Accounts
.panel-body .panel-body
.oauth-buttons.append-bottom-10 .oauth-buttons.append-bottom-10
%p Click on icon to activate signin with one of the following services %p Click on icon to activate signin with one of the following services
- enabled_social_providers.each do |provider| - button_based_providers.each do |provider|
.btn-group .btn-group
= link_to oauth_image_tag(provider), omniauth_authorize_path(User, provider), = link_to provider_image_tag(provider), user_omniauth_authorize_path(provider), method: :post, class: "btn btn-lg #{'active' if auth_active?(provider)}", "data-no-turbolink" => "true"
method: :post, class: "btn btn-lg #{'active' if oauth_active?(provider)}"
- if oauth_active?(provider) - if auth_active?(provider)
= link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'btn btn-lg' do = link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'btn btn-lg' do
= icon('close') = icon('close')
- if show_profile_username_tab? - if current_user.can_change_username?
.panel.panel-warning.update-username .panel.panel-warning.update-username
.panel-heading .panel-heading
Change Username Change Username
...@@ -94,7 +94,7 @@ ...@@ -94,7 +94,7 @@
%div %div
= f.submit 'Save username', class: "btn btn-warning" = f.submit 'Save username', class: "btn btn-warning"
- if show_profile_remove_tab? - if signup_enabled?
.panel.panel-danger.remove-account .panel.panel-danger.remove-account
.panel-heading .panel-heading
Remove account Remove account
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
Download the Google Authenticator application from App Store for iOS or Google Download the Google Authenticator application from App Store for iOS or Google
Play for Android and scan this code. Play for Android and scan this code.
More information is available in the #{link_to('documentation', help_page_path('workflow', 'two_factor_authentication'))}. More information is available in the #{link_to('documentation', help_page_path('profile', 'two_factor_authentication'))}.
%hr %hr
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
%td.old_line.diff-line-num{data: {linenumber: line_old}} %td.old_line.diff-line-num{data: {linenumber: line_old}}
= link_to raw(line_old), "#" = link_to raw(line_old), "#"
%td.new_line= link_to raw(line_new) , "#" %td.new_line= link_to raw(line_new) , "#"
%td.line_content.noteable_line= line %td.line_content.noteable_line= ' ' * @form.indent + line
- if @form.unfold? && @form.bottom? && @form.to < @blob.loc - if @form.unfold? && @form.bottom? && @form.to < @blob.loc
%tr.line_holder{ id: @form.to } %tr.line_holder{ id: @form.to }
......
- too_big = diff_file.diff_lines.count > Commit::DIFF_SAFE_LINES - too_big = diff_file.diff_lines.count > Commit::DIFF_SAFE_LINES
- if too_big - if too_big
%a.supp_diff_link Changes suppressed. Click to show .suppressed-container
%a.show-suppressed-diff.js-show-suppressed-diff Changes suppressed. Click to show.
%table.text-file{class: "#{'hide' if too_big}"} %table.text-file{class: "#{'hide' if too_big}"}
- last_line = 0 - last_line = 0
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
Too many changes to show. Too many changes to show.
.pull-right .pull-right
- unless diff_hard_limit_enabled? - unless diff_hard_limit_enabled?
= link_to "Reload with full diff", url_for(params.merge(force_show_diff: true)), class: "btn btn-sm btn-warning" = link_to "Reload with full diff", url_for(params.merge(force_show_diff: true, format: :html)), class: "btn btn-sm btn-warning"
- if current_controller?(:commit) or current_controller?(:merge_requests) - if current_controller?(:commit) or current_controller?(:merge_requests)
- if current_controller?(:commit) - if current_controller?(:commit)
......
...@@ -3,43 +3,42 @@ ...@@ -3,43 +3,42 @@
.issue-check .issue-check
= check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue" = check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue"
= cache issue do .issue-title
.issue-title %span.issue-title-text
%span.issue-title-text = link_to_gfm issue.title, issue_path(issue), class: "row_title"
= link_to_gfm issue.title, issue_path(issue), class: "row_title" .issue-labels
.issue-labels - issue.labels.each do |label|
- issue.labels.each do |label| = link_to_label(label, project: issue.project)
= link_to_label(label, project: issue.project) .pull-right.light
.pull-right.light - if issue.closed?
- if issue.closed? %span
%span CLOSED
CLOSED - if issue.assignee
- if issue.assignee = link_to_member(@project, issue.assignee, name: false)
= link_to_member(@project, issue.assignee, name: false) - note_count = issue.notes.user.count
- note_count = issue.notes.user.count - if note_count > 0
- if note_count > 0
&nbsp;
%span
%i.fa.fa-comments
= note_count
- else
&nbsp;
%span.issue-no-comments
%i.fa.fa-comments
= 0
.issue-info
= "#{issue.to_reference} opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')} by #{link_to_member(@project, issue.author, avatar: false)}".html_safe
- if issue.votes_count > 0
= render 'votes/votes_inline', votable: issue
- if issue.milestone
&nbsp; &nbsp;
%span %span
%i.fa.fa-clock-o %i.fa.fa-comments
= issue.milestone.title = note_count
- if issue.tasks? - else
%span.task-status &nbsp;
= issue.task_status %span.issue-no-comments
%i.fa.fa-comments
= 0
.issue-info
= "#{issue.to_reference} opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')} by #{link_to_member(@project, issue.author, avatar: false)}".html_safe
- if issue.votes_count > 0
= render 'votes/votes_inline', votable: issue
- if issue.milestone
&nbsp;
%span
%i.fa.fa-clock-o
= issue.milestone.title
- if issue.tasks?
%span.task-status
= issue.task_status
.pull-right.issue-updated-at .pull-right.issue-updated-at
%small updated #{time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_update_ago')} %small updated #{time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_update_ago')}
...@@ -66,11 +66,9 @@ ...@@ -66,11 +66,9 @@
#notes.notes.tab-pane.voting_notes #notes.notes.tab-pane.voting_notes
= render "projects/merge_requests/discussion" = render "projects/merge_requests/discussion"
#commits.commits.tab-pane #commits.commits.tab-pane
- if current_page?(action: 'commits') - # This tab is always loaded via AJAX
= render "projects/merge_requests/show/commits"
#diffs.diffs.tab-pane #diffs.diffs.tab-pane
- if current_page?(action: 'diffs') - # This tab is always loaded via AJAX
= render "projects/merge_requests/show/diffs"
.mr-loading-status .mr-loading-status
= spinner = spinner
......
...@@ -3,10 +3,7 @@ ...@@ -3,10 +3,7 @@
= note_target_fields(note) = note_target_fields(note)
= render layout: 'projects/md_preview', locals: { preview_class: 'note-text' } do = render layout: 'projects/md_preview', locals: { preview_class: 'note-text' } do
= render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-task-list-field' = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-task-list-field'
= render 'projects/notes/hints'
.comment-hints.clearfix
.pull-left #{link_to 'Markdown ', help_page_path('markdown', 'markdown'),{ target: '_blank', tabindex: -1 }}
.pull-right #{link_to 'Attach a file', '#', class: 'markdown-selector', tabindex: -1 }
.note-form-actions .note-form-actions
.buttons .buttons
......
...@@ -8,18 +8,8 @@ ...@@ -8,18 +8,8 @@
= f.hidden_field :noteable_type = f.hidden_field :noteable_type
= render layout: 'projects/md_preview', locals: { preview_class: "note-text", referenced_users: true } do = render layout: 'projects/md_preview', locals: { preview_class: "note-text", referenced_users: true } do
= render 'projects/zen', f: f, attr: :note, = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text'
classes: 'note_text js-note-text' = render 'projects/notes/hints'
.comment-hints.clearfix
.pull-left
= link_to "Markdown ", help_page_path("markdown", "markdown"),{ target: '_blank', tabindex: -1 }
tip:
= random_markdown_tip
.pull-right
= link_to '#', class: 'markdown-selector', tabindex: -1 do
Attach a file
= icon('paperclip')
.error-alert .error-alert
.note-form-actions .note-form-actions
......
.comment-hints.clearfix
.pull-left
= link_to 'Markdown', help_page_path('markdown', 'markdown'), target: '_blank', tabindex: -1
tip:
= random_markdown_tip
.pull-right
= link_to '#', class: 'markdown-selector', tabindex: -1 do
= icon('paperclip')
Attach a file
...@@ -56,10 +56,9 @@ ...@@ -56,10 +56,9 @@
.note-body{class: note_editable?(note) ? 'js-task-list-container' : ''} .note-body{class: note_editable?(note) ? 'js-task-list-container' : ''}
= cache [note, 'markdown', user_color_scheme_class] do .note-text
.note-text = preserve do
= preserve do = markdown(note.note, {no_header_anchors: true})
= markdown(note.note, {no_header_anchors: true})
= render 'projects/notes/edit_form', note: note = render 'projects/notes/edit_form', note: note
- if note.attachment.url - if note.attachment.url
......
...@@ -11,9 +11,11 @@ ...@@ -11,9 +11,11 @@
- if @logs.present? - if @logs.present?
:plain :plain
var current_url = location.href.replace(/\/?$/, '/'); var current_url = location.href.replace(/\/?$/, '/');
var log_url = '#{namespace_project_tree_url(@project.namespace, @project, tree_join(@ref, @path || '/'))}'.replace(/\/?$/, '/'); var log_url = "#{escape_javascript(@log_url)}".replace(/\/?$/, '/');
if(current_url == log_url) { if(current_url == log_url) {
// Load 10 more commit log for each file in tree // Load more commit logs for each file in tree
// if we still on the same page // if we still on the same page
ajaxGet('#{logs_file_namespace_project_ref_path(@project.namespace, @project, @ref, @path || '', offset: (@offset + @limit))}'); var url = "#{escape_javascript(@more_log_url)}";
ajaxGet(url);
} }
...@@ -49,5 +49,5 @@ ...@@ -49,5 +49,5 @@
:javascript :javascript
// Load last commit log for each file in tree // Load last commit log for each file in tree
$('#tree-slider').waitForImages(function() { $('#tree-slider').waitForImages(function() {
ajaxGet('#{@logs_path}'); ajaxGet("#{escape_javascript(@logs_path)}");
}); });
...@@ -3,6 +3,10 @@ ...@@ -3,6 +3,10 @@
%h3.page-title %h3.page-title
= @page.title = @page.title
= render 'main_links' = render 'main_links'
.wiki-last-edit-by
Last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
- if @page.historical? - if @page.historical?
.warning_message .warning_message
This is an old version of this page. This is an old version of this page.
...@@ -16,6 +20,6 @@ ...@@ -16,6 +20,6 @@
= render_wiki_content(@page) = render_wiki_content(@page)
%hr %hr
.wiki-last-edit-by .wiki-last-edit-by
Last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)} Last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
- else - else
none none
.issuable-context-selectbox .issuable-context-selectbox
- if can?(current_user, :"admin_#{issuable.class.to_s.underscore}", @project) - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
= users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true) = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true)
%div.prepend-top-20.clearfix %div.prepend-top-20.clearfix
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
- else - else
none none
.issuable-context-selectbox .issuable-context-selectbox
- if can?(current_user, :"admin_#{issuable.class.to_s.underscore}", @project) - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
= f.select(:milestone_id, milestone_options(issuable), { include_blank: 'Select milestone' }, {class: 'select2 select2-compact js-select2 js-milestone'}) = f.select(:milestone_id, milestone_options(issuable), { include_blank: 'Select milestone' }, {class: 'select2 select2-compact js-select2 js-milestone'})
= hidden_field_tag :issuable_context = hidden_field_tag :issuable_context
= f.submit class: 'btn hide' = f.submit class: 'btn hide'
......
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
.clearfix .clearfix
.error-alert .error-alert
%hr %hr
- if can?(current_user, :admin_issue, @project) - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
.form-group .form-group
.issue-assignee .issue-assignee
= f.label :assignee_id, class: 'control-label' do = f.label :assignee_id, class: 'control-label' do
......
...@@ -209,20 +209,29 @@ production: &base ...@@ -209,20 +209,29 @@ production: &base
# arguments, followed by optional 'args' which can be either a hash or an array. # arguments, followed by optional 'args' which can be either a hash or an array.
# Documentation for this is available at http://doc.gitlab.com/ce/integration/omniauth.html # Documentation for this is available at http://doc.gitlab.com/ce/integration/omniauth.html
providers: providers:
# - { name: 'google_oauth2', app_id: 'YOUR_APP_ID', # - { name: 'google_oauth2',
# label: 'Google',
# app_id: 'YOUR_APP_ID',
# app_secret: 'YOUR_APP_SECRET', # app_secret: 'YOUR_APP_SECRET',
# args: { access_type: 'offline', approval_prompt: '' } } # args: { access_type: 'offline', approval_prompt: '' } }
# - { name: 'twitter', app_id: 'YOUR_APP_ID', # - { name: 'twitter',
# app_secret: 'YOUR_APP_SECRET'} # app_id: 'YOUR_APP_ID',
# - { name: 'github', app_id: 'YOUR_APP_ID', # app_secret: 'YOUR_APP_SECRET' }
# - { name: 'github',
# label: 'GitHub',
# app_id: 'YOUR_APP_ID',
# app_secret: 'YOUR_APP_SECRET', # app_secret: 'YOUR_APP_SECRET',
# args: { scope: 'user:email' } } # args: { scope: 'user:email' } }
# - { name: 'gitlab', app_id: 'YOUR_APP_ID', # - { name: 'gitlab',
# label: 'GitLab.com',
# app_id: 'YOUR_APP_ID',
# app_secret: 'YOUR_APP_SECRET', # app_secret: 'YOUR_APP_SECRET',
# args: { scope: 'api' } } # args: { scope: 'api' } }
# - { name: 'bitbucket', app_id: 'YOUR_APP_ID', # - { name: 'bitbucket',
# app_secret: 'YOUR_APP_SECRET'} # app_id: 'YOUR_APP_ID',
# - { name: 'saml', # app_secret: 'YOUR_APP_SECRET' }
# - { name: 'saml',
# label: 'Our SAML Provider',
# args: { # args: {
# assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback', # assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
# idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8', # idp_cert_fingerprint: '43:51:43:a1:b5:fc:8b:b7:0a:3a:a9:b1:0f:66:73:a8',
......
class AddSessionExpireDelayForApplicationSettings < ActiveRecord::Migration class AddSessionExpireDelayForApplicationSettings < ActiveRecord::Migration
def change def change
add_column :application_settings, :session_expire_delay, :integer, default: 10080, null: false unless column_exists?(:application_settings, :session_expire_delay)
add_column :application_settings, :session_expire_delay, :integer, default: 10080, null: false
end
end end
end end
\ No newline at end of file
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
Step-by-step guides on the basics of working with Git and GitLab. Step-by-step guides on the basics of working with Git and GitLab.
* [Start using Git on the commandline](start-using-git.md) * [Start using Git on the command line](start-using-git.md)
* [Create and add your SSH Keys](create-your-ssh-keys.md) * [Create and add your SSH Keys](create-your-ssh-keys.md)
...@@ -17,3 +17,5 @@ Step-by-step guides on the basics of working with Git and GitLab. ...@@ -17,3 +17,5 @@ Step-by-step guides on the basics of working with Git and GitLab.
* [Create a branch](create-branch.md) * [Create a branch](create-branch.md)
* [Fork a project](fork-project.md) * [Fork a project](fork-project.md)
* [Add a file](add-file.md)
# How to add a file
You can create a file in your [shell](command-line-commands.md) or in GitLab.
To create a file in GitLab, sign in to [GitLab.com](https://gitlab.com).
Select a project on the right side of your screen:
![Select a project](basicsimages/select_project.png)
It's a good idea to [create a branch](create-branch.md), but it's not necessary.
Go to the directory where you'd like to add the file and click on the "+" sign next to the name of the project and directory:
![Create a file](basicsimages/create_file.png)
Name your file (you can't add spaces, so you can use hyphens or underscores). Don't forget to include the markup language you'd like to use :
![File name](basicsimages/file_name.png)
Add all the information that you'd like to include in your file:
![Add information](basicsimages/white_space.png)
Add a commit message based on what you just added and then click on "commit changes":
![Commit changes](basicsimages/commit_changes.png)
### Note
Besides its regular files, every directory needs a README.md or README.html file which works like an index, telling
what the directory is about. It's the first document you'll find when you open a directory.
# GitLab buttons in Gmail
GitLab supports [Google actions in email](https://developers.google.com/gmail/markup/actions/actions-overview).
If correctly setup, emails that require an action will be marked in Gmail.
![gitlab_actions](gitlab_actions.png)
To get this functioning, you need to be registered with Google.
[See how to register with Google in this document.](https://developers.google.com/gmail/markup/registering-with-google)
To aid the registering with Google, GitLab offers a rake task that will send an email to Google whitelisting email address from your GitLab server.
To check what would be sent to the Google email address, run the rake task:
```bash
bundle exec rake gitlab:mail_google_schema_whitelisting RAILS_ENV=production
```
**This will not send the email but give you the output of how the mail will look.**
Copy the output of the rake task to [Google email markup tester](https://www.google.com/webmasters/markup-tester/u/0/) and press "Validate".
If you receive "No errors detected" message from the tester you can send the email using:
```bash
bundle exec rake gitlab:mail_google_schema_whitelisting RAILS_ENV=production SEND=true
```
...@@ -84,7 +84,7 @@ Existing users can enable OmniAuth for specific providers after the account is c ...@@ -84,7 +84,7 @@ Existing users can enable OmniAuth for specific providers after the account is c
1. Sign in normally - whether standard sign in, LDAP, or another OmniAuth provider. 1. Sign in normally - whether standard sign in, LDAP, or another OmniAuth provider.
1. Go to profile settings (the silhouette icon in the top right corner). 1. Go to profile settings (the silhouette icon in the top right corner).
1. Select the "Account" tab. 1. Select the "Account" tab.
1. Under "Social Accounts" select the desired OmniAuth provider, such as Twitter. 1. Under "Connected Accounts" select the desired OmniAuth provider, such as Twitter.
1. The user will be redirected to the provider. Once the user authorized GitLab they will be redirected back to GitLab. 1. The user will be redirected to the provider. Once the user authorized GitLab they will be redirected back to GitLab.
The chosen OmniAuth provider is now active and can be used to sign in to GitLab from then on. The chosen OmniAuth provider is now active and can be used to sign in to GitLab from then on.
......
...@@ -17,6 +17,7 @@ If a user is a GitLab administrator they receive all permissions. ...@@ -17,6 +17,7 @@ If a user is a GitLab administrator they receive all permissions.
| Create code snippets | | ✓ | ✓ | ✓ | ✓ | | Create code snippets | | ✓ | ✓ | ✓ | ✓ |
| Manage issue tracker | | ✓ | ✓ | ✓ | ✓ | | Manage issue tracker | | ✓ | ✓ | ✓ | ✓ |
| Manage labels | | ✓ | ✓ | ✓ | ✓ | | Manage labels | | ✓ | ✓ | ✓ | ✓ |
| Manage merge requests | | | ✓ | ✓ | ✓ |
| Create new merge request | | | ✓ | ✓ | ✓ | | Create new merge request | | | ✓ | ✓ | ✓ |
| Create new branches | | | ✓ | ✓ | ✓ | | Create new branches | | | ✓ | ✓ | ✓ |
| Push to non-protected branches | | | ✓ | ✓ | ✓ | | Push to non-protected branches | | | ✓ | ✓ | ✓ |
......
...@@ -68,7 +68,7 @@ Xth: (2 working days before the 22nd) ...@@ -68,7 +68,7 @@ Xth: (2 working days before the 22nd)
Xth: (1 working day before the 22nd) Xth: (1 working day before the 22nd)
- [ ] Merge CE stable into EE stable - [ ] Merge CE stable into EE stable
- [ ] Create (hopefully final) CE, EE, CI release candidates (#LINK) - [ ] Create CE, EE, CI release candidates (#LINK) (hopefully final ones with the same commit as the release tomorrow)
- [ ] Create Omnibus tags and build packages for the latest release candidates - [ ] Create Omnibus tags and build packages for the latest release candidates
- [ ] Update GitLab.com with the latest RC (#LINK) - [ ] Update GitLab.com with the latest RC (#LINK)
- [ ] Update ci.gitLab.com with the latest RC (#LINK) - [ ] Update ci.gitLab.com with the latest RC (#LINK)
...@@ -81,10 +81,10 @@ workday to quickly fix any issues. ...@@ -81,10 +81,10 @@ workday to quickly fix any issues.
- [ ] Merge CE stable into EE stable (#LINK) - [ ] Merge CE stable into EE stable (#LINK)
- [ ] Create the 'x.y.0' tag with the [release tools](https://dev.gitlab.org/gitlab/release-tools) (#LINK) - [ ] Create the 'x.y.0' tag with the [release tools](https://dev.gitlab.org/gitlab/release-tools) (#LINK)
- [ ] BEFORE 11AM CET Create and push omnibus tags for x.y.0 (will auto-release the packages) (#LINK) - [ ] Try to do before 11AM CET: Create and push omnibus tags for x.y.0 (will auto-release the packages) (#LINK)
- [ ] BEFORE 12AM CET Publish the release blog post (#LINK) - [ ] Try to do before 12AM CET: Publish the release blog post (#LINK)
- [ ] Tweet about the release (blog post) (#LINK) - [ ] Tweet about the release (blog post) (#LINK)
- [ ] Schedule a second tweet of the release announcement at 6PM CET / 9AM PST - [ ] Schedule a second tweet of the release announcement with the same text at 6PM CET / 9AM PST
``` ```
...@@ -220,4 +220,4 @@ Consider creating a post on Hacker News. ...@@ -220,4 +220,4 @@ Consider creating a post on Hacker News.
## Create a WIP blogpost for the next release ## Create a WIP blogpost for the next release
Create a WIP blogpost using [release blog template](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/doc/release_blog_template.md). Create a WIP blogpost using [release blog template](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/doc/release_blog_template.md).
\ No newline at end of file
...@@ -6,4 +6,7 @@ ...@@ -6,4 +6,7 @@
4. [SVN](migrating_from_svn.md) 4. [SVN](migrating_from_svn.md)
### Note ### Note
* If you'd like to migrate from a self-hosted GitLab instance to GitLab.com, you can copy your repos by changing the remote and pushing to the new server; but issues and merge requests can't be imported. * If you'd like to migrate from a self-hosted GitLab instance to GitLab.com, you can copy your repos by changing the remote and pushing to the new server; but issues and merge requests can't be imported.
\ No newline at end of file
* Repositories are imported to GitLab via HTTP.
If the repository is too large, it can timeout. We have a soft limit of 10GB.
...@@ -158,3 +158,10 @@ Feature: Project Source Browse Files ...@@ -158,3 +158,10 @@ Feature: Project Source Browse Files
Given I visit project source page for "6d394385cf567f80a8fd85055db1ab4c5295806f" Given I visit project source page for "6d394385cf567f80a8fd85055db1ab4c5295806f"
And I click on ".gitignore" file in repo And I click on ".gitignore" file in repo
Then I don't see the permalink link Then I don't see the permalink link
@javascript
Scenario: I browse code with single quotes in the ref
Given I switch ref to 'test'
And I see the ref 'test' has been selected
And I visit the 'test' tree
Then I see the commit data
...@@ -3,6 +3,14 @@ class Spinach::Features::AdminUsers < Spinach::FeatureSteps ...@@ -3,6 +3,14 @@ class Spinach::Features::AdminUsers < Spinach::FeatureSteps
include SharedPaths include SharedPaths
include SharedAdmin include SharedAdmin
before do
allow(Devise).to receive(:omniauth_providers).and_return([:twitter, :twitter_updated])
end
after do
allow(Devise).to receive(:omniauth_providers).and_call_original
end
step 'I should see all users' do step 'I should see all users' do
User.all.each do |user| User.all.each do |user|
expect(page).to have_content user.name expect(page).to have_content user.name
...@@ -121,7 +129,6 @@ class Spinach::Features::AdminUsers < Spinach::FeatureSteps ...@@ -121,7 +129,6 @@ class Spinach::Features::AdminUsers < Spinach::FeatureSteps
end end
step 'I visit "Pete" identities page in admin' do step 'I visit "Pete" identities page in admin' do
allow(Gitlab::OAuth::Provider).to receive(:names).and_return(%w(twitter twitter_updated))
visit admin_user_identities_path(@user) visit admin_user_identities_path(@user)
end end
......
...@@ -193,6 +193,23 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps ...@@ -193,6 +193,23 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
FileUtils.rm_f(File.join(@project.repository.path, 'hooks', 'pre-receive')) FileUtils.rm_f(File.join(@project.repository.path, 'hooks', 'pre-receive'))
end end
step "I switch ref to 'test'" do
select "'test'", from: 'ref'
end
step "I see the ref 'test' has been selected" do
expect(page).to have_selector '.select2-chosen', text: "'test'"
end
step "I visit the 'test' tree" do
visit namespace_project_tree_path(@project.namespace, @project, "'test'")
end
step 'I see the commit data' do
expect(page).to have_css('.tree-commit-link', visible: true)
expect(page).not_to have_content('Loading commit data...')
end
private private
def set_new_content def set_new_content
......
...@@ -229,7 +229,7 @@ module API ...@@ -229,7 +229,7 @@ module API
authorize! :read_merge_request, merge_request authorize! :read_merge_request, merge_request
present paginate(merge_request.notes), with: Entities::MRNote present paginate(merge_request.notes.fresh), with: Entities::MRNote
end end
# Post comment to merge request # Post comment to merge request
......
...@@ -26,7 +26,12 @@ module Grack ...@@ -26,7 +26,12 @@ module Grack
auth! auth!
if project && authorized_request? if project && authorized_request?
@app.call(env) if ENV['GITLAB_GRACK_AUTH_ONLY'] == '1'
# Tell gitlab-git-http-server the request is OK, and what the GL_ID is
render_grack_auth_ok
else
@app.call(env)
end
elsif @user.nil? && !@gitlab_ci elsif @user.nil? && !@gitlab_ci
unauthorized unauthorized
else else
...@@ -174,6 +179,10 @@ module Grack ...@@ -174,6 +179,10 @@ module Grack
end end
end end
def render_grack_auth_ok
[200, { "Content-Type" => "application/json" }, [JSON.dump({ 'GL_ID' => Gitlab::ShellEnv.gl_id(@user) })]]
end
def render_not_found def render_not_found
[404, { "Content-Type" => "text/plain" }, ["Not Found"]] [404, { "Content-Type" => "text/plain" }, ["Not Found"]]
end end
......
...@@ -7,7 +7,7 @@ module Gitlab ...@@ -7,7 +7,7 @@ module Gitlab
def set_env(user) def set_env(user)
# Set GL_ID env variable # Set GL_ID env variable
if user if user
ENV['GL_ID'] = "user-#{user.id}" ENV['GL_ID'] = gl_id(user)
end end
end end
...@@ -15,5 +15,14 @@ module Gitlab ...@@ -15,5 +15,14 @@ module Gitlab
# Reset GL_ID env variable # Reset GL_ID env variable
ENV['GL_ID'] = nil ENV['GL_ID'] = nil
end end
def gl_id(user)
if user.present?
"user-#{user.id}"
else
# This empty string is used in the render_grack_auth_ok method
""
end
end
end end
end end
module Gitlab module Gitlab
module BitbucketImport module BitbucketImport
class Client class Client
class Unauthorized < StandardError; end
attr_reader :consumer, :api attr_reader :consumer, :api
def initialize(access_token = nil, access_token_secret = nil) def initialize(access_token = nil, access_token_secret = nil)
...@@ -46,23 +48,23 @@ module Gitlab ...@@ -46,23 +48,23 @@ module Gitlab
end end
def user def user
JSON.parse(api.get("/api/1.0/user").body) JSON.parse(get("/api/1.0/user").body)
end end
def issues(project_identifier) def issues(project_identifier)
JSON.parse(api.get("/api/1.0/repositories/#{project_identifier}/issues").body) JSON.parse(get("/api/1.0/repositories/#{project_identifier}/issues").body)
end end
def issue_comments(project_identifier, issue_id) def issue_comments(project_identifier, issue_id)
JSON.parse(api.get("/api/1.0/repositories/#{project_identifier}/issues/#{issue_id}/comments").body) JSON.parse(get("/api/1.0/repositories/#{project_identifier}/issues/#{issue_id}/comments").body)
end end
def project(project_identifier) def project(project_identifier)
JSON.parse(api.get("/api/1.0/repositories/#{project_identifier}").body) JSON.parse(get("/api/1.0/repositories/#{project_identifier}").body)
end end
def find_deploy_key(project_identifier, key) def find_deploy_key(project_identifier, key)
JSON.parse(api.get("/api/1.0/repositories/#{project_identifier}/deploy-keys").body).find do |deploy_key| JSON.parse(get("/api/1.0/repositories/#{project_identifier}/deploy-keys").body).find do |deploy_key|
deploy_key["key"].chomp == key.chomp deploy_key["key"].chomp == key.chomp
end end
end end
...@@ -82,11 +84,18 @@ module Gitlab ...@@ -82,11 +84,18 @@ module Gitlab
end end
def projects def projects
JSON.parse(api.get("/api/1.0/user/repositories").body).select { |repo| repo["scm"] == "git" } JSON.parse(get("/api/1.0/user/repositories").body).select { |repo| repo["scm"] == "git" }
end end
private private
def get(url)
response = api.get(url)
raise Unauthorized if (400..499).include?(response.code.to_i)
response
end
def config def config
Gitlab.config.omniauth.providers.find { |provider| provider.name == "bitbucket"} Gitlab.config.omniauth.providers.find { |provider| provider.name == "bitbucket"}
end end
......
module Gitlab module Gitlab
module OAuth module OAuth
class Provider class Provider
def self.names def self.providers
providers = [] Devise.omniauth_providers
end
Gitlab.config.ldap.servers.values.each do |server| def self.enabled?(name)
providers << server['provider_name'] providers.include?(name.to_sym)
end end
Gitlab.config.omniauth.providers.each do |provider| def self.ldap_provider?(name)
providers << provider['name'] name.to_s.start_with?('ldap')
end
def self.config_for(name)
name = name.to_s
if ldap_provider?(name)
Gitlab::LDAP::Config.new(name).options
else
Gitlab.config.omniauth.providers.find { |provider| provider.name == name }
end end
end
providers def self.label_for(name)
config = config_for(name)
(config && config['label']) || name.to_s.titleize
end end
end end
end end
......
...@@ -33,7 +33,7 @@ module Gitlab ...@@ -33,7 +33,7 @@ module Gitlab
merge_repo.git.push(default_options, :origin, merge_request.target_branch) merge_repo.git.push(default_options, :origin, merge_request.target_branch)
# remove source branch # remove source branch
if merge_request.should_remove_source_branch && !project.root_ref?(merge_request.source_branch) && !merge_request.for_fork? if merge_request.remove_source_branch?
# will raise CommandFailed when push fails # will raise CommandFailed when push fails
merge_repo.git.push(default_options, :origin, ":#{merge_request.source_branch}") merge_repo.git.push(default_options, :origin, ":#{merge_request.source_branch}")
end end
......
...@@ -22,10 +22,10 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML ...@@ -22,10 +22,10 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
ERB::Util.html_escape_once(text) ERB::Util.html_escape_once(text)
end end
# Stolen from Rugments::Plugins::Redcarpet as this module is not required # Stolen from Rouge::Plugins::Redcarpet as this module is not required
# from Rugments's gem root. # from Rouge's gem root.
def block_code(code, language) def block_code(code, language)
lexer = Rugments::Lexer.find_fancy(language, code) || Rugments::Lexers::PlainText lexer = Rouge::Lexer.find_fancy(language, code) || Rouge::Lexers::PlainText
# XXX HACK: Redcarpet strips hard tabs out of code blocks, # XXX HACK: Redcarpet strips hard tabs out of code blocks,
# so we assume you're not using leading spaces that aren't tabs, # so we assume you're not using leading spaces that aren't tabs,
...@@ -34,7 +34,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML ...@@ -34,7 +34,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
code.gsub!(/^ /, "\t") code.gsub!(/^ /, "\t")
end end
formatter = Rugments::Formatters::HTML.new( formatter = Rouge::Formatters::HTMLGitlab.new(
cssclass: "code highlight #{@color_scheme} #{lexer.tag}" cssclass: "code highlight #{@color_scheme} #{lexer.tag}"
) )
formatter.format(lexer.lex(code)) formatter.format(lexer.lex(code))
......
require 'cgi'
module Rouge
module Formatters
class HTMLGitlab < Rouge::Formatter
tag 'html_gitlab'
# Creates a new <tt>Rouge::Formatter::HTMLGitlab</tt> instance.
#
# [+nowrap+] If set to True, don't wrap the output at all, not
# even inside a <tt><pre></tt> tag (default: false).
# [+cssclass+] CSS class for the wrapping <tt><div></tt> tag
# (default: 'highlight').
# [+linenos+] If set to 'table', output line numbers as a table
# with two cells, one containing the line numbers,
# the other the whole code. This is copy paste friendly,
# but may cause alignment problems with some browsers
# or fonts. If set to 'inline', the line numbers will
# be integrated in the <tt><pre></tt> tag that contains
# the code (default: nil).
# [+linenostart+] The line number for the first line (default: 1).
# [+lineanchors+] If set to true the formatter will wrap each output
# line in an anchor tag with a name of L-linenumber.
# This allows easy linking to certain lines
# (default: false).
# [+lineanchorsid+] If lineanchors is true the name of the anchors can
# be changed with lineanchorsid to e.g. foo-linenumber
# (default: 'L').
# [+anchorlinenos+] If set to true, will wrap line numbers in <tt><a></tt>
# tags. Used in combination with linenos and lineanchors
# (default: false).
# [+inline_theme+] Inline CSS styles for the <pre> tag (default: false).
def initialize(
nowrap: false,
cssclass: 'highlight',
linenos: nil,
linenostart: 1,
lineanchors: false,
lineanchorsid: 'L',
anchorlinenos: false,
inline_theme: nil
)
@nowrap = nowrap
@cssclass = cssclass
@linenos = linenos
@linenostart = linenostart
@lineanchors = lineanchors
@lineanchorsid = lineanchorsid
@anchorlinenos = anchorlinenos
@inline_theme = Theme.find(@inline_theme).new if @inline_theme.is_a?(String)
end
def render(tokens)
case @linenos
when 'table'
render_tableized(tokens)
when 'inline'
render_untableized(tokens)
else
render_untableized(tokens)
end
end
alias_method :format, :render
private
def render_untableized(tokens)
data = process_tokens(tokens)
html = ''
html << "<pre class=\"#{@cssclass}\"><code>" unless @nowrap
html << wrap_lines(data[:code])
html << "</code></pre>\n" unless @nowrap
html
end
def render_tableized(tokens)
data = process_tokens(tokens)
html = ''
html << "<div class=\"#{@cssclass}\">" unless @nowrap
html << '<table><tbody>'
html << "<td class=\"linenos\"><pre>"
html << wrap_linenos(data[:numbers])
html << '</pre></td>'
html << "<td class=\"lines\"><pre><code>"
html << wrap_lines(data[:code])
html << '</code></pre></td>'
html << '</tbody></table>'
html << '</div>' unless @nowrap
html
end
def process_tokens(tokens)
num_lines = 0
last_val = ''
rendered = ''
tokens.each do |tok, val|
last_val = val
num_lines += val.scan(/\n/).size
rendered << span(tok, val)
end
numbers = (@linenostart..num_lines + @linenostart - 1).to_a
{ numbers: numbers, code: rendered }
end
def wrap_linenos(numbers)
if @anchorlinenos
numbers.map! do |number|
"<a href=\"##{@lineanchorsid}#{number}\">#{number}</a>"
end
end
numbers.join("\n")
end
def wrap_lines(rendered)
if @lineanchors
lines = rendered.split("\n")
lines = lines.each_with_index.map do |line, index|
number = index + @linenostart
if @linenos == 'inline'
"<a name=\"L#{number}\"></a>" \
"<span class=\"linenos\">#{number}</span>" \
"<span id=\"#{@lineanchorsid}#{number}\" class=\"line\">#{line}" \
'</span>'
else
"<span id=\"#{@lineanchorsid}#{number}\" class=\"line\">#{line}" \
'</span>'
end
end
lines.join("\n")
else
if @linenos == 'inline'
lines = rendered.split("\n")
lines = lines.each_with_index.map do |line, index|
number = index + @linenostart
"<span class=\"linenos\">#{number}</span>#{line}"
end
lines.join("\n")
else
rendered
end
end
end
def span(tok, val)
# http://stackoverflow.com/a/1600584/2587286
val = CGI.escapeHTML(val)
if tok.shortname.empty?
val
else
if @inline_theme
rules = @inline_theme.style_for(tok).rendered_rules
"<span style=\"#{rules.to_a.join(';')}\">#{val}</span>"
else
"<span class=\"#{tok.shortname}\">#{val}</span>"
end
end
end
end
end
end
...@@ -38,6 +38,11 @@ upstream gitlab { ...@@ -38,6 +38,11 @@ upstream gitlab {
server unix:/home/git/gitlab/tmp/sockets/gitlab.socket fail_timeout=0; server unix:/home/git/gitlab/tmp/sockets/gitlab.socket fail_timeout=0;
} }
## Experimental: gitlab-git-http-server
# upstream gitlab-git-http-server {
# server localhost:8181;
# }
## Normal HTTP host ## Normal HTTP host
server { server {
## Either remove "default_server" from the listen line below, ## Either remove "default_server" from the listen line below,
...@@ -109,6 +114,26 @@ server { ...@@ -109,6 +114,26 @@ server {
proxy_pass http://gitlab; proxy_pass http://gitlab;
} }
## Experimental: send Git HTTP traffic to gitlab-git-http-server instead of Unicorn
# location ~ [-\/\w\.]+\.git\/ {
# ## If you use HTTPS make sure you disable gzip compression
# ## to be safe against BREACH attack.
# # gzip off;
# ## https://github.com/gitlabhq/gitlabhq/issues/694
# ## Some requests take more than 30 seconds.
# proxy_read_timeout 300;
# proxy_connect_timeout 300;
# proxy_redirect off;
# proxy_set_header Host $http_host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
# proxy_pass http://gitlab-git-http-server;
# }
## Enable gzip compression as per rails guide: ## Enable gzip compression as per rails guide:
## http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression ## http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression
## WARNING: If you are using relative urls remove the block below ## WARNING: If you are using relative urls remove the block below
......
...@@ -42,6 +42,11 @@ upstream gitlab { ...@@ -42,6 +42,11 @@ upstream gitlab {
server unix:/home/git/gitlab/tmp/sockets/gitlab.socket fail_timeout=0; server unix:/home/git/gitlab/tmp/sockets/gitlab.socket fail_timeout=0;
} }
## Experimental: gitlab-git-http-server
# upstream gitlab-git-http-server {
# server localhost:8181;
# }
## Redirects all HTTP traffic to the HTTPS host ## Redirects all HTTP traffic to the HTTPS host
server { server {
## Either remove "default_server" from the listen line below, ## Either remove "default_server" from the listen line below,
...@@ -156,6 +161,26 @@ server { ...@@ -156,6 +161,26 @@ server {
proxy_pass http://gitlab; proxy_pass http://gitlab;
} }
## Experimental: send Git HTTP traffic to gitlab-git-http-server instead of Unicorn
# location ~ [-\/\w\.]+\.git\/ {
# ## If you use HTTPS make sure you disable gzip compression
# ## to be safe against BREACH attack.
# gzip off;
# ## https://github.com/gitlabhq/gitlabhq/issues/694
# ## Some requests take more than 30 seconds.
# proxy_read_timeout 300;
# proxy_connect_timeout 300;
# proxy_redirect off;
# proxy_set_header Host $http_host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-Ssl on;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
# proxy_pass http://gitlab-git-http-server;
# }
## Enable gzip compression as per rails guide: ## Enable gzip compression as per rails guide:
## http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression ## http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression
## WARNING: If you are using relative urls remove the block below ## WARNING: If you are using relative urls remove the block below
......
require "#{Rails.root}/app/helpers/emails_helper"
require 'action_view/helpers'
extend ActionView::Helpers
include ActionView::Context
include EmailsHelper
namespace :gitlab do
desc "Email google whitelisting email with example email for actions in inbox"
task mail_google_schema_whitelisting: :environment do
subject = "Rails | Implemented feature"
url = "#{Gitlab.config.gitlab.url}/base/rails-project/issues/#{rand(1..100)}#note_#{rand(10..1000)}"
schema = email_action(url)
body = email_template(schema, url)
mail = Notify.test_email("schema.whitelisting+sample@gmail.com", subject, body.html_safe)
if send_now
mail.deliver
else
puts "WOULD SEND:"
end
puts mail
end
def email_template(schema, url)
"<html lang='en'>
<head>
<meta content='text/html; charset=utf-8' http-equiv='Content-Type'>
<title>
GitLab
</title>
</meta>
</head>
<style>
img {
max-width: 100%;
height: auto;
}
p.details {
font-style:italic;
color:#777
}
.footer p {
font-size:small;
color:#777
}
</style>
<body>
<div class='content'>
<div>
<p>I like it :+1: </p>
</div>
</div>
<div class='footer' style='margin-top: 10px;'>
<p>
<br>
<a href=\"#{url}\">View it on GitLab</a>
You're receiving this notification because you are a member of the Base / Rails Project project team.
#{schema}
</p>
</div>
</body>
</html>"
end
def send_now
if ENV['SEND'] == "true"
true
else
false
end
end
end
namespace :gitlab do
desc "GitLab | Update commit count for projects"
task update_commit_count: :environment do
projects = Project.where(commit_count: 0)
puts "#{projects.size} projects need to be updated. This might take a while."
ask_to_continue unless ENV['force'] == 'yes'
projects.find_each(batch_size: 100) do |project|
print "#{project.name_with_namespace.yellow} ... "
unless project.repo_exists?
puts "skipping, because the repo is empty".magenta
next
end
project.update_commit_count
puts project.commit_count.to_s.green
end
end
end
...@@ -8,4 +8,5 @@ class UnfoldForm ...@@ -8,4 +8,5 @@ class UnfoldForm
attribute :bottom, Boolean attribute :bottom, Boolean
attribute :unfold, Boolean, default: true attribute :unfold, Boolean, default: true
attribute :offset, Integer attribute :offset, Integer
attribute :indent, Integer, default: 0
end end
require 'spec_helper' require 'spec_helper'
describe UsersController do describe UsersController do
let(:user) { create(:user, username: 'user1', name: 'User 1', email: 'user1@gitlab.com') } let(:user) { create(:user) }
before do
sign_in(user)
end
describe 'GET #show' do describe 'GET #show' do
render_views it 'is case-insensitive' do
user = create(:user, username: 'CamelCaseUser')
sign_in(user)
get :show, username: user.username.downcase
it 'renders the show template' do expect(response).to be_success
get :show, username: user.username end
expect(response.status).to eq(200)
expect(response).to render_template('show') context 'with rendered views' do
render_views
it 'renders the show template' do
sign_in(user)
get :show, username: user.username
expect(response).to be_success
expect(response).to render_template('show')
end
end end
end end
describe 'GET #calendar' do describe 'GET #calendar' do
it 'renders calendar' do it 'renders calendar' do
sign_in(user)
get :calendar, username: user.username get :calendar, username: user.username
expect(response).to render_template('calendar') expect(response).to render_template('calendar')
end end
end end
...@@ -30,6 +43,8 @@ describe UsersController do ...@@ -30,6 +43,8 @@ describe UsersController do
before do before do
allow_any_instance_of(User).to receive(:contributed_projects_ids).and_return([project.id]) allow_any_instance_of(User).to receive(:contributed_projects_ids).and_return([project.id])
sign_in(user)
project.team << [user, :developer] project.team << [user, :developer]
end end
......
require 'spec_helper' require 'spec_helper'
describe "Admin::Projects", feature: true do describe "Admin::Projects", feature: true do
include AccessMatchers
describe "GET /admin/projects" do describe "GET /admin/projects" do
subject { admin_namespaces_projects_path } subject { admin_namespaces_projects_path }
......
require 'spec_helper' require 'spec_helper'
describe "Dashboard access", feature: true do describe "Dashboard access", feature: true do
include AccessMatchers
describe "GET /dashboard" do describe "GET /dashboard" do
subject { dashboard_path } subject { dashboard_path }
......
require 'spec_helper'
describe "Group access", feature: true do
describe "GET /projects/new" do
it { expect(new_group_path).to be_allowed_for :admin }
it { expect(new_group_path).to be_allowed_for :user }
it { expect(new_group_path).to be_denied_for :visitor }
end
describe "Group" do
let(:group) { create(:group) }
let(:owner) { create(:owner) }
let(:master) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
let(:nonmember) { create(:user) }
before do
group.add_user(owner, Gitlab::Access::OWNER)
group.add_user(master, Gitlab::Access::MASTER)
group.add_user(reporter, Gitlab::Access::REPORTER)
group.add_user(guest, Gitlab::Access::GUEST)
end
describe "GET /groups/:path" do
subject { group_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
describe "GET /groups/:path/issues" do
subject { issues_group_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
describe "GET /groups/:path/merge_requests" do
subject { merge_requests_group_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
describe "GET /groups/:path/group_members" do
subject { group_group_members_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
describe "GET /groups/:path/edit" do
subject { edit_group_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_denied_for master }
it { is_expected.to be_denied_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
describe "GET /groups/:path/projects" do
subject { projects_group_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_denied_for master }
it { is_expected.to be_denied_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
end
end
require 'spec_helper'
describe "Group with internal project access", feature: true do
describe "Group" do
let(:group) { create(:group) }
let(:owner) { create(:owner) }
let(:master) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
let(:nonmember) { create(:user) }
before do
group.add_user(owner, Gitlab::Access::OWNER)
group.add_user(master, Gitlab::Access::MASTER)
group.add_user(reporter, Gitlab::Access::REPORTER)
group.add_user(guest, Gitlab::Access::GUEST)
create(:project, :internal, group: group)
end
describe "GET /groups/:path" do
subject { group_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :visitor }
end
describe "GET /groups/:path/issues" do
subject { issues_group_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :visitor }
end
describe "GET /groups/:path/merge_requests" do
subject { merge_requests_group_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :visitor }
end
describe "GET /groups/:path/group_members" do
subject { group_group_members_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :visitor }
end
describe "GET /groups/:path/edit" do
subject { edit_group_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_denied_for master }
it { is_expected.to be_denied_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
end
end
require 'spec_helper'
describe "Group access", feature: true do
describe "Group" do
let(:group) { create(:group) }
let(:owner) { create(:owner) }
let(:master) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
let(:nonmember) { create(:user) }
before do
group.add_user(owner, Gitlab::Access::OWNER)
group.add_user(master, Gitlab::Access::MASTER)
group.add_user(reporter, Gitlab::Access::REPORTER)
group.add_user(guest, Gitlab::Access::GUEST)
create(:project, :internal, path: "internal_project", group: group)
create(:project, :public, path: "public_project", group: group)
end
describe "GET /groups/:path" do
subject { group_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
describe "GET /groups/:path/issues" do
subject { issues_group_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
describe "GET /groups/:path/merge_requests" do
subject { merge_requests_group_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
describe "GET /groups/:path/group_members" do
subject { group_group_members_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
describe "GET /groups/:path/edit" do
subject { edit_group_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_denied_for master }
it { is_expected.to be_denied_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
end
end
require 'spec_helper'
describe "Group with public project access", feature: true do
describe "Group" do
let(:group) { create(:group) }
let(:owner) { create(:owner) }
let(:master) { create(:user) }
let(:reporter) { create(:user) }
let(:guest) { create(:user) }
let(:nonmember) { create(:user) }
before do
group.add_user(owner, Gitlab::Access::OWNER)
group.add_user(master, Gitlab::Access::MASTER)
group.add_user(reporter, Gitlab::Access::REPORTER)
group.add_user(guest, Gitlab::Access::GUEST)
create(:project, :public, group: group)
end
describe "GET /groups/:path" do
subject { group_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
describe "GET /groups/:path/issues" do
subject { issues_group_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
describe "GET /groups/:path/merge_requests" do
subject { merge_requests_group_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
describe "GET /groups/:path/group_members" do
subject { group_group_members_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_allowed_for master }
it { is_expected.to be_allowed_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for guest }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
describe "GET /groups/:path/edit" do
subject { edit_group_path(group) }
it { is_expected.to be_allowed_for owner }
it { is_expected.to be_denied_for master }
it { is_expected.to be_denied_for reporter }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for guest }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
end
end
require 'rails_helper'
describe 'Group access', feature: true do
include AccessMatchers
def group
@group ||= create(:group)
end
def create_project(access_level)
if access_level == :mixed
create(:empty_project, :public, group: group)
create(:empty_project, :internal, group: group)
else
create(:empty_project, access_level, group: group)
end
end
def group_member(access_level, group = group)
level = Object.const_get("Gitlab::Access::#{access_level.upcase}")
create(:user).tap do |user|
group.add_user(user, level)
end
end
describe 'GET /groups/new' do
subject { new_group_path }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :visitor }
end
describe 'GET /groups/:path' do
subject { group_path(group) }
context 'with public projects' do
let!(:project) { create_project(:public) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
context 'with mixed projects' do
let!(:project) { create_project(:mixed) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
context 'with internal projects' do
let!(:project) { create_project(:internal) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :visitor }
end
context 'with no projects' do
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
end
describe 'GET /groups/:path/issues' do
subject { issues_group_path(group) }
context 'with public projects' do
let!(:project) { create_project(:public) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
context 'with mixed projects' do
let!(:project) { create_project(:mixed) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
context 'with internal projects' do
let!(:project) { create_project(:internal) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :visitor }
end
context 'with no projects' do
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
end
describe 'GET /groups/:path/merge_requests' do
subject { merge_requests_group_path(group) }
context 'with public projects' do
let!(:project) { create_project(:public) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
context 'with mixed projects' do
let!(:project) { create_project(:mixed) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
context 'with internal projects' do
let!(:project) { create_project(:internal) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :visitor }
end
context 'with no projects' do
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
end
describe 'GET /groups/:path/group_members' do
subject { group_group_members_path(group) }
context 'with public projects' do
let!(:project) { create_project(:public) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
context 'with mixed projects' do
let!(:project) { create_project(:mixed) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_allowed_for :visitor }
end
context 'with internal projects' do
let!(:project) { create_project(:internal) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :visitor }
end
context 'with no projects' do
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_allowed_for group_member(:master) }
it { is_expected.to be_allowed_for group_member(:reporter) }
it { is_expected.to be_allowed_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
end
describe 'GET /groups/:path/edit' do
subject { edit_group_path(group) }
context 'with public projects' do
let!(:project) { create_project(:public) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_denied_for group_member(:master) }
it { is_expected.to be_denied_for group_member(:reporter) }
it { is_expected.to be_denied_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
context 'with mixed projects' do
let!(:project) { create_project(:mixed) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_denied_for group_member(:master) }
it { is_expected.to be_denied_for group_member(:reporter) }
it { is_expected.to be_denied_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
context 'with internal projects' do
let!(:project) { create_project(:internal) }
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_denied_for group_member(:master) }
it { is_expected.to be_denied_for group_member(:reporter) }
it { is_expected.to be_denied_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
context 'with no projects' do
it { is_expected.to be_allowed_for group_member(:owner) }
it { is_expected.to be_denied_for group_member(:master) }
it { is_expected.to be_denied_for group_member(:reporter) }
it { is_expected.to be_denied_for group_member(:guest) }
it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_denied_for :user }
it { is_expected.to be_denied_for :visitor }
end
end
end
require 'spec_helper' require 'spec_helper'
describe "Profile access", feature: true do describe "Profile access", feature: true do
before do include AccessMatchers
@u1 = create(:user)
end
describe "GET /login" do
it { expect(new_user_session_path).not_to be_not_found_for :visitor }
end
describe "GET /profile/keys" do describe "GET /profile/keys" do
subject { profile_keys_path } subject { profile_keys_path }
it { is_expected.to be_allowed_for @u1 }
it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
...@@ -21,7 +14,6 @@ describe "Profile access", feature: true do ...@@ -21,7 +14,6 @@ describe "Profile access", feature: true do
describe "GET /profile" do describe "GET /profile" do
subject { profile_path } subject { profile_path }
it { is_expected.to be_allowed_for @u1 }
it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
...@@ -30,7 +22,6 @@ describe "Profile access", feature: true do ...@@ -30,7 +22,6 @@ describe "Profile access", feature: true do
describe "GET /profile/account" do describe "GET /profile/account" do
subject { profile_account_path } subject { profile_account_path }
it { is_expected.to be_allowed_for @u1 }
it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
...@@ -39,7 +30,6 @@ describe "Profile access", feature: true do ...@@ -39,7 +30,6 @@ describe "Profile access", feature: true do
describe "GET /profile/preferences" do describe "GET /profile/preferences" do
subject { profile_preferences_path } subject { profile_preferences_path }
it { is_expected.to be_allowed_for @u1 }
it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
...@@ -48,7 +38,6 @@ describe "Profile access", feature: true do ...@@ -48,7 +38,6 @@ describe "Profile access", feature: true do
describe "GET /profile/audit_log" do describe "GET /profile/audit_log" do
subject { audit_log_profile_path } subject { audit_log_profile_path }
it { is_expected.to be_allowed_for @u1 }
it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
...@@ -57,7 +46,6 @@ describe "Profile access", feature: true do ...@@ -57,7 +46,6 @@ describe "Profile access", feature: true do
describe "GET /profile/notifications" do describe "GET /profile/notifications" do
subject { profile_notifications_path } subject { profile_notifications_path }
it { is_expected.to be_allowed_for @u1 }
it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :admin }
it { is_expected.to be_allowed_for :user } it { is_expected.to be_allowed_for :user }
it { is_expected.to be_denied_for :visitor } it { is_expected.to be_denied_for :visitor }
......
require 'spec_helper' require 'spec_helper'
describe "Internal Project Access", feature: true do describe "Internal Project Access", feature: true do
include AccessMatchers
let(:project) { create(:project, :internal) } let(:project) { create(:project, :internal) }
let(:master) { create(:user) } let(:master) { create(:user) }
......
require 'spec_helper' require 'spec_helper'
describe "Private Project Access", feature: true do describe "Private Project Access", feature: true do
include AccessMatchers
let(:project) { create(:project) } let(:project) { create(:project) }
let(:master) { create(:user) } let(:master) { create(:user) }
......
require 'spec_helper' require 'spec_helper'
describe "Public Project Access", feature: true do describe "Public Project Access", feature: true do
include AccessMatchers
let(:project) { create(:project) } let(:project) { create(:project) }
let(:master) { create(:user) } let(:master) { create(:user) }
...@@ -17,7 +19,6 @@ describe "Public Project Access", feature: true do ...@@ -17,7 +19,6 @@ describe "Public Project Access", feature: true do
# readonly # readonly
project.team << [reporter, :reporter] project.team << [reporter, :reporter]
end end
describe "Project should be public" do describe "Project should be public" do
......
require "spec_helper"
describe AuthHelper do
describe "button_based_providers" do
it 'returns all enabled providers' do
allow(helper).to receive(:auth_providers) { [:twitter, :github] }
expect(helper.button_based_providers).to include(*[:twitter, :github])
end
it 'does not return ldap provider' do
allow(helper).to receive(:auth_providers) { [:twitter, :ldapmain] }
expect(helper.button_based_providers).to include(:twitter)
end
it 'returns empty array' do
allow(helper).to receive(:auth_providers) { [] }
expect(helper.button_based_providers).to eq([])
end
end
end
require "spec_helper"
describe OauthHelper do
describe "additional_providers" do
it 'returns all enabled providers' do
allow(helper).to receive(:enabled_oauth_providers) { [:twitter, :github] }
expect(helper.additional_providers).to include(*[:twitter, :github])
end
it 'does not return ldap provider' do
allow(helper).to receive(:enabled_oauth_providers) { [:twitter, :ldapmain] }
expect(helper.additional_providers).to include(:twitter)
end
it 'returns empty array' do
allow(helper).to receive(:enabled_oauth_providers) { [] }
expect(helper.additional_providers).to eq([])
end
end
end
...@@ -22,7 +22,7 @@ describe ProjectsHelper do ...@@ -22,7 +22,7 @@ describe ProjectsHelper do
let(:user) { create(:user) } let(:user) { create(:user) }
it "returns false if there are no approipriate permissions" do it "returns false if there are no appropriate permissions" do
allow(helper).to receive(:can?) { false } allow(helper).to receive(:can?) { false }
expect(helper.can_change_visibility_level?(project, user)).to be_falsey expect(helper.can_change_visibility_level?(project, user)).to be_falsey
...@@ -52,4 +52,22 @@ describe ProjectsHelper do ...@@ -52,4 +52,22 @@ describe ProjectsHelper do
end end
end end
end end
describe "readme_cache_key" do
let(:project) { create(:project) }
before do
helper.instance_variable_set(:@project, project)
end
it "returns a valid cach key" do
expect(helper.send(:readme_cache_key)).to eq("#{project.id}-#{project.commit.id}-readme")
end
it "returns a valid cache key if HEAD does not exist" do
allow(project).to receive(:commit) { nil }
expect(helper.send(:readme_cache_key)).to eq("#{project.id}-nil-readme")
end
end
end end
...@@ -51,6 +51,12 @@ describe 'MergeRequestTabs', -> ...@@ -51,6 +51,12 @@ describe 'MergeRequestTabs', ->
expect(@subject('notes')).toBe('/foo/bar/merge_requests/1') expect(@subject('notes')).toBe('/foo/bar/merge_requests/1')
expect(@subject('commits')).toBe('/foo/bar/merge_requests/1/commits') expect(@subject('commits')).toBe('/foo/bar/merge_requests/1/commits')
it 'changes from diffs.html', ->
@class._location = stubLocation(pathname: '/foo/bar/merge_requests/1/diffs.html')
expect(@subject('notes')).toBe('/foo/bar/merge_requests/1')
expect(@subject('commits')).toBe('/foo/bar/merge_requests/1/commits')
it 'changes from notes', -> it 'changes from notes', ->
@class._location = stubLocation(pathname: '/foo/bar/merge_requests/1') @class._location = stubLocation(pathname: '/foo/bar/merge_requests/1')
......
...@@ -442,6 +442,18 @@ describe User do ...@@ -442,6 +442,18 @@ describe User do
end end
end end
describe '.find_by_username!' do
it 'raises RecordNotFound' do
expect { described_class.find_by_username!('JohnDoe') }.
to raise_error(ActiveRecord::RecordNotFound)
end
it 'is case-insensitive' do
user = create(:user, username: 'JohnDoe')
expect(described_class.find_by_username!('JOHNDOE')).to eq user
end
end
describe 'all_ssh_keys' do describe 'all_ssh_keys' do
it { is_expected.to have_many(:keys).dependent(:destroy) } it { is_expected.to have_many(:keys).dependent(:destroy) }
......
...@@ -8,6 +8,7 @@ describe API::API, api: true do ...@@ -8,6 +8,7 @@ describe API::API, api: true do
let!(:merge_request_closed) { create(:merge_request, state: "closed", author: user, assignee: user, source_project: project, target_project: project, title: "Closed test") } let!(:merge_request_closed) { create(:merge_request, state: "closed", author: user, assignee: user, source_project: project, target_project: project, title: "Closed test") }
let!(:merge_request_merged) { create(:merge_request, state: "merged", author: user, assignee: user, source_project: project, target_project: project, title: "Merged test") } let!(:merge_request_merged) { create(:merge_request, state: "merged", author: user, assignee: user, source_project: project, target_project: project, title: "Merged test") }
let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") } let!(:note) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "a comment on a MR") }
let!(:note2) { create(:note_on_merge_request, author: user, project: project, noteable: merge_request, note: "another comment on a MR") }
before do before do
project.team << [user, :reporters] project.team << [user, :reporters]
...@@ -397,13 +398,14 @@ describe API::API, api: true do ...@@ -397,13 +398,14 @@ describe API::API, api: true do
end end
describe "GET :id/merge_request/:merge_request_id/comments" do describe "GET :id/merge_request/:merge_request_id/comments" do
it "should return merge_request comments" do it "should return merge_request comments ordered by created_at" do
get api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user) get api("/projects/#{project.id}/merge_request/#{merge_request.id}/comments", user)
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(json_response).to be_an Array expect(json_response).to be_an Array
expect(json_response.length).to eq(1) expect(json_response.length).to eq(2)
expect(json_response.first['note']).to eq("a comment on a MR") expect(json_response.first['note']).to eq("a comment on a MR")
expect(json_response.first['author']['id']).to eq(user.id) expect(json_response.first['author']['id']).to eq(user.id)
expect(json_response.last['note']).to eq("another comment on a MR")
end end
it "should return a 404 error if merge_request_id not found" do it "should return a 404 error if merge_request_id not found" do
......
...@@ -4,13 +4,19 @@ describe Projects::CreateService do ...@@ -4,13 +4,19 @@ describe Projects::CreateService do
describe :create_by_user do describe :create_by_user do
before do before do
@user = create :user @user = create :user
@admin = create :user, admin: true
@opts = { @opts = {
name: "GitLab", name: "GitLab",
namespace: @user.namespace namespace: @user.namespace
} }
end end
it 'creates services on Project creation' do
project = create_project(@user, @opts)
project.reload
expect(project.services).not_to be_empty
end
context 'user namespace' do context 'user namespace' do
before do before do
@project = create_project(@user, @opts) @project = create_project(@user, @opts)
...@@ -75,7 +81,9 @@ describe Projects::CreateService do ...@@ -75,7 +81,9 @@ describe Projects::CreateService do
end end
it 'should allow a restricted visibility level for admins' do it 'should allow a restricted visibility level for admins' do
project = create_project(@admin, @opts) admin = create(:admin)
project = create_project(admin, @opts)
expect(project.errors.any?).to be(false) expect(project.errors.any?).to be(false)
expect(project.saved?).to be(true) expect(project.saved?).to be(true)
end end
......
RSpec::Matchers.define :be_valid_commit do
match do |actual|
actual &&
actual.id == ValidCommit::ID &&
actual.message == ValidCommit::MESSAGE &&
actual.author_name == ValidCommit::AUTHOR_FULL_NAME
end
end
def emulate_user(user)
user = case user
when :user then create(:user)
when :visitor then nil
when :admin then create(:admin)
else user
end
login_with(user) if user
end
RSpec::Matchers.define :be_allowed_for do |user|
match do |url|
emulate_user(user)
visit url
status_code != 404 && current_path != new_user_session_path
end
end
RSpec::Matchers.define :be_denied_for do |user|
match do |url|
emulate_user(user)
visit url
status_code == 404 || current_path == new_user_session_path
end
end
RSpec::Matchers.define :be_not_found_for do |user|
match do |url|
emulate_user(user)
visit url
status_code == 404
end
end
RSpec::Matchers.define :include_module do |expected|
match do
described_class.included_modules.include?(expected)
end
description do
"includes the #{expected} module"
end
failure_message do
"expected #{described_class} to include the #{expected} module"
end
end
# Extend shoulda-matchers
module Shoulda::Matchers::ActiveModel
class ValidateLengthOfMatcher
# Shortcut for is_at_least and is_at_most
def is_within(range)
is_at_least(range.min) && is_at_most(range.max)
end
end
end
# AccessMatchers
#
# The custom matchers contained in this module are used to test a user's access
# to a URL by emulating a specific user or type of user account, visiting the
# URL, and then checking the response status code and resulting path.
module AccessMatchers
extend RSpec::Matchers::DSL
include Warden::Test::Helpers
def emulate_user(user)
case user
when :user
login_as(create(:user))
when :visitor
logout
when :admin
login_as(create(:admin))
when User
login_as(user)
else
raise ArgumentError, "cannot emulate user #{user}"
end
end
def description_for(user, type)
if user.kind_of?(User)
# User#inspect displays too much information for RSpec's description
# messages
"be #{type} for supplied User"
else
"be #{type} for #{user}"
end
end
matcher :be_allowed_for do |user|
match do |url|
emulate_user(user)
visit url
status_code != 404 && current_path != new_user_session_path
end
description { description_for(user, 'allowed') }
end
matcher :be_denied_for do |user|
match do |url|
emulate_user(user)
visit url
status_code == 404 || current_path == new_user_session_path
end
description { description_for(user, 'denied') }
end
end
RSpec::Matchers.define :include_module do |expected|
match do
described_class.included_modules.include?(expected)
end
description do
"includes the #{expected} module"
end
failure_message do
"expected #{described_class} to include the #{expected} module"
end
end
# Extend shoulda-matchers
module Shoulda::Matchers::ActiveModel
class ValidateLengthOfMatcher
# Shortcut for is_at_least and is_at_most
def is_within(range)
is_at_least(range.min) && is_at_most(range.max)
end
end
end
...@@ -12,7 +12,8 @@ module TestEnv ...@@ -12,7 +12,8 @@ module TestEnv
'fix' => '12d65c8', 'fix' => '12d65c8',
'improve/awesome' => '5937ac0', 'improve/awesome' => '5937ac0',
'markdown' => '0ed8c6c', 'markdown' => '0ed8c6c',
'master' => '5937ac0' 'master' => '5937ac0',
"'test'" => 'e56497b',
} }
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily # gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
......
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