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 EnvironmentsService */
/* global Flash */
//= require vue
//= require vue-resource
......@@ -10,41 +11,6 @@
(() => {
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', {
props: {
store: {
......@@ -81,10 +47,6 @@
},
computed: {
filteredEnvironments() {
return filterEnvironmentsByState(filterState(this.visibility), this.state.environments);
},
scope() {
return this.$options.getQueryParameter('scope');
},
......@@ -111,7 +73,7 @@
const scope = this.$options.getQueryParameter('scope');
if (scope) {
this.visibility = scope;
this.store.storeVisibility(scope);
}
this.isLoading = true;
......@@ -121,6 +83,10 @@
.then((json) => {
this.store.storeEnvironments(json);
this.isLoading = false;
})
.catch(() => {
this.isLoading = false;
new Flash('An error occurred while fetching the environments.', 'alert');
});
},
......@@ -188,7 +154,7 @@
<div class="blank-state blank-state-no-icon"
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.
</h2>
<p class="blank-state-text">
......@@ -202,13 +168,13 @@
<a
v-if="canCreateEnvironmentParsed"
:href="newEnvironmentPath"
class="btn btn-create">
class="btn btn-create js-new-environment-button">
New Environment
</a>
</div>
<div class="table-holder"
v-if="!isLoading && state.environments.length > 0">
v-if="!isLoading && state.filteredEnvironments.length > 0">
<table class="table ci-table environments">
<thead>
<tr>
......@@ -221,7 +187,7 @@
</tr>
</thead>
<tbody>
<template v-for="model in filteredEnvironments"
<template v-for="model in state.filteredEnvironments"
v-bind:model="model">
<tr
......
......@@ -10,6 +10,8 @@
this.state.environments = [];
this.state.stoppedCounter = 0;
this.state.availableCounter = 0;
this.state.visibility = 'available';
this.state.filteredEnvironments = [];
return this;
},
......@@ -59,7 +61,7 @@
if (occurs.length) {
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 {
acc.push({
name: environment.environment_type,
......@@ -73,13 +75,70 @@
}
return acc;
}, []).sort(this.sortByName);
}, []).slice().sort(this.sortByName);
this.state.environments = environmentsTree;
this.filterEnvironmentsByVisibility(this.state.environments);
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.
*
......
......@@ -10,9 +10,9 @@
* The container should be the table element.
*
* The stage icon clicked needs to have the following HTML structure:
* <div>
* <button class="dropdown js-builds-dropdown-button"></button>
* <div class="js-builds-dropdown-container"></div>
* <div class="dropdown">
* <button class="dropdown js-builds-dropdown-button" data-toggle="dropdown"></button>
* <div class="js-builds-dropdown-container dropdown-menu"></div>
* </div>
*/
(() => {
......@@ -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() {
const dropdownButtonSelector = 'button.js-builds-dropdown-button';
$(this.container).off('click', dropdownButtonSelector, this.getBuildsList)
.on('click', dropdownButtonSelector, this.getBuildsList);
$(this.container).on('shown.bs.dropdown', this.getBuildsList);
}
/**
......@@ -52,11 +50,14 @@
/**
* 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}
*/
getBuildsList(e) {
const button = e.currentTarget;
const button = e.relatedTarget;
const endpoint = button.dataset.stageEndpoint;
return $.ajax({
......
......@@ -57,7 +57,7 @@
return function(response) {
var error;
if (response.errorCode) {
error = new U2FError(response.errorCode);
error = new U2FError(response.errorCode, 'authenticate');
return _this.renderError(error);
} else {
return _this.renderAuthenticated(JSON.stringify(response));
......
......@@ -5,21 +5,21 @@
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
this.U2FError = (function() {
function U2FError(errorCode) {
function U2FError(errorCode, u2fFlowType) {
this.errorCode = errorCode;
this.message = bind(this.message, this);
this.httpsDisabled = window.location.protocol !== 'https:';
this.u2fFlowType = u2fFlowType;
}
U2FError.prototype.message = function() {
switch (false) {
case !(this.errorCode === u2f.ErrorCodes.BAD_REQUEST && this.httpsDisabled):
return "U2F only works with HTTPS-enabled websites. Contact your administrator for more details.";
case this.errorCode !== u2f.ErrorCodes.DEVICE_INELIGIBLE:
return "This device has already been registered with us.";
default:
return "There was a problem communicating with your device.";
if (this.errorCode === u2f.ErrorCodes.BAD_REQUEST && this.httpsDisabled) {
return 'U2F only works with HTTPS-enabled websites. Contact your administrator for more details.';
} else if (this.errorCode === u2f.ErrorCodes.DEVICE_INELIGIBLE) {
if (this.u2fFlowType === 'authenticate') return 'This device has not been registered with us.';
if (this.u2fFlowType === 'register') return 'This device has already been registered with us.';
}
return "There was a problem communicating with your device.";
};
return U2FError;
......
......@@ -39,7 +39,7 @@
return function(response) {
var error;
if (response.errorCode) {
error = new U2FError(response.errorCode);
error = new U2FError(response.errorCode, 'register');
return _this.renderError(error);
} else {
return _this.renderRegistered(JSON.stringify(response));
......
......@@ -10,7 +10,7 @@
max-width: 100%;
}
*:first-child {
*:first-child:not(.katex-display) {
margin-top: 0;
}
......
......@@ -377,6 +377,10 @@
display: inline-block;
padding: 5px;
&:nth-of-type(7n) {
padding-right: 0;
}
.author_link {
display: block;
}
......
......@@ -6,21 +6,15 @@ class Projects::HooksController < Projects::ApplicationController
layout "project_settings"
def index
@hooks = @project.hooks
@hook = ProjectHook.new
end
def create
@hook = @project.hooks.new(hook_params)
@hook.save
if @hook.valid?
redirect_to namespace_project_hooks_path(@project.namespace, @project)
else
unless @hook.valid?
@hooks = @project.hooks.select(&:persisted?)
render :index
flash[:alert] = @hook.errors.full_messages.join.html_safe
end
redirect_to namespace_project_settings_integrations_path(@project.namespace, @project)
end
def test
......@@ -44,7 +38,7 @@ class Projects::HooksController < Projects::ApplicationController
def destroy
hook.destroy
redirect_to namespace_project_hooks_path(@project.namespace, @project)
redirect_to namespace_project_settings_integrations_path(@project.namespace, @project)
end
private
......
......@@ -9,10 +9,6 @@ class Projects::ServicesController < Projects::ApplicationController
layout "project_settings"
def index
@services = @project.find_or_initialize_services
end
def edit
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
end
# 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)
namespace_project_settings_members_path(project.namespace, project, *args)
end
......
......@@ -13,6 +13,49 @@ class ApplicationSetting < ActiveRecord::Base
[\r\n] # any number of newline characters
}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 :import_sources
serialize :disabled_oauth_sign_in_sources, Array
......@@ -163,46 +206,7 @@ class ApplicationSetting < ActiveRecord::Base
end
def self.create_from_defaults
create(
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,
)
create(DEFAULTS)
end
def home_page_url_column_exist
......
......@@ -92,6 +92,12 @@ module Ci
end
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|
build.run_after_commit do
BuildHooksWorker.perform_async(id)
......
......@@ -2,6 +2,7 @@ module Ci
class Runner < ActiveRecord::Base
extend Ci::Model
RUNNER_QUEUE_EXPIRY_TIME = 60.minutes
LAST_CONTACT_TIME = 1.hour.ago
AVAILABLE_SCOPES = %w[specific shared active paused online]
FORM_EDITABLE = %i[description tag_list active run_untagged locked]
......@@ -21,6 +22,8 @@ module Ci
scope :online, ->() { where('contacted_at > ?', LAST_CONTACT_TIME) }
scope :ordered, ->() { order(id: :desc) }
after_save :tick_runner_queue, if: :form_editable_changed?
scope :owned_or_shared, ->(project_id) do
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)
......@@ -122,8 +125,38 @@ module Ci
]
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
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
unless has_tags? || run_untagged?
errors.add(:tags_list,
......
......@@ -4,6 +4,8 @@ class Key < ActiveRecord::Base
include AfterCommitQueue
include Sortable
LAST_USED_AT_REFRESH_TIME = 1.day.to_i
belongs_to :user
before_validation :generate_fingerprint
......@@ -50,7 +52,10 @@ class Key < ActiveRecord::Base
end
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
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 @@
= link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys' do
%span
Deploy Keys
= nav_link(controller: :hooks) do
= link_to namespace_project_hooks_path(@project.namespace, @project), title: 'Webhooks' do
= nav_link(controller: :integrations) do
= link_to namespace_project_settings_integrations_path(@project.namespace, @project), title: 'Integrations' do
%span
Webhooks
= nav_link(controller: :services) do
= link_to namespace_project_services_path(@project.namespace, @project), title: 'Services' do
%span
Services
Integrations
= nav_link(controller: :protected_branches) do
= link_to namespace_project_protected_branches_path(@project.namespace, @project), title: 'Protected Branches' do
%span
......
......@@ -13,7 +13,7 @@
%a.close{ href: "#", "data-dismiss" => "modal" } ×
%h3.page-title== #{label} this #{commit.change_type_title(current_user)}
.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
= label_tag 'target_branch', target_label, class: 'control-label'
.col-sm-10
......
- page_title "Services"
.row.prepend-top-default.append-bottom-default
.col-lg-3
%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
.col-lg-3
%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
end
end
end
namespace :settings do
resource :members, only: [:show]
resource :integrations, only: [:show]
end
# 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
## University
[University](university/README.md) contain guides to learn Git and GitLab through courses and videos.
## User documentation
- [Account Security](user/account/security.md) Securing your account via two-factor authentication, etc.
......@@ -19,7 +23,6 @@
- [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.
- [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 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.
Once you have your token, pass it to the API using either the `private_token`
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
......@@ -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"
[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-5951]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5951
......@@ -61,7 +61,18 @@ correctly with your CI builds:
1. First, make sure you have used [relative URLs](#configuring-the-gitmodules-file)
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
before_script:
......
......@@ -1034,6 +1034,41 @@ variables:
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
> Introduced in GitLab, it requires GitLab Runner v1.9+.
......
......@@ -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 supports so far. At any time you can revoke any access by just clicking
**Revoke**.
GitLab's OAuth applications support scopes, which allow various actions that any given
application 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 access by just clicking **Revoke**.
[oauth]: http://oauth.net/2/ "OAuth website"
......@@ -52,7 +52,7 @@ for anonymous users. The group page now has a visibility level icon.
## 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.
When visiting the public page of a user, you can only see the projects which
......@@ -60,10 +60,13 @@ you are privileged to.
If the public level is restricted, user profiles are only visible to logged in users.
## Restricting the use of public or internal projects
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
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.
......@@ -39,10 +39,10 @@ Feature: Project Active Tab
# Sub Tabs: Settings
Scenario: On Project Settings/Hooks
Scenario: On Project Settings/Integrations
Given I visit my project's settings page
And I click the "Hooks" tab
Then the active sub nav should be Hooks
And I click the "Integrations" tab
Then the active sub nav should be Integrations
And no other sub navs should be active
And the active main tab should be Settings
......
......@@ -27,8 +27,8 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
end
end
step 'I click the "Hooks" tab' do
click_link('Webhooks')
step 'I click the "Integrations" tab' do
click_link('Integrations')
end
step 'I click the "Deploy Keys" tab' do
......@@ -39,8 +39,8 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
ensure_active_sub_nav('Members')
end
step 'the active sub nav should be Hooks' do
ensure_active_sub_nav('Webhooks')
step 'the active sub nav should be Integrations' do
ensure_active_sub_nav('Integrations')
end
step 'the active sub nav should be Deploy Keys' do
......
......@@ -36,12 +36,12 @@ class Spinach::Features::ProjectHooks < Spinach::FeatureSteps
end
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)
end
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("SSL Verification: enabled")
end
......@@ -57,7 +57,7 @@ class Spinach::Features::ProjectHooks < Spinach::FeatureSteps
end
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',
text: 'Hook executed successfully: HTTP 200'
end
......
......@@ -4,7 +4,7 @@ class Spinach::Features::ProjectServices < Spinach::FeatureSteps
include SharedPaths
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
step 'I should see list of available services' do
......
......@@ -256,7 +256,7 @@ module SharedPaths
end
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
step 'I visit project deploy keys page' do
......
......@@ -226,7 +226,7 @@ module API
end
def render_api_error!(message, status)
error!({ 'message' => message }, status)
error!({ 'message' => message }, status, header)
end
def handle_api_exception(exception)
......
......@@ -16,6 +16,13 @@ module Ci
not_found! unless current_runner.active?
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)
if build
......@@ -26,6 +33,8 @@ module Ci
else
Gitlab::Metrics.add_event(:build_not_found)
header 'X-GitLab-Last-Update', new_update
build_not_found!
end
end
......
......@@ -9,7 +9,9 @@ module Gitlab
end
def ensure_application_settings!
if connect_to_db?
return fake_application_settings unless connect_to_db?
unless ENV['IN_MEMORY_APPLICATION_SETTINGS'] == 'true'
begin
settings = ::ApplicationSetting.current
# In case Redis isn't running or the Redis UNIX socket file is not available
......@@ -20,43 +22,23 @@ module Gitlab
settings ||= ::ApplicationSetting.create_from_defaults unless ActiveRecord::Migrator.needs_migration?
end
settings || fake_application_settings
settings || in_memory_application_settings
end
def sidekiq_throttling_enabled?
current_application_settings.sidekiq_throttling_enabled?
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
OpenStruct.new(
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,
)
OpenStruct.new(ApplicationSetting::DEFAULTS)
end
private
......
module Gitlab
module GithubImport
class ProjectCreator
include Gitlab::CurrentSettings
attr_reader :repo, :name, :namespace, :current_user, :session_data, :type
def initialize(repo, name, namespace, current_user, session_data, type: 'github')
......@@ -34,7 +36,7 @@ module Gitlab
end
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
#
......
module Gitlab
module ImportExport
class MembersMapper
attr_reader :missing_author_ids
def initialize(exported_members:, user:, project:)
@exported_members = exported_members
@exported_members = user.admin? ? exported_members : []
@user = user
@project = project
@missing_author_ids = []
# This needs to run first, as second call would be from #map
# which means project members already exist.
......@@ -39,7 +36,6 @@ module Gitlab
def missing_keys_tracking_hash
Hash.new do |_, key|
@missing_author_ids << key
default_user_id
end
end
......@@ -64,7 +60,7 @@ module Gitlab
end
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
def user_arel
......
......@@ -14,7 +14,7 @@ module Gitlab
priorities: :label_priorities,
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
......@@ -80,17 +80,13 @@ module Gitlab
# is left.
def set_note_author
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')
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
def missing_author?(old_author_id)
!admin_user? || @members_mapper.missing_author_ids.include?(old_author_id)
def has_author?(old_author_id)
admin_user? && @members_mapper.map.keys.include?(old_author_id)
end
def missing_author_note(updated_at, author_name)
......
require 'spec_helper'
describe HealthCheckController do
include StubENV
let(:token) { current_application_settings.health_check_access_token }
let(:json_response) { JSON.parse(response.body) }
let(:xml_response) { Hash.from_xml(response.body)['hash'] }
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
end
describe 'GET #index' do
context 'when services are up but NO access token' 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'
feature 'Admin disables Git access protocol', feature: true do
include StubENV
let(:project) { create(:empty_project, :empty_repo) }
let(:admin) { create(:admin) }
background do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
login_as(admin)
end
......
require 'spec_helper'
feature "Admin Health Check", feature: true do
include StubENV
include WaitForAjax
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
login_as :admin
end
......@@ -12,11 +14,12 @@ feature "Admin Health Check", feature: true do
visit admin_health_check_path
end
it { page.has_text? 'Health Check' }
it { page.has_text? 'Health information can be retrieved' }
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
expect(page).to have_content("Access token is #{token}")
expect(page).to have_selector('#health-check-token', text: token)
end
......
require 'spec_helper'
describe "Admin Runners" do
include StubENV
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
login_as :admin
end
......
require 'spec_helper'
feature 'Admin updates settings', feature: true do
before(:each) do
include StubENV
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
login_as :admin
visit admin_application_settings_path
end
......
require 'rails_helper'
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
project = create(:empty_project)
......
......@@ -2,7 +2,8 @@ require 'spec_helper'
describe 'Cherry-pick Merge Requests' do
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) }
before do
......
......@@ -2,7 +2,8 @@ require 'spec_helper'
include WaitForAjax
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_merge) { project.commit('e56497bb5f03a90a51293fc6d516788730953899') }
......
......@@ -262,8 +262,8 @@ describe "Internal Project Access", feature: true do
it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/hooks" do
subject { namespace_project_hooks_path(project.namespace, project) }
describe "GET /:project_path/settings/integrations" do
subject { namespace_project_settings_integrations_path(project.namespace, project) }
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
......
......@@ -234,8 +234,8 @@ describe "Private Project Access", feature: true do
it { is_expected.to be_denied_for(:visitor) }
end
describe "GET /:project_path/hooks" do
subject { namespace_project_hooks_path(project.namespace, project) }
describe "GET /:project_path/namespace/hooks" do
subject { namespace_project_settings_integrations_path(project.namespace, project) }
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
......
......@@ -400,8 +400,8 @@ describe "Public Project Access", feature: true do
it { is_expected.to be_allowed_for(:visitor) }
end
describe "GET /:project_path/hooks" do
subject { namespace_project_hooks_path(project.namespace, project) }
describe "GET /:project_path/settings/integrations" do
subject { namespace_project_settings_integrations_path(project.namespace, project) }
it { is_expected.to be_allowed_for(:admin) }
it { is_expected.to be_allowed_for(:owner).of(project) }
......
......@@ -165,7 +165,7 @@ describe 'Dashboard Todos', feature: true do
end
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
it 'links to the pipelines for the merge request' do
......
......@@ -2,13 +2,13 @@ require 'spec_helper'
describe MoveToProjectFinder do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:project) { create(:empty_project) }
let(:no_access_project) { create(:project) }
let(:guest_project) { create(:project) }
let(:reporter_project) { create(:project) }
let(:developer_project) { create(:project) }
let(:master_project) { create(:project) }
let(:no_access_project) { create(:empty_project) }
let(:guest_project) { create(:empty_project) }
let(:reporter_project) { create(:empty_project) }
let(:developer_project) { create(:empty_project) }
let(:master_project) { create(:empty_project) }
subject { described_class.new(user) }
......@@ -37,7 +37,7 @@ describe MoveToProjectFinder do
it 'does not return archived projects' do
reporter_project.team << [user, :reporter]
reporter_project.update_attributes(archived: true)
other_reporter_project = create(:project)
other_reporter_project = create(:empty_project)
other_reporter_project.team << [user, :reporter]
expect(subject.execute(project).to_a).to eq([other_reporter_project])
......@@ -46,7 +46,7 @@ describe MoveToProjectFinder do
it 'does not return projects for which issues are disabled' do
reporter_project.team << [user, :reporter]
reporter_project.update_attributes(issues_enabled: false)
other_reporter_project = create(:project)
other_reporter_project = create(:empty_project)
other_reporter_project.team << [user, :reporter]
expect(subject.execute(project).to_a).to eq([other_reporter_project])
......@@ -83,10 +83,10 @@ describe MoveToProjectFinder do
end
it 'returns projects matching a search query' do
foo_project = create(:project)
foo_project = create(:empty_project)
foo_project.team << [user, :master]
wadus_project = create(:project, name: 'wadus')
wadus_project = create(:empty_project, name: 'wadus')
wadus_project.team << [user, :master]
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 = [
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
#environments-list-view{ data: { environments_data: "https://gitlab.com/foo/environments",
#environments-list-view{ data: { environments_data: "foo/environments",
"can-create-deployment" => "true",
"can-read-environment" => "true",
"can-create-environment" => "true",
......
%div.js-builds-dropdown-tests
%button.dropdown.js-builds-dropdown-button{'data-stage-endpoint' => 'foobar'}
%div.js-builds-dropdown-tests.dropdown.dropdown.js-mini-pipeline-graph
%button.js-builds-dropdown-button{'data-stage-endpoint' => 'foobar', data: { toggle: '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
require 'spec_helper'
describe Gitlab::CurrentSettings do
include StubENV
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
end
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)
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
it 'does not attempt to connect to DB or Redis' do
allow_any_instance_of(Gitlab::CurrentSettings).to receive(:connect_to_db?).and_return(false)
expect(ApplicationSetting).not_to receive(:current)
it 'attempts to use cached values first' do
expect(ApplicationSetting).to receive(:current)
expect(ApplicationSetting).not_to receive(:last)
expect(current_application_settings).to eq fake_application_settings
expect(current_application_settings).to be_a(ApplicationSetting)
end
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(current_application_settings).to be_a(ApplicationSetting)
end
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(:last).and_call_original
expect(current_application_settings).to be_a(ApplicationSetting)
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
......@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::ImportExport::MembersMapper, services: true 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(:user2) { create(:user, authorized_projects_populated: true) }
let(:exported_user_id) { 99 }
......@@ -24,7 +24,7 @@ describe Gitlab::ImportExport::MembersMapper, services: true do
{
"id" => exported_user_id,
"email" => user2.email,
"username" => user2.username
"username" => 'test'
}
},
{
......@@ -48,6 +48,10 @@ describe Gitlab::ImportExport::MembersMapper, services: true do
exported_members: exported_members, user: user, project: project)
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
expect(members_mapper.map[exported_user_id]).to eq(user2.id)
end
......@@ -56,12 +60,6 @@ describe Gitlab::ImportExport::MembersMapper, services: true do
expect(members_mapper.map[-1]).to eq(user.id)
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
members_mapper.map
......@@ -74,5 +72,25 @@ describe Gitlab::ImportExport::MembersMapper, services: true do
expect(user.authorized_project?(project)).to be true
expect(user2.authorized_project?(project)).to be true
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
......@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::ImportExport::RelationFactory, lib: true do
let(:project) { create(:empty_project) }
let(:members_mapper) { double('members_mapper').as_null_object }
let(:user) { create(:user) }
let(:user) { create(:admin) }
let(:created_object) do
described_class.create(relation_sym: relation_sym,
relation_hash: relation_hash,
......@@ -122,4 +122,60 @@ describe Gitlab::ImportExport::RelationFactory, lib: true do
expect(created_object.values).not_to include(99)
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
......@@ -1401,4 +1401,14 @@ describe Ci::Build, :models do
it { is_expected.to eq(%w[predefined project pipeline yaml secret]) }
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
......@@ -263,6 +263,62 @@ describe Ci::Runner, models: true do
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
let(:runner) { create(:ci_runner) }
let(:project) { create(:project) }
......
......@@ -30,13 +30,32 @@ describe Key, models: true do
end
describe "#update_last_used_at" do
it "enqueues a UseKeyWorker job" do
key = create(:key)
let(: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)
key.update_last_used_at
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
context "validation of uniqueness (based on fingerprint uniqueness)" do
......
......@@ -65,7 +65,7 @@ describe MergeRequest, models: true do
end
describe '#target_branch_sha' do
let(:project) { create(:project) }
let(:project) { create(:project, :repository) }
subject { create(:merge_request, source_project: project, target_project: project) }
......@@ -150,7 +150,7 @@ describe MergeRequest, models: true do
end
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"
end
......@@ -245,8 +245,8 @@ describe MergeRequest, models: true do
describe '#for_fork?' do
it 'returns true if the merge request is for a fork' do
subject.source_project = create(:project, namespace: create(:group))
subject.target_project = create(:project, namespace: create(:group))
subject.source_project = build_stubbed(:empty_project, namespace: create(:group))
subject.target_project = build_stubbed(:empty_project, namespace: create(:group))
expect(subject.for_fork?).to be_truthy
end
......@@ -501,8 +501,8 @@ describe MergeRequest, models: true do
end
describe '#diverged_commits_count' do
let(:project) { create(:project) }
let(:fork_project) { create(:project, forked_from_project: project) }
let(:project) { create(:project, :repository) }
let(:fork_project) { create(:project, :repository, forked_from_project: project) }
context 'when the target branch does not exist anymore' do
subject { create(:merge_request, source_project: project, target_project: project) }
......@@ -727,7 +727,7 @@ describe MergeRequest, models: true do
end
describe '#participants' do
let(:project) { create(:project, :public) }
let(:project) { create(:empty_project, :public) }
let(:mr) do
create(:merge_request, source_project: project, target_project: project)
......@@ -768,7 +768,7 @@ describe MergeRequest, models: true do
end
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) }
......@@ -810,7 +810,7 @@ describe MergeRequest, models: true do
end
describe '#mergeable?' do
let(:project) { create(:project) }
let(:project) { create(:empty_project) }
subject { create(:merge_request, source_project: project) }
......@@ -830,7 +830,7 @@ describe MergeRequest, models: true do
end
describe '#mergeable_state?' do
let(:project) { create(:project) }
let(:project) { create(:project, :repository) }
subject { create(:merge_request, source_project: project) }
......@@ -957,7 +957,7 @@ describe MergeRequest, models: true do
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
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
before do
......@@ -991,7 +991,7 @@ describe MergeRequest, models: true do
end
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
before do
......@@ -1006,7 +1006,7 @@ describe MergeRequest, models: true do
end
describe "#environments" do
let(:project) { create(:project) }
let(:project) { create(:project, :repository) }
let(:merge_request) { create(:merge_request, source_project: project) }
context 'with multiple environments' do
......@@ -1024,7 +1024,7 @@ describe MergeRequest, models: true do
context 'with environments on 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)
end
end
......@@ -1401,8 +1401,8 @@ describe MergeRequest, models: true do
end
describe "#source_project_missing?" do
let(:project) { create(:project) }
let(:fork_project) { create(:project, forked_from_project: project) }
let(:project) { create(:empty_project) }
let(:fork_project) { create(:empty_project, forked_from_project: project) }
let(:user) { create(:user) }
let(:unlink_project) { Projects::UnlinkForkService.new(fork_project, user) }
......@@ -1439,8 +1439,8 @@ describe MergeRequest, models: true do
end
describe "#closed_without_fork?" do
let(:project) { create(:project) }
let(:fork_project) { create(:project, forked_from_project: project) }
let(:project) { create(:empty_project) }
let(:fork_project) { create(:empty_project, forked_from_project: project) }
let(:user) { create(:user) }
let(:unlink_project) { Projects::UnlinkForkService.new(fork_project, user) }
......@@ -1485,9 +1485,9 @@ describe MergeRequest, models: true do
end
context 'forked project' do
let(:project) { create(:project) }
let(:project) { create(:empty_project) }
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
create(:closed_merge_request,
......@@ -1531,7 +1531,7 @@ describe MergeRequest, models: true do
status: status)
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(:user) { create(:user) }
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
let(:stranger) { create(:user) }
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 << [master, :master]
project.request_access(access_requester)
......
......@@ -8,7 +8,7 @@ describe API::Boards, api: true do
let(:non_member) { create(:user) }
let(:guest) { create(:user) }
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
create(:label, title: 'Development', color: '#FFAABB', project: project)
......@@ -188,7 +188,7 @@ describe API::Boards, api: true do
context "when the user is project owner" do
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
delete api("#{base_url}/#{dev_list.id}", owner)
......
......@@ -5,8 +5,8 @@ describe API::DeployKeys, api: true do
let(:user) { create(:user) }
let(:admin) { create(:admin) }
let(:project) { create(:project, creator_id: user.id) }
let(:project2) { create(:project, creator_id: user.id) }
let(:project) { create(:empty_project, creator_id: user.id) }
let(:project2) { create(:empty_project, creator_id: user.id) }
let(:deploy_key) { create(:deploy_key, public: true) }
let!(:deploy_keys_project) do
......
......@@ -5,7 +5,7 @@ describe API::Environments, api: true do
let(:user) { 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) }
before do
......
require 'spec_helper'
describe API::API, api: true do
describe API::Projects, api: true do
include ApiHelpers
let(:user) { create(:user) }
let(:user2) { create(:user) }
......
......@@ -10,9 +10,9 @@ describe API::Groups, api: true do
let(:admin) { create(:admin) }
let!(:group1) { create(:group, avatar: File.open(uploaded_image_temp_path)) }
let!(:group2) { create(:group, :private) }
let!(:project1) { create(:project, namespace: group1) }
let!(:project2) { create(:project, namespace: group2) }
let!(:project3) { create(:project, namespace: group1, path: 'test', visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
let!(:project1) { create(:empty_project, namespace: group1) }
let!(:project2) { create(:empty_project, namespace: group2) }
let!(:project3) { create(:empty_project, namespace: group1, path: 'test', visibility_level: Gitlab::VisibilityLevel::PRIVATE) }
before do
group1.add_owner(user1)
......@@ -163,7 +163,7 @@ describe API::Groups, api: true do
describe "GET /groups/:id" do
context "when authenticated as user" 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)
get api("/groups/#{group1.id}", user1)
......@@ -287,7 +287,7 @@ describe API::Groups, api: true do
expect(json_response.length).to eq(2)
project_names = json_response.map { |proj| proj['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
it "returns the group's projects with simple representation" do
......@@ -297,11 +297,11 @@ describe API::Groups, api: true do
expect(json_response.length).to eq(2)
project_names = json_response.map { |proj| proj['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
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'
......@@ -462,7 +462,7 @@ describe API::Groups, api: true do
end
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}" }
before(:each) do
......
......@@ -12,6 +12,7 @@ describe API::Helpers, api: true do
let(:params) { {} }
let(:env) { { 'REQUEST_METHOD' => 'GET' } }
let(:request) { Rack::Request.new(env) }
let(:header) { }
def set_env(user_or_token, identifier)
clear_env
......@@ -46,7 +47,7 @@ describe API::Helpers, api: true do
allow_any_instance_of(self.class).to receive(:doorkeeper_guard){ value }
end
def error!(message, status)
def error!(message, status, header)
raise Exception.new("#{status} - #{message}")
end
......
......@@ -239,7 +239,7 @@ describe API::Internal, api: true do
end
context "blocked user" do
let(:personal_project) { create(:project, namespace: user.namespace) }
let(:personal_project) { create(:empty_project, namespace: user.namespace) }
before do
user.block
......@@ -265,7 +265,7 @@ describe API::Internal, api: true do
end
context "archived project" do
let(:personal_project) { create(:project, namespace: user.namespace) }
let(:personal_project) { create(:empty_project, namespace: user.namespace) }
before do
project.team << [user, :developer]
......@@ -337,8 +337,7 @@ describe API::Internal, api: true do
context 'ssh access has been disabled' do
before do
settings = ::ApplicationSetting.create_from_defaults
settings.update_attribute(:enabled_git_access_protocol, 'http')
stub_application_setting(enabled_git_access_protocol: 'http')
end
it 'rejects the SSH push' do
......@@ -360,8 +359,7 @@ describe API::Internal, api: true do
context 'http access has been disabled' do
before do
settings = ::ApplicationSetting.create_from_defaults
settings.update_attribute(:enabled_git_access_protocol, 'ssh')
stub_application_setting(enabled_git_access_protocol: 'ssh')
end
it 'rejects the HTTP push' do
......@@ -383,8 +381,7 @@ describe API::Internal, api: true do
context 'web actions are always allowed' do
it 'allows WEB push' do
settings = ::ApplicationSetting.create_from_defaults
settings.update_attribute(:enabled_git_access_protocol, 'ssh')
stub_application_setting(enabled_git_access_protocol: 'ssh')
project.team << [user, :developer]
push(key, project, 'web')
......
......@@ -11,7 +11,7 @@ describe API::Issues, api: true do
let(:author) { create(:author) }
let(:assignee) { create(:assignee) }
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
create :closed_issue,
author: user,
......@@ -224,7 +224,7 @@ describe API::Issues, api: true do
describe "GET /groups/:id/issues" do
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
create :closed_issue,
author: user,
......@@ -1052,7 +1052,7 @@ describe API::Issues, api: true do
context "when the user is project owner" do
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
delete api("/projects/#{project.id}/issues/#{issue.id}", owner)
......@@ -1071,8 +1071,8 @@ describe API::Issues, api: true do
end
describe '/projects/:id/issues/:issue_id/move' do
let!(:target_project) { create(: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_project) { create(:empty_project, path: 'project2', creator_id: user.id, namespace: user.namespace ) }
let!(:target_project2) { create(:empty_project, creator_id: non_member.id, namespace: non_member.namespace ) }
it 'moves an issue' do
post api("/projects/#{project.id}/issues/#{issue.id}/move", user),
......
......@@ -4,7 +4,7 @@ describe API::Labels, api: true do
include ApiHelpers
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!(:priority_label) { create(:label, title: 'bug', project: project, priority: 3) }
......
......@@ -9,7 +9,7 @@ describe API::Members, api: true do
let(:stranger) { create(:user) }
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 << [master, :master]
project.request_access(access_requester)
......
......@@ -308,8 +308,8 @@ describe API::MergeRequests, api: true do
context 'forked projects' do
let!(:user2) { create(:user) }
let!(:fork_project) { create(: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!(:fork_project) { create(:empty_project, forked_from_project: project, namespace: user2.namespace, creator_id: user2.id) }
let!(:unrelated_project) { create(:empty_project, namespace: create(:user).namespace, creator_id: user2.id) }
before :each do |each|
fork_project.team << [user2, :reporter]
......
......@@ -3,7 +3,7 @@ require 'spec_helper'
describe API::Notes, api: true do
include ApiHelpers
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!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) }
let!(:snippet) { create(:project_snippet, project: project, author: user) }
......@@ -14,12 +14,12 @@ describe API::Notes, api: true do
# For testing the cross-reference of a private issue in a public issue
let(:private_user) { create(:user) }
let(:private_project) do
create(:project, namespace: private_user.namespace).
create(:empty_project, namespace: private_user.namespace).
tap { |p| p.team << [private_user, :master] }
end
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!(:cross_reference_note) do
......@@ -265,7 +265,7 @@ describe API::Notes, api: true do
end
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
......
......@@ -5,7 +5,7 @@ describe API::NotificationSettings, api: true do
let(:user) { create(:user) }
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
it "returns global notification settings for the current user" do
......
......@@ -4,7 +4,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do
include ApiHelpers
let(:user) { 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
create(:project_hook,
:all_events_enabled,
......@@ -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
test_user = create(:user)
other_project = create(:project)
other_project = create(:empty_project)
other_project.team << [test_user, :master]
delete api("/projects/#{other_project.id}/hooks/#{hook.id}", test_user)
......
......@@ -8,8 +8,8 @@ describe API::Projects, api: true do
let(:user2) { create(:user) }
let(:user3) { create(:user) }
let(:admin) { create(:admin) }
let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
let(:project2) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace) }
let(:project) { create(:empty_project, 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(:project_member) { create(:project_member, :master, user: user, project: project) }
let(:project_member2) { create(:project_member, :developer, user: user3, project: project) }
......@@ -32,7 +32,7 @@ describe API::Projects, api: true do
access_level: ProjectMember::MASTER)
end
let(:project4) do
create(:project,
create(:empty_project,
name: 'third_project',
path: 'third_project',
creator_id: user4.id,
......@@ -252,7 +252,7 @@ describe API::Projects, api: true do
end
end
let!(:public_project) { create(:project, :public) }
let!(:public_project) { create(:empty_project, :public) }
before do
project
project2
......@@ -283,7 +283,7 @@ describe API::Projects, api: true do
end
describe 'GET /projects/starred' do
let(:public_project) { create(:project, :public) }
let(:public_project) { create(:empty_project, :public) }
before do
project_member2
......@@ -583,7 +583,7 @@ describe API::Projects, api: true do
describe 'GET /projects/:id' do
context 'when unauthenticated' do
it 'returns the public projects' do
public_project = create(:project, :public)
public_project = create(:empty_project, :public)
get api("/projects/#{public_project.id}")
......@@ -665,7 +665,7 @@ describe API::Projects, api: true do
it 'handles users with dots' do
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)
expect(response).to have_http_status(200)
......@@ -711,7 +711,7 @@ describe API::Projects, api: true do
end
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) }
......@@ -756,7 +756,7 @@ describe API::Projects, api: true do
context 'when unauthenticated' do
it_behaves_like 'project events response' do
let(:project) { create(:project, :public) }
let(:project) { create(:empty_project, :public) }
let(:current_user) { nil }
end
end
......@@ -807,7 +807,7 @@ describe API::Projects, api: true do
context 'when unauthenticated' do
it_behaves_like 'project users response' do
let(:project) { create(:project, :public) }
let(:project) { create(:empty_project, :public) }
let(:current_user) { nil }
end
end
......@@ -921,11 +921,11 @@ describe API::Projects, api: true do
end
describe :fork_admin do
let(:project_fork_target) { create(:project) }
let(:project_fork_source) { create(:project, :public) }
let(:project_fork_target) { create(:empty_project) }
let(:project_fork_source) { create(:empty_project, :public) }
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
post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
......@@ -966,7 +966,7 @@ describe API::Projects, api: true do
end
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
project_fork_target.group.add_owner user
......@@ -1121,7 +1121,6 @@ describe API::Projects, api: true do
it_behaves_like 'project search response', query: 'one.dot.two', results: 1 do
let(:current_user) { user }
end
end
context 'when authenticated as a different user' do
......
......@@ -7,8 +7,8 @@ describe API::Runners, api: true do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:project) { create(:project, creator_id: user.id) }
let(:project2) { create(:project, creator_id: user.id) }
let(:project) { create(:empty_project, creator_id: user.id) }
let(:project2) { create(:empty_project, creator_id: user.id) }
let!(:shared_runner) { create(:ci_runner, :shared) }
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