Commit df3db8e8 authored by Regis's avatar Regis

Merge branch 'master' into keep_mini_graph_down_pipelines_index

parents f90685b8 ff75bd04
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign, no-new */
/* global Vue */ /* global Vue */
/* global EnvironmentsService */ /* global EnvironmentsService */
/* global Flash */
//= require vue //= require vue
//= require vue-resource //= require vue-resource
...@@ -10,41 +11,6 @@ ...@@ -10,41 +11,6 @@
(() => { (() => {
window.gl = window.gl || {}; window.gl = window.gl || {};
/**
* Given the visibility prop provided by the url query parameter and which
* changes according to the active tab we need to filter which environments
* should be visible.
*
* The environments array is a recursive tree structure and we need to filter
* both root level environments and children environments.
*
* In order to acomplish that, both `filterState` and `filterEnvironmentsByState`
* functions work together.
* The first one works as the filter that verifies if the given environment matches
* the given state.
* The second guarantees both root level and children elements are filtered as well.
*/
const filterState = state => environment => environment.state === state && environment;
/**
* Given the filter function and the array of environments will return only
* the environments that match the state provided to the filter function.
*
* @param {Function} fn
* @param {Array} array
* @return {Array}
*/
const filterEnvironmentsByState = (fn, arr) => arr.map((item) => {
if (item.children) {
const filteredChildren = filterEnvironmentsByState(fn, item.children).filter(Boolean);
if (filteredChildren.length) {
item.children = filteredChildren;
return item;
}
}
return fn(item);
}).filter(Boolean);
gl.environmentsList.EnvironmentsComponent = Vue.component('environment-component', { gl.environmentsList.EnvironmentsComponent = Vue.component('environment-component', {
props: { props: {
store: { store: {
...@@ -81,10 +47,6 @@ ...@@ -81,10 +47,6 @@
}, },
computed: { computed: {
filteredEnvironments() {
return filterEnvironmentsByState(filterState(this.visibility), this.state.environments);
},
scope() { scope() {
return this.$options.getQueryParameter('scope'); return this.$options.getQueryParameter('scope');
}, },
...@@ -111,7 +73,7 @@ ...@@ -111,7 +73,7 @@
const scope = this.$options.getQueryParameter('scope'); const scope = this.$options.getQueryParameter('scope');
if (scope) { if (scope) {
this.visibility = scope; this.store.storeVisibility(scope);
} }
this.isLoading = true; this.isLoading = true;
...@@ -121,6 +83,10 @@ ...@@ -121,6 +83,10 @@
.then((json) => { .then((json) => {
this.store.storeEnvironments(json); this.store.storeEnvironments(json);
this.isLoading = false; this.isLoading = false;
})
.catch(() => {
this.isLoading = false;
new Flash('An error occurred while fetching the environments.', 'alert');
}); });
}, },
...@@ -188,7 +154,7 @@ ...@@ -188,7 +154,7 @@
<div class="blank-state blank-state-no-icon" <div class="blank-state blank-state-no-icon"
v-if="!isLoading && state.environments.length === 0"> v-if="!isLoading && state.environments.length === 0">
<h2 class="blank-state-title"> <h2 class="blank-state-title js-blank-state-title">
You don't have any environments right now. You don't have any environments right now.
</h2> </h2>
<p class="blank-state-text"> <p class="blank-state-text">
...@@ -202,13 +168,13 @@ ...@@ -202,13 +168,13 @@
<a <a
v-if="canCreateEnvironmentParsed" v-if="canCreateEnvironmentParsed"
:href="newEnvironmentPath" :href="newEnvironmentPath"
class="btn btn-create"> class="btn btn-create js-new-environment-button">
New Environment New Environment
</a> </a>
</div> </div>
<div class="table-holder" <div class="table-holder"
v-if="!isLoading && state.environments.length > 0"> v-if="!isLoading && state.filteredEnvironments.length > 0">
<table class="table ci-table environments"> <table class="table ci-table environments">
<thead> <thead>
<tr> <tr>
...@@ -221,7 +187,7 @@ ...@@ -221,7 +187,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<template v-for="model in filteredEnvironments" <template v-for="model in state.filteredEnvironments"
v-bind:model="model"> v-bind:model="model">
<tr <tr
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
this.state.environments = []; this.state.environments = [];
this.state.stoppedCounter = 0; this.state.stoppedCounter = 0;
this.state.availableCounter = 0; this.state.availableCounter = 0;
this.state.visibility = 'available';
this.state.filteredEnvironments = [];
return this; return this;
}, },
...@@ -59,7 +61,7 @@ ...@@ -59,7 +61,7 @@
if (occurs.length) { if (occurs.length) {
acc[acc.indexOf(occurs[0])].children.push(environment); acc[acc.indexOf(occurs[0])].children.push(environment);
acc[acc.indexOf(occurs[0])].children.sort(this.sortByName); acc[acc.indexOf(occurs[0])].children.slice().sort(this.sortByName);
} else { } else {
acc.push({ acc.push({
name: environment.environment_type, name: environment.environment_type,
...@@ -73,13 +75,70 @@ ...@@ -73,13 +75,70 @@
} }
return acc; return acc;
}, []).sort(this.sortByName); }, []).slice().sort(this.sortByName);
this.state.environments = environmentsTree; this.state.environments = environmentsTree;
this.filterEnvironmentsByVisibility(this.state.environments);
return environmentsTree; return environmentsTree;
}, },
storeVisibility(visibility) {
this.state.visibility = visibility;
},
/**
* Given the visibility prop provided by the url query parameter and which
* changes according to the active tab we need to filter which environments
* should be visible.
*
* The environments array is a recursive tree structure and we need to filter
* both root level environments and children environments.
*
* In order to acomplish that, both `filterState` and `filterEnvironmentsByVisibility`
* functions work together.
* The first one works as the filter that verifies if the given environment matches
* the given state.
* The second guarantees both root level and children elements are filtered as well.
*
* Given array of environments will return only
* the environments that match the state stored.
*
* @param {Array} array
* @return {Array}
*/
filterEnvironmentsByVisibility(arr) {
const filteredEnvironments = arr.map((item) => {
if (item.children) {
const filteredChildren = this.filterEnvironmentsByVisibility(
item.children,
).filter(Boolean);
if (filteredChildren.length) {
item.children = filteredChildren;
return item;
}
}
return this.filterState(this.state.visibility, item);
}).filter(Boolean);
this.state.filteredEnvironments = filteredEnvironments;
return filteredEnvironments;
},
/**
* Given the state and the environment,
* returns only if the environment state matches the one provided.
*
* @param {String} state
* @param {Object} environment
* @return {Object}
*/
filterState(state, environment) {
return environment.state === state && environment;
},
/** /**
* Toggles folder open property given the environment type. * Toggles folder open property given the environment type.
* *
......
...@@ -10,9 +10,9 @@ ...@@ -10,9 +10,9 @@
* The container should be the table element. * The container should be the table element.
* *
* The stage icon clicked needs to have the following HTML structure: * The stage icon clicked needs to have the following HTML structure:
* <div> * <div class="dropdown">
* <button class="dropdown js-builds-dropdown-button"></button> * <button class="dropdown js-builds-dropdown-button" data-toggle="dropdown"></button>
* <div class="js-builds-dropdown-container"></div> * <div class="js-builds-dropdown-container dropdown-menu"></div>
* </div> * </div>
*/ */
(() => { (() => {
...@@ -26,13 +26,11 @@ ...@@ -26,13 +26,11 @@
} }
/** /**
* Adds and removes the event listener. * Adds the event listener when the dropdown is opened.
* All dropdown events are fired at the .dropdown-menu's parent element.
*/ */
bindEvents() { bindEvents() {
const dropdownButtonSelector = 'button.js-builds-dropdown-button'; $(this.container).on('shown.bs.dropdown', this.getBuildsList);
$(this.container).off('click', dropdownButtonSelector, this.getBuildsList)
.on('click', dropdownButtonSelector, this.getBuildsList);
} }
/** /**
...@@ -52,11 +50,14 @@ ...@@ -52,11 +50,14 @@
/** /**
* For the clicked stage, gets the list of builds. * For the clicked stage, gets the list of builds.
* *
* @param {Object} e * All dropdown events have a relatedTarget property,
* whose value is the toggling anchor element.
*
* @param {Object} e bootstrap dropdown event
* @return {Promise} * @return {Promise}
*/ */
getBuildsList(e) { getBuildsList(e) {
const button = e.currentTarget; const button = e.relatedTarget;
const endpoint = button.dataset.stageEndpoint; const endpoint = button.dataset.stageEndpoint;
return $.ajax({ return $.ajax({
......
...@@ -57,7 +57,7 @@ ...@@ -57,7 +57,7 @@
return function(response) { return function(response) {
var error; var error;
if (response.errorCode) { if (response.errorCode) {
error = new U2FError(response.errorCode); error = new U2FError(response.errorCode, 'authenticate');
return _this.renderError(error); return _this.renderError(error);
} else { } else {
return _this.renderAuthenticated(JSON.stringify(response)); return _this.renderAuthenticated(JSON.stringify(response));
......
...@@ -5,21 +5,21 @@ ...@@ -5,21 +5,21 @@
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; }; var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
this.U2FError = (function() { this.U2FError = (function() {
function U2FError(errorCode) { function U2FError(errorCode, u2fFlowType) {
this.errorCode = errorCode; this.errorCode = errorCode;
this.message = bind(this.message, this); this.message = bind(this.message, this);
this.httpsDisabled = window.location.protocol !== 'https:'; this.httpsDisabled = window.location.protocol !== 'https:';
this.u2fFlowType = u2fFlowType;
} }
U2FError.prototype.message = function() { U2FError.prototype.message = function() {
switch (false) { if (this.errorCode === u2f.ErrorCodes.BAD_REQUEST && this.httpsDisabled) {
case !(this.errorCode === u2f.ErrorCodes.BAD_REQUEST && this.httpsDisabled): return 'U2F only works with HTTPS-enabled websites. Contact your administrator for more details.';
return "U2F only works with HTTPS-enabled websites. Contact your administrator for more details."; } else if (this.errorCode === u2f.ErrorCodes.DEVICE_INELIGIBLE) {
case this.errorCode !== u2f.ErrorCodes.DEVICE_INELIGIBLE: if (this.u2fFlowType === 'authenticate') return 'This device has not been registered with us.';
return "This device has already been registered with us."; if (this.u2fFlowType === 'register') return 'This device has already been registered with us.';
default:
return "There was a problem communicating with your device.";
} }
return "There was a problem communicating with your device.";
}; };
return U2FError; return U2FError;
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
return function(response) { return function(response) {
var error; var error;
if (response.errorCode) { if (response.errorCode) {
error = new U2FError(response.errorCode); error = new U2FError(response.errorCode, 'register');
return _this.renderError(error); return _this.renderError(error);
} else { } else {
return _this.renderRegistered(JSON.stringify(response)); return _this.renderRegistered(JSON.stringify(response));
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
max-width: 100%; max-width: 100%;
} }
*:first-child { *:first-child:not(.katex-display) {
margin-top: 0; margin-top: 0;
} }
......
...@@ -377,6 +377,10 @@ ...@@ -377,6 +377,10 @@
display: inline-block; display: inline-block;
padding: 5px; padding: 5px;
&:nth-of-type(7n) {
padding-right: 0;
}
.author_link { .author_link {
display: block; display: block;
} }
......
...@@ -6,21 +6,15 @@ class Projects::HooksController < Projects::ApplicationController ...@@ -6,21 +6,15 @@ class Projects::HooksController < Projects::ApplicationController
layout "project_settings" layout "project_settings"
def index
@hooks = @project.hooks
@hook = ProjectHook.new
end
def create def create
@hook = @project.hooks.new(hook_params) @hook = @project.hooks.new(hook_params)
@hook.save @hook.save
if @hook.valid? unless @hook.valid?
redirect_to namespace_project_hooks_path(@project.namespace, @project)
else
@hooks = @project.hooks.select(&:persisted?) @hooks = @project.hooks.select(&:persisted?)
render :index flash[:alert] = @hook.errors.full_messages.join.html_safe
end end
redirect_to namespace_project_settings_integrations_path(@project.namespace, @project)
end end
def test def test
...@@ -44,7 +38,7 @@ class Projects::HooksController < Projects::ApplicationController ...@@ -44,7 +38,7 @@ class Projects::HooksController < Projects::ApplicationController
def destroy def destroy
hook.destroy hook.destroy
redirect_to namespace_project_hooks_path(@project.namespace, @project) redirect_to namespace_project_settings_integrations_path(@project.namespace, @project)
end end
private private
......
...@@ -9,10 +9,6 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -9,10 +9,6 @@ class Projects::ServicesController < Projects::ApplicationController
layout "project_settings" layout "project_settings"
def index
@services = @project.find_or_initialize_services
end
def edit def edit
end end
......
module Projects
module Settings
class IntegrationsController < Projects::ApplicationController
include ServiceParams
before_action :authorize_admin_project!
layout "project_settings"
def show
@hooks = @project.hooks
@hook = ProjectHook.new
# Services
@services = @project.find_or_initialize_services
end
end
end
end
...@@ -208,6 +208,10 @@ module GitlabRoutingHelper ...@@ -208,6 +208,10 @@ module GitlabRoutingHelper
end end
# Settings # Settings
def project_settings_integrations_path(project, *args)
namespace_project_settings_integrations_path(project.namespace, project, *args)
end
def project_settings_members_path(project, *args) def project_settings_members_path(project, *args)
namespace_project_settings_members_path(project.namespace, project, *args) namespace_project_settings_members_path(project.namespace, project, *args)
end end
......
...@@ -13,6 +13,49 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -13,6 +13,49 @@ class ApplicationSetting < ActiveRecord::Base
[\r\n] # any number of newline characters [\r\n] # any number of newline characters
}x }x
DEFAULTS_CE = {
after_sign_up_text: nil,
akismet_enabled: false,
container_registry_token_expire_delay: 5,
default_branch_protection: Settings.gitlab['default_branch_protection'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_projects_limit: Settings.gitlab['default_projects_limit'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
disabled_oauth_sign_in_sources: [],
domain_whitelist: Settings.gitlab['domain_whitelist'],
gravatar_enabled: Settings.gravatar['enabled'],
help_page_text: nil,
housekeeping_bitmaps_enabled: true,
housekeeping_enabled: true,
housekeeping_full_repack_period: 50,
housekeeping_gc_period: 200,
housekeeping_incremental_repack_period: 10,
import_sources: Gitlab::ImportSources.values,
koding_enabled: false,
koding_url: nil,
max_artifacts_size: Settings.artifacts['max_size'],
max_attachment_size: Settings.gitlab['max_attachment_size'],
plantuml_enabled: false,
plantuml_url: nil,
recaptcha_enabled: false,
repository_checks_enabled: true,
repository_storages: ['default'],
require_two_factor_authentication: false,
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
session_expire_delay: Settings.gitlab['session_expire_delay'],
send_user_confirmation_email: false,
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
shared_runners_text: nil,
sidekiq_throttling_enabled: false,
sign_in_text: nil,
signin_enabled: Settings.gitlab['signin_enabled'],
signup_enabled: Settings.gitlab['signup_enabled'],
two_factor_grace_period: 48,
user_default_external: false
}
DEFAULTS = DEFAULTS_CE
serialize :restricted_visibility_levels serialize :restricted_visibility_levels
serialize :import_sources serialize :import_sources
serialize :disabled_oauth_sign_in_sources, Array serialize :disabled_oauth_sign_in_sources, Array
...@@ -163,46 +206,7 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -163,46 +206,7 @@ class ApplicationSetting < ActiveRecord::Base
end end
def self.create_from_defaults def self.create_from_defaults
create( create(DEFAULTS)
default_projects_limit: Settings.gitlab['default_projects_limit'],
default_branch_protection: Settings.gitlab['default_branch_protection'],
signup_enabled: Settings.gitlab['signup_enabled'],
signin_enabled: Settings.gitlab['signin_enabled'],
gravatar_enabled: Settings.gravatar['enabled'],
sign_in_text: nil,
after_sign_up_text: nil,
help_page_text: nil,
shared_runners_text: nil,
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
max_attachment_size: Settings.gitlab['max_attachment_size'],
session_expire_delay: Settings.gitlab['session_expire_delay'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
domain_whitelist: Settings.gitlab['domain_whitelist'],
import_sources: Gitlab::ImportSources.values,
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
max_artifacts_size: Settings.artifacts['max_size'],
require_two_factor_authentication: false,
two_factor_grace_period: 48,
recaptcha_enabled: false,
akismet_enabled: false,
koding_enabled: false,
koding_url: nil,
plantuml_enabled: false,
plantuml_url: nil,
repository_checks_enabled: true,
disabled_oauth_sign_in_sources: [],
send_user_confirmation_email: false,
container_registry_token_expire_delay: 5,
repository_storages: ['default'],
user_default_external: false,
sidekiq_throttling_enabled: false,
housekeeping_enabled: true,
housekeeping_bitmaps_enabled: true,
housekeeping_incremental_repack_period: 10,
housekeeping_full_repack_period: 50,
housekeeping_gc_period: 200,
)
end end
def home_page_url_column_exist def home_page_url_column_exist
......
...@@ -92,6 +92,12 @@ module Ci ...@@ -92,6 +92,12 @@ module Ci
end end
state_machine :status do state_machine :status do
after_transition any => [:pending] do |build|
build.run_after_commit do
BuildQueueWorker.perform_async(id)
end
end
after_transition pending: :running do |build| after_transition pending: :running do |build|
build.run_after_commit do build.run_after_commit do
BuildHooksWorker.perform_async(id) BuildHooksWorker.perform_async(id)
......
...@@ -2,6 +2,7 @@ module Ci ...@@ -2,6 +2,7 @@ module Ci
class Runner < ActiveRecord::Base class Runner < ActiveRecord::Base
extend Ci::Model extend Ci::Model
RUNNER_QUEUE_EXPIRY_TIME = 60.minutes
LAST_CONTACT_TIME = 1.hour.ago LAST_CONTACT_TIME = 1.hour.ago
AVAILABLE_SCOPES = %w[specific shared active paused online] AVAILABLE_SCOPES = %w[specific shared active paused online]
FORM_EDITABLE = %i[description tag_list active run_untagged locked] FORM_EDITABLE = %i[description tag_list active run_untagged locked]
...@@ -21,6 +22,8 @@ module Ci ...@@ -21,6 +22,8 @@ module Ci
scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) } scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) }
scope :ordered, ->() { order(id: :desc) } scope :ordered, ->() { order(id: :desc) }
after_save :tick_runner_queue, if: :form_editable_changed?
scope :owned_or_shared, ->(project_id) do scope :owned_or_shared, ->(project_id) do
joins('LEFT JOIN ci_runner_projects ON ci_runner_projects.runner_id = ci_runners.id') joins('LEFT JOIN ci_runner_projects ON ci_runner_projects.runner_id = ci_runners.id')
.where("ci_runner_projects.gl_project_id = :project_id OR ci_runners.is_shared = true", project_id: project_id) .where("ci_runner_projects.gl_project_id = :project_id OR ci_runners.is_shared = true", project_id: project_id)
...@@ -122,8 +125,38 @@ module Ci ...@@ -122,8 +125,38 @@ module Ci
] ]
end end
def tick_runner_queue
SecureRandom.hex.tap do |new_update|
Gitlab::Redis.with do |redis|
redis.set(runner_queue_key, new_update, ex: RUNNER_QUEUE_EXPIRY_TIME)
end
end
end
def ensure_runner_queue_value
Gitlab::Redis.with do |redis|
value = SecureRandom.hex
redis.set(runner_queue_key, value, ex: RUNNER_QUEUE_EXPIRY_TIME, nx: true)
redis.get(runner_queue_key)
end
end
def is_runner_queue_value_latest?(value)
ensure_runner_queue_value == value if value.present?
end
private private
def runner_queue_key
"runner:build_queue:#{self.token}"
end
def form_editable_changed?
FORM_EDITABLE.any? do |editable|
public_send("#{editable}_changed?")
end
end
def tag_constraints def tag_constraints
unless has_tags? || run_untagged? unless has_tags? || run_untagged?
errors.add(:tags_list, errors.add(:tags_list,
......
...@@ -4,6 +4,8 @@ class Key < ActiveRecord::Base ...@@ -4,6 +4,8 @@ class Key < ActiveRecord::Base
include AfterCommitQueue include AfterCommitQueue
include Sortable include Sortable
LAST_USED_AT_REFRESH_TIME = 1.day.to_i
belongs_to :user belongs_to :user
before_validation :generate_fingerprint before_validation :generate_fingerprint
...@@ -50,7 +52,10 @@ class Key < ActiveRecord::Base ...@@ -50,7 +52,10 @@ class Key < ActiveRecord::Base
end end
def update_last_used_at def update_last_used_at
UseKeyWorker.perform_async(self.id) lease = Gitlab::ExclusiveLease.new("key_update_last_used_at:#{id}", timeout: LAST_USED_AT_REFRESH_TIME)
return unless lease.try_obtain
UseKeyWorker.perform_async(id)
end end
def add_to_shell def add_to_shell
......
module Ci
class UpdateBuildQueueService
def execute(build)
build.project.runners.each do |runner|
if runner.can_pick?(build)
runner.tick_runner_queue
end
end
return unless build.project.shared_runners_enabled?
Ci::Runner.shared.each do |runner|
if runner.can_pick?(build)
runner.tick_runner_queue
end
end
end
end
end
...@@ -8,14 +8,10 @@ ...@@ -8,14 +8,10 @@
= link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys' do = link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys' do
%span %span
Deploy Keys Deploy Keys
= nav_link(controller: :hooks) do = nav_link(controller: :integrations) do
= link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Webhooks' do = link_to namespace_project_settings_integrations_path(@project.namespace, @project), title: 'Integrations' do
%span %span
Webhooks Integrations
= nav_link(controller: :services) do
= link_to namespace_project_services_path(@project.namespace, @project), title: 'Services' do
%span
Services
= nav_link(controller: :protected_branches) do = nav_link(controller: :protected_branches) do
= link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches' do = link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches' do
%span %span
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
%a.close{ href: "#", "data-dismiss" => "modal" } × %a.close{ href: "#", "data-dismiss" => "modal" } ×
%h3.page-title== #{label} this #{commit.change_type_title(current_user)} %h3.page-title== #{label} this #{commit.change_type_title(current_user)}
.modal-body .modal-body
= form_tag [type.underscore, @project.namespace, @project, commit], method: :post, remote: false, class: "form-horizontal js-#{type}-form js-requires-input" do = form_tag [type.underscore, @project.namespace.becomes(Namespace), @project, commit], method: :post, remote: false, class: "form-horizontal js-#{type}-form js-requires-input" do
.form-group.branch .form-group.branch
= label_tag 'target_branch', target_label, class: 'control-label' = label_tag 'target_branch', target_label, class: 'control-label'
.col-sm-10 .col-sm-10
......
- page_title "Services"
.row.prepend-top-default.append-bottom-default .row.prepend-top-default.append-bottom-default
.col-lg-3 .col-lg-3
%h4.prepend-top-0 %h4.prepend-top-0
......
- page_title 'Integrations'
= render 'projects/hooks/index'
= render 'projects/services/index'
- page_title "Webhooks"
- context_title = @project ? 'project' : 'group'
.row.prepend-top-default .row.prepend-top-default
.col-lg-3 .col-lg-3
%h4.prepend-top-0 %h4.prepend-top-0
......
class BuildQueueWorker
include Sidekiq::Worker
include BuildQueue
def perform(build_id)
Ci::Build.find_by(id: build_id).try do |build|
Ci::UpdateBuildQueueService.new.execute(build)
end
end
end
---
title: Reduce DB-load for build-queues by storing last_update in Redis
merge_request: 8084
author:
---
title: Handle HTTP errors in environment list
merge_request:
author:
---
title: Add margin to markdown math blocks
merge_request:
author:
---
title: Fixes builds dropdown making request when clicked to be closed
merge_request: 8545
author:
---
title: Fix import/export wrong user mapping
merge_request:
author:
---
title: Fix participants margins to fit on one line
merge_request:
author:
---
title: Record used SSH keys only once per day
merge_request: 8655
author:
...@@ -307,9 +307,9 @@ constraints(ProjectUrlConstrainer.new) do ...@@ -307,9 +307,9 @@ constraints(ProjectUrlConstrainer.new) do
end end
end end
end end
namespace :settings do namespace :settings do
resource :members, only: [:show] resource :members, only: [:show]
resource :integrations, only: [:show]
end end
# Since both wiki and repository routing contains wildcard characters # Since both wiki and repository routing contains wildcard characters
......
class AddEstimateToIssuablesCe < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
unless column_exists?(:issues, :time_estimate)
add_column :issues, :time_estimate, :integer
end
unless column_exists?(:merge_requests, :time_estimate)
add_column :merge_requests, :time_estimate, :integer
end
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddTimeEstimateToIssuables < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
# When a migration requires downtime you **must** uncomment the following
# constant and define a short and easy to understand explanation as to why the
# migration requires downtime.
# DOWNTIME_REASON = ''
# When using the methods "add_concurrent_index" or "add_column_with_default"
# you must disable the use of transactions as these methods can not run in an
# existing transaction. When using "add_concurrent_index" make sure that this
# method is the _only_ method called in the migration, any other changes
# should go in a separate migration. This ensures that upon failure _only_ the
# index creation fails and can be retried or reverted easily.
#
# To disable transactions uncomment the following line and remove these
# comments:
# disable_ddl_transaction!
def change
add_column :issues, :time_estimate, :integer
add_column :merge_requests, :time_estimate, :integer
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class CreateTimelogs < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
# When a migration requires downtime you **must** uncomment the following
# constant and define a short and easy to understand explanation as to why the
# migration requires downtime.
# DOWNTIME_REASON = ''
# When using the methods "add_concurrent_index" or "add_column_with_default"
# you must disable the use of transactions as these methods can not run in an
# existing transaction. When using "add_concurrent_index" make sure that this
# method is the _only_ method called in the migration, any other changes
# should go in a separate migration. This ensures that upon failure _only_ the
# index creation fails and can be retried or reverted easily.
#
# To disable transactions uncomment the following line and remove these
# comments:
# disable_ddl_transaction!
def change
create_table :timelogs do |t|
t.integer :time_spent, null: false
t.references :trackable, polymorphic: true
t.references :user
t.timestamps null: false
end
add_index :timelogs, [:trackable_type, :trackable_id]
add_index :timelogs, :user_id
end
end
class CreateTimelogsCe < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
unless table_exists?(:timelogs)
create_table :timelogs do |t|
t.integer :time_spent, null: false
t.references :trackable, polymorphic: true
t.references :user
t.timestamps null: false
end
add_index :timelogs, [:trackable_type, :trackable_id]
add_index :timelogs, :user_id
end
end
end
# GitLab Community Edition documentation # GitLab Community Edition documentation
## University
[University](university/README.md) contain guides to learn Git and GitLab through courses and videos.
## User documentation ## User documentation
- [Account Security](user/account/security.md) Securing your account via two-factor authentication, etc. - [Account Security](user/account/security.md) Securing your account via two-factor authentication, etc.
...@@ -19,7 +23,6 @@ ...@@ -19,7 +23,6 @@
- [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects. - [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
- [Webhooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project. - [Webhooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
- [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN. - [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
- [University](university/README.md) Learn Git and GitLab through videos and courses.
- [Git Attributes](user/project/git_attributes.md) Managing Git attributes using a `.gitattributes` file. - [Git Attributes](user/project/git_attributes.md) Managing Git attributes using a `.gitattributes` file.
- [Git cheatsheet](https://gitlab.com/gitlab-com/marketing/raw/master/design/print/git-cheatsheet/print-pdf/git-cheatsheet.pdf) Download a PDF describing the most used Git operations. - [Git cheatsheet](https://gitlab.com/gitlab-com/marketing/raw/master/design/print/git-cheatsheet/print-pdf/git-cheatsheet.pdf) Download a PDF describing the most used Git operations.
......
...@@ -104,6 +104,13 @@ that needs access to the GitLab API. ...@@ -104,6 +104,13 @@ that needs access to the GitLab API.
Once you have your token, pass it to the API using either the `private_token` Once you have your token, pass it to the API using either the `private_token`
parameter or the `PRIVATE-TOKEN` header. parameter or the `PRIVATE-TOKEN` header.
> [Introduced][ce-5951] in GitLab 8.15.
Personal Access Tokens can be created with one or more scopes that allow various actions
that a given token can perform. Although there are only two scopes available at the
moment – `read_user` and `api` – the groundwork has been laid to add more scopes easily.
At any time you can revoke any personal access token by just clicking **Revoke**.
### Session Cookie ### Session Cookie
...@@ -380,3 +387,4 @@ programming languages. Visit the [GitLab website] for a complete list. ...@@ -380,3 +387,4 @@ programming languages. Visit the [GitLab website] for a complete list.
[GitLab website]: https://about.gitlab.com/applications/#api-clients "Clients using the GitLab API" [GitLab website]: https://about.gitlab.com/applications/#api-clients "Clients using the GitLab API"
[lib-api-url]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/api/api.rb [lib-api-url]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/api/api.rb
[ce-3749]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3749 [ce-3749]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3749
[ce-5951]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5951
...@@ -61,7 +61,18 @@ correctly with your CI builds: ...@@ -61,7 +61,18 @@ correctly with your CI builds:
1. First, make sure you have used [relative URLs](#configuring-the-gitmodules-file) 1. First, make sure you have used [relative URLs](#configuring-the-gitmodules-file)
for the submodules located in the same GitLab server. for the submodules located in the same GitLab server.
1. Then, use `git submodule sync/update` in `before_script`: 1. Next, if you are using `gitlab-ci-multi-runner` v1.10+, you can set the
`GIT_SUBMODULE_STRATEGY` variable to either `normal` or `recursive` to tell
the runner to fetch your submodules before the build:
```yaml
variables:
GIT_SUBMODULE_STRATEGY: recursive
```
See the [`.gitlab-ci.yml` reference](yaml/README.md#git-submodule-strategy)
for more details about `GIT_SUBMODULE_STRATEGY`.
1. If you are using an older version of `gitlab-ci-multi-runner`, then use
`git submodule sync/update` in `before_script`:
```yaml ```yaml
before_script: before_script:
......
...@@ -1034,6 +1034,41 @@ variables: ...@@ -1034,6 +1034,41 @@ variables:
GIT_STRATEGY: none GIT_STRATEGY: none
``` ```
## Git Submodule Strategy
> Requires GitLab Runner v1.10+.
The `GIT_SUBMODULE_STRATEGY` variable is used to control if / how Git
submodules are included when fetching the code before a build. Like
`GIT_STRATEGY`, it can be set in either the global [`variables`](#variables)
section or the [`variables`](#job-variables) section for individual jobs.
There are three posible values: `none`, `normal`, and `recursive`:
- `none` means that submodules will not be included when fetching the project
code. This is the default, which matches the pre-v1.10 behavior.
- `normal` means that only the top-level submodules will be included. It is
equivalent to:
```
$ git submodule sync
$ git submodule update --init
```
- `recursive` means that all submodules (including submodules of submodules)
will be included. It is equivalent to:
```
$ git submodule sync --recursive
$ git submodule update --init --recursive
```
Note that for this feature to work correctly, the submodules must be configured
(in `.gitmodules`) with either:
- the HTTP(S) URL of a publicly-accessible repository, or
- a relative path to another repository on the same GitLab server. See the
[Git submodules](../git_submodules.md) documentation.
## Build stages attempts ## Build stages attempts
> Introduced in GitLab, it requires GitLab Runner v1.9+. > Introduced in GitLab, it requires GitLab Runner v1.9+.
......
...@@ -74,8 +74,10 @@ in the **Authorized applications** section under **Profile Settings > Applicatio ...@@ -74,8 +74,10 @@ in the **Authorized applications** section under **Profile Settings > Applicatio
--- ---
As you can see, the default scope `api` is used, which is the only scope that GitLab's OAuth applications support scopes, which allow various actions that any given
GitLab supports so far. At any time you can revoke any access by just clicking application can perform. Although there are only two scopes available at the
**Revoke**. moment – `read_user` and `api` – the groundwork has been laid to add more scopes easily.
At any time you can revoke any access by just clicking **Revoke**.
[oauth]: http://oauth.net/2/ "OAuth website" [oauth]: http://oauth.net/2/ "OAuth website"
...@@ -52,7 +52,7 @@ for anonymous users. The group page now has a visibility level icon. ...@@ -52,7 +52,7 @@ for anonymous users. The group page now has a visibility level icon.
## Visibility of users ## Visibility of users
The public page of a user, located at `/u/username`, is always visible whether The public page of a user, located at `/username`, is always visible whether
you are logged in or not. you are logged in or not.
When visiting the public page of a user, you can only see the projects which When visiting the public page of a user, you can only see the projects which
...@@ -60,10 +60,13 @@ you are privileged to. ...@@ -60,10 +60,13 @@ you are privileged to.
If the public level is restricted, user profiles are only visible to logged in users. If the public level is restricted, user profiles are only visible to logged in users.
## Restricting the use of public or internal projects ## Restricting the use of public or internal projects
In the Admin area under **Settings** (`/admin/application_settings`), you can In the Admin area under **Settings** (`/admin/application_settings`), you can
restrict the use of visibility levels for users when they create a project or a restrict the use of visibility levels for users when they create a project or a
snippet. This is useful to prevent people exposing their repositories to public snippet:
![Restrict visibility levels](img/restrict_visibility_levels.png)
This is useful to prevent people exposing their repositories to public
by accident. The restricted visibility settings do not apply to admin users. by accident. The restricted visibility settings do not apply to admin users.
...@@ -39,10 +39,10 @@ Feature: Project Active Tab ...@@ -39,10 +39,10 @@ Feature: Project Active Tab
# Sub Tabs: Settings # Sub Tabs: Settings
Scenario: On Project Settings/Hooks Scenario: On Project Settings/Integrations
Given I visit my project's settings page Given I visit my project's settings page
And I click the "Hooks" tab And I click the "Integrations" tab
Then the active sub nav should be Hooks Then the active sub nav should be Integrations
And no other sub navs should be active And no other sub navs should be active
And the active main tab should be Settings And the active main tab should be Settings
......
...@@ -27,8 +27,8 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps ...@@ -27,8 +27,8 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
end end
end end
step 'I click the "Hooks" tab' do step 'I click the "Integrations" tab' do
click_link('Webhooks') click_link('Integrations')
end end
step 'I click the "Deploy Keys" tab' do step 'I click the "Deploy Keys" tab' do
...@@ -39,8 +39,8 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps ...@@ -39,8 +39,8 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
ensure_active_sub_nav('Members') ensure_active_sub_nav('Members')
end end
step 'the active sub nav should be Hooks' do step 'the active sub nav should be Integrations' do
ensure_active_sub_nav('Webhooks') ensure_active_sub_nav('Integrations')
end end
step 'the active sub nav should be Deploy Keys' do step 'the active sub nav should be Deploy Keys' do
......
...@@ -36,12 +36,12 @@ class Spinach::Features::ProjectHooks < Spinach::FeatureSteps ...@@ -36,12 +36,12 @@ class Spinach::Features::ProjectHooks < Spinach::FeatureSteps
end end
step 'I should see newly created hook' do step 'I should see newly created hook' do
expect(current_path).to eq namespace_project_hooks_path(current_project.namespace, current_project) expect(current_path).to eq namespace_project_settings_integrations_path(current_project.namespace, current_project)
expect(page).to have_content(@url) expect(page).to have_content(@url)
end end
step 'I should see newly created hook with SSL verification enabled' do step 'I should see newly created hook with SSL verification enabled' do
expect(current_path).to eq namespace_project_hooks_path(current_project.namespace, current_project) expect(current_path).to eq namespace_project_settings_integrations_path(current_project.namespace, current_project)
expect(page).to have_content(@url) expect(page).to have_content(@url)
expect(page).to have_content("SSL Verification: enabled") expect(page).to have_content("SSL Verification: enabled")
end end
...@@ -57,7 +57,7 @@ class Spinach::Features::ProjectHooks < Spinach::FeatureSteps ...@@ -57,7 +57,7 @@ class Spinach::Features::ProjectHooks < Spinach::FeatureSteps
end end
step 'hook should be triggered' do step 'hook should be triggered' do
expect(current_path).to eq namespace_project_hooks_path(current_project.namespace, current_project) expect(current_path).to eq namespace_project_settings_integrations_path(current_project.namespace, current_project)
expect(page).to have_selector '.flash-notice', expect(page).to have_selector '.flash-notice',
text: 'Hook executed successfully: HTTP 200' text: 'Hook executed successfully: HTTP 200'
end end
......
...@@ -4,7 +4,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps ...@@ -4,7 +4,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
include SharedPaths include SharedPaths
step 'I visit project "Shop" services page' do step 'I visit project "Shop" services page' do
visit namespace_project_services_path(@project.namespace, @project) visit namespace_project_settings_integrations_path(@project.namespace, @project)
end end
step 'I should see list of available services' do step 'I should see list of available services' do
......
...@@ -256,7 +256,7 @@ module SharedPaths ...@@ -256,7 +256,7 @@ module SharedPaths
end end
step 'I visit project hooks page' do step 'I visit project hooks page' do
visit namespace_project_hooks_path(@project.namespace, @project) visit namespace_project_settings_integrations_path(@project.namespace, @project)
end end
step 'I visit project deploy keys page' do step 'I visit project deploy keys page' do
......
...@@ -226,7 +226,7 @@ module API ...@@ -226,7 +226,7 @@ module API
end end
def render_api_error!(message, status) def render_api_error!(message, status)
error!({ 'message' => message }, status) error!({ 'message' => message }, status, header)
end end
def handle_api_exception(exception) def handle_api_exception(exception)
......
...@@ -16,6 +16,13 @@ module Ci ...@@ -16,6 +16,13 @@ module Ci
not_found! unless current_runner.active? not_found! unless current_runner.active?
update_runner_info update_runner_info
if current_runner.is_runner_queue_value_latest?(params[:last_update])
header 'X-GitLab-Last-Update', params[:last_update]
return build_not_found!
end
new_update = current_runner.ensure_runner_queue_value
build = Ci::RegisterBuildService.new.execute(current_runner) build = Ci::RegisterBuildService.new.execute(current_runner)
if build if build
...@@ -26,6 +33,8 @@ module Ci ...@@ -26,6 +33,8 @@ module Ci
else else
Gitlab::Metrics.add_event(:build_not_found) Gitlab::Metrics.add_event(:build_not_found)
header 'X-GitLab-Last-Update', new_update
build_not_found! build_not_found!
end end
end end
......
...@@ -9,7 +9,9 @@ module Gitlab ...@@ -9,7 +9,9 @@ module Gitlab
end end
def ensure_application_settings! def ensure_application_settings!
if connect_to_db? return fake_application_settings unless connect_to_db?
unless ENV['IN_MEMORY_APPLICATION_SETTINGS'] == 'true'
begin begin
settings = ::ApplicationSetting.current settings = ::ApplicationSetting.current
# In case Redis isn't running or the Redis UNIX socket file is not available # In case Redis isn't running or the Redis UNIX socket file is not available
...@@ -20,43 +22,23 @@ module Gitlab ...@@ -20,43 +22,23 @@ module Gitlab
settings ||= ::ApplicationSetting.create_from_defaults unless ActiveRecord::Migrator.needs_migration? settings ||= ::ApplicationSetting.create_from_defaults unless ActiveRecord::Migrator.needs_migration?
end end
settings || fake_application_settings settings || in_memory_application_settings
end end
def sidekiq_throttling_enabled? def sidekiq_throttling_enabled?
current_application_settings.sidekiq_throttling_enabled? current_application_settings.sidekiq_throttling_enabled?
end end
def in_memory_application_settings
@in_memory_application_settings ||= ApplicationSetting.new(ApplicationSetting::DEFAULTS)
# In case migrations the application_settings table is not created yet,
# we fallback to a simple OpenStruct
rescue ActiveRecord::StatementInvalid
fake_application_settings
end
def fake_application_settings def fake_application_settings
OpenStruct.new( OpenStruct.new(ApplicationSetting::DEFAULTS)
default_projects_limit: Settings.gitlab['default_projects_limit'],
default_branch_protection: Settings.gitlab['default_branch_protection'],
signup_enabled: Settings.gitlab['signup_enabled'],
signin_enabled: Settings.gitlab['signin_enabled'],
gravatar_enabled: Settings.gravatar['enabled'],
koding_enabled: false,
plantuml_enabled: false,
sign_in_text: nil,
after_sign_up_text: nil,
help_page_text: nil,
shared_runners_text: nil,
restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
max_attachment_size: Settings.gitlab['max_attachment_size'],
session_expire_delay: Settings.gitlab['session_expire_delay'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
domain_whitelist: Settings.gitlab['domain_whitelist'],
import_sources: %w[gitea github bitbucket gitlab google_code fogbugz git gitlab_project],
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
max_artifacts_size: Settings.artifacts['max_size'],
require_two_factor_authentication: false,
two_factor_grace_period: 48,
akismet_enabled: false,
repository_checks_enabled: true,
container_registry_token_expire_delay: 5,
user_default_external: false,
sidekiq_throttling_enabled: false,
)
end end
private private
......
module Gitlab module Gitlab
module GithubImport module GithubImport
class ProjectCreator class ProjectCreator
include Gitlab::CurrentSettings
attr_reader :repo, :name, :namespace, :current_user, :session_data, :type attr_reader :repo, :name, :namespace, :current_user, :session_data, :type
def initialize(repo, name, namespace, current_user, session_data, type: 'github') def initialize(repo, name, namespace, current_user, session_data, type: 'github')
...@@ -34,7 +36,7 @@ module Gitlab ...@@ -34,7 +36,7 @@ module Gitlab
end end
def visibility_level def visibility_level
repo.private ? Gitlab::VisibilityLevel::PRIVATE : ApplicationSetting.current.default_project_visibility repo.private ? Gitlab::VisibilityLevel::PRIVATE : current_application_settings.default_project_visibility
end end
# #
......
module Gitlab module Gitlab
module ImportExport module ImportExport
class MembersMapper class MembersMapper
attr_reader :missing_author_ids
def initialize(exported_members:, user:, project:) def initialize(exported_members:, user:, project:)
@exported_members = exported_members @exported_members = user.admin? ? exported_members : []
@user = user @user = user
@project = project @project = project
@missing_author_ids = []
# This needs to run first, as second call would be from #map # This needs to run first, as second call would be from #map
# which means project members already exist. # which means project members already exist.
...@@ -39,7 +36,6 @@ module Gitlab ...@@ -39,7 +36,6 @@ module Gitlab
def missing_keys_tracking_hash def missing_keys_tracking_hash
Hash.new do |_, key| Hash.new do |_, key|
@missing_author_ids << key
default_user_id default_user_id
end end
end end
...@@ -64,7 +60,7 @@ module Gitlab ...@@ -64,7 +60,7 @@ module Gitlab
end end
def find_project_user_query(member) def find_project_user_query(member)
user_arel[:username].eq(member['user']['username']).or(user_arel[:email].eq(member['user']['email'])) user_arel[:email].eq(member['user']['email']).or(user_arel[:username].eq(member['user']['username']))
end end
def user_arel def user_arel
......
...@@ -14,7 +14,7 @@ module Gitlab ...@@ -14,7 +14,7 @@ module Gitlab
priorities: :label_priorities, priorities: :label_priorities,
label: :project_label }.freeze label: :project_label }.freeze
USER_REFERENCES = %w[author_id assignee_id updated_by_id user_id created_by_id merge_user_id].freeze USER_REFERENCES = %w[author_id assignee_id updated_by_id user_id created_by_id merge_user_id resolved_by_id].freeze
PROJECT_REFERENCES = %w[project_id source_project_id gl_project_id target_project_id].freeze PROJECT_REFERENCES = %w[project_id source_project_id gl_project_id target_project_id].freeze
...@@ -80,17 +80,13 @@ module Gitlab ...@@ -80,17 +80,13 @@ module Gitlab
# is left. # is left.
def set_note_author def set_note_author
old_author_id = @relation_hash['author_id'] old_author_id = @relation_hash['author_id']
# Users with admin access can map users
@relation_hash['author_id'] = admin_user? ? @members_mapper.map[old_author_id] : @members_mapper.default_user_id
author = @relation_hash.delete('author') author = @relation_hash.delete('author')
update_note_for_missing_author(author['name']) if missing_author?(old_author_id) update_note_for_missing_author(author['name']) unless has_author?(old_author_id)
end end
def missing_author?(old_author_id) def has_author?(old_author_id)
!admin_user? || @members_mapper.missing_author_ids.include?(old_author_id) admin_user? && @members_mapper.map.keys.include?(old_author_id)
end end
def missing_author_note(updated_at, author_name) def missing_author_note(updated_at, author_name)
......
require 'spec_helper' require 'spec_helper'
describe HealthCheckController do describe HealthCheckController do
include StubENV
let(:token) { current_application_settings.health_check_access_token } let(:token) { current_application_settings.health_check_access_token }
let(:json_response) { JSON.parse(response.body) } let(:json_response) { JSON.parse(response.body) }
let(:xml_response) { Hash.from_xml(response.body)['hash'] } let(:xml_response) { Hash.from_xml(response.body)['hash'] }
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
end
describe 'GET #index' do describe 'GET #index' do
context 'when services are up but NO access token' do context 'when services are up but NO access token' do
it 'returns a not found page' do it 'returns a not found page' do
......
require 'spec_helper'
describe Projects::Settings::IntegrationsController do
let(:project) { create(:project, :public) }
let(:user) { create(:user) }
before do
project.team << [user, :master]
sign_in(user)
end
describe 'GET show' do
it 'renders show with 200 status code' do
get :show, namespace_id: project.namespace, project_id: project
expect(response).to have_http_status(200)
expect(response).to render_template(:show)
end
end
end
require 'rails_helper' require 'rails_helper'
feature 'Admin disables Git access protocol', feature: true do feature 'Admin disables Git access protocol', feature: true do
include StubENV
let(:project) { create(:empty_project, :empty_repo) } let(:project) { create(:empty_project, :empty_repo) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
background do background do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
login_as(admin) login_as(admin)
end end
......
require 'spec_helper' require 'spec_helper'
feature "Admin Health Check", feature: true do feature "Admin Health Check", feature: true do
include StubENV
include WaitForAjax include WaitForAjax
before do before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
login_as :admin login_as :admin
end end
...@@ -12,11 +14,12 @@ feature "Admin Health Check", feature: true do ...@@ -12,11 +14,12 @@ feature "Admin Health Check", feature: true do
visit admin_health_check_path visit admin_health_check_path
end end
it { page.has_text? 'Health Check' }
it { page.has_text? 'Health information can be retrieved' }
it 'has a health check access token' do it 'has a health check access token' do
page.has_text? 'Health Check'
page.has_text? 'Health information can be retrieved'
token = current_application_settings.health_check_access_token token = current_application_settings.health_check_access_token
expect(page).to have_content("Access token is #{token}") expect(page).to have_content("Access token is #{token}")
expect(page).to have_selector('#health-check-token', text: token) expect(page).to have_selector('#health-check-token', text: token)
end end
......
require 'spec_helper' require 'spec_helper'
describe "Admin Runners" do describe "Admin Runners" do
include StubENV
before do before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
login_as :admin login_as :admin
end end
......
require 'spec_helper' require 'spec_helper'
feature 'Admin updates settings', feature: true do feature 'Admin updates settings', feature: true do
before(:each) do include StubENV
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
login_as :admin login_as :admin
visit admin_application_settings_path visit admin_application_settings_path
end end
......
require 'rails_helper' require 'rails_helper'
feature 'Admin uses repository checks', feature: true do feature 'Admin uses repository checks', feature: true do
before { login_as :admin } include StubENV
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
login_as :admin
end
scenario 'to trigger a single check' do scenario 'to trigger a single check' do
project = create(:empty_project) project = create(:empty_project)
......
...@@ -2,7 +2,8 @@ require 'spec_helper' ...@@ -2,7 +2,8 @@ require 'spec_helper'
describe 'Cherry-pick Merge Requests' do describe 'Cherry-pick Merge Requests' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project) } let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user) } let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user) }
before do before do
......
...@@ -2,7 +2,8 @@ require 'spec_helper' ...@@ -2,7 +2,8 @@ require 'spec_helper'
include WaitForAjax include WaitForAjax
describe 'Cherry-pick Commits' do describe 'Cherry-pick Commits' do
let(:project) { create(:project) } let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
let(:master_pickable_commit) { project.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') } let(:master_pickable_commit) { project.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') }
let(:master_pickable_merge) { project.commit('e56497bb5f03a90a51293fc6d516788730953899') } let(:master_pickable_merge) { project.commit('e56497bb5f03a90a51293fc6d516788730953899') }
......
...@@ -262,8 +262,8 @@ describe "Internal Project Access", feature: true do ...@@ -262,8 +262,8 @@ describe "Internal Project Access", feature: true do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/hooks" do describe "GET /:project_path/settings/integrations" do
subject { namespace_project_hooks_path(project.namespace, project) } subject { namespace_project_settings_integrations_path(project.namespace, project) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) } it { is_expected.to be_allowed_for(:owner).of(project) }
......
...@@ -234,8 +234,8 @@ describe "Private Project Access", feature: true do ...@@ -234,8 +234,8 @@ describe "Private Project Access", feature: true do
it { is_expected.to be_denied_for(:visitor) } it { is_expected.to be_denied_for(:visitor) }
end end
describe "GET /:project_path/hooks" do describe "GET /:project_path/namespace/hooks" do
subject { namespace_project_hooks_path(project.namespace, project) } subject { namespace_project_settings_integrations_path(project.namespace, project) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) } it { is_expected.to be_allowed_for(:owner).of(project) }
......
...@@ -400,8 +400,8 @@ describe "Public Project Access", feature: true do ...@@ -400,8 +400,8 @@ describe "Public Project Access", feature: true do
it { is_expected.to be_allowed_for(:visitor) } it { is_expected.to be_allowed_for(:visitor) }
end end
describe "GET /:project_path/hooks" do describe "GET /:project_path/settings/integrations" do
subject { namespace_project_hooks_path(project.namespace, project) } subject { namespace_project_settings_integrations_path(project.namespace, project) }
it { is_expected.to be_allowed_for(:admin) } it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) } it { is_expected.to be_allowed_for(:owner).of(project) }
......
...@@ -165,7 +165,7 @@ describe 'Dashboard Todos', feature: true do ...@@ -165,7 +165,7 @@ describe 'Dashboard Todos', feature: true do
end end
it 'shows the todo' do it 'shows the todo' do
expect(page).to have_content 'The build failed for your merge request' expect(page).to have_content 'The build failed for merge request'
end end
it 'links to the pipelines for the merge request' do it 'links to the pipelines for the merge request' do
......
...@@ -2,13 +2,13 @@ require 'spec_helper' ...@@ -2,13 +2,13 @@ require 'spec_helper'
describe MoveToProjectFinder do describe MoveToProjectFinder do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project) } let(:project) { create(:empty_project) }
let(:no_access_project) { create(:project) } let(:no_access_project) { create(:empty_project) }
let(:guest_project) { create(:project) } let(:guest_project) { create(:empty_project) }
let(:reporter_project) { create(:project) } let(:reporter_project) { create(:empty_project) }
let(:developer_project) { create(:project) } let(:developer_project) { create(:empty_project) }
let(:master_project) { create(:project) } let(:master_project) { create(:empty_project) }
subject { described_class.new(user) } subject { described_class.new(user) }
...@@ -37,7 +37,7 @@ describe MoveToProjectFinder do ...@@ -37,7 +37,7 @@ describe MoveToProjectFinder do
it 'does not return archived projects' do it 'does not return archived projects' do
reporter_project.team << [user, :reporter] reporter_project.team << [user, :reporter]
reporter_project.update_attributes(archived: true) reporter_project.update_attributes(archived: true)
other_reporter_project = create(:project) other_reporter_project = create(:empty_project)
other_reporter_project.team << [user, :reporter] other_reporter_project.team << [user, :reporter]
expect(subject.execute(project).to_a).to eq([other_reporter_project]) expect(subject.execute(project).to_a).to eq([other_reporter_project])
...@@ -46,7 +46,7 @@ describe MoveToProjectFinder do ...@@ -46,7 +46,7 @@ describe MoveToProjectFinder do
it 'does not return projects for which issues are disabled' do it 'does not return projects for which issues are disabled' do
reporter_project.team << [user, :reporter] reporter_project.team << [user, :reporter]
reporter_project.update_attributes(issues_enabled: false) reporter_project.update_attributes(issues_enabled: false)
other_reporter_project = create(:project) other_reporter_project = create(:empty_project)
other_reporter_project.team << [user, :reporter] other_reporter_project.team << [user, :reporter]
expect(subject.execute(project).to_a).to eq([other_reporter_project]) expect(subject.execute(project).to_a).to eq([other_reporter_project])
...@@ -83,10 +83,10 @@ describe MoveToProjectFinder do ...@@ -83,10 +83,10 @@ describe MoveToProjectFinder do
end end
it 'returns projects matching a search query' do it 'returns projects matching a search query' do
foo_project = create(:project) foo_project = create(:empty_project)
foo_project.team << [user, :master] foo_project.team << [user, :master]
wadus_project = create(:project, name: 'wadus') wadus_project = create(:empty_project, name: 'wadus')
wadus_project.team << [user, :master] wadus_project.team << [user, :master]
expect(subject.execute(project).to_a).to eq([wadus_project, foo_project]) expect(subject.execute(project).to_a).to eq([wadus_project, foo_project])
......
/* global Vue, environment */
//= require vue
//= require vue-resource
//= require flash
//= require environments/stores/environments_store
//= require environments/components/environment
//= require ./mock_data
describe('Environment', () => {
preloadFixtures('environments/environments');
let component;
beforeEach(() => {
loadFixtures('environments/environments');
});
describe('successfull request', () => {
describe('without environments', () => {
const environmentsEmptyResponseInterceptor = (request, next) => {
next(request.respondWith(JSON.stringify([]), {
status: 200,
}));
};
beforeEach(() => {
Vue.http.interceptors.push(environmentsEmptyResponseInterceptor);
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, environmentsEmptyResponseInterceptor,
);
});
it('should render the empty state', (done) => {
component = new gl.environmentsList.EnvironmentsComponent({
el: document.querySelector('#environments-list-view'),
propsData: {
store: gl.environmentsList.EnvironmentsStore.create(),
},
});
setTimeout(() => {
expect(
component.$el.querySelector('.js-new-environment-button').textContent,
).toContain('New Environment');
expect(
component.$el.querySelector('.js-blank-state-title').textContent,
).toContain('You don\'t have any environments right now.');
done();
}, 0);
});
});
describe('with environments', () => {
const environmentsResponseInterceptor = (request, next) => {
next(request.respondWith(JSON.stringify([environment]), {
status: 200,
}));
};
beforeEach(() => {
Vue.http.interceptors.push(environmentsResponseInterceptor);
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, environmentsResponseInterceptor,
);
});
it('should render a table with environments', (done) => {
component = new gl.environmentsList.EnvironmentsComponent({
el: document.querySelector('#environments-list-view'),
propsData: {
store: gl.environmentsList.EnvironmentsStore.create(),
},
});
setTimeout(() => {
expect(
component.$el.querySelectorAll('table tbody tr').length,
).toEqual(1);
done();
}, 0);
});
});
});
describe('unsuccessfull request', () => {
const environmentsErrorResponseInterceptor = (request, next) => {
next(request.respondWith(JSON.stringify([]), {
status: 500,
}));
};
beforeEach(() => {
Vue.http.interceptors.push(environmentsErrorResponseInterceptor);
});
afterEach(() => {
Vue.http.interceptors = _.without(
Vue.http.interceptors, environmentsErrorResponseInterceptor,
);
});
it('should render empty state', (done) => {
component = new gl.environmentsList.EnvironmentsComponent({
el: document.querySelector('#environments-list-view'),
propsData: {
store: gl.environmentsList.EnvironmentsStore.create(),
},
});
setTimeout(() => {
expect(
component.$el.querySelector('.js-blank-state-title').textContent,
).toContain('You don\'t have any environments right now.');
done();
}, 0);
});
});
});
...@@ -133,3 +133,17 @@ const environmentsList = [ ...@@ -133,3 +133,17 @@ const environmentsList = [
updated_at: '2016-11-07T11:11:16.525Z', updated_at: '2016-11-07T11:11:16.525Z',
}, },
]; ];
const environment = {
id: 4,
name: 'production',
state: 'available',
external_url: 'http://production.',
environment_type: null,
last_deployment: {},
'stoppable?': false,
environment_path: '/root/review-app/environments/4',
stop_path: '/root/review-app/environments/4/stop',
created_at: '2016-12-16T11:51:04.690Z',
updated_at: '2016-12-16T12:04:51.133Z',
};
%div %div
#environments-list-view{ data: { environments_data: "https://gitlab.com/foo/environments", #environments-list-view{ data: { environments_data: "foo/environments",
"can-create-deployment" => "true", "can-create-deployment" => "true",
"can-read-environment" => "true", "can-read-environment" => "true",
"can-create-environment" => "true", "can-create-environment" => "true",
......
%div.js-builds-dropdown-tests %div.js-builds-dropdown-tests.dropdown.dropdown.js-mini-pipeline-graph
%button.dropdown.js-builds-dropdown-button{'data-stage-endpoint' => 'foobar'} %button.js-builds-dropdown-button{'data-stage-endpoint' => 'foobar', data: { toggle: 'dropdown'} }
Dropdown Dropdown
%div.js-builds-dropdown-container
%div.js-builds-dropdown-list
%div.js-builds-dropdown-loading.builds-dropdown-loading.hidden %ul.dropdown-menu.mini-pipeline-graph-dropdown-menu.js-builds-dropdown-container
.js-builds-dropdown-list.scrollable-menu
.js-builds-dropdown-loading.builds-dropdown-loading.hidden
%span.fa.fa-spinner.fa-spin %span.fa.fa-spinner.fa-spin
require 'spec_helper' require 'spec_helper'
describe Gitlab::CurrentSettings do describe Gitlab::CurrentSettings do
include StubENV
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
end
describe '#current_application_settings' do describe '#current_application_settings' do
it 'attempts to use cached values first' do context 'with DB available' do
before do
allow_any_instance_of(Gitlab::CurrentSettings).to receive(:connect_to_db?).and_return(true) allow_any_instance_of(Gitlab::CurrentSettings).to receive(:connect_to_db?).and_return(true)
expect(ApplicationSetting).to receive(:current).and_return(::ApplicationSetting.create_from_defaults)
expect(ApplicationSetting).not_to receive(:last)
expect(current_application_settings).to be_a(ApplicationSetting)
end end
it 'does not attempt to connect to DB or Redis' do it 'attempts to use cached values first' do
allow_any_instance_of(Gitlab::CurrentSettings).to receive(:connect_to_db?).and_return(false) expect(ApplicationSetting).to receive(:current)
expect(ApplicationSetting).not_to receive(:current)
expect(ApplicationSetting).not_to receive(:last) expect(ApplicationSetting).not_to receive(:last)
expect(current_application_settings).to eq fake_application_settings expect(current_application_settings).to be_a(ApplicationSetting)
end end
it 'falls back to DB if Redis returns an empty value' do it 'falls back to DB if Redis returns an empty value' do
allow_any_instance_of(Gitlab::CurrentSettings).to receive(:connect_to_db?).and_return(true)
expect(ApplicationSetting).to receive(:last).and_call_original expect(ApplicationSetting).to receive(:last).and_call_original
expect(current_application_settings).to be_a(ApplicationSetting) expect(current_application_settings).to be_a(ApplicationSetting)
end end
it 'falls back to DB if Redis fails' do it 'falls back to DB if Redis fails' do
allow_any_instance_of(Gitlab::CurrentSettings).to receive(:connect_to_db?).and_return(true)
expect(ApplicationSetting).to receive(:current).and_raise(::Redis::BaseError) expect(ApplicationSetting).to receive(:current).and_raise(::Redis::BaseError)
expect(ApplicationSetting).to receive(:last).and_call_original expect(ApplicationSetting).to receive(:last).and_call_original
expect(current_application_settings).to be_a(ApplicationSetting) expect(current_application_settings).to be_a(ApplicationSetting)
end end
end end
context 'with DB unavailable' do
before do
allow_any_instance_of(Gitlab::CurrentSettings).to receive(:connect_to_db?).and_return(false)
end
it 'returns an in-memory ApplicationSetting object' do
expect(ApplicationSetting).not_to receive(:current)
expect(ApplicationSetting).not_to receive(:last)
expect(current_application_settings).to be_a(OpenStruct)
end
end
context 'when ENV["IN_MEMORY_APPLICATION_SETTINGS"] is true' do
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'true')
end
it 'returns an in-memory ApplicationSetting object' do
expect(ApplicationSetting).not_to receive(:current)
expect(ApplicationSetting).not_to receive(:last)
expect(current_application_settings).to be_a(ApplicationSetting)
expect(current_application_settings).not_to be_persisted
end
end
end
end end
...@@ -2,7 +2,7 @@ require 'spec_helper' ...@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::ImportExport::MembersMapper, services: true do describe Gitlab::ImportExport::MembersMapper, services: true do
describe 'map members' do describe 'map members' do
let(:user) { create(:user, authorized_projects_populated: true) } let(:user) { create(:admin, authorized_projects_populated: true) }
let(:project) { create(:project, :public, name: 'searchable_project') } let(:project) { create(:project, :public, name: 'searchable_project') }
let(:user2) { create(:user, authorized_projects_populated: true) } let(:user2) { create(:user, authorized_projects_populated: true) }
let(:exported_user_id) { 99 } let(:exported_user_id) { 99 }
...@@ -24,7 +24,7 @@ describe Gitlab::ImportExport::MembersMapper, services: true do ...@@ -24,7 +24,7 @@ describe Gitlab::ImportExport::MembersMapper, services: true do
{ {
"id" => exported_user_id, "id" => exported_user_id,
"email" => user2.email, "email" => user2.email,
"username" => user2.username "username" => 'test'
} }
}, },
{ {
...@@ -48,6 +48,10 @@ describe Gitlab::ImportExport::MembersMapper, services: true do ...@@ -48,6 +48,10 @@ describe Gitlab::ImportExport::MembersMapper, services: true do
exported_members: exported_members, user: user, project: project) exported_members: exported_members, user: user, project: project)
end end
it 'includes the exported user ID in the map' do
expect(members_mapper.map.keys).to include(exported_user_id)
end
it 'maps a project member' do it 'maps a project member' do
expect(members_mapper.map[exported_user_id]).to eq(user2.id) expect(members_mapper.map[exported_user_id]).to eq(user2.id)
end end
...@@ -56,12 +60,6 @@ describe Gitlab::ImportExport::MembersMapper, services: true do ...@@ -56,12 +60,6 @@ describe Gitlab::ImportExport::MembersMapper, services: true do
expect(members_mapper.map[-1]).to eq(user.id) expect(members_mapper.map[-1]).to eq(user.id)
end end
it 'updates missing author IDs on missing project member' do
members_mapper.map[-1]
expect(members_mapper.missing_author_ids.first).to eq(-1)
end
it 'has invited members with no user' do it 'has invited members with no user' do
members_mapper.map members_mapper.map
...@@ -74,5 +72,25 @@ describe Gitlab::ImportExport::MembersMapper, services: true do ...@@ -74,5 +72,25 @@ describe Gitlab::ImportExport::MembersMapper, services: true do
expect(user.authorized_project?(project)).to be true expect(user.authorized_project?(project)).to be true
expect(user2.authorized_project?(project)).to be true expect(user2.authorized_project?(project)).to be true
end end
context 'user is not an admin' do
let(:user) { create(:user, authorized_projects_populated: true) }
it 'does not map a project member' do
expect(members_mapper.map[exported_user_id]).to eq(user.id)
end
it 'defaults to importer project member if it does not exist' do
expect(members_mapper.map[-1]).to eq(user.id)
end
end
context 'chooses the one with an email first' do
let(:user3) { create(:user, username: 'test') }
it 'maps the project member that has a matching email first' do
expect(members_mapper.map[exported_user_id]).to eq(user2.id)
end
end
end end
end end
...@@ -3,7 +3,7 @@ require 'spec_helper' ...@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::ImportExport::RelationFactory, lib: true do describe Gitlab::ImportExport::RelationFactory, lib: true do
let(:project) { create(:empty_project) } let(:project) { create(:empty_project) }
let(:members_mapper) { double('members_mapper').as_null_object } let(:members_mapper) { double('members_mapper').as_null_object }
let(:user) { create(:user) } let(:user) { create(:admin) }
let(:created_object) do let(:created_object) do
described_class.create(relation_sym: relation_sym, described_class.create(relation_sym: relation_sym,
relation_hash: relation_hash, relation_hash: relation_hash,
...@@ -122,4 +122,60 @@ describe Gitlab::ImportExport::RelationFactory, lib: true do ...@@ -122,4 +122,60 @@ describe Gitlab::ImportExport::RelationFactory, lib: true do
expect(created_object.values).not_to include(99) expect(created_object.values).not_to include(99)
end end
end end
context 'Notes user references' do
let(:relation_sym) { :notes }
let(:new_user) { create(:user) }
let(:exported_member) do
{
"id" => 111,
"access_level" => 30,
"source_id" => 1,
"source_type" => "Project",
"user_id" => 3,
"notification_level" => 3,
"created_at" => "2016-11-18T09:29:42.634Z",
"updated_at" => "2016-11-18T09:29:42.634Z",
"user" => {
"id" => 999,
"email" => new_user.email,
"username" => new_user.username
}
}
end
let(:relation_hash) do
{
"id" => 4947,
"note" => "merged",
"noteable_type" => "MergeRequest",
"author_id" => 999,
"created_at" => "2016-11-18T09:29:42.634Z",
"updated_at" => "2016-11-18T09:29:42.634Z",
"project_id" => 1,
"attachment" => {
"url" => nil
},
"noteable_id" => 377,
"system" => true,
"author" => {
"name" => "Administrator"
},
"events" => [
]
}
end
let(:members_mapper) do
Gitlab::ImportExport::MembersMapper.new(
exported_members: [exported_member],
user: user,
project: project)
end
it 'maps the right author to the imported note' do
expect(created_object.author).to eq(new_user)
end
end
end end
...@@ -1401,4 +1401,14 @@ describe Ci::Build, :models do ...@@ -1401,4 +1401,14 @@ describe Ci::Build, :models do
it { is_expected.to eq(%w[predefined project pipeline yaml secret]) } it { is_expected.to eq(%w[predefined project pipeline yaml secret]) }
end end
end end
describe 'State transition: any => [:pending]' do
let(:build) { create(:ci_build, :created) }
it 'queues BuildQueueWorker' do
expect(BuildQueueWorker).to receive(:perform_async).with(build.id)
build.enqueue
end
end
end end
...@@ -263,6 +263,62 @@ describe Ci::Runner, models: true do ...@@ -263,6 +263,62 @@ describe Ci::Runner, models: true do
end end
end end
describe '#tick_runner_queue' do
let(:runner) { create(:ci_runner) }
it 'returns a new last_update value' do
expect(runner.tick_runner_queue).not_to be_empty
end
end
describe '#ensure_runner_queue_value' do
let(:runner) { create(:ci_runner) }
it 'sets a new last_update value when it is called the first time' do
last_update = runner.ensure_runner_queue_value
expect_value_in_redis.to eq(last_update)
end
it 'does not change if it is not expired and called again' do
last_update = runner.ensure_runner_queue_value
expect(runner.ensure_runner_queue_value).to eq(last_update)
expect_value_in_redis.to eq(last_update)
end
context 'updates runner queue after changing editable value' do
let!(:last_update) { runner.ensure_runner_queue_value }
before do
runner.update(description: 'new runner')
end
it 'sets a new last_update value' do
expect_value_in_redis.not_to eq(last_update)
end
end
context 'does not update runner value after save' do
let!(:last_update) { runner.ensure_runner_queue_value }
before do
runner.touch
end
it 'has an old last_update value' do
expect_value_in_redis.to eq(last_update)
end
end
def expect_value_in_redis
Gitlab::Redis.with do |redis|
runner_queue_key = runner.send(:runner_queue_key)
expect(redis.get(runner_queue_key))
end
end
end
describe '.assignable_for' do describe '.assignable_for' do
let(:runner) { create(:ci_runner) } let(:runner) { create(:ci_runner) }
let(:project) { create(:project) } let(:project) { create(:project) }
......
...@@ -30,13 +30,32 @@ describe Key, models: true do ...@@ -30,13 +30,32 @@ describe Key, models: true do
end end
describe "#update_last_used_at" do describe "#update_last_used_at" do
it "enqueues a UseKeyWorker job" do let(:key) { create(:key) }
key = create(:key)
context 'when key was not updated during the last day' do
before do
allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).
and_return('000000')
end
it 'enqueues a UseKeyWorker job' do
expect(UseKeyWorker).to receive(:perform_async).with(key.id) expect(UseKeyWorker).to receive(:perform_async).with(key.id)
key.update_last_used_at key.update_last_used_at
end end
end end
context 'when key was updated during the last day' do
before do
allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).
and_return(false)
end
it 'does not enqueue a UseKeyWorker job' do
expect(UseKeyWorker).not_to receive(:perform_async)
key.update_last_used_at
end
end
end
end end
context "validation of uniqueness (based on fingerprint uniqueness)" do context "validation of uniqueness (based on fingerprint uniqueness)" do
......
...@@ -65,7 +65,7 @@ describe MergeRequest, models: true do ...@@ -65,7 +65,7 @@ describe MergeRequest, models: true do
end end
describe '#target_branch_sha' do describe '#target_branch_sha' do
let(:project) { create(:project) } let(:project) { create(:project, :repository) }
subject { create(:merge_request, source_project: project, target_project: project) } subject { create(:merge_request, source_project: project, target_project: project) }
...@@ -150,7 +150,7 @@ describe MergeRequest, models: true do ...@@ -150,7 +150,7 @@ describe MergeRequest, models: true do
end end
it 'supports a cross-project reference' do it 'supports a cross-project reference' do
another_project = build(:project, name: 'another-project', namespace: project.namespace) another_project = build(:empty_project, name: 'another-project', namespace: project.namespace)
expect(merge_request.to_reference(another_project)).to eq "sample-project!1" expect(merge_request.to_reference(another_project)).to eq "sample-project!1"
end end
...@@ -245,8 +245,8 @@ describe MergeRequest, models: true do ...@@ -245,8 +245,8 @@ describe MergeRequest, models: true do
describe '#for_fork?' do describe '#for_fork?' do
it 'returns true if the merge request is for a fork' do it 'returns true if the merge request is for a fork' do
subject.source_project = create(:project, namespace: create(:group)) subject.source_project = build_stubbed(:empty_project, namespace: create(:group))
subject.target_project = create(:project, namespace: create(:group)) subject.target_project = build_stubbed(:empty_project, namespace: create(:group))
expect(subject.for_fork?).to be_truthy expect(subject.for_fork?).to be_truthy
end end
...@@ -501,8 +501,8 @@ describe MergeRequest, models: true do ...@@ -501,8 +501,8 @@ describe MergeRequest, models: true do
end end
describe '#diverged_commits_count' do describe '#diverged_commits_count' do
let(:project) { create(:project) } let(:project) { create(:project, :repository) }
let(:fork_project) { create(:project, forked_from_project: project) } let(:fork_project) { create(:project, :repository, forked_from_project: project) }
context 'when the target branch does not exist anymore' do context 'when the target branch does not exist anymore' do
subject { create(:merge_request, source_project: project, target_project: project) } subject { create(:merge_request, source_project: project, target_project: project) }
...@@ -727,7 +727,7 @@ describe MergeRequest, models: true do ...@@ -727,7 +727,7 @@ describe MergeRequest, models: true do
end end
describe '#participants' do describe '#participants' do
let(:project) { create(:project, :public) } let(:project) { create(:empty_project, :public) }
let(:mr) do let(:mr) do
create(:merge_request, source_project: project, target_project: project) create(:merge_request, source_project: project, target_project: project)
...@@ -768,7 +768,7 @@ describe MergeRequest, models: true do ...@@ -768,7 +768,7 @@ describe MergeRequest, models: true do
end end
describe '#check_if_can_be_merged' do describe '#check_if_can_be_merged' do
let(:project) { create(:project, only_allow_merge_if_build_succeeds: true) } let(:project) { create(:empty_project, only_allow_merge_if_build_succeeds: true) }
subject { create(:merge_request, source_project: project, merge_status: :unchecked) } subject { create(:merge_request, source_project: project, merge_status: :unchecked) }
...@@ -810,7 +810,7 @@ describe MergeRequest, models: true do ...@@ -810,7 +810,7 @@ describe MergeRequest, models: true do
end end
describe '#mergeable?' do describe '#mergeable?' do
let(:project) { create(:project) } let(:project) { create(:empty_project) }
subject { create(:merge_request, source_project: project) } subject { create(:merge_request, source_project: project) }
...@@ -830,7 +830,7 @@ describe MergeRequest, models: true do ...@@ -830,7 +830,7 @@ describe MergeRequest, models: true do
end end
describe '#mergeable_state?' do describe '#mergeable_state?' do
let(:project) { create(:project) } let(:project) { create(:project, :repository) }
subject { create(:merge_request, source_project: project) } subject { create(:merge_request, source_project: project) }
...@@ -957,7 +957,7 @@ describe MergeRequest, models: true do ...@@ -957,7 +957,7 @@ describe MergeRequest, models: true do
let(:merge_request) { create(:merge_request_with_diff_notes, source_project: project) } let(:merge_request) { create(:merge_request_with_diff_notes, source_project: project) }
context 'when project.only_allow_merge_if_all_discussions_are_resolved == true' do context 'when project.only_allow_merge_if_all_discussions_are_resolved == true' do
let(:project) { create(:project, only_allow_merge_if_all_discussions_are_resolved: true) } let(:project) { create(:project, :repository, only_allow_merge_if_all_discussions_are_resolved: true) }
context 'with all discussions resolved' do context 'with all discussions resolved' do
before do before do
...@@ -991,7 +991,7 @@ describe MergeRequest, models: true do ...@@ -991,7 +991,7 @@ describe MergeRequest, models: true do
end end
context 'when project.only_allow_merge_if_all_discussions_are_resolved == false' do context 'when project.only_allow_merge_if_all_discussions_are_resolved == false' do
let(:project) { create(:project, only_allow_merge_if_all_discussions_are_resolved: false) } let(:project) { create(:project, :repository, only_allow_merge_if_all_discussions_are_resolved: false) }
context 'with unresolved discussions' do context 'with unresolved discussions' do
before do before do
...@@ -1006,7 +1006,7 @@ describe MergeRequest, models: true do ...@@ -1006,7 +1006,7 @@ describe MergeRequest, models: true do
end end
describe "#environments" do describe "#environments" do
let(:project) { create(:project) } let(:project) { create(:project, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) } let(:merge_request) { create(:merge_request, source_project: project) }
context 'with multiple environments' do context 'with multiple environments' do
...@@ -1024,7 +1024,7 @@ describe MergeRequest, models: true do ...@@ -1024,7 +1024,7 @@ describe MergeRequest, models: true do
context 'with environments on source project' do context 'with environments on source project' do
let(:source_project) do let(:source_project) do
create(:project) do |fork_project| create(:project, :repository) do |fork_project|
fork_project.create_forked_project_link(forked_to_project_id: fork_project.id, forked_from_project_id: project.id) fork_project.create_forked_project_link(forked_to_project_id: fork_project.id, forked_from_project_id: project.id)
end end
end end
...@@ -1401,8 +1401,8 @@ describe MergeRequest, models: true do ...@@ -1401,8 +1401,8 @@ describe MergeRequest, models: true do
end end
describe "#source_project_missing?" do describe "#source_project_missing?" do
let(:project) { create(:project) } let(:project) { create(:empty_project) }
let(:fork_project) { create(:project, forked_from_project: project) } let(:fork_project) { create(:empty_project, forked_from_project: project) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:unlink_project) { Projects::UnlinkForkService.new(fork_project, user) } let(:unlink_project) { Projects::UnlinkForkService.new(fork_project, user) }
...@@ -1439,8 +1439,8 @@ describe MergeRequest, models: true do ...@@ -1439,8 +1439,8 @@ describe MergeRequest, models: true do
end end
describe "#closed_without_fork?" do describe "#closed_without_fork?" do
let(:project) { create(:project) } let(:project) { create(:empty_project) }
let(:fork_project) { create(:project, forked_from_project: project) } let(:fork_project) { create(:empty_project, forked_from_project: project) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:unlink_project) { Projects::UnlinkForkService.new(fork_project, user) } let(:unlink_project) { Projects::UnlinkForkService.new(fork_project, user) }
...@@ -1485,9 +1485,9 @@ describe MergeRequest, models: true do ...@@ -1485,9 +1485,9 @@ describe MergeRequest, models: true do
end end
context 'forked project' do context 'forked project' do
let(:project) { create(:project) } let(:project) { create(:empty_project) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:fork_project) { create(:project, forked_from_project: project, namespace: user.namespace) } let(:fork_project) { create(:empty_project, forked_from_project: project, namespace: user.namespace) }
let!(:merge_request) do let!(:merge_request) do
create(:closed_merge_request, create(:closed_merge_request,
...@@ -1531,7 +1531,7 @@ describe MergeRequest, models: true do ...@@ -1531,7 +1531,7 @@ describe MergeRequest, models: true do
status: status) status: status)
end end
let(:project) { create(:project, :public, only_allow_merge_if_build_succeeds: true) } let(:project) { create(:project, :public, :repository, only_allow_merge_if_build_succeeds: true) }
let(:developer) { create(:user) } let(:developer) { create(:user) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:merge_request) { create(:merge_request, source_project: project) } let(:merge_request) { create(:merge_request, source_project: project) }
......
This diff is collapsed.
This diff is collapsed.
...@@ -9,7 +9,7 @@ describe API::AccessRequests, api: true do ...@@ -9,7 +9,7 @@ describe API::AccessRequests, api: true do
let(:stranger) { create(:user) } let(:stranger) { create(:user) }
let(:project) do let(:project) do
create(:project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project| create(:empty_project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project|
project.team << [developer, :developer] project.team << [developer, :developer]
project.team << [master, :master] project.team << [master, :master]
project.request_access(access_requester) project.request_access(access_requester)
......
...@@ -8,7 +8,7 @@ describe API::Boards, api: true do ...@@ -8,7 +8,7 @@ describe API::Boards, api: true do
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
let(:guest) { create(:user) } let(:guest) { create(:user) }
let(:admin) { create(:user, :admin) } let(:admin) { create(:user, :admin) }
let!(:project) { create(:project, :public, creator_id: user.id, namespace: user.namespace ) } let!(:project) { create(:empty_project, :public, creator_id: user.id, namespace: user.namespace ) }
let!(:dev_label) do let!(:dev_label) do
create(:label, title: 'Development', color: '#FFAABB', project: project) create(:label, title: 'Development', color: '#FFAABB', project: project)
...@@ -188,7 +188,7 @@ describe API::Boards, api: true do ...@@ -188,7 +188,7 @@ describe API::Boards, api: true do
context "when the user is project owner" do context "when the user is project owner" do
let(:owner) { create(:user) } let(:owner) { create(:user) }
let(:project) { create(:project, namespace: owner.namespace) } let(:project) { create(:empty_project, namespace: owner.namespace) }
it "deletes the list if an admin requests it" do it "deletes the list if an admin requests it" do
delete api("#{base_url}/#{dev_list.id}", owner) delete api("#{base_url}/#{dev_list.id}", owner)
......
...@@ -5,8 +5,8 @@ describe API::DeployKeys, api: true do ...@@ -5,8 +5,8 @@ describe API::DeployKeys, api: true do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let(:project) { create(:project, creator_id: user.id) } let(:project) { create(:empty_project, creator_id: user.id) }
let(:project2) { create(:project, creator_id: user.id) } let(:project2) { create(:empty_project, creator_id: user.id) }
let(:deploy_key) { create(:deploy_key, public: true) } let(:deploy_key) { create(:deploy_key, public: true) }
let!(:deploy_keys_project) do let!(:deploy_keys_project) do
......
...@@ -5,7 +5,7 @@ describe API::Environments, api: true do ...@@ -5,7 +5,7 @@ describe API::Environments, api: true do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
let(:project) { create(:project, :private, namespace: user.namespace) } let(:project) { create(:empty_project, :private, namespace: user.namespace) }
let!(:environment) { create(:environment, project: project) } let!(:environment) { create(:environment, project: project) }
before do before do
......
require 'spec_helper' require 'spec_helper'
describe API::API, api: true do describe API::Projects, api: true do
include ApiHelpers include ApiHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:user2) { create(:user) } let(:user2) { create(:user) }
......
...@@ -10,9 +10,9 @@ describe API::Groups, api: true do ...@@ -10,9 +10,9 @@ describe API::Groups, api: true do
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let!(:group1) { create(:group, avatar: File.open(uploaded_image_temp_path)) } let!(:group1) { create(:group, avatar: File.open(uploaded_image_temp_path)) }
let!(:group2) { create(:group, :private) } let!(:group2) { create(:group, :private) }
let!(:project1) { create(:project, namespace: group1) } let!(:project1) { create(:empty_project, namespace: group1) }
let!(:project2) { create(:project, namespace: group2) } let!(:project2) { create(:empty_project, namespace: group2) }
let!(:project3) { create(:project, namespace: group1, path: 'test', visibility_level: Gitlab::VisibilityLevel::PRIVATE) } let!(:project3) { create(:empty_project, namespace: group1, path: 'test', visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
before do before do
group1.add_owner(user1) group1.add_owner(user1)
...@@ -163,7 +163,7 @@ describe API::Groups, api: true do ...@@ -163,7 +163,7 @@ describe API::Groups, api: true do
describe "GET /groups/:id" do describe "GET /groups/:id" do
context "when authenticated as user" do context "when authenticated as user" do
it "returns one of user1's groups" do it "returns one of user1's groups" do
project = create(:project, namespace: group2, path: 'Foo') project = create(:empty_project, namespace: group2, path: 'Foo')
create(:project_group_link, project: project, group: group1) create(:project_group_link, project: project, group: group1)
get api("/groups/#{group1.id}", user1) get api("/groups/#{group1.id}", user1)
...@@ -287,7 +287,7 @@ describe API::Groups, api: true do ...@@ -287,7 +287,7 @@ describe API::Groups, api: true do
expect(json_response.length).to eq(2) expect(json_response.length).to eq(2)
project_names = json_response.map { |proj| proj['name' ] } project_names = json_response.map { |proj| proj['name' ] }
expect(project_names).to match_array([project1.name, project3.name]) expect(project_names).to match_array([project1.name, project3.name])
expect(json_response.first['default_branch']).to be_present expect(json_response.first['visibility_level']).to be_present
end end
it "returns the group's projects with simple representation" do it "returns the group's projects with simple representation" do
...@@ -297,11 +297,11 @@ describe API::Groups, api: true do ...@@ -297,11 +297,11 @@ describe API::Groups, api: true do
expect(json_response.length).to eq(2) expect(json_response.length).to eq(2)
project_names = json_response.map { |proj| proj['name' ] } project_names = json_response.map { |proj| proj['name' ] }
expect(project_names).to match_array([project1.name, project3.name]) expect(project_names).to match_array([project1.name, project3.name])
expect(json_response.first['default_branch']).not_to be_present expect(json_response.first['visibility_level']).not_to be_present
end end
it 'filters the groups projects' do it 'filters the groups projects' do
public_project = create(:project, :public, path: 'test1', group: group1) public_project = create(:empty_project, :public, path: 'test1', group: group1)
get api("/groups/#{group1.id}/projects", user1), visibility: 'public' get api("/groups/#{group1.id}/projects", user1), visibility: 'public'
...@@ -462,7 +462,7 @@ describe API::Groups, api: true do ...@@ -462,7 +462,7 @@ describe API::Groups, api: true do
end end
describe "POST /groups/:id/projects/:project_id" do describe "POST /groups/:id/projects/:project_id" do
let(:project) { create(:project) } let(:project) { create(:empty_project) }
let(:project_path) { "#{project.namespace.path}%2F#{project.path}" } let(:project_path) { "#{project.namespace.path}%2F#{project.path}" }
before(:each) do before(:each) do
......
...@@ -12,6 +12,7 @@ describe API::Helpers, api: true do ...@@ -12,6 +12,7 @@ describe API::Helpers, api: true do
let(:params) { {} } let(:params) { {} }
let(:env) { { 'REQUEST_METHOD' => 'GET' } } let(:env) { { 'REQUEST_METHOD' => 'GET' } }
let(:request) { Rack::Request.new(env) } let(:request) { Rack::Request.new(env) }
let(:header) { }
def set_env(user_or_token, identifier) def set_env(user_or_token, identifier)
clear_env clear_env
...@@ -46,7 +47,7 @@ describe API::Helpers, api: true do ...@@ -46,7 +47,7 @@ describe API::Helpers, api: true do
allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ value } allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ value }
end end
def error!(message, status) def error!(message, status, header)
raise Exception.new("#{status} - #{message}") raise Exception.new("#{status} - #{message}")
end end
......
...@@ -239,7 +239,7 @@ describe API::Internal, api: true do ...@@ -239,7 +239,7 @@ describe API::Internal, api: true do
end end
context "blocked user" do context "blocked user" do
let(:personal_project) { create(:project, namespace: user.namespace) } let(:personal_project) { create(:empty_project, namespace: user.namespace) }
before do before do
user.block user.block
...@@ -265,7 +265,7 @@ describe API::Internal, api: true do ...@@ -265,7 +265,7 @@ describe API::Internal, api: true do
end end
context "archived project" do context "archived project" do
let(:personal_project) { create(:project, namespace: user.namespace) } let(:personal_project) { create(:empty_project, namespace: user.namespace) }
before do before do
project.team << [user, :developer] project.team << [user, :developer]
...@@ -337,8 +337,7 @@ describe API::Internal, api: true do ...@@ -337,8 +337,7 @@ describe API::Internal, api: true do
context 'ssh access has been disabled' do context 'ssh access has been disabled' do
before do before do
settings = ::ApplicationSetting.create_from_defaults stub_application_setting(enabled_git_access_protocol: 'http')
settings.update_attribute(:enabled_git_access_protocol, 'http')
end end
it 'rejects the SSH push' do it 'rejects the SSH push' do
...@@ -360,8 +359,7 @@ describe API::Internal, api: true do ...@@ -360,8 +359,7 @@ describe API::Internal, api: true do
context 'http access has been disabled' do context 'http access has been disabled' do
before do before do
settings = ::ApplicationSetting.create_from_defaults stub_application_setting(enabled_git_access_protocol: 'ssh')
settings.update_attribute(:enabled_git_access_protocol, 'ssh')
end end
it 'rejects the HTTP push' do it 'rejects the HTTP push' do
...@@ -383,8 +381,7 @@ describe API::Internal, api: true do ...@@ -383,8 +381,7 @@ describe API::Internal, api: true do
context 'web actions are always allowed' do context 'web actions are always allowed' do
it 'allows WEB push' do it 'allows WEB push' do
settings = ::ApplicationSetting.create_from_defaults stub_application_setting(enabled_git_access_protocol: 'ssh')
settings.update_attribute(:enabled_git_access_protocol, 'ssh')
project.team << [user, :developer] project.team << [user, :developer]
push(key, project, 'web') push(key, project, 'web')
......
...@@ -11,7 +11,7 @@ describe API::Issues, api: true do ...@@ -11,7 +11,7 @@ describe API::Issues, api: true do
let(:author) { create(:author) } let(:author) { create(:author) }
let(:assignee) { create(:assignee) } let(:assignee) { create(:assignee) }
let(:admin) { create(:user, :admin) } let(:admin) { create(:user, :admin) }
let!(:project) { create(:project, :public, creator_id: user.id, namespace: user.namespace ) } let!(:project) { create(:empty_project, :public, creator_id: user.id, namespace: user.namespace ) }
let!(:closed_issue) do let!(:closed_issue) do
create :closed_issue, create :closed_issue,
author: user, author: user,
...@@ -224,7 +224,7 @@ describe API::Issues, api: true do ...@@ -224,7 +224,7 @@ describe API::Issues, api: true do
describe "GET /groups/:id/issues" do describe "GET /groups/:id/issues" do
let!(:group) { create(:group) } let!(:group) { create(:group) }
let!(:group_project) { create(:project, :public, creator_id: user.id, namespace: group) } let!(:group_project) { create(:empty_project, :public, creator_id: user.id, namespace: group) }
let!(:group_closed_issue) do let!(:group_closed_issue) do
create :closed_issue, create :closed_issue,
author: user, author: user,
...@@ -1052,7 +1052,7 @@ describe API::Issues, api: true do ...@@ -1052,7 +1052,7 @@ describe API::Issues, api: true do
context "when the user is project owner" do context "when the user is project owner" do
let(:owner) { create(:user) } let(:owner) { create(:user) }
let(:project) { create(:project, namespace: owner.namespace) } let(:project) { create(:empty_project, namespace: owner.namespace) }
it "deletes the issue if an admin requests it" do it "deletes the issue if an admin requests it" do
delete api("/projects/#{project.id}/issues/#{issue.id}", owner) delete api("/projects/#{project.id}/issues/#{issue.id}", owner)
...@@ -1071,8 +1071,8 @@ describe API::Issues, api: true do ...@@ -1071,8 +1071,8 @@ describe API::Issues, api: true do
end end
describe '/projects/:id/issues/:issue_id/move' do describe '/projects/:id/issues/:issue_id/move' do
let!(:target_project) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace ) } let!(:target_project) { create(:empty_project, path: 'project2', creator_id: user.id, namespace: user.namespace ) }
let!(:target_project2) { create(:project, creator_id: non_member.id, namespace: non_member.namespace ) } let!(:target_project2) { create(:empty_project, creator_id: non_member.id, namespace: non_member.namespace ) }
it 'moves an issue' do it 'moves an issue' do
post api("/projects/#{project.id}/issues/#{issue.id}/move", user), post api("/projects/#{project.id}/issues/#{issue.id}/move", user),
......
...@@ -4,7 +4,7 @@ describe API::Labels, api: true do ...@@ -4,7 +4,7 @@ describe API::Labels, api: true do
include ApiHelpers include ApiHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) } let(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) }
let!(:label1) { create(:label, title: 'label1', project: project) } let!(:label1) { create(:label, title: 'label1', project: project) }
let!(:priority_label) { create(:label, title: 'bug', project: project, priority: 3) } let!(:priority_label) { create(:label, title: 'bug', project: project, priority: 3) }
......
...@@ -9,7 +9,7 @@ describe API::Members, api: true do ...@@ -9,7 +9,7 @@ describe API::Members, api: true do
let(:stranger) { create(:user) } let(:stranger) { create(:user) }
let(:project) do let(:project) do
create(:project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project| create(:empty_project, :public, :access_requestable, creator_id: master.id, namespace: master.namespace) do |project|
project.team << [developer, :developer] project.team << [developer, :developer]
project.team << [master, :master] project.team << [master, :master]
project.request_access(access_requester) project.request_access(access_requester)
......
...@@ -308,8 +308,8 @@ describe API::MergeRequests, api: true do ...@@ -308,8 +308,8 @@ describe API::MergeRequests, api: true do
context 'forked projects' do context 'forked projects' do
let!(:user2) { create(:user) } let!(:user2) { create(:user) }
let!(:fork_project) { create(:project, forked_from_project: project, namespace: user2.namespace, creator_id: user2.id) } let!(:fork_project) { create(:empty_project, forked_from_project: project, namespace: user2.namespace, creator_id: user2.id) }
let!(:unrelated_project) { create(:project, namespace: create(:user).namespace, creator_id: user2.id) } let!(:unrelated_project) { create(:empty_project, namespace: create(:user).namespace, creator_id: user2.id) }
before :each do |each| before :each do |each|
fork_project.team << [user2, :reporter] fork_project.team << [user2, :reporter]
......
...@@ -3,7 +3,7 @@ require 'spec_helper' ...@@ -3,7 +3,7 @@ require 'spec_helper'
describe API::Notes, api: true do describe API::Notes, api: true do
include ApiHelpers include ApiHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let!(:project) { create(:project, :public, namespace: user.namespace) } let!(:project) { create(:empty_project, :public, namespace: user.namespace) }
let!(:issue) { create(:issue, project: project, author: user) } let!(:issue) { create(:issue, project: project, author: user) }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) } let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) }
let!(:snippet) { create(:project_snippet, project: project, author: user) } let!(:snippet) { create(:project_snippet, project: project, author: user) }
...@@ -14,12 +14,12 @@ describe API::Notes, api: true do ...@@ -14,12 +14,12 @@ describe API::Notes, api: true do
# For testing the cross-reference of a private issue in a public issue # For testing the cross-reference of a private issue in a public issue
let(:private_user) { create(:user) } let(:private_user) { create(:user) }
let(:private_project) do let(:private_project) do
create(:project, namespace: private_user.namespace). create(:empty_project, namespace: private_user.namespace).
tap { |p| p.team << [private_user, :master] } tap { |p| p.team << [private_user, :master] }
end end
let(:private_issue) { create(:issue, project: private_project) } let(:private_issue) { create(:issue, project: private_project) }
let(:ext_proj) { create(:project, :public) } let(:ext_proj) { create(:empty_project, :public) }
let(:ext_issue) { create(:issue, project: ext_proj) } let(:ext_issue) { create(:issue, project: ext_proj) }
let!(:cross_reference_note) do let!(:cross_reference_note) do
...@@ -265,7 +265,7 @@ describe API::Notes, api: true do ...@@ -265,7 +265,7 @@ describe API::Notes, api: true do
end end
context 'when user does not have access to create noteable' do context 'when user does not have access to create noteable' do
let(:private_issue) { create(:issue, project: create(:project, :private)) } let(:private_issue) { create(:issue, project: create(:empty_project, :private)) }
## ##
# We are posting to project user has access to, but we use issue id # We are posting to project user has access to, but we use issue id
......
...@@ -5,7 +5,7 @@ describe API::NotificationSettings, api: true do ...@@ -5,7 +5,7 @@ describe API::NotificationSettings, api: true do
let(:user) { create(:user) } let(:user) { create(:user) }
let!(:group) { create(:group) } let!(:group) { create(:group) }
let!(:project) { create(:project, :public, creator_id: user.id, namespace: group) } let!(:project) { create(:empty_project, :public, creator_id: user.id, namespace: group) }
describe "GET /notification_settings" do describe "GET /notification_settings" do
it "returns global notification settings for the current user" do it "returns global notification settings for the current user" do
......
...@@ -4,7 +4,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do ...@@ -4,7 +4,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do
include ApiHelpers include ApiHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:user3) { create(:user) } let(:user3) { create(:user) }
let!(:project) { create(:project, creator_id: user.id, namespace: user.namespace) } let!(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) }
let!(:hook) do let!(:hook) do
create(:project_hook, create(:project_hook,
:all_events_enabled, :all_events_enabled,
...@@ -204,7 +204,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do ...@@ -204,7 +204,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do
it "returns a 404 if a user attempts to delete project hooks he/she does not own" do it "returns a 404 if a user attempts to delete project hooks he/she does not own" do
test_user = create(:user) test_user = create(:user)
other_project = create(:project) other_project = create(:empty_project)
other_project.team << [test_user, :master] other_project.team << [test_user, :master]
delete api("/projects/#{other_project.id}/hooks/#{hook.id}", test_user) delete api("/projects/#{other_project.id}/hooks/#{hook.id}", test_user)
......
...@@ -8,8 +8,8 @@ describe API::Projects, api: true do ...@@ -8,8 +8,8 @@ describe API::Projects, api: true do
let(:user2) { create(:user) } let(:user2) { create(:user) }
let(:user3) { create(:user) } let(:user3) { create(:user) }
let(:admin) { create(:admin) } let(:admin) { create(:admin) }
let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) } let(:project) { create(:empty_project, creator_id: user.id, namespace: user.namespace) }
let(:project2) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace) } let(:project2) { create(:empty_project, path: 'project2', creator_id: user.id, namespace: user.namespace) }
let(:snippet) { create(:project_snippet, :public, author: user, project: project, title: 'example') } let(:snippet) { create(:project_snippet, :public, author: user, project: project, title: 'example') }
let(:project_member) { create(:project_member, :master, user: user, project: project) } let(:project_member) { create(:project_member, :master, user: user, project: project) }
let(:project_member2) { create(:project_member, :developer, user: user3, project: project) } let(:project_member2) { create(:project_member, :developer, user: user3, project: project) }
...@@ -32,7 +32,7 @@ describe API::Projects, api: true do ...@@ -32,7 +32,7 @@ describe API::Projects, api: true do
access_level: ProjectMember::MASTER) access_level: ProjectMember::MASTER)
end end
let(:project4) do let(:project4) do
create(:project, create(:empty_project,
name: 'third_project', name: 'third_project',
path: 'third_project', path: 'third_project',
creator_id: user4.id, creator_id: user4.id,
...@@ -252,7 +252,7 @@ describe API::Projects, api: true do ...@@ -252,7 +252,7 @@ describe API::Projects, api: true do
end end
end end
let!(:public_project) { create(:project, :public) } let!(:public_project) { create(:empty_project, :public) }
before do before do
project project
project2 project2
...@@ -283,7 +283,7 @@ describe API::Projects, api: true do ...@@ -283,7 +283,7 @@ describe API::Projects, api: true do
end end
describe 'GET /projects/starred' do describe 'GET /projects/starred' do
let(:public_project) { create(:project, :public) } let(:public_project) { create(:empty_project, :public) }
before do before do
project_member2 project_member2
...@@ -583,7 +583,7 @@ describe API::Projects, api: true do ...@@ -583,7 +583,7 @@ describe API::Projects, api: true do
describe 'GET /projects/:id' do describe 'GET /projects/:id' do
context 'when unauthenticated' do context 'when unauthenticated' do
it 'returns the public projects' do it 'returns the public projects' do
public_project = create(:project, :public) public_project = create(:empty_project, :public)
get api("/projects/#{public_project.id}") get api("/projects/#{public_project.id}")
...@@ -665,7 +665,7 @@ describe API::Projects, api: true do ...@@ -665,7 +665,7 @@ describe API::Projects, api: true do
it 'handles users with dots' do it 'handles users with dots' do
dot_user = create(:user, username: 'dot.user') dot_user = create(:user, username: 'dot.user')
project = create(:project, creator_id: dot_user.id, namespace: dot_user.namespace) project = create(:empty_project, creator_id: dot_user.id, namespace: dot_user.namespace)
get api("/projects/#{dot_user.namespace.name}%2F#{project.path}", dot_user) get api("/projects/#{dot_user.namespace.name}%2F#{project.path}", dot_user)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
...@@ -711,7 +711,7 @@ describe API::Projects, api: true do ...@@ -711,7 +711,7 @@ describe API::Projects, api: true do
end end
context 'group project' do context 'group project' do
let(:project2) { create(:project, group: create(:group)) } let(:project2) { create(:empty_project, group: create(:group)) }
before { project2.group.add_owner(user) } before { project2.group.add_owner(user) }
...@@ -756,7 +756,7 @@ describe API::Projects, api: true do ...@@ -756,7 +756,7 @@ describe API::Projects, api: true do
context 'when unauthenticated' do context 'when unauthenticated' do
it_behaves_like 'project events response' do it_behaves_like 'project events response' do
let(:project) { create(:project, :public) } let(:project) { create(:empty_project, :public) }
let(:current_user) { nil } let(:current_user) { nil }
end end
end end
...@@ -807,7 +807,7 @@ describe API::Projects, api: true do ...@@ -807,7 +807,7 @@ describe API::Projects, api: true do
context 'when unauthenticated' do context 'when unauthenticated' do
it_behaves_like 'project users response' do it_behaves_like 'project users response' do
let(:project) { create(:project, :public) } let(:project) { create(:empty_project, :public) }
let(:current_user) { nil } let(:current_user) { nil }
end end
end end
...@@ -921,11 +921,11 @@ describe API::Projects, api: true do ...@@ -921,11 +921,11 @@ describe API::Projects, api: true do
end end
describe :fork_admin do describe :fork_admin do
let(:project_fork_target) { create(:project) } let(:project_fork_target) { create(:empty_project) }
let(:project_fork_source) { create(:project, :public) } let(:project_fork_source) { create(:empty_project, :public) }
describe 'POST /projects/:id/fork/:forked_from_id' do describe 'POST /projects/:id/fork/:forked_from_id' do
let(:new_project_fork_source) { create(:project, :public) } let(:new_project_fork_source) { create(:empty_project, :public) }
it "is not available for non admin users" do it "is not available for non admin users" do
post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user) post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
...@@ -966,7 +966,7 @@ describe API::Projects, api: true do ...@@ -966,7 +966,7 @@ describe API::Projects, api: true do
end end
context 'when users belong to project group' do context 'when users belong to project group' do
let(:project_fork_target) { create(:project, group: create(:group)) } let(:project_fork_target) { create(:empty_project, group: create(:group)) }
before do before do
project_fork_target.group.add_owner user project_fork_target.group.add_owner user
...@@ -1121,7 +1121,6 @@ describe API::Projects, api: true do ...@@ -1121,7 +1121,6 @@ describe API::Projects, api: true do
it_behaves_like 'project search response', query: 'one.dot.two', results: 1 do it_behaves_like 'project search response', query: 'one.dot.two', results: 1 do
let(:current_user) { user } let(:current_user) { user }
end end
end end
context 'when authenticated as a different user' do context 'when authenticated as a different user' do
......
...@@ -7,8 +7,8 @@ describe API::Runners, api: true do ...@@ -7,8 +7,8 @@ describe API::Runners, api: true do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:user2) { create(:user) } let(:user2) { create(:user) }
let(:project) { create(:project, creator_id: user.id) } let(:project) { create(:empty_project, creator_id: user.id) }
let(:project2) { create(:project, creator_id: user.id) } let(:project2) { create(:empty_project, creator_id: user.id) }
let!(:shared_runner) { create(:ci_runner, :shared) } let!(:shared_runner) { create(:ci_runner, :shared) }
let!(:unused_specific_runner) { create(:ci_runner) } let!(:unused_specific_runner) { create(:ci_runner) }
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment