BigW Consortium Gitlab

Commit a623add8 by Regis

Merge branch 'master' into auto-pipelines-vue

parents 18cfacc9 46eb0ebf
......@@ -4,37 +4,6 @@ entry.
## 8.14.0 (2016-11-22)
- Use separate email-token for incoming email and revert back the inactive feature. !5914
- Replace jQuery.timeago with timeago.js. !6274 (ClemMakesApps)
- Add CI notifications. Who triggered a pipeline would receive an email after the pipeline is succeeded or failed. Users could also update notification settings accordingly. !6342
- Finer-grained Git gargage collection. !6588
- Introduce better credential and error checking to `rake gitlab:ldap:check`. !6601
- Process commits using a dedicated Sidekiq worker. !6802
- Fix showing pipeline status for a given commit from correct branch. !7034
- Add query param to filter users by external & blocked type. !7109 (Yatish Mehta)
- Issues atom feed url reflect filters on dashboard. !7114 (Lucas Deschamps)
- Add setting to only allow merge requests to be merged when all discussions are resolved. !7125 (Rodolfo Arruda)
- Remove an extra leading space from diff paste data. !7133 (Hiroyuki Sato)
- Fix 404 on network page when entering non-existent git revision. !7172 (Hiroyuki Sato)
- Rewrite git blame spinach feature tests to rspec feature tests. !7197 (Lisanne Fellinger)
- Only skip group when it's actually a group in the "Share with group" select. !7262
- Introduce round-robin project creation to spread load over multiple shards. !7266
- Ensure merge request's "remove branch" accessors return booleans. !7267
- Expose label IDs in API. !7275 (Rares Sfirlogea)
- Fix invalid filename validation on eslint. !7281
- API: Ability to retrieve version information. !7286 (Robert Schilling)
- Set default Sidekiq retries to 3. !7294
- Return 400 when creating a system hook fails. !7350 (Robert Schilling)
- Use the Gitlab Workhorse HTTP header in the admin dashboard. (Chris Wright)
- Add an index for project_id in project_import_data to improve performance.
- Fix broken link to observatory cli on Frontend Dev Guide. (Sam Rose)
- Faster search inside Project.
- Clicking "force remove source branch" label now toggles the checkbox again.
- Allow to test JIRA service settings without having a repository.
- Fix: Guest sees some repository details and gets 404.
- Bump omniauth-gitlab to 1.0.2 to fix incompatibility with omniauth-oauth2.
- Fix: Todos Filter Shows All Users.
- Fix broken commits search.
- Show correct environment log in admin/logs (@duk3luk3 !7191)
- Fix Milestone dropdown not stay selected for `Upcoming` and `No Milestone` option !7117
- Diff collapse won't shift when collapsing.
......
......@@ -53,6 +53,7 @@
/*= require_directory ./u2f */
/*= require_directory . */
/*= require fuzzaldrin-plus */
/*= require es6-promise.auto */
(function () {
document.addEventListener('page:fetch', gl.utils.cleanupBeforeFetch);
......
......@@ -141,6 +141,10 @@
&.btn-save {
@include btn-outline($white-light, $green-normal, $green-normal, $green-light, $white-light, $green-light);
}
&.btn-remove {
@include btn-outline($white-light, $red-normal, $red-normal, $red-light, $white-light, $red-light);
}
}
&.btn-gray {
......
......@@ -63,7 +63,11 @@ header {
&:focus,
&:active {
background-color: $background-color;
color: darken($gl-icon-color, 50%);
color: darken($gl-icon-color, 30%);
.todos-pending-count {
background: darken($todo-alert-blue, 10%);
}
}
.fa-caret-down {
......@@ -153,7 +157,7 @@ header {
padding-right: 20px;
margin: 0;
font-size: 19px;
max-width: 400px;
max-width: 385px;
display: inline-block;
line-height: $header-height;
font-weight: normal;
......@@ -194,7 +198,7 @@ header {
cursor: pointer;
&:hover {
color: darken($color: $gl-text-color, $amount: 50%);
color: darken($color: $gl-text-color, $amount: 30%);
}
}
......@@ -223,6 +227,14 @@ header {
}
}
.page-sidebar-pinned.right-sidebar-expanded {
@media (max-width: $screen-lg-min) {
.header-content .title {
width: 300px;
}
}
}
@media (max-width: $screen-xs-max) {
header .container-fluid {
font-size: 18px;
......
......@@ -252,7 +252,7 @@ $award-emoji-new-btn-icon-color: #dcdcdc;
*/
$search-input-border-color: rgba(#4688f1, .8);
$search-input-focus-shadow-color: $dropdown-input-focus-shadow;
$search-input-width: 244px;
$search-input-width: 220px;
$location-badge-color: #aaa;
$location-badge-bg: $gray-normal;
$location-badge-active-bg: #4f91f8;
......
......@@ -141,6 +141,22 @@ ul.notes {
}
}
.page-sidebar-pinned.right-sidebar-expanded {
@media (max-width: $screen-lg-min) {
.note-header {
.note-headline-light {
display: block;
}
.note-actions {
position: absolute;
right: 0;
top: 0;
}
}
}
}
// Diff code in discussion view
.discussion-body .diff-file {
.file-title {
......
......@@ -33,10 +33,9 @@
}
.search-input {
padding-right: 20px;
border: none;
font-size: 14px;
padding: 0;
padding: 0 20px 0 0;
margin-left: 5px;
line-height: 25px;
width: 98%;
......@@ -158,7 +157,6 @@
width: 68%;
}
}
}
.search-holder {
......@@ -235,5 +233,4 @@
&:focus {
color: $gl-link-color;
}
}
......@@ -4,7 +4,7 @@ class Projects::BranchesController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
before_action :authorize_download_code!
before_action :authorize_push_code!, only: [:new, :create, :destroy]
before_action :authorize_push_code!, only: [:new, :create, :destroy, :destroy_all_merged]
def index
@sort = params[:sort].presence || sort_value_name
......@@ -62,6 +62,13 @@ class Projects::BranchesController < Projects::ApplicationController
end
end
def destroy_all_merged
DeleteMergedBranchesService.new(@project, current_user).async_execute
redirect_to namespace_project_branches_path(@project.namespace, @project),
notice: 'Merged branches are being deleted. This can take some time depending on the number of branches. Please refresh the page to see changes.'
end
private
def ref
......
......@@ -31,10 +31,6 @@ class Projects::LfsApiController < Projects::GitHttpClientController
private
def objects
@objects ||= (params[:objects] || []).to_a
end
def existing_oids
@existing_oids ||= begin
storage_project.lfs_objects.where(oid: objects.map { |o| o['oid'].to_s }).pluck(:oid)
......
......@@ -30,6 +30,10 @@ module LfsHelper
ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code?
end
def objects
@objects ||= (params[:objects] || []).to_a
end
def user_can_download_code?
has_authentication_ability?(:download_code) && can?(user, :download_code, project)
end
......
module TriggersHelper
def builds_trigger_url(project_id)
def builds_trigger_url(project_id, ref: nil)
if ref.nil?
"#{Settings.gitlab.url}/api/v3/projects/#{project_id}/trigger/builds"
else
"#{Settings.gitlab.url}/api/v3/projects/#{project_id}/ref/#{ref}/trigger/builds"
end
end
end
......@@ -35,6 +35,7 @@ class Project < ActiveRecord::Base
default_value_for :builds_enabled, gitlab_config_features.builds
default_value_for :wiki_enabled, gitlab_config_features.wiki
default_value_for :snippets_enabled, gitlab_config_features.snippets
default_value_for :only_allow_merge_if_all_discussions_are_resolved, false
after_create :ensure_dir_exist
after_create :create_project_feature, unless: :project_feature
......@@ -1334,10 +1335,6 @@ class Project < ActiveRecord::Base
end
end
def only_allow_merge_if_all_discussions_are_resolved
super || false
end
private
def pushes_since_gc_redis_key
......
......@@ -631,7 +631,7 @@ class Repository
@head_tree ||= Tree.new(self, head_commit.sha, nil)
end
def tree(sha = :head, path = nil)
def tree(sha = :head, path = nil, recursive: false)
if sha == :head
if path.nil?
return head_tree
......@@ -640,7 +640,7 @@ class Repository
end
end
Tree.new(self, sha, path)
Tree.new(self, sha, path, recursive: recursive)
end
def blob_at_branch(branch_name, path)
......
......@@ -3,15 +3,16 @@ class Tree
attr_accessor :repository, :sha, :path, :entries
def initialize(repository, sha, path = '/')
def initialize(repository, sha, path = '/', recursive: false)
path = '/' if path.blank?
@repository = repository
@sha = sha
@path = path
@recursive = recursive
git_repo = @repository.raw_repository
@entries = Gitlab::Git::Tree.where(git_repo, @sha, @path)
@entries = get_entries(git_repo, @sha, @path, recursive: @recursive)
end
def readme
......@@ -58,4 +59,21 @@ class Tree
def sorted_entries
trees + blobs + submodules
end
private
def get_entries(git_repo, sha, path, recursive: false)
current_path_entries = Gitlab::Git::Tree.where(git_repo, sha, path)
ordered_entries = []
current_path_entries.each do |entry|
ordered_entries << entry
if recursive && entry.dir?
ordered_entries.concat(get_entries(git_repo, sha, entry.path, recursive: true))
end
end
ordered_entries
end
end
require_relative 'base_service'
class DeleteMergedBranchesService < BaseService
def async_execute
DeleteMergedBranchesWorker.perform_async(project.id, current_user.id)
end
def execute
raise Gitlab::Access::AccessDeniedError unless can?(current_user, :push_code, project)
branches = project.repository.branch_names
branches = branches.select { |branch| project.repository.merged_to_root_ref?(branch) }
branches.each do |branch|
DeleteBranchService.new(project, current_user).execute(branch)
end
end
end
......@@ -84,7 +84,7 @@
= render "shared/empty_states/todos_all_done.svg"
- if todos_filter_empty?
%h4.text-center
Good job! Looks like you don't have any todos left.
= Gitlab.config.gitlab.no_todos_messages.sample
%p.text-center
Are you looking for things to do? Take a look at
= succeed "," do
......
......@@ -26,6 +26,8 @@
= sort_title_oldest_updated
- if can? current_user, :push_code, @project
= link_to namespace_project_merged_branches_path(@project.namespace, @project), class: 'btn btn-inverted btn-remove has-tooltip', title: "Delete all branches that are merged into '#{@project.repository.root_ref}'", method: :delete, data: { confirm: "Deleting the merged branches cannot be undone. Are you sure?", container: 'body' } do
Delete merged branches
= link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do
New branch
......
......@@ -25,7 +25,7 @@
- elsif diff_file.renamed_file
.nothing-here-block File moved
- elsif blob.image?
- old_blob = diff_file.old_blob(diff_commit)
- old_blob = diff_file.old_blob(diff_file.old_content_commit || @base_commit)
= render "projects/diffs/image", diff_file: diff_file, old_file: old_blob, file: blob
- else
.nothing-here-block No preview for this file type
......@@ -76,6 +76,16 @@
script:
- "curl -X POST -F token=TOKEN -F ref=REF_NAME #{builds_trigger_url(@project.id)}"
%h5.prepend-top-default
Use webhook
%p.light
Add the following webhook to another project for Push and Tag push events.
The project will be rebuilt at the corresponding event.
%pre
:plain
#{builds_trigger_url(@project.id, ref: 'REF_NAME')}?token=TOKEN
%h5.prepend-top-default
Pass build variables
%p.light
......@@ -83,10 +93,18 @@
%code variables[VARIABLE]=VALUE
to an API request. Variable values can be used to distinguish between triggered builds and normal builds.
%pre.append-bottom-0
With cURL:
%pre
:plain
curl -X POST \
-F token=TOKEN \
-F "ref=REF_NAME" \
-F "variables[RUN_NIGHTLY_BUILD]=true" \
#{builds_trigger_url(@project.id)}
%p.light
With webhook:
%pre.append-bottom-0
:plain
#{builds_trigger_url(@project.id, ref: 'REF_NAME')}?token=TOKEN&variables[RUN_NIGHTLY_BUILD]=true
class DeleteMergedBranchesWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
def perform(project_id, user_id)
begin
project = Project.find(project_id)
rescue ActiveRecord::RecordNotFound
return
end
user = User.find(user_id)
begin
DeleteMergedBranchesService.new(project, user).execute
rescue Gitlab::Access::AccessDeniedError
return
end
end
end
---
title: Add setting to only allow merge requests to be merged when all discussions are resolved
merge_request: 7125
author: Rodolfo Arruda
---
title: Add button to delete all merged branches
merge_request: 6449
author: Toon Claes
---
title: Use the Gitlab Workhorse HTTP header in the admin dashboard
merge_request:
author: Chris Wright
---
title: 'Fix: Todos Filter Shows All Users'
merge_request:
author:
---
title: Issues atom feed url reflect filters on dashboard
merge_request: 7114
author: Lucas Deschamps
---
title: Rewrite git blame spinach feature tests to rspec feature tests
merge_request: 7197
author: Lisanne Fellinger
---
title: Make it possible to trigger builds from webhooks
merge_request: 7022
author: Dmitry Poray
---
title: Add query param to filter users by external & blocked type
merge_request: 7109
author: Yatish Mehta
---
title: Only skip group when it's actually a group in the "Share with group" select
merge_request: 7262
author:
---
title: 'Fix: Guest sees some repository details and gets 404'
merge_request:
author:
---
title: Introduce round-robin project creation to spread load over multiple shards
merge_request: 7266
author:
---
title: Ensure merge request's "remove branch" accessors return booleans
merge_request: 7267
author:
---
title: Fix broken commits search
merge_request:
author:
---
title: Adds es6-promise Polyfill
merge_request: 7482
author:
---
title: Expose label IDs in API
merge_request: 7275
author: Rares Sfirlogea
---
title: Add an index for project_id in project_import_data to improve performance
merge_request:
author:
---
title: "API: Ability to retrieve version information"
merge_request: 7286
author: Robert Schilling
---
title: Return 400 when creating a system hook fails
merge_request: 7350
author: Robert Schilling
---
title: Fix broken link to observatory cli on Frontend Dev Guide
merge_request:
author: Sam Rose
---
title: Faster search inside Project
merge_request:
author:
---
title: Fix 404 on network page when entering non-existent git revision
merge_request: 7172
author: Hiroyuki Sato
---
title: Fix invalid filename validation on eslint
merge_request: 7281
author:
---
title: Give search-input correct padding-right value
merge_request: 7407
author: Philip Karpiak
---
title: Clicking "force remove source branch" label now toggles the checkbox again
merge_request:
author:
---
title: Omniauth auto link LDAP user falls back to find by DN when user cannot be found
by UID
merge_request: 7002
author:
---
title: Finer-grained Git gargage collection
merge_request: 6588
author:
---
title: Show random messages when the To Do list is empty
merge_request: 6818
author: Josep Llaneras
---
title: Allow to test JIRA service settings without having a repository
merge_request:
author:
---
title: Introduce better credential and error checking to `rake gitlab:ldap:check`
merge_request: 6601
author:
---
title: API: allow recursive tree request
merge_request: 6088
author: Rebeca Méndez
---
title: Add CI notifications. Who triggered a pipeline would receive an email after
the pipeline is succeeded or failed. Users could also update notification settings
accordingly
merge_request: 6342
author:
---
title: Process commits using a dedicated Sidekiq worker
merge_request: 6802
author:
---
title: Remove an extra leading space from diff paste data
merge_request: 7133
author: Hiroyuki Sato
---
title: Bump omniauth-gitlab to 1.0.2 to fix incompatibility with omniauth-oauth2
merge_request:
author:
---
title: Fix showing pipeline status for a given commit from correct branch
merge_request: 7034
author:
---
title: Set default Sidekiq retries to 3
merge_request: 7294
author:
---
title: Fix Error 500 when creating a merge request that contains an image that was deleted and added
merge_request: 7457
author:
---
title: Replace jQuery.timeago with timeago.js
merge_request: 6274
author: ClemMakesApps
---
title: Use separate email-token for incoming email and revert back the inactive feature
merge_request: 5914
author:
......@@ -215,6 +215,7 @@ Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(
Settings.gitlab['domain_whitelist'] ||= []
Settings.gitlab['import_sources'] ||= %w[github bitbucket gitlab google_code fogbugz git gitlab_project]
Settings.gitlab['trusted_proxies'] ||= []
Settings.gitlab['no_todos_messages'] ||= YAML.load_file(Rails.root.join('config', 'no_todos_messages.yml'))
#
# CI
......
# When the Todos list on the user's dashboard becomes empty, one of the messages below shows up randomly.
#
# If you come up with a fun one, please feel free to contribute it to GitLab!
# https://about.gitlab.com/contributing/
---
- Good job! Looks like you don't have any todos left.
- Coffee really tastes better without any todos left.
- Isn't an empty To Do list beautiful?
- Time for a rewarding coffee break
- Give yourself a pat on the back!
- High five!
- Hence forth you shall be known as 'Todo Destroyer'
\ No newline at end of file
......@@ -125,6 +125,7 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only:
end
resources :branches, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
delete :merged_branches, controller: 'branches', action: :destroy_all_merged
resources :tags, only: [:index, :show, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } do
resource :release, only: [:edit, :update]
end
......
......@@ -34,6 +34,7 @@
- [project_service, 1]
- [clear_database_cache, 1]
- [delete_user, 1]
- [delete_merged_branches, 1]
- [expire_build_instance_artifacts, 1]
- [group_destroy, 1]
- [irker, 1]
......
......@@ -240,3 +240,21 @@ Example response:
"branch_name": "newbranch"
}
```
## Delete merged branches
Will delete all branches that are merged into the project's default branch.
```
DELETE /projects/:id/repository/merged_branches
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer | yes | The ID of a project |
It returns `200` to indicate deletion of all merged branches was started.
```bash
curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/5/repository/merged_branches"
```
......@@ -13,44 +13,58 @@ Parameters:
- `id` (required) - The ID of a project
- `path` (optional) - The path inside repository. Used to get contend of subdirectories
- `ref_name` (optional) - The name of a repository branch or tag or if not given the default branch
- `recursive` (optional) - Boolean value used to get a recursive tree (false by default)
```json
[
{
"name": "assets",
"id": "a1e8f8d745cc87e3a9248358d9352bb7f9a0aeba",
"name": "html",
"type": "tree",
"mode": "040000",
"id": "6229c43a7e16fcc7e95f923f8ddadb8281d9c6c6"
"path": "files/html",
"mode": "040000"
},
{
"name": "contexts",
"id": "4535904260b1082e14f867f7a24fd8c21495bde3",
"name": "images",
"type": "tree",
"mode": "040000",
"id": "faf1cdf33feadc7973118ca42d35f1e62977e91f"
"path": "files/images",
"mode": "040000"
},
{
"name": "controllers",
"id": "31405c5ddef582c5a9b7a85230413ff90e2fe720",
"name": "js",
"type": "tree",
"mode": "040000",
"id": "95633e8d258bf3dfba3a5268fb8440d263218d74"
"path": "files/js",
"mode": "040000"
},
{
"name": "Rakefile",
"type": "blob",
"mode": "100644",
"id": "35b2f05cbb4566b71b34554cf184a9d0bd9d46d6"
"id": "cc71111cfad871212dc99572599a568bfe1e7e00",
"name": "lfs",
"type": "tree",
"path": "files/lfs",
"mode": "040000"
},
{
"name": "VERSION",
"type": "blob",
"mode": "100644",
"id": "803e4a4f3727286c3093c63870c2b6524d30ec4f"
"id": "fd581c619bf59cfdfa9c8282377bb09c2f897520",
"name": "markdown",
"type": "tree",
"path": "files/markdown",
"mode": "040000"
},
{
"id": "23ea4d11a4bdd960ee5320c5cb65b5b3fdbc60db",
"name": "ruby",
"type": "tree",
"path": "files/ruby",
"mode": "040000"
},
{
"name": "config.ru",
"id": "7d70e02340bac451f281cecf0a980907974bd8be",
"name": "whitespace",
"type": "blob",
"mode": "100644",
"id": "dfd2d862237323aa599be31b473d70a8a817943b"
"path": "files/whitespace",
"mode": "100644"
}
]
```
......
......@@ -58,6 +58,22 @@ below.
See the [Examples](#examples) section for more details on how to actually
trigger a rebuild.
## Trigger a build from webhook
> Introduced in GitLab 8.14.
To trigger a build from webhook of another project you need to add the following
webhook url for Push and Tag push events:
```
https://gitlab.example.com/api/v3/projects/:id/ref/:ref/trigger/builds?token=TOKEN
```
> **Note**:
- `ref` should be passed as part of url in order to take precedence over `ref`
from webhook body that designates the branchref that fired the trigger in the source repository.
- `ref` should be url encoded if contains slashes.
## Pass build variables to a trigger
You can pass any number of arbitrary variables in the trigger API call and they
......@@ -169,6 +185,14 @@ curl --request POST \
https://gitlab.example.com/api/v3/projects/9/trigger/builds
```
### Using webhook to trigger builds
You can add the following webhook to another project in order to trigger a build:
```
https://gitlab.example.com/api/v3/projects/9/ref/master/trigger/builds?token=TOKEN&variables[UPLOAD_TO_S3]=true
```
### Using cron to trigger nightly builds
Whether you craft a script or just run cURL directly, you can trigger builds
......
......@@ -48,7 +48,11 @@ at Super User also has relevant information.
**Omnibus Trusted Chain**
It is enough to concatenate the certificate to the main trusted certificate:
[Install the self signed certificate or custom certificate authorities](http://docs.gitlab.com/omnibus/common_installation_problems/README.html#using-self-signed-certificate-or-custom-certificate-authorities)
in to GitLab Omnibus.
It is enough to concatenate the certificate to the main trusted certificate
however it may be overwritten during upgrades:
```bash
cat jira.pem >> /opt/gitlab/embedded/ssl/certs/cacert.pem
......
......@@ -48,7 +48,7 @@ module API
put ':id/access_requests/:user_id/approve' do
source = find_source(source_type, params[:id])
member = ::Members::ApproveAccessRequestService.new(source, current_user, declared(params)).execute
member = ::Members::ApproveAccessRequestService.new(source, current_user, declared_params).execute
status :created
present member.user, with: Entities::Member, member: member
......
......@@ -128,6 +128,18 @@ module API
render_api_error!(result[:message], result[:return_code])
end
end
# Delete all merged branches
#
# Parameters:
# id (required) - The ID of a project
# Example Request:
# DELETE /projects/:id/repository/branches/delete_merged
delete ":id/repository/merged_branches" do
DeleteMergedBranchesService.new(user_project, current_user).async_execute
status(200)
end
end
end
end
......@@ -36,8 +36,7 @@ module API
optional :font, type: String, desc: 'Foreground color'
end
post do
create_params = declared(params, include_missing: false).to_h
message = BroadcastMessage.create(create_params)
message = BroadcastMessage.create(declared_params(include_missing: false))
if message.persisted?
present message, with: Entities::BroadcastMessage
......@@ -73,9 +72,8 @@ module API
end
put ':id' do
message = find_message
update_params = declared(params, include_missing: false).to_h
if message.update(update_params)
if message.update(declared_params(include_missing: false))
present message, with: Entities::BroadcastMessage
else
render_validation_error!(message)
......
......@@ -53,7 +53,7 @@ module API
post ":id/repository/commits" do
authorize! :push_code, user_project
attrs = declared(params)
attrs = declared_params
attrs[:source_branch] = attrs[:branch_name]
attrs[:target_branch] = attrs[:branch_name]
attrs[:actions].map! do |action|
......
......@@ -82,7 +82,7 @@ module API
end
post ":id/#{path}/:key_id/enable" do
key = ::Projects::EnableDeployKeyService.new(user_project,
current_user, declared(params)).execute
current_user, declared_params).execute
if key
present key, with: Entities::SSHKey
......
......@@ -159,7 +159,7 @@ module API
end
class RepoTreeObject < Grape::Entity
expose :id, :name, :type
expose :id, :name, :type, :path
expose :mode do |obj, options|
filemode = obj.mode.to_s(8)
......
......@@ -32,8 +32,7 @@ module API
post ':id/environments' do
authorize! :create_environment, user_project
create_params = declared(params, include_parent_namespaces: false).to_h
environment = user_project.environments.create(create_params)
environment = user_project.environments.create(declared_params)
if environment.persisted?
present environment, with: Entities::Environment
......@@ -56,7 +55,7 @@ module API
environment = user_project.environments.find(params[:environment_id])
update_params = declared(params, include_missing: false).extract!(:name, :external_url).to_h
update_params = declared_params(include_missing: false).extract!(:name, :external_url)
if environment.update(update_params)
present environment, with: Entities::Environment
else
......
module API
# groups API
class Groups < Grape::API
before { authenticate! }
helpers do
params :optional_params do
optional :description, type: String, desc: 'The description of the group'
optional :visibility_level, type: Integer, desc: 'The visibility level of the group'
optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group'
optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
end
end
resource :groups do
# Get a groups list
#
# Parameters:
# skip_groups (optional) - Array of group ids to exclude from list
# all_available (optional, boolean) - Show all group that you have access to
# Example Request:
# GET /groups
desc 'Get a groups list' do
success Entities::Group
end
params do
optional :skip_groups, type: Array[Integer], desc: 'Array of group ids to exclude from list'
optional :all_available, type: Boolean, desc: 'Show all group that you have access to'
optional :search, type: String, desc: 'Search for a specific group'
end
get do
@groups = if current_user.admin
groups = if current_user.admin
Group.all
elsif params[:all_available]
GroupsFinder.new.execute(current_user)
......@@ -20,99 +29,83 @@ module API
current_user.groups
end
@groups = @groups.search(params[:search]) if params[:search].present?
@groups = @groups.where.not(id: params[:skip_groups]) if params[:skip_groups].present?
@groups = paginate @groups
present @groups, with: Entities::Group
groups = groups.search(params[:search]) if params[:search].present?
groups = groups.where.not(id: params[:skip_groups]) if params[:skip_groups].present?
present paginate(groups), with: Entities::Group
end
# Get list of owned groups for authenticated user
#
# Example Request:
# GET /groups/owned
desc 'Get list of owned groups for authenticated user' do
success Entities::Group
end
get '/owned' do
@groups = current_user.owned_groups
@groups = paginate @groups
present @groups, with: Entities::Group, user: current_user
groups = current_user.owned_groups
present paginate(groups), with: Entities::Group, user: current_user
end
# Create group. Available only for users who can create groups.
#
# Parameters:
# name (required) - The name of the group
# path (required) - The path of the group
# description (optional) - The description of the group
# visibility_level (optional) - The visibility level of the group
# lfs_enabled (optional) - Enable/disable LFS for the projects in this group
# request_access_enabled (optional) - Allow users to request member access
# Example Request:
# POST /groups
desc 'Create a group. Available only for users who can create groups.' do
success Entities::Group
end
params do
requires :name, type: String, desc: 'The name of the group'
requires :path, type: String, desc: 'The path of the group'
use :optional_params
end
post do
authorize! :create_group
required_attributes! [:name, :path]
attrs = attributes_for_keys [:name, :path, :description, :visibility_level, :lfs_enabled, :request_access_enabled]
@group = Group.new(attrs)
group = ::Groups::CreateService.new(current_user, declared_params(include_missing: false)).execute
if @group.save
@group.add_owner(current_user)
present @group, with: Entities::Group
if group.persisted?
present group, with: Entities::Group
else
render_api_error!("Failed to save group #{@group.errors.messages}", 400)
render_api_error!("Failed to save group #{group.errors.messages}", 400)
end
end
end
# Update group. Available only for users who can administrate groups.
#
# Parameters:
# id (required) - The ID of a group
# path (optional) - The path of the group
# description (optional) - The description of the group
# visibility_level (optional) - The visibility level of the group
# lfs_enabled (optional) - Enable/disable LFS for the projects in this group
# request_access_enabled (optional) - Allow users to request member access
# Example Request:
# PUT /groups/:id
params do
requires :id, type: String, desc: 'The ID of a group'
end
resource :groups do
desc 'Update a group. Available only for users who can administrate groups.' do
success Entities::Group
end
params do
optional :name, type: String, desc: 'The name of the group'
optional :path, type: String, desc: 'The path of the group'
use :optional_params
at_least_one_of :name, :path, :description, :visibility_level,
:lfs_enabled, :request_access_enabled
end
put ':id' do
group = find_group(params[:id])
authorize! :admin_group, group
attrs = attributes_for_keys [:name, :path, :description, :visibility_level, :lfs_enabled, :request_access_enabled]
if ::Groups::UpdateService.new(group, current_user, attrs).execute
if ::Groups::UpdateService.new(group, current_user, declared_params(include_missing: false)).execute
present group, with: Entities::GroupDetail
else
render_validation_error!(group)
end
end
# Get a single group, with containing projects
#
# Parameters:
# id (required) - The ID of a group
# Example Request:
# GET /groups/:id
desc 'Get a single group, with containing projects.' do
success Entities::GroupDetail
end
get ":id" do
group = find_group(params[:id])
present group, with: Entities::GroupDetail
end
# Remove group
#
# Parameters:
# id (required) - The ID of a group
# Example Request:
# DELETE /groups/:id
desc 'Remove a group.'
delete ":id" do
group = find_group(params[:id])
authorize! :admin_group, group
DestroyGroupService.new(group, current_user).execute
end
# Get a list of projects in this group
#
# Example Request:
# GET /groups/:id/projects
desc 'Get a list of projects in this group.' do
success Entities::Project
end
get ":id/projects" do
group = find_group(params[:id])
projects = GroupProjectsFinder.new(group).execute(current_user)
......@@ -120,13 +113,12 @@ module API
present projects, with: Entities::Project, user: current_user
end
# Transfer a project to the Group namespace
#
# Parameters:
# id - group id
# project_id - project id
# Example Request:
# POST /groups/:id/projects/:project_id
desc 'Transfer a project to the group namespace. Available only for admin.' do
success Entities::GroupDetail
end
params do
requires :project_id, type: String, desc: 'The ID of the project'
end
post ":id/projects/:project_id" do
authenticated_as_admin!
group = Group.find_by(id: params[:id])
......@@ -134,7 +126,7 @@ module API
result = ::Projects::TransferService.new(project, current_user).execute(group)
if result
present group
present group, with: Entities::GroupDetail
else
render_api_error!("Failed to transfer project #{project.errors.messages}", 400)
end
......
......@@ -30,10 +30,7 @@ module API
conflict!('Label already exists') if label
priority = params.delete(:priority)
label_params = declared(params,
include_parent_namespaces: false,
include_missing: false).to_h
label = user_project.labels.create(label_params)
label = user_project.labels.create(declared_params(include_missing: false))
if label.valid?
label.prioritize!(user_project, priority) if priority
......@@ -77,11 +74,9 @@ module API
update_priority = params.key?(:priority)
priority = params.delete(:priority)
label_params = declared(params,
include_parent_namespaces: false,
include_missing: false).to_h
label_params = declared_params(include_missing: false)
# Rename new name to the actual label attribute name
label_params[:name] = label_params.delete('new_name') if label_params.key?('new_name')
label_params[:name] = label_params.delete(:new_name) if label_params.key?(:new_name)
render_validation_error!(label) unless label.update(label_params)
......
......@@ -120,7 +120,7 @@ module API
if member.nil?
{ message: "Access revoked", id: params[:user_id].to_i }
else
::Members::DestroyService.new(source, current_user, declared(params)).execute
::Members::DestroyService.new(source, current_user, declared_params).execute
present member.user, with: Entities::Member, member: member
end
......
......@@ -5,23 +5,23 @@ module API
NOTEABLE_TYPES = [Issue, MergeRequest, Snippet]
params do
requires :id, type: String, desc: 'The ID of a project'
end
resource :projects do
NOTEABLE_TYPES.each do |noteable_type|
noteables_str = noteable_type.to_s.underscore.pluralize
noteable_id_str = "#{noteable_type.to_s.underscore}_id"
# Get a list of project +noteable+ notes
#
# Parameters:
# id (required) - The ID of a project
# noteable_id (required) - The ID of an issue or snippet
# Example Request:
# GET /projects/:id/issues/:noteable_id/notes
# GET /projects/:id/snippets/:noteable_id/notes
get ":id/#{noteables_str}/:#{noteable_id_str}/notes" do
@noteable = user_project.send(noteables_str.to_sym).find(params[noteable_id_str.to_sym])
if can?(current_user, noteable_read_ability_name(@noteable), @noteable)
desc 'Get a list of project +noteable+ notes' do
success Entities::Note
end
params do
requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
end
get ":id/#{noteables_str}/:noteable_id/notes" do
noteable = user_project.send(noteables_str.to_sym).find(params[:noteable_id])
if can?(current_user, noteable_read_ability_name(noteable), noteable)
# We exclude notes that are cross-references and that cannot be viewed
# by the current user. By doing this exclusion at this level and not
# at the DB query level (which we cannot in that case), the current
......@@ -31,7 +31,7 @@ module API
# paginate() only works with a relation. This could lead to a
# mismatch between the pagination headers info and the actual notes
# array returned, but this is really a edge-case.
paginate(@noteable.notes).
paginate(noteable.notes).
reject { |n| n.cross_reference_not_visible_for?(current_user) }
present notes, with: Entities::Note
else
......@@ -39,44 +39,40 @@ module API
end
end
# Get a single +noteable+ note
#
# Parameters:
# id (required) - The ID of a project
# noteable_id (required) - The ID of an issue or snippet
# note_id (required) - The ID of a note
# Example Request:
# GET /projects/:id/issues/:noteable_id/notes/:note_id
# GET /projects/:id/snippets/:noteable_id/notes/:note_id
get ":id/#{noteables_str}/:#{noteable_id_str}/notes/:note_id" do
@noteable = user_project.send(noteables_str.to_sym).find(params[noteable_id_str.to_sym])
@note = @noteable.notes.find(params[:note_id])
can_read_note = can?(current_user, noteable_read_ability_name(@noteable), @noteable) && !@note.cross_reference_not_visible_for?(current_user)
desc 'Get a single +noteable+ note' do
success Entities::Note
end
params do
requires :note_id, type: Integer, desc: 'The ID of a note'
requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
end
get ":id/#{noteables_str}/:noteable_id/notes/:note_id" do
noteable = user_project.send(noteables_str.to_sym).find(params[:noteable_id])
note = noteable.notes.find(params[:note_id])
can_read_note = can?(current_user, noteable_read_ability_name(noteable), noteable) && !note.cross_reference_not_visible_for?(current_user)
if can_read_note
present @note, with: Entities::Note
present note, with: Entities::Note
else
not_found!("Note")
end
end
# Create a new +noteable+ note
#
# Parameters:
# id (required) - The ID of a project
# noteable_id (required) - The ID of an issue or snippet
# body (required) - The content of a note
# created_at (optional) - The date
# Example Request:
# POST /projects/:id/issues/:noteable_id/notes
# POST /projects/:id/snippets/:noteable_id/notes
post ":id/#{noteables_str}/:#{noteable_id_str}/notes" do
desc 'Create a new +noteable+ note' do
success Entities::Note
end
params do
requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
requires :body, type: String, desc: 'The content of a note'
optional :created_at, type: String, desc: 'The creation date of the note'
end
post ":id/#{noteables_str}/:noteable_id/notes" do
required_attributes! [:body]
opts = {
note: params[:body],
noteable_type: noteables_str.classify,
noteable_id: params[noteable_id_str]
noteable_id: params[:noteable_id]
}
if params[:created_at] && (current_user.is_admin? || user_project.owner == current_user)
......@@ -92,19 +88,15 @@ module API
end
end
# Modify existing +noteable+ note
#
# Parameters:
# id (required) - The ID of a project
# noteable_id (required) - The ID of an issue or snippet
# node_id (required) - The ID of a note
# body (required) - New content of a note
# Example Request:
# PUT /projects/:id/issues/:noteable_id/notes/:note_id
# PUT /projects/:id/snippets/:noteable_id/notes/:node_id
put ":id/#{noteables_str}/:#{noteable_id_str}/notes/:note_id" do
required_attributes! [:body]
desc 'Update an existing +noteable+ note' do
success Entities::Note
end
params do
requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
requires :note_id, type: Integer, desc: 'The ID of a note'
requires :body, type: String, desc: 'The content of a note'
end
put ":id/#{noteables_str}/:noteable_id/notes/:note_id" do
note = user_project.notes.find(params[:note_id])
authorize! :admin_note, note
......@@ -113,25 +105,23 @@ module API
note: params[:body]
}
@note = ::Notes::UpdateService.new(user_project, current_user, opts).execute(note)
note = ::Notes::UpdateService.new(user_project, current_user, opts).execute(note)
if @note.valid?
present @note, with: Entities::Note
if note.valid?
present note, with: Entities::Note
else
render_api_error!("Failed to save note #{note.errors.messages}", 400)
end
end
# Delete a +noteable+ note
#
# Parameters:
# id (required) - The ID of a project
# noteable_id (required) - The ID of an issue, MR, or snippet
# node_id (required) - The ID of a note
# Example Request:
# DELETE /projects/:id/issues/:noteable_id/notes/:note_id
# DELETE /projects/:id/snippets/:noteable_id/notes/:node_id
delete ":id/#{noteables_str}/:#{noteable_id_str}/notes/:note_id" do
desc 'Delete a +noteable+ note' do
success Entities::Note
end
params do
requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
requires :note_id, type: Integer, desc: 'The ID of a note'
end
delete ":id/#{noteables_str}/:noteable_id/notes/:note_id" do
note = user_project.notes.find(params[:note_id])
authorize! :admin_note, note
......
......@@ -33,10 +33,9 @@ module API
begin
notification_setting.transaction do
new_notification_email = params.delete(:notification_email)
declared_params = declared(params, include_missing: false).to_h
current_user.update(notification_email: new_notification_email) if new_notification_email
notification_setting.update(declared_params)
notification_setting.update(declared_params(include_missing: false))
end
rescue ArgumentError => e # catch level enum error
render_api_error! e.to_s, 400
......@@ -81,9 +80,7 @@ module API
notification_setting = current_user.notification_settings_for(source)
begin
declared_params = declared(params, include_missing: false).to_h
notification_setting.update(declared_params)
notification_setting.update(declared_params(include_missing: false))
rescue ArgumentError => e # catch level enum error
render_api_error! e.to_s, 400
end
......
......@@ -51,8 +51,7 @@ module API
use :project_hook_properties
end
post ":id/hooks" do
new_hook_params = declared(params, include_missing: false, include_parent_namespaces: false).to_h
hook = user_project.hooks.new(new_hook_params)
hook = user_project.hooks.new(declared_params(include_missing: false))
if hook.save
present hook, with: Entities::ProjectHook
......@@ -71,12 +70,9 @@ module API
use :project_hook_properties
end
put ":id/hooks/:hook_id" do
hook = user_project.hooks.find(params[:hook_id])
new_params = declared(params, include_missing: false, include_parent_namespaces: false).to_h
new_params.delete('hook_id')
hook = user_project.hooks.find(params.delete(:hook_id))
if hook.update_attributes(new_params)
if hook.update_attributes(declared_params(include_missing: false))
present hook, with: Entities::ProjectHook
else
error!("Invalid url given", 422) if hook.errors[:url].present?
......
......@@ -21,16 +21,18 @@ module API
# Parameters:
# id (required) - The ID of a project
# ref_name (optional) - The name of a repository branch or tag, if not given the default branch is used
# recursive (optional) - Used to get a recursive tree
# Example Request:
# GET /projects/:id/repository/tree
get ':id/repository/tree' do
ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
path = params[:path] || nil
recursive = to_boolean(params[:recursive])
commit = user_project.commit(ref)
not_found!('Tree') unless commit
tree = user_project.repository.tree(commit.id, path)
tree = user_project.repository.tree(commit.id, path, recursive: recursive)
present tree.sorted_entries, with: Entities::RepoTreeObject
end
......
......@@ -27,7 +27,7 @@ module API
optional :enable_ssl_verification, type: Boolean, desc: "Do SSL verification when triggering the hook"
end
post do
hook = SystemHook.new declared(params, include_missing: false).to_h
hook = SystemHook.new(declared_params(include_missing: false))
if hook.save
present hook, with: Entities::Hook
......
......@@ -40,10 +40,9 @@ module API
end
post ':id/repository/tags' do
authorize_push_project
create_params = declared(params)
result = CreateTagService.new(user_project, current_user).
execute(create_params[:tag_name], create_params[:ref], create_params[:message], create_params[:release_description])
execute(params[:tag_name], params[:ref], params[:message], params[:release_description])
if result[:status] == :success
present result[:tag],
......
......@@ -12,7 +12,7 @@ module API
requires :token, type: String, desc: 'The unique token of trigger'
optional :variables, type: Hash, desc: 'The list of variables to be injected into build'
end
post ":id/trigger/builds" do
post ":id/(ref/:ref/)trigger/builds" do
project = Project.find_with_namespace(params[:id]) || Project.find_by(id: params[:id])
trigger = Ci::Trigger.find_by_token(params[:token].to_s)
not_found! unless project && trigger
......
......@@ -335,7 +335,7 @@ module API
requires :id, type: String, desc: 'The user ID'
end
get ':id/events' do
user = User.find_by(id: declared(params).id)
user = User.find_by(id: params[:id])
not_found!('User') unless user
events = user.events.
......
......@@ -55,6 +55,12 @@ module Gitlab
repository.commit(deleted_file ? old_ref : new_ref)
end
def old_content_commit
return unless diff_refs
repository.commit(old_ref)
end
def old_ref
diff_refs.try(:base_sha)
end
......@@ -111,13 +117,10 @@ module Gitlab
diff_lines.count(&:removed?)
end
def old_blob(commit = content_commit)
def old_blob(commit = old_content_commit)
return unless commit
parent_id = commit.parent_id
return unless parent_id
repository.blob_at(parent_id, old_path)
repository.blob_at(commit.id, old_path)
end
def blob(commit = content_commit)
......
......@@ -102,6 +102,8 @@ module Gitlab
Gitlab::LDAP::Config.providers.each do |provider|
adapter = Gitlab::LDAP::Adapter.new(provider)
@ldap_person = Gitlab::LDAP::Person.find_by_uid(auth_hash.uid, adapter)
# The `uid` might actually be a DN. Try it next.
@ldap_person ||= Gitlab::LDAP::Person.find_by_dn(auth_hash.uid, adapter)
break if @ldap_person
end
@ldap_person
......
......@@ -3,11 +3,11 @@ require 'spec_helper'
describe Projects::BranchesController do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:developer) { create(:user) }
before do
sign_in(user)
project.team << [user, :master]
project.team << [user, :developer]
allow(project).to receive(:branches).and_return(['master', 'foo/bar/baz'])
allow(project).to receive(:tags).and_return(['v1.0.0', 'v2.0.0'])
......@@ -19,6 +19,8 @@ describe Projects::BranchesController do
context "on creation of a new branch" do
before do
sign_in(user)
post :create,
namespace_id: project.namespace.to_param,
project_id: project.to_param,
......@@ -68,6 +70,10 @@ describe Projects::BranchesController do
let(:branch) { "1-feature-branch" }
let!(:issue) { create(:issue, project: project) }
before do
sign_in(user)
end
it 'redirects' do
post :create,
namespace_id: project.namespace.to_param,
......@@ -94,6 +100,10 @@ describe Projects::BranchesController do
describe "POST destroy with HTML format" do
render_views
before do
sign_in(user)
end
it 'returns 303' do
post :destroy,
format: :html,
......@@ -109,6 +119,8 @@ describe Projects::BranchesController do
render_views
before do
sign_in(user)
post :destroy,
format: :js,
id: branch,
......@@ -139,4 +151,42 @@ describe Projects::BranchesController do
it { expect(response).to have_http_status(404) }
end
end
describe "DELETE destroy_all_merged" do
def destroy_all_merged
delete :destroy_all_merged,
namespace_id: project.namespace.to_param,
project_id: project.to_param
end
context 'when user is allowed to push' do
before do
sign_in(user)
end
it 'redirects to branches' do
destroy_all_merged
expect(response).to redirect_to namespace_project_branches_path(project.namespace, project)
end
it 'starts worker to delete merged branches' do
expect_any_instance_of(DeleteMergedBranchesService).to receive(:async_execute)
destroy_all_merged
end
end
context 'when user is not allowed to push' do
before do
sign_in(developer)
end
it 'responds with status 404' do
destroy_all_merged
expect(response).to have_http_status(404)
end
end
end
end
......@@ -67,4 +67,14 @@ feature 'Create New Merge Request', feature: true, js: true do
expect(page).to have_content('Source branch "non-exist-source" does not exist')
expect(page).to have_content('Target branch "non-exist-target" does not exist')
end
context 'when a branch contains commits that both delete and add the same image' do
it 'renders the diff successfully' do
visit new_namespace_project_merge_request_path(project.namespace, project, merge_request: { target_branch: 'master', source_branch: 'deleted-image-test' })
click_link "Changes"
expect(page).to have_content "6049019_460s.jpg"
end
end
end
......@@ -44,7 +44,7 @@ describe 'Dashboard Todos', feature: true do
end
it 'shows "All done" message' do
expect(page).to have_content("Good job! Looks like you don't have any todos left.")
expect(page).to have_selector('.todos-all-done', count: 1)
end
end
......@@ -64,7 +64,7 @@ describe 'Dashboard Todos', feature: true do
end
it 'shows "All done" message' do
expect(page).to have_content("Good job! Looks like you don't have any todos left.")
expect(page).to have_selector('.todos-all-done', count: 1)
end
end
end
......@@ -152,7 +152,7 @@ describe 'Dashboard Todos', feature: true do
within('.todos-pending-count') { expect(page).to have_content '0' }
expect(page).to have_content 'To do 0'
expect(page).to have_content 'Done 0'
expect(page).to have_content "Good job! Looks like you don't have any todos left."
expect(page).to have_selector('.todos-all-done', count: 1)
end
end
end
......
......@@ -46,4 +46,28 @@ describe Gitlab::Diff::File, lib: true do
expect(diff_file.collapsed?).to eq(false)
end
end
describe '#old_content_commit' do
it 'returns base commit' do
old_content_commit = diff_file.old_content_commit
expect(old_content_commit.id).to eq('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9')
end
end
describe '#old_blob' do
it 'returns blob of commit of base commit' do
old_data = diff_file.old_blob.data
expect(old_data).to include('raise "System commands must be given as an array of strings"')
end
end
describe '#blob' do
it 'returns blob of new commit' do
data = diff_file.blob.data
expect(data).to include('raise RuntimeError, "System commands must be given as an array of strings"')
end
end
end
......@@ -137,11 +137,12 @@ describe Gitlab::OAuth::User, lib: true do
allow(ldap_user).to receive(:username) { uid }
allow(ldap_user).to receive(:email) { ['johndoe@example.com', 'john2@example.com'] }
allow(ldap_user).to receive(:dn) { 'uid=user1,ou=People,dc=example' }
allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user)
end
context "and no account for the LDAP user" do
it "creates a user with dual LDAP and omniauth identities" do
allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user)
oauth_user.save
expect(gl_user).to be_valid
......@@ -159,6 +160,8 @@ describe Gitlab::OAuth::User, lib: true do
context "and LDAP user has an account already" do
let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=user1,ou=People,dc=example', provider: 'ldapmain', username: 'john') }
it "adds the omniauth identity to the LDAP account" do
allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user)
oauth_user.save
expect(gl_user).to be_valid
......@@ -172,6 +175,24 @@ describe Gitlab::OAuth::User, lib: true do
])
end
end
context 'when an LDAP person is not found by uid' do
it 'tries to find an LDAP person by DN and adds the omniauth identity to the user' do
allow(Gitlab::LDAP::Person).to receive(:find_by_uid).and_return(nil)
allow(Gitlab::LDAP::Person).to receive(:find_by_dn).and_return(ldap_user)
oauth_user.save
identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } }
expect(identities_as_hash)
.to match_array(
[
{ provider: 'ldapmain', extern_uid: 'uid=user1,ou=People,dc=example' },
{ provider: 'twitter', extern_uid: uid }
]
)
end
end
end
context "and no corresponding LDAP person" do
......
......@@ -299,4 +299,20 @@ describe API::API, api: true do
expect(json_response['message']).to eq('Cannot remove HEAD branch')
end
end
describe "DELETE /projects/:id/repository/merged_branches" do
before do
allow_any_instance_of(Repository).to receive(:rm_branch).and_return(true)
end
it 'returns 200' do
delete api("/projects/#{project.id}/repository/merged_branches", user)
expect(response).to have_http_status(200)
end
it 'returns a 403 error if guest' do
delete api("/projects/#{project.id}/repository/merged_branches", user2)
expect(response).to have_http_status(403)
end
end
end
......@@ -167,7 +167,7 @@ describe API::API, api: true do
end
it 'returns 404 for a non existing group' do
put api('/groups/1328', user1)
put api('/groups/1328', user1), name: new_group_name
expect(response).to have_http_status(404)
end
......
......@@ -494,12 +494,6 @@ describe API::API, api: true do
expect(json_response['milestone']['id']).to eq(milestone.id)
end
it "returns 400 when source_branch is specified" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user),
source_branch: "master", target_branch: "master"
expect(response).to have_http_status(400)
end
it "returns merge_request with renamed target_branch" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), target_branch: "wiki"
expect(response).to have_http_status(200)
......
......@@ -18,6 +18,7 @@ describe API::API, api: true do
it "returns project commits" do
get api("/projects/#{project.id}/repository/tree", user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
......@@ -43,6 +44,40 @@ describe API::API, api: true do
end
end
describe 'GET /projects/:id/repository/tree?recursive=1' do
context 'authorized user' do
before { project.team << [user2, :reporter] }
it 'should return recursive project paths tree' do
get api("/projects/#{project.id}/repository/tree?recursive=1", user)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response[4]['name']).to eq('html')
expect(json_response[4]['path']).to eq('files/html')
expect(json_response[4]['type']).to eq('tree')
expect(json_response[4]['mode']).to eq('040000')
end
it 'returns a 404 for unknown ref' do
get api("/projects/#{project.id}/repository/tree?ref_name=foo&recursive=1", user)
expect(response).to have_http_status(404)
expect(json_response).to be_an Object
json_response['message'] == '404 Tree Not Found'
end
end
context "unauthorized user" do
it "does not return project commits" do
get api("/projects/#{project.id}/repository/tree?recursive=1")
expect(response).to have_http_status(401)
end
end
end
describe "GET /projects/:id/repository/blobs/:sha" do
it "gets the raw file contents" do
get api("/projects/#{project.id}/repository/blobs/master?filepath=README.md", user)
......
......@@ -54,6 +54,13 @@ describe API::API do
expect(pipeline.builds.size).to eq(5)
end
it 'creates builds on webhook from other gitlab repository and branch' do
expect do
post api("/projects/#{project.id}/ref/master/trigger/builds?token=#{trigger_token}"), { ref: 'refs/heads/other-branch' }
end.to change(project.builds, :count).by(5)
expect(response).to have_http_status(201)
end
it 'returns bad request with no builds created if there\'s no commit for that ref' do
post api("/projects/#{project.id}/trigger/builds"), options.merge(ref: 'other-branch')
expect(response).to have_http_status(400)
......
require 'spec_helper'
describe DeleteMergedBranchesService, services: true do
subject(:service) { described_class.new(project, project.owner) }
let(:project) { create(:project) }
context '#execute' do
context 'unprotected branches' do
before do
service.execute
end
it 'deletes a branch that was merged' do
expect(project.repository.branch_names).not_to include('improve/awesome')
end
it 'keeps branch that is unmerged' do
expect(project.repository.branch_names).to include('feature')
end
it 'keeps "master"' do
expect(project.repository.branch_names).to include('master')
end
end
context 'protected branches' do
before do
create(:protected_branch, name: 'improve/awesome', project: project)
service.execute
end
it 'keeps protected branch' do
expect(project.repository.branch_names).to include('improve/awesome')
end
end
context 'user without rights' do
let(:user) { create(:user) }
it 'cannot execute' do
expect { described_class.new(project, user).execute }.to raise_error(Gitlab::Access::AccessDeniedError)
end
end
end
context '#async_execute' do
it 'calls DeleteMergedBranchesWorker async' do
expect(DeleteMergedBranchesWorker).to receive(:perform_async)
service.async_execute
end
end
end
......@@ -35,6 +35,7 @@ module TestEnv
'conflict-missing-side' => 'eb227b3',
'conflict-non-utf8' => 'd0a293c',
'conflict-too-large' => '39fa04f',
'deleted-image-test' => '6c17798'
}
# 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