Commit 2a62f47b authored by Fatih Acet's avatar Fatih Acet

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into revert-c676283b-existing

parents 1956bd5e c9396bc7
...@@ -453,6 +453,10 @@ Style/VariableName: ...@@ -453,6 +453,10 @@ Style/VariableName:
EnforcedStyle: snake_case EnforcedStyle: snake_case
Enabled: true Enabled: true
# Use the configured style when numbering variables.
Style/VariableNumber:
Enabled: false
# Use when x then ... for one-line cases. # Use when x then ... for one-line cases.
Style/WhenThen: Style/WhenThen:
Enabled: true Enabled: true
......
This diff is collapsed.
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 8.13.0 (unreleased) v 8.13.0 (unreleased)
- Update runner version only when updating contacted_at
- Add link from system note to compare with previous version - Add link from system note to compare with previous version
- Use gitlab-shell v3.6.2 (GIT TRACE logging) - Use gitlab-shell v3.6.2 (GIT TRACE logging)
- Fix centering of custom header logos (Ashley Dumaine) - Fix centering of custom header logos (Ashley Dumaine)
...@@ -15,12 +16,15 @@ v 8.13.0 (unreleased) ...@@ -15,12 +16,15 @@ v 8.13.0 (unreleased)
- Simplify Mentionable concern instance methods - Simplify Mentionable concern instance methods
- Fix permission for setting an issue's due date - Fix permission for setting an issue's due date
- Expose expires_at field when sharing project on API - Expose expires_at field when sharing project on API
- Fix VueJS template tags being rendered in code comments
- Fix issue with page scrolling to top when closing or pinning sidebar (lukehowell) - Fix issue with page scrolling to top when closing or pinning sidebar (lukehowell)
- Allow the Koding integration to be configured through the API - Allow the Koding integration to be configured through the API
- Added soft wrap button to repository file/blob editor - Added soft wrap button to repository file/blob editor
- Add word-wrap to issue title on issue and milestone boards (ClemMakesApps) - Add word-wrap to issue title on issue and milestone boards (ClemMakesApps)
- Fix todos page mobile viewport layout (ClemMakesApps)
- Fix robots.txt disallowing access to groups starting with "s" (Matt Harrison) - Fix robots.txt disallowing access to groups starting with "s" (Matt Harrison)
- Close open merge request without source project (Katarzyna Kobierska Ula Budziszewska) - Close open merge request without source project (Katarzyna Kobierska Ula Budziszewska)
- Fix that manual jobs would no longer block jobs in the next stage. !6604
- Add configurable email subject suffix (Fu Xu) - Add configurable email subject suffix (Fu Xu)
- Use a ConnectionPool for Rails.cache on Sidekiq servers - Use a ConnectionPool for Rails.cache on Sidekiq servers
- Replace `alias_method_chain` with `Module#prepend` - Replace `alias_method_chain` with `Module#prepend`
...@@ -34,6 +38,7 @@ v 8.13.0 (unreleased) ...@@ -34,6 +38,7 @@ v 8.13.0 (unreleased)
- Add missing values to linter !6276 (Katarzyna Kobierska Ula Budziszewska) - Add missing values to linter !6276 (Katarzyna Kobierska Ula Budziszewska)
- Fix Long commit messages overflow viewport in file tree - Fix Long commit messages overflow viewport in file tree
- Revert avoid touching file system on Build#artifacts? - Revert avoid touching file system on Build#artifacts?
- Stop using a Redis lease when updating the project activity timestamp whenever a new event is created
- Add broadcast messages and alerts below sub-nav - Add broadcast messages and alerts below sub-nav
- Better empty state for Groups view - Better empty state for Groups view
- Update ruby-prof to 0.16.2. !6026 (Elan Ruusamäe) - Update ruby-prof to 0.16.2. !6026 (Elan Ruusamäe)
...@@ -43,6 +48,7 @@ v 8.13.0 (unreleased) ...@@ -43,6 +48,7 @@ v 8.13.0 (unreleased)
- Optimize GitHub importing for speed and memory - Optimize GitHub importing for speed and memory
- API: expose pipeline data in builds API (!6502, Guilherme Salazar) - API: expose pipeline data in builds API (!6502, Guilherme Salazar)
- Notify the Merger about merge after successful build (Dimitris Karakasilis) - Notify the Merger about merge after successful build (Dimitris Karakasilis)
- Reduce queries needed to find users using their SSH keys when pushing commits
- Fix broken repository 500 errors in project list - Fix broken repository 500 errors in project list
- Close todos when accepting merge requests via the API !6486 (tonygambone) - Close todos when accepting merge requests via the API !6486 (tonygambone)
- Changed Slack service user referencing from full name to username (Sebastian Poxhofer) - Changed Slack service user referencing from full name to username (Sebastian Poxhofer)
...@@ -50,6 +56,8 @@ v 8.13.0 (unreleased) ...@@ -50,6 +56,8 @@ v 8.13.0 (unreleased)
v 8.12.4 (unreleased) v 8.12.4 (unreleased)
- Fix type mismatch bug when closing Jira issue - Fix type mismatch bug when closing Jira issue
- Skip wiki creation when GitHub project has wiki enabled
- Fix failed project deletion when feature visibility set to private
- Fix issues importing services via Import/Export - Fix issues importing services via Import/Export
- Restrict failed login attempts for users with 2FA enabled - Restrict failed login attempts for users with 2FA enabled
- Fix "Copy to clipboard" tooltip to say "Copied!" when clipboard button is clicked. (lukehowell) - Fix "Copy to clipboard" tooltip to say "Copied!" when clipboard button is clicked. (lukehowell)
...@@ -57,7 +65,7 @@ v 8.12.4 (unreleased) ...@@ -57,7 +65,7 @@ v 8.12.4 (unreleased)
v 8.12.3 v 8.12.3
- Update Gitlab Shell to support low IO priority for storage moves - Update Gitlab Shell to support low IO priority for storage moves
v 8.12.2 (unreleased) v 8.12.2
- Fix Import/Export not recognising correctly the imported services. - Fix Import/Export not recognising correctly the imported services.
- Fix snippets pagination - Fix snippets pagination
- Fix "Create project" button layout when visibility options are restricted - Fix "Create project" button layout when visibility options are restricted
......
...@@ -295,7 +295,7 @@ group :development, :test do ...@@ -295,7 +295,7 @@ group :development, :test do
gem 'spring-commands-spinach', '~> 1.1.0' gem 'spring-commands-spinach', '~> 1.1.0'
gem 'spring-commands-teaspoon', '~> 0.0.2' gem 'spring-commands-teaspoon', '~> 0.0.2'
gem 'rubocop', '~> 0.42.0', require: false gem 'rubocop', '~> 0.43.0', require: false
gem 'rubocop-rspec', '~> 1.5.0', require: false gem 'rubocop-rspec', '~> 1.5.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false gem 'scss_lint', '~> 0.47.0', require: false
gem 'haml_lint', '~> 0.18.2', require: false gem 'haml_lint', '~> 0.18.2', require: false
......
...@@ -487,7 +487,7 @@ GEM ...@@ -487,7 +487,7 @@ GEM
orm_adapter (0.5.0) orm_adapter (0.5.0)
paranoia (2.1.4) paranoia (2.1.4)
activerecord (~> 4.0) activerecord (~> 4.0)
parser (2.3.1.2) parser (2.3.1.4)
ast (~> 2.2) ast (~> 2.2)
pg (0.18.4) pg (0.18.4)
pkg-config (1.1.7) pkg-config (1.1.7)
...@@ -620,7 +620,7 @@ GEM ...@@ -620,7 +620,7 @@ GEM
rspec-retry (0.4.5) rspec-retry (0.4.5)
rspec-core rspec-core
rspec-support (3.5.0) rspec-support (3.5.0)
rubocop (0.42.0) rubocop (0.43.0)
parser (>= 2.3.1.1, < 3.0) parser (>= 2.3.1.1, < 3.0)
powerpack (~> 0.1) powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0) rainbow (>= 1.99.1, < 3.0)
...@@ -938,7 +938,7 @@ DEPENDENCIES ...@@ -938,7 +938,7 @@ DEPENDENCIES
rqrcode-rails3 (~> 0.1.7) rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.5.0) rspec-rails (~> 3.5.0)
rspec-retry (~> 0.4.5) rspec-retry (~> 0.4.5)
rubocop (~> 0.42.0) rubocop (~> 0.43.0)
rubocop-rspec (~> 1.5.0) rubocop-rspec (~> 1.5.0)
ruby-fogbugz (~> 0.2.1) ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.16.2) ruby-prof (~> 0.16.2)
......
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
-webkit-flex-direction: column; -webkit-flex-direction: column;
flex-direction: column; flex-direction: column;
margin-left: 10px; margin-left: 10px;
min-width: 55px;
} }
.todo-item { .todo-item {
...@@ -120,6 +121,14 @@ ...@@ -120,6 +121,14 @@
} }
} }
@media (max-width: $screen-sm-max) {
.todos-filters {
.dropdown-menu-toggle {
width: 135px;
}
}
}
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
.todo { .todo {
.avatar { .avatar {
...@@ -141,4 +150,14 @@ ...@@ -141,4 +150,14 @@
padding-left: 10px; padding-left: 10px;
} }
} }
.todos-filters {
.row-content-block {
padding-bottom: 50px;
}
.dropdown-menu-toggle {
width: 100%;
}
}
} }
...@@ -196,7 +196,7 @@ module Ci ...@@ -196,7 +196,7 @@ module Ci
end end
def has_warnings? def has_warnings?
builds.latest.ignored.any? builds.latest.failed_but_allowed.any?
end end
def config_processor def config_processor
......
...@@ -2,7 +2,7 @@ module Ci ...@@ -2,7 +2,7 @@ module Ci
class Runner < ActiveRecord::Base class Runner < ActiveRecord::Base
extend Ci::Model extend Ci::Model
LAST_CONTACT_TIME = 2.hours.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]
......
...@@ -24,7 +24,22 @@ class CommitStatus < ActiveRecord::Base ...@@ -24,7 +24,22 @@ class CommitStatus < ActiveRecord::Base
scope :retried, -> { where.not(id: latest) } scope :retried, -> { where.not(id: latest) }
scope :ordered, -> { order(:name) } scope :ordered, -> { order(:name) }
scope :ignored, -> { where(allow_failure: true, status: [:failed, :canceled]) }
scope :failed_but_allowed, -> do
where(allow_failure: true, status: [:failed, :canceled])
end
scope :exclude_ignored, -> do
quoted_when = connection.quote_column_name('when')
# We want to ignore failed_but_allowed jobs
where("allow_failure = ? OR status IN (?)",
false, all_state_names - [:failed, :canceled]).
# We want to ignore skipped manual jobs
where("#{quoted_when} <> ? OR status <> ?", 'manual', 'skipped').
# We want to ignore skipped on_failure
where("#{quoted_when} <> ? OR status <> ?", 'on_failure', 'skipped')
end
scope :latest_ci_stages, -> { latest.ordered.includes(project: :namespace) } scope :latest_ci_stages, -> { latest.ordered.includes(project: :namespace) }
scope :retried_ci_stages, -> { retried.ordered.includes(project: :namespace) } scope :retried_ci_stages, -> { retried.ordered.includes(project: :namespace) }
...@@ -111,7 +126,7 @@ class CommitStatus < ActiveRecord::Base ...@@ -111,7 +126,7 @@ class CommitStatus < ActiveRecord::Base
end end
end end
def ignored? def failed_but_allowed?
allow_failure? && (failed? || canceled?) allow_failure? && (failed? || canceled?)
end end
......
...@@ -8,32 +8,32 @@ module HasStatus ...@@ -8,32 +8,32 @@ module HasStatus
class_methods do class_methods do
def status_sql def status_sql
scope = all scope = if respond_to?(:exclude_ignored)
exclude_ignored
else
all
end
builds = scope.select('count(*)').to_sql builds = scope.select('count(*)').to_sql
created = scope.created.select('count(*)').to_sql created = scope.created.select('count(*)').to_sql
success = scope.success.select('count(*)').to_sql success = scope.success.select('count(*)').to_sql
ignored = scope.ignored.select('count(*)').to_sql if scope.respond_to?(:ignored)
ignored ||= '0'
pending = scope.pending.select('count(*)').to_sql pending = scope.pending.select('count(*)').to_sql
running = scope.running.select('count(*)').to_sql running = scope.running.select('count(*)').to_sql
canceled = scope.canceled.select('count(*)').to_sql
skipped = scope.skipped.select('count(*)').to_sql skipped = scope.skipped.select('count(*)').to_sql
canceled = scope.canceled.select('count(*)').to_sql
deduce_status = "(CASE "(CASE
WHEN (#{builds})=(#{success}) THEN 'success'
WHEN (#{builds})=(#{created}) THEN 'created' WHEN (#{builds})=(#{created}) THEN 'created'
WHEN (#{builds})=(#{skipped}) THEN 'skipped' WHEN (#{builds})=(#{success})+(#{skipped}) THEN 'skipped'
WHEN (#{builds})=(#{success})+(#{ignored})+(#{skipped}) THEN 'success' WHEN (#{builds})=(#{success})+(#{skipped})+(#{canceled}) THEN 'canceled'
WHEN (#{builds})=(#{created})+(#{pending})+(#{skipped}) THEN 'pending' WHEN (#{builds})=(#{created})+(#{skipped})+(#{pending}) THEN 'pending'
WHEN (#{builds})=(#{canceled})+(#{success})+(#{ignored})+(#{skipped}) THEN 'canceled'
WHEN (#{running})+(#{pending})+(#{created})>0 THEN 'running' WHEN (#{running})+(#{pending})+(#{created})>0 THEN 'running'
ELSE 'failed' ELSE 'failed'
END)" END)"
deduce_status
end end
def status def status
all.pluck(self.status_sql).first all.pluck(status_sql).first
end end
def started_at def started_at
...@@ -43,6 +43,10 @@ module HasStatus ...@@ -43,6 +43,10 @@ module HasStatus
def finished_at def finished_at
all.maximum(:finished_at) all.maximum(:finished_at)
end end
def all_state_names
state_machines.values.flat_map(&:states).flat_map { |s| s.map(&:name) }
end
end end
included do included do
......
...@@ -328,13 +328,15 @@ class Event < ActiveRecord::Base ...@@ -328,13 +328,15 @@ class Event < ActiveRecord::Base
def reset_project_activity def reset_project_activity
return unless project return unless project
# Don't even bother obtaining a lock if the last update happened less than # Don't bother updating if we know the project was updated recently.
# 60 minutes ago.
return if recent_update? return if recent_update?
return unless try_obtain_lease # At this point it's possible for multiple threads/processes to try to
# update the project. Only one query should actually perform the update,
project.update_column(:last_activity_at, created_at) # hence we add the extra WHERE clause for last_activity_at.
Project.unscoped.where(id: project_id).
where('last_activity_at > ?', RESET_PROJECT_ACTIVITY_INTERVAL.ago).
update_all(last_activity_at: created_at)
end end
private private
...@@ -342,11 +344,4 @@ class Event < ActiveRecord::Base ...@@ -342,11 +344,4 @@ class Event < ActiveRecord::Base
def recent_update? def recent_update?
project.last_activity_at > RESET_PROJECT_ACTIVITY_INTERVAL.ago project.last_activity_at > RESET_PROJECT_ACTIVITY_INTERVAL.ago
end end
def try_obtain_lease
Gitlab::ExclusiveLease.
new("project:update_last_activity_at:#{project.id}",
timeout: RESET_PROJECT_ACTIVITY_INTERVAL.to_i).
try_obtain
end
end end
...@@ -20,7 +20,10 @@ class ProjectFeature < ActiveRecord::Base ...@@ -20,7 +20,10 @@ class ProjectFeature < ActiveRecord::Base
FEATURES = %i(issues merge_requests wiki snippets builds) FEATURES = %i(issues merge_requests wiki snippets builds)
belongs_to :project # Default scopes force us to unscope here since a service may need to check
# permissions for a project in pending_delete
# http://stackoverflow.com/questions/1540645/how-to-disable-default-scope-for-a-belongs-to
belongs_to :project, -> { unscope(where: :pending_delete) }
default_value_for :builds_access_level, value: ENABLED, allows_nil: false default_value_for :builds_access_level, value: ENABLED, allows_nil: false
default_value_for :issues_access_level, value: ENABLED, allows_nil: false default_value_for :issues_access_level, value: ENABLED, allows_nil: false
......
...@@ -279,6 +279,11 @@ class User < ActiveRecord::Base ...@@ -279,6 +279,11 @@ class User < ActiveRecord::Base
find_by('users.username = ? OR users.id = ?', name_or_id.to_s, name_or_id.to_i) find_by('users.username = ? OR users.id = ?', name_or_id.to_s, name_or_id.to_i)
end end
# Returns a user for the given SSH key.
def find_by_ssh_key_id(key_id)
find_by(id: Key.unscoped.select(:user_id).where(id: key_id))
end
def build_user(attrs = {}) def build_user(attrs = {})
User.new(attrs) User.new(attrs)
end end
......
...@@ -7,6 +7,8 @@ module Projects ...@@ -7,6 +7,8 @@ module Projects
def execute def execute
forked_from_project_id = params.delete(:forked_from_project_id) forked_from_project_id = params.delete(:forked_from_project_id)
import_data = params.delete(:import_data) import_data = params.delete(:import_data)
@skip_wiki = params.delete(:skip_wiki)
@project = Project.new(params) @project = Project.new(params)
# Make sure that the user is allowed to use the specified visibility level # Make sure that the user is allowed to use the specified visibility level
...@@ -80,7 +82,7 @@ module Projects ...@@ -80,7 +82,7 @@ module Projects
log_info("#{@project.owner.name} created a new project \"#{@project.name_with_namespace}\"") log_info("#{@project.owner.name} created a new project \"#{@project.name_with_namespace}\"")
unless @project.gitlab_project_import? unless @project.gitlab_project_import?
@project.create_wiki if @project.feature_available?(:wiki, current_user) @project.create_wiki unless skip_wiki?
@project.build_missing_services @project.build_missing_services
@project.create_labels @project.create_labels
...@@ -94,6 +96,10 @@ module Projects ...@@ -94,6 +96,10 @@ module Projects
end end
end end
def skip_wiki?
!@project.feature_available?(:wiki, current_user) || @skip_wiki
end
def save_project_and_import_data(import_data) def save_project_and_import_data(import_data)
Project.transaction do Project.transaction do
@project.create_or_update_import_data(data: import_data[:data], credentials: import_data[:credentials]) if import_data @project.create_or_update_import_data(data: import_data[:data], credentials: import_data[:credentials]) if import_data
......
...@@ -12,7 +12,7 @@ revisions (to reduce disk space and increase performance) and removing ...@@ -12,7 +12,7 @@ revisions (to reduce disk space and increase performance) and removing
unreachable objects which may have been created from prior invocations of unreachable objects which may have been created from prior invocations of
`git add`. `git add`.
You can find this option under your **[Project] > Settings**. You can find this option under your **[Project] > Edit Project**.
--- ---
......
...@@ -221,7 +221,7 @@ time. ...@@ -221,7 +221,7 @@ time.
*Note: The following commands are run without root privileges. You should be *Note: The following commands are run without root privileges. You should be
able to run docker with your regular user account.* able to run docker with your regular user account.*
First start with creating a file named `build script`: First start with creating a file named `build_script`:
```bash ```bash
cat <<EOF > build_script cat <<EOF > build_script
......
...@@ -25,7 +25,7 @@ module Banzai ...@@ -25,7 +25,7 @@ module Banzai
return if customized?(whitelist[:transformers]) return if customized?(whitelist[:transformers])
# Allow code highlighting # Allow code highlighting
whitelist[:attributes]['pre'] = %w(class) whitelist[:attributes]['pre'] = %w(class v-pre)
whitelist[:attributes]['span'] = %w(class) whitelist[:attributes]['span'] = %w(class)
# Allow table alignment # Allow table alignment
......
...@@ -30,7 +30,7 @@ module Banzai ...@@ -30,7 +30,7 @@ module Banzai
# users can still access an issue/comment/etc. # users can still access an issue/comment/etc.
end end
highlighted = %(<pre class="#{css_classes}"><code>#{code}</code></pre>) highlighted = %(<pre class="#{css_classes}" v-pre="true"><code>#{code}</code></pre>)
# Extracted to a method to measure it # Extracted to a method to measure it
replace_parent_pre_element(node, highlighted) replace_parent_pre_element(node, highlighted)
......
...@@ -12,10 +12,9 @@ module Ci ...@@ -12,10 +12,9 @@ module Ci
# POST /builds/register # POST /builds/register
post "register" do post "register" do
authenticate_runner! authenticate_runner!
update_runner_last_contact(save: false)
update_runner_info
required_attributes! [:token] required_attributes! [:token]
not_found! unless current_runner.active? not_found! unless current_runner.active?
update_runner_info
build = Ci::RegisterBuildService.new.execute(current_runner) build = Ci::RegisterBuildService.new.execute(current_runner)
...@@ -41,10 +40,11 @@ module Ci ...@@ -41,10 +40,11 @@ module Ci
# PUT /builds/:id # PUT /builds/:id
put ":id" do put ":id" do
authenticate_runner! authenticate_runner!
update_runner_last_contact
build = Ci::Build.where(runner_id: current_runner.id).running.find(params[:id]) build = Ci::Build.where(runner_id: current_runner.id).running.find(params[:id])
forbidden!('Build has been erased!') if build.erased? forbidden!('Build has been erased!') if build.erased?
update_runner_info
build.update_attributes(trace: params[:trace]) if params[:trace] build.update_attributes(trace: params[:trace]) if params[:trace]
Gitlab::Metrics.add_event(:update_build, Gitlab::Metrics.add_event(:update_build,
......
...@@ -3,7 +3,7 @@ module Ci ...@@ -3,7 +3,7 @@ module Ci
module Helpers module Helpers
BUILD_TOKEN_HEADER = "HTTP_BUILD_TOKEN" BUILD_TOKEN_HEADER = "HTTP_BUILD_TOKEN"
BUILD_TOKEN_PARAM = :token BUILD_TOKEN_PARAM = :token
UPDATE_RUNNER_EVERY = 40 * 60 UPDATE_RUNNER_EVERY = 10 * 60
def authenticate_runners! def authenticate_runners!
forbidden! unless runner_registration_token_valid? forbidden! unless runner_registration_token_valid?
...@@ -30,14 +30,22 @@ module Ci ...@@ -30,14 +30,22 @@ module Ci
token && (build.valid_token?(token) || build.project.valid_runners_token?(token)) token && (build.valid_token?(token) || build.project.valid_runners_token?(token))
end end
def update_runner_last_contact(save: true) def update_runner_info
# Use a random threshold to prevent beating DB updates return unless update_runner?
# it generates a distribution between: [40m, 80m]
current_runner.contacted_at = Time.now
current_runner.assign_attributes(get_runner_version_from_params)
current_runner.save if current_runner.changed?
end
def update_runner?
# Use a random threshold to prevent beating DB updates.
# It generates a distribution between [40m, 80m].
#
contacted_at_max_age = UPDATE_RUNNER_EVERY + Random.rand(UPDATE_RUNNER_EVERY) contacted_at_max_age = UPDATE_RUNNER_EVERY + Random.rand(UPDATE_RUNNER_EVERY)
if current_runner.contacted_at.nil? || Time.now - current_runner.contacted_at >= contacted_at_max_age
current_runner.contacted_at = Time.now current_runner.contacted_at.nil? ||
current_runner.save if current_runner.changed? && save (Time.now - current_runner.contacted_at) >= contacted_at_max_age
end
end end
def build_not_found! def build_not_found!
...@@ -57,11 +65,6 @@ module Ci ...@@ -57,11 +65,6 @@ module Ci
attributes_for_keys(["name", "version", "revision", "platform", "architecture"], params["info"]) attributes_for_keys(["name", "version", "revision", "platform", "architecture"], params["info"])
end end
def update_runner_info
current_runner.assign_attributes(get_runner_version_from_params)
current_runner.save if current_runner.changed?
end
def max_artifacts_size def max_artifacts_size
current_application_settings.max_artifacts_size.megabytes.to_i current_application_settings.max_artifacts_size.megabytes.to_i
end end
......
...@@ -170,10 +170,9 @@ module Gitlab ...@@ -170,10 +170,9 @@ module Gitlab
end end
def import_wiki def import_wiki
unless project.wiki_enabled? unless project.wiki.repository_exists?
wiki = WikiFormatter.new(project) wiki = WikiFormatter.new(project)
gitlab_shell.import_repository(project.repository_storage_path, wiki.path_with_namespace, wiki.import_url) gitlab_shell.import_repository(project.repository_storage_path, wiki.path_with_namespace, wiki.import_url)
project.project.update_attribute(:wiki_access_level, ProjectFeature::ENABLED)
end end
rescue Gitlab::Shell::Error => e rescue Gitlab::Shell::Error => e
# GitHub error message when the wiki repo has not been created, # GitHub error message when the wiki repo has not been created,
......
module Gitlab module Gitlab
module GithubImport module GithubImport
class ProjectCreator class ProjectCreator
attr_reader :repo, :namespace, :current_user, :session_data attr_reader :repo, :name, :namespace, :current_user, :session_data
def initialize(repo, name, namespace, current_user, session_data) def initialize(repo, name, namespace, current_user, session_data)
@repo = repo @repo = repo
...@@ -12,24 +12,37 @@ module Gitlab ...@@ -12,24 +12,37 @@ module Gitlab
end end
def execute def execute
project = ::Projects::CreateService.new( ::Projects::CreateService.new(
current_user, current_user,
name: @name, name: name,
path: @name, path: name,
description: repo.description, description: repo.description,
namespace_id: namespace.id, namespace_id: namespace.id,
visibility_level: repo.private ? Gitlab::VisibilityLevel::PRIVATE : ApplicationSetting.current.default_project_visibility, visibility_level: visibility_level,
import_type: "github", import_type: "github",
import_source: repo.full_name, import_source: repo.full_name,
import_url: repo.clone_url.sub("https://", "https://#{@session_data[:github_access_token]}@") import_url: import_url,
skip_wiki: skip_wiki
).execute ).execute
end
private
# If repo has wiki we'll import it later def import_url
if repo.has_wiki? && project repo.clone_url.sub('https://', "https://#{session_data[:github_access_token]}@")
project.project_feature.update_attribute(:wiki_access_level, ProjectFeature::DISABLED) end
end
def visibility_level
repo.private ? Gitlab::VisibilityLevel::PRIVATE : ApplicationSetting.current.default_project_visibility
end
project #
# If the GitHub project repository has wiki, we should not create the
# default wiki. Otherwise the GitHub importer will fail because the wiki
# repository already exist.
#
def skip_wiki
repo.has_wiki?
end end
end end
end end
......
...@@ -5,19 +5,61 @@ module Gitlab ...@@ -5,19 +5,61 @@ module Gitlab
def identify(identifier, project, newrev) def identify(identifier, project, newrev)
if identifier.blank? if identifier.blank?
# Local push from gitlab # Local push from gitlab
email = project.commit(newrev).author_email rescue nil identify_using_commit(project, newrev)
User.find_by(email: email) if email
elsif identifier =~ /\Auser-\d+\Z/ elsif identifier =~ /\Auser-\d+\Z/
# git push over http # git push over http
user_id = identifier.gsub("user-", "") identify_using_user(identifier)
User.find_by(id: user_id)
elsif identifier =~ /\Akey-\d+\Z/ elsif identifier =~ /\Akey-\d+\Z/
# git push over ssh # git push over ssh
key_id = identifier.gsub("key-", "") identify_using_ssh_key(identifier)
Key.find_by(id: key_id).try(:user) end
end
# Tries to identify a user based on a commit SHA.
def identify_using_commit(project, ref)
commit = project.commit(ref)
return if !commit || !commit.author_email
email = commit.author_email
identify_with_cache(:email, email) do
User.find_by(email: email)
end end
end end
# Tries to identify a user based on a user identifier (e.g. "user-123").
def identify_using_user(identifier)
user_id = identifier.gsub("user-", "")
identify_with_cache(:user, user_id) do
User.find_by(id: user_id)
end
end
# Tries to identify a user based on an SSH key identifier (e.g. "key-123").
def identify_using_ssh_key(identifier)
key_id = identifier.gsub("key-", "")
identify_with_cache(:ssh_key, key_id) do
User.find_by_ssh_key_id(key_id)
end
end
def identify_with_cache(category, key)
if identification_cache[category].key?(key)
identification_cache[category][key]
else
identification_cache[category][key] = yield
end
end
def identification_cache
@identification_cache ||= {
email: {},
user: {},
ssh_key: {}
}
end
end end
end end
...@@ -240,6 +240,18 @@ describe 'Comments', feature: true do ...@@ -240,6 +240,18 @@ describe 'Comments', feature: true do
is_expected.to have_css('.notes_holder .note', count: 1) is_expected.to have_css('.notes_holder .note', count: 1)
is_expected.to have_button('Reply...') is_expected.to have_button('Reply...')
end end
it 'adds code to discussion' do
click_button 'Reply...'
page.within(first('.js-discussion-note-form')) do
fill_in 'note[note]', with: '```{{ test }}```'
click_button('Comment')
end
expect(page).to have_content('{{ test }}')
end
end end
end end
end end
......
...@@ -6,21 +6,21 @@ describe Banzai::Filter::SyntaxHighlightFilter, lib: true do ...@@ -6,21 +6,21 @@ describe Banzai::Filter::SyntaxHighlightFilter, lib: true do
context "when no language is specified" do context "when no language is specified" do
it "highlights as plaintext" do it "highlights as plaintext" do
result = filter('<pre><code>def fun end</code></pre>') result = filter('<pre><code>def fun end</code></pre>')
expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext"><code>def fun end</code></pre>') expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext" v-pre="true"><code>def fun end</code></pre>')
end end
end end
context "when a valid language is specified" do context "when a valid language is specified" do
it "highlights as that language" do it "highlights as that language" do
result = filter('<pre><code class="ruby">def fun end</code></pre>') result = filter('<pre><code class="ruby">def fun end</code></pre>')
expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight ruby"><code><span class="k">def</span> <span class="nf">fun</span> <span class="k">end</span></code></pre>') expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight ruby" v-pre="true"><code><span class="k">def</span> <span class="nf">fun</span> <span class="k">end</span></code></pre>')
end end
end end
context "when an invalid language is specified" do context "when an invalid language is specified" do
it "highlights as plaintext" do it "highlights as plaintext" do
result = filter('<pre><code class="gnuplot">This is a test</code></pre>') result = filter('<pre><code class="gnuplot">This is a test</code></pre>')
expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext"><code>This is a test</code></pre>') expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext" v-pre="true"><code>This is a test</code></pre>')
end end
end end
...@@ -31,7 +31,7 @@ describe Banzai::Filter::SyntaxHighlightFilter, lib: true do ...@@ -31,7 +31,7 @@ describe Banzai::Filter::SyntaxHighlightFilter, lib: true do
it "highlights as plaintext" do it "highlights as plaintext" do
result = filter('<pre><code class="ruby">This is a test</code></pre>') result = filter('<pre><code class="ruby">This is a test</code></pre>')
expect(result.to_html).to eq('<pre class="code highlight"><code>This is a test</code></pre>') expect(result.to_html).to eq('<pre class="code highlight" v-pre="true"><code>This is a test</code></pre>')
end end
end end
end end
...@@ -33,7 +33,7 @@ describe Gitlab::GithubImport::ProjectCreator, lib: true do ...@@ -33,7 +33,7 @@ describe Gitlab::GithubImport::ProjectCreator, lib: true do
expect(project.import_data.credentials).to eq(user: 'asdffg', password: nil) expect(project.import_data.credentials).to eq(user: 'asdffg', password: nil)
end end
context 'when Github project is private' do context 'when GitHub project is private' do
it 'sets project visibility to private' do it 'sets project visibility to private' do
repo.private = true repo.private = true
...@@ -43,7 +43,7 @@ describe Gitlab::GithubImport::ProjectCreator, lib: true do ...@@ -43,7 +43,7 @@ describe Gitlab::GithubImport::ProjectCreator, lib: true do
end end
end end
context 'when Github project is public' do context 'when GitHub project is public' do
before do before do
allow_any_instance_of(ApplicationSetting).to receive(:default_project_visibility).and_return(Gitlab::VisibilityLevel::INTERNAL) allow_any_instance_of(ApplicationSetting).to receive(:default_project_visibility).and_return(Gitlab::VisibilityLevel::INTERNAL)
end end
...@@ -56,5 +56,25 @@ describe Gitlab::GithubImport::ProjectCreator, lib: true do ...@@ -56,5 +56,25 @@ describe Gitlab::GithubImport::ProjectCreator, lib: true do
expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL) expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
end end
end end
context 'when GitHub project has wiki' do
it 'does not create the wiki repository' do
allow(repo).to receive(:has_wiki?).and_return(true)
project = service.execute
expect(project.wiki.repository_exists?).to eq false
end
end
context 'when GitHub project does not have wiki' do
it 'creates the wiki repository' do
allow(repo).to receive(:has_wiki?).and_return(false)
project = service.execute
expect(project.wiki.repository_exists?).to eq true
end
end
end end
end end
require 'spec_helper'
describe Gitlab::Identifier do
let(:identifier) do
Class.new { include Gitlab::Identifier }.new
end
let(:project) { create(:empty_project) }
let(:user) { create(:user) }
let(:key) { create(:key, user: user) }
describe '#identify' do
context 'without an identifier' do
it 'identifies the user using a commit' do
expect(identifier).to receive(:identify_using_commit).
with(project, '123')
identifier.identify('', project, '123')
end
end
context 'with a user identifier' do
it 'identifies the user using a user ID' do
expect(identifier).to receive(:identify_using_user).
with("user-#{user.id}")
identifier.identify("user-#{user.id}", project, '123')
end
end
context 'with an SSH key identifier' do
it 'identifies the user using an SSH key ID' do
expect(identifier).to receive(:identify_using_ssh_key).
with("key-#{key.id}")
identifier.identify("key-#{key.id}", project, '123')
end
end
end
describe '#identify_using_commit' do
it "returns the User for an existing commit author's Email address" do
commit = double(:commit, author_email: user.email)
expect(project).to receive(:commit).with('123').and_return(commit)
expect(identifier.identify_using_commit(project, '123')).to eq(user)
end
it 'returns nil when no user could be found' do
allow(project).to receive(:commit).with('123').and_return(nil)
expect(identifier.identify_using_commit(project, '123')).to be_nil
end
it 'returns nil when the commit does not have an author Email' do
commit = double(:commit, author_email: nil)
expect(project).to receive(:commit).with('123').and_return(commit)
expect(identifier.identify_using_commit(project, '123')).to be_nil
end
it 'caches the found users per Email' do
commit = double(:commit, author_email: user.email)
expect(project).to receive(:commit).with('123').twice.and_return(commit)
expect(User).to receive(:find_by).once.and_call_original
2.times do
expect(identifier.identify_using_commit(project, '123')).to eq(user)
end
end
end
describe '#identify_using_user' do
it 'returns the User for an existing ID in the identifier' do
found = identifier.identify_using_user("user-#{user.id}")
expect(found).to eq(user)
end
it 'returns nil for a non existing user ID' do
found = identifier.identify_using_user('user--1')
expect(found).to be_nil
end
it 'caches the found users per ID' do
expect(User).to receive(:find_by).once.and_call_original
2.times do
found = identifier.identify_using_user("user-#{user.id}")
expect(found).to eq(user)
end
end
end
describe '#identify_using_ssh_key' do
it 'returns the User for an existing SSH key' do
found = identifier.identify_using_ssh_key("key-#{key.id}")
expect(found).to eq(user)
end
it 'returns nil for an invalid SSH key' do
found = identifier.identify_using_ssh_key('key--1')
expect(found).to be_nil
end
it 'caches the found users per key' do
expect(User).to receive(:find_by_ssh_key_id).once.and_call_original
2.times do
found = identifier.identify_using_ssh_key("key-#{key.id}")
expect(found).to eq(user)
end
end
end
end
...@@ -39,8 +39,8 @@ describe Ci::Build, models: true do ...@@ -39,8 +39,8 @@ describe Ci::Build, models: true do
end end
end end
describe '#ignored?' do describe '#failed_but_allowed?' do
subject { build.ignored? } subject { build.failed_but_allowed? }
context 'when build is not allowed to fail' do context 'when build is not allowed to fail' do
before do before do
......
...@@ -7,7 +7,11 @@ describe CommitStatus, models: true do ...@@ -7,7 +7,11 @@ describe CommitStatus, models: true do
create(:ci_pipeline, project: project, sha: project.commit.id) create(:ci_pipeline, project: project, sha: project.commit.id)
end end
let(:commit_status) { create(:commit_status, pipeline: pipeline) } let(:commit_status) { create_status }
def create_status(args = {})
create(:commit_status, args.merge(pipeline: pipeline))
end
it { is_expected.to belong_to(:pipeline) } it { is_expected.to belong_to(:pipeline) }
it { is_expected.to belong_to(:user) } it { is_expected.to belong_to(:user) }
...@@ -125,32 +129,53 @@ describe CommitStatus, models: true do ...@@ -125,32 +129,53 @@ describe CommitStatus, models: true do
describe '.latest' do describe '.latest' do
subject { CommitStatus.latest.order(:id) } subject { CommitStatus.latest.order(:id) }
before do let(:statuses) do
@commit1 = FactoryGirl.create :commit_status, pipeline: pipeline, name: 'aa', ref: 'bb', status: 'running' [create_status(name: 'aa', ref: 'bb', status: 'running'),
@commit2 = FactoryGirl.create :commit_status, pipeline: pipeline, name: 'cc', ref: 'cc', status: 'pending' create_status(name: 'cc', ref: 'cc', status: 'pending'),
@commit3 = FactoryGirl.create :commit_status, pipeline: pipeline, name: 'aa', ref: 'cc', status: 'success' create_status(name: 'aa', ref: 'cc', status: 'success'),
@commit4 = FactoryGirl.create :commit_status, pipeline: pipeline, name: 'cc', ref: 'bb', status: 'success' create_status(name: 'cc', ref: 'bb', status: 'success'),
@commit5 = FactoryGirl.create :commit_status, pipeline: pipeline, name: 'aa', ref: 'bb', status: 'success' create_status(name: 'aa', ref: 'bb', status: 'success')]
end end
it 'returns unique statuses' do it 'returns unique statuses' do
is_expected.to eq([@commit4, @commit5]) is_expected.to eq(statuses.values_at(3, 4))
end end
end end
describe '.running_or_pending' do describe '.running_or_pending' do
subject { CommitStatus.running_or_pending.order(:id) } subject { CommitStatus.running_or_pending.order(:id) }
before do let(:statuses) do
@commit1 = FactoryGirl.create :commit_status, pipeline: pipeline, name: 'aa', ref: 'bb', status: 'running' [create_status(name: 'aa', ref: 'bb', status: 'running'),
@commit2 = FactoryGirl.create :commit_status, pipeline: pipeline, name: 'cc', ref: 'cc', status: 'pending' create_status(name: 'cc', ref: 'cc', status: 'pending'),
@commit3 = FactoryGirl.create :commit_status, pipeline: pipeline, name: 'aa', ref: nil, status: 'success' create_status(name: 'aa', ref: nil, status: 'success'),
@commit4 = FactoryGirl.create :commit_status, pipeline: pipeline, name: 'dd', ref: nil, status: 'failed' create_status(name: 'dd', ref: nil, status: 'failed'),
@commit5 = FactoryGirl.create :commit_status, pipeline: pipeline, name: 'ee', ref: nil, status: 'canceled' create_status(name: 'ee', ref: nil, status: 'canceled')]
end end
it 'returns statuses that are running or pending' do it 'returns statuses that are running or pending' do
is_expected.to eq([@commit1, @commit2]) is_expected.to eq(statuses.values_at(0, 1))
end
end
describe '.exclude_ignored' do
subject { CommitStatus.exclude_ignored.order(:id) }
let(:statuses) do
[create_status(when: 'manual', status: 'skipped'),
create_status(when: 'manual', status: 'success'),
create_status(when: 'manual', status: 'failed'),
create_status(when: 'on_failure', status: 'skipped'),
create_status(when: 'on_failure', status: 'success'),
create_status(when: 'on_failure', status: 'failed'),
create_status(allow_failure: true, status: 'success'),
create_status(allow_failure: true, status: 'failed'),
create_status(allow_failure: false, status: 'success'),
create_status(allow_failure: false, status: 'failed')]
end
it 'returns statuses without what we want to ignore' do
is_expected.to eq(statuses.values_at(1, 2, 4, 5, 6, 8, 9))
end end
end end
......
require 'spec_helper' require 'spec_helper'
describe HasStatus do describe HasStatus do
before do
@object = Object.new
@object.extend(HasStatus::ClassMethods)
end
describe '.status' do describe '.status' do
before do subject { CommitStatus.status }
allow(@object).to receive(:all).and_return(CommitStatus.where(id: statuses))
end
subject { @object.status }
shared_examples 'build status summary' do shared_examples 'build status summary' do
context 'all successful' do context 'all successful' do
let(:statuses) { Array.new(2) { create(type, status: :success) } } let!(:statuses) { Array.new(2) { create(type, status: :success) } }
it { is_expected.to eq 'success' } it { is_expected.to eq 'success' }
end end
context 'at least one failed' do context 'at least one failed' do
let(:statuses) do let!(:statuses) do
[create(type, status: :success), create(type, status: :failed)] [create(type, status: :success), create(type, status: :failed)]
end end
...@@ -28,7 +19,7 @@ describe HasStatus do ...@@ -28,7 +19,7 @@ describe HasStatus do
end end
context 'at least one running' do context 'at least one running' do
let(:statuses) do let!(:statuses) do
[create(type, status: :success), create(type, status: :running)] [create(type, status: :success), create(type, status: :running)]
end end
...@@ -36,7 +27,7 @@ describe HasStatus do ...@@ -36,7 +27,7 @@ describe HasStatus do
end end
context 'at least one pending' do context 'at least one pending' do
let(:statuses) do let!(:statuses) do
[create(type, status: :success), create(type, status: :pending)] [create(type, status: :success), create(type, status: :pending)]
end end
...@@ -44,7 +35,7 @@ describe HasStatus do ...@@ -44,7 +35,7 @@ describe HasStatus do
end end
context 'success and failed but allowed to fail' do context 'success and failed but allowed to fail' do
let(:statuses) do let!(:statuses) do
[create(type, status: :success), [create(type, status: :success),
create(type, status: :failed, allow_failure: true)] create(type, status: :failed, allow_failure: true)]
end end
...@@ -53,12 +44,15 @@ describe HasStatus do ...@@ -53,12 +44,15 @@ describe HasStatus do
end end
context 'one failed but allowed to fail' do context 'one failed but allowed to fail' do
let(:statuses) { [create(type, status: :failed, allow_failure: true)] } let!(:statuses) do
[create(type, status: :failed, allow_failure: true)]
end
it { is_expected.to eq 'success' } it { is_expected.to eq 'success' }
end end
context 'success and canceled' do context 'success and canceled' do
let(:statuses) do let!(:statuses) do
[create(type, status: :success), create(type, status: :canceled)] [create(type, status: :success), create(type, status: :canceled)]
end end
...@@ -66,7 +60,7 @@ describe HasStatus do ...@@ -66,7 +60,7 @@ describe HasStatus do
end end
context 'one failed and one canceled' do context 'one failed and one canceled' do
let(:statuses) do let!(:statuses) do
[create(type, status: :failed), create(type, status: :canceled)] [create(type, status: :failed), create(type, status: :canceled)]
end end
...@@ -74,7 +68,7 @@ describe HasStatus do ...@@ -74,7 +68,7 @@ describe HasStatus do
end end
context 'one failed but allowed to fail and one canceled' do context 'one failed but allowed to fail and one canceled' do
let(:statuses) do let!(:statuses) do
[create(type, status: :failed, allow_failure: true), [create(type, status: :failed, allow_failure: true),
create(type, status: :canceled)] create(type, status: :canceled)]
end end
...@@ -83,7 +77,7 @@ describe HasStatus do ...@@ -83,7 +77,7 @@ describe HasStatus do
end end
context 'one running one canceled' do context 'one running one canceled' do
let(:statuses) do let!(:statuses) do
[create(type, status: :running), create(type, status: :canceled)] [create(type, status: :running), create(type, status: :canceled)]
end end
...@@ -91,14 +85,15 @@ describe HasStatus do ...@@ -91,14 +85,15 @@ describe HasStatus do
end end
context 'all canceled' do context 'all canceled' do
let(:statuses) do let!(:statuses) do
[create(type, status: :canceled), create(type, status: :canceled)] [create(type, status: :canceled), create(type, status: :canceled)]
end end
it { is_expected.to eq 'canceled' } it { is_expected.to eq 'canceled' }
end end
context 'success and canceled but allowed to fail' do context 'success and canceled but allowed to fail' do
let(:statuses) do let!(:statuses) do
[create(type, status: :success), [create(type, status: :success),
create(type, status: :canceled, allow_failure: true)] create(type, status: :canceled, allow_failure: true)]
end end
...@@ -107,7 +102,7 @@ describe HasStatus do ...@@ -107,7 +102,7 @@ describe HasStatus do
end end
context 'one finished and second running but allowed to fail' do context 'one finished and second running but allowed to fail' do
let(:statuses) do let!(:statuses) do
[create(type, status: :success), [create(type, status: :success),
create(type, status: :running, allow_failure: true)] create(type, status: :running, allow_failure: true)]
end end
...@@ -118,11 +113,13 @@ describe HasStatus do ...@@ -118,11 +113,13 @@ describe HasStatus do
context 'ci build statuses' do context 'ci build statuses' do
let(:type) { :ci_build } let(:type) { :ci_build }
it_behaves_like 'build status summary' it_behaves_like 'build status summary'
end end
context 'generic commit statuses' do context 'generic commit statuses' do
let(:type) { :generic_commit_status } let(:type) { :generic_commit_status }
it_behaves_like 'build status summary' it_behaves_like 'build status summary'
end end
end end
......
...@@ -173,13 +173,11 @@ describe Event, models: true do ...@@ -173,13 +173,11 @@ describe Event, models: true do
it 'updates the project' do it 'updates the project' do
project.update(last_activity_at: 1.year.ago) project.update(last_activity_at: 1.year.ago)
expect_any_instance_of(Gitlab::ExclusiveLease). create_event(project, project.owner)
to receive(:try_obtain).and_return(true)
expect(project).to receive(:update_column). project.reload
with(:last_activity_at, a_kind_of(Time))
create_event(project, project.owner) project.last_activity_at <= 1.minute.ago
end end
end end
end end
......
...@@ -308,8 +308,7 @@ describe Project, models: true do ...@@ -308,8 +308,7 @@ describe Project, models: true do
end end
describe 'last_activity methods' do describe 'last_activity methods' do
let(:timestamp) { Time.now - 2.hours } let(:project) { create(:project, last_activity_at: 2.hours.ago) }
let(:project) { create(:project, created_at: timestamp, updated_at: timestamp) }
describe 'last_activity' do describe 'last_activity' do
it 'alias last_activity to last_event' do it 'alias last_activity to last_event' do
...@@ -321,7 +320,6 @@ describe Project, models: true do ...@@ -321,7 +320,6 @@ describe Project, models: true do
describe 'last_activity_date' do describe 'last_activity_date' do
it 'returns the creation date of the project\'s last event if present' do it 'returns the creation date of the project\'s last event if present' do
expect_any_instance_of(Event).to receive(:try_obtain_lease).and_return(true)
new_event = create(:event, project: project, created_at: Time.now) new_event = create(:event, project: project, created_at: Time.now)
expect(project.last_activity_at.to_i).to eq(new_event.created_at.to_i) expect(project.last_activity_at.to_i).to eq(new_event.created_at.to_i)
......
...@@ -610,6 +610,23 @@ describe User, models: true do ...@@ -610,6 +610,23 @@ describe User, models: true do
end end
end end
describe '.find_by_ssh_key_id' do
context 'using an existing SSH key ID' do
let(:user) { create(:user) }
let(:key) { create(:key, user: user) }
it 'returns the corresponding User' do
expect(described_class.find_by_ssh_key_id(key.id)).to eq(user)
end
end
context 'using an invalid SSH key ID' do
it 'returns nil' do
expect(described_class.find_by_ssh_key_id(-1)).to be_nil
end
end
end
describe '.by_login' do describe '.by_login' do
let(:username) { 'John' } let(:username) { 'John' }
let!(:user) { create(:user, username: username) } let!(:user) { create(:user, username: username) }
......
...@@ -35,18 +35,24 @@ describe Ci::API::API do ...@@ -35,18 +35,24 @@ describe Ci::API::API do
end end
end end
it "starts a build" do context 'when there is a pending build' do
register_builds info: { platform: :darwin } it 'starts a build' do
register_builds info: { platform: :darwin }
expect(response).to have_http_status(201)
expect(json_response['sha']).to eq(build.sha) expect(response).to have_http_status(201)
expect(runner.reload.platform).to eq("darwin") expect(json_response['sha']).to eq(build.sha)
expect(json_response["options"]).to eq({ "image" => "ruby:2.1", "services" => ["postgres"] }) expect(runner.reload.platform).to eq("darwin")
expect(json_response["variables"]).to include( expect(json_response["options"]).to eq({ "image" => "ruby:2.1", "services" => ["postgres"] })
{ "key" => "CI_BUILD_NAME", "value" => "spinach", "public" => true }, expect(json_response["variables"]).to include(
{ "key" => "CI_BUILD_STAGE", "value" => "test", "public" => true }, { "key" => "CI_BUILD_NAME", "value" => "spinach", "public" => true },
{ "key" => "DB_NAME", "value" => "postgres", "public" => true } { "key" => "CI_BUILD_STAGE", "value" => "test", "public" => true },
) { "key" => "DB_NAME", "value" => "postgres", "public" => true }
)
end
it 'updates runner info' do
expect { register_builds }.to change { runner.reload.contacted_at }
end
end end
context 'when builds are finished' do context 'when builds are finished' do
...@@ -159,13 +165,18 @@ describe Ci::API::API do ...@@ -159,13 +165,18 @@ describe Ci::API::API do
end end
context 'when runner is paused' do context 'when runner is paused' do
let(:inactive_runner) { create(:ci_runner, :inactive, token: "InactiveRunner") } let(:runner) { create(:ci_runner, :inactive, token: 'InactiveRunner') }
before do it 'responds with 404' do
register_builds inactive_runner.token register_builds
expect(response).to have_http_status 404
end end
it { expect(response).to have_http_status 404 } it 'does not update runner info' do
expect { register_builds }
.not_to change { runner.reload.contacted_at }
end
end end
def register_builds(token = runner.token, **params) def register_builds(token = runner.token, **params)
......
...@@ -18,7 +18,7 @@ describe Ci::ProcessPipelineService, services: true do ...@@ -18,7 +18,7 @@ describe Ci::ProcessPipelineService, services: true do
all_builds.where.not(status: [:created, :skipped]) all_builds.where.not(status: [:created, :skipped])
end end
def create_builds def process_pipeline
described_class.new(pipeline.project, user).execute(pipeline) described_class.new(pipeline.project, user).execute(pipeline)
end end
...@@ -36,26 +36,26 @@ describe Ci::ProcessPipelineService, services: true do ...@@ -36,26 +36,26 @@ describe Ci::ProcessPipelineService, services: true do
end end
it 'processes a pipeline' do it 'processes a pipeline' do
expect(create_builds).to be_truthy expect(process_pipeline).to be_truthy
succeed_pending succeed_pending
expect(builds.success.count).to eq(2) expect(builds.success.count).to eq(2)
expect(create_builds).to be_truthy expect(process_pipeline).to be_truthy
succeed_pending succeed_pending
expect(builds.success.count).to eq(4) expect(builds.success.count).to eq(4)
expect(create_builds).to be_truthy expect(process_pipeline).to be_truthy
succeed_pending succeed_pending
expect(builds.success.count).to eq(5) expect(builds.success.count).to eq(5)
expect(create_builds).to be_falsey expect(process_pipeline).to be_falsey
end end
it 'does not process pipeline if existing stage is running' do it 'does not process pipeline if existing stage is running' do
expect(create_builds).to be_truthy expect(process_pipeline).to be_truthy
expect(builds.pending.count).to eq(2) expect(builds.pending.count).to eq(2)
expect(create_builds).to be_falsey expect(process_pipeline).to be_falsey
expect(builds.pending.count).to eq(2) expect(builds.pending.count).to eq(2)
end end
end end
...@@ -67,7 +67,7 @@ describe Ci::ProcessPipelineService, services: true do ...@@ -67,7 +67,7 @@ describe Ci::ProcessPipelineService, services: true do
end end
it 'automatically triggers a next stage when build finishes' do it 'automatically triggers a next stage when build finishes' do
expect(create_builds).to be_truthy expect(process_pipeline).to be_truthy
expect(builds.pluck(:status)).to contain_exactly('pending') expect(builds.pluck(:status)).to contain_exactly('pending')
pipeline.builds.running_or_pending.each(&:drop) pipeline.builds.running_or_pending.each(&:drop)
...@@ -88,7 +88,7 @@ describe Ci::ProcessPipelineService, services: true do ...@@ -88,7 +88,7 @@ describe Ci::ProcessPipelineService, services: true do
context 'when builds are successful' do context 'when builds are successful' do
it 'properly creates builds' do it 'properly creates builds' do
expect(create_builds).to be_truthy expect(process_pipeline).to be_truthy
expect(builds.pluck(:name)).to contain_exactly('build') expect(builds.pluck(:name)).to contain_exactly('build')
expect(builds.pluck(:status)).to contain_exactly('pending') expect(builds.pluck(:status)).to contain_exactly('pending')
pipeline.builds.running_or_pending.each(&:success) pipeline.builds.running_or_pending.each(&:success)
...@@ -113,7 +113,7 @@ describe Ci::ProcessPipelineService, services: true do ...@@ -113,7 +113,7 @@ describe Ci::ProcessPipelineService, services: true do
context 'when test job fails' do context 'when test job fails' do
it 'properly creates builds' do it 'properly creates builds' do
expect(create_builds).to be_truthy expect(process_pipeline).to be_truthy
expect(builds.pluck(:name)).to contain_exactly('build') expect(builds.pluck(:name)).to contain_exactly('build')
expect(builds.pluck(:status)).to contain_exactly('pending') expect(builds.pluck(:status)).to contain_exactly('pending')
pipeline.builds.running_or_pending.each(&:success) pipeline.builds.running_or_pending.each(&:success)
...@@ -138,7 +138,7 @@ describe Ci::ProcessPipelineService, services: true do ...@@ -138,7 +138,7 @@ describe Ci::ProcessPipelineService, services: true do
context 'when test and test_failure jobs fail' do context 'when test and test_failure jobs fail' do
it 'properly creates builds' do it 'properly creates builds' do
expect(create_builds).to be_truthy expect(process_pipeline).to be_truthy
expect(builds.pluck(:name)).to contain_exactly('build') expect(builds.pluck(:name)).to contain_exactly('build')
expect(builds.pluck(:status)).to contain_exactly('pending') expect(builds.pluck(:status)).to contain_exactly('pending')
pipeline.builds.running_or_pending.each(&:success) pipeline.builds.running_or_pending.each(&:success)
...@@ -164,7 +164,7 @@ describe Ci::ProcessPipelineService, services: true do ...@@ -164,7 +164,7 @@ describe Ci::ProcessPipelineService, services: true do
context 'when deploy job fails' do context 'when deploy job fails' do
it 'properly creates builds' do it 'properly creates builds' do
expect(create_builds).to be_truthy expect(process_pipeline).to be_truthy
expect(builds.pluck(:name)).to contain_exactly('build') expect(builds.pluck(:name)).to contain_exactly('build')
expect(builds.pluck(:status)).to contain_exactly('pending') expect(builds.pluck(:status)).to contain_exactly('pending')
pipeline.builds.running_or_pending.each(&:success) pipeline.builds.running_or_pending.each(&:success)
...@@ -189,7 +189,7 @@ describe Ci::ProcessPipelineService, services: true do ...@@ -189,7 +189,7 @@ describe Ci::ProcessPipelineService, services: true do
context 'when build is canceled in the second stage' do context 'when build is canceled in the second stage' do
it 'does not schedule builds after build has been canceled' do it 'does not schedule builds after build has been canceled' do
expect(create_builds).to be_truthy expect(process_pipeline).to be_truthy
expect(builds.pluck(:name)).to contain_exactly('build') expect(builds.pluck(:name)).to contain_exactly('build')
expect(builds.pluck(:status)).to contain_exactly('pending') expect(builds.pluck(:status)).to contain_exactly('pending')
pipeline.builds.running_or_pending.each(&:success) pipeline.builds.running_or_pending.each(&:success)
...@@ -208,7 +208,7 @@ describe Ci::ProcessPipelineService, services: true do ...@@ -208,7 +208,7 @@ describe Ci::ProcessPipelineService, services: true do
context 'when listing manual actions' do context 'when listing manual actions' do
it 'returns only for skipped builds' do it 'returns only for skipped builds' do
# currently all builds are created # currently all builds are created
expect(create_builds).to be_truthy expect(process_pipeline).to be_truthy
expect(manual_actions).to be_empty expect(manual_actions).to be_empty
# succeed stage build # succeed stage build
...@@ -230,6 +230,69 @@ describe Ci::ProcessPipelineService, services: true do ...@@ -230,6 +230,69 @@ describe Ci::ProcessPipelineService, services: true do
end end
end end
context 'when there are manual/on_failure jobs in earlier stages' do
before do
builds
process_pipeline
builds.each(&:reload)
end
context 'when first stage has only manual jobs' do
let(:builds) do
[create_build('build', 0, 'manual'),
create_build('check', 1),
create_build('test', 2)]
end
it 'starts from the second stage' do
expect(builds.map(&:status)).to eq(%w[skipped pending created])
end
end
context 'when second stage has only manual jobs' do
let(:builds) do
[create_build('check', 0),
create_build('build', 1, 'manual'),
create_build('test', 2)]
end
it 'skips second stage and continues on third stage' do
expect(builds.map(&:status)).to eq(%w[pending created created])
builds.first.success
builds.each(&:reload)
expect(builds.map(&:status)).to eq(%w[success skipped pending])
end
end
context 'when second stage has only on_failure jobs' do
let(:builds) do
[create_build('check', 0),
create_build('build', 1, 'on_failure'),
create_build('test', 2)]
end
it 'skips second stage and continues on third stage' do
expect(builds.map(&:status)).to eq(%w[pending created created])
builds.first.success
builds.each(&:reload)
expect(builds.map(&:status)).to eq(%w[success skipped pending])
end
end
def create_build(name, stage_idx, when_value = nil)
create(:ci_build,
:created,
pipeline: pipeline,
name: name,
stage_idx: stage_idx,
when: when_value)
end
end
context 'when failed build in the middle stage is retried' do context 'when failed build in the middle stage is retried' do
context 'when failed build is the only unsuccessful build in the stage' do context 'when failed build is the only unsuccessful build in the stage' do
before do before do
...@@ -242,7 +305,7 @@ describe Ci::ProcessPipelineService, services: true do ...@@ -242,7 +305,7 @@ describe Ci::ProcessPipelineService, services: true do
end end
it 'does trigger builds in the next stage' do it 'does trigger builds in the next stage' do
expect(create_builds).to be_truthy expect(process_pipeline).to be_truthy
expect(builds.pluck(:name)).to contain_exactly('build:1', 'build:2') expect(builds.pluck(:name)).to contain_exactly('build:1', 'build:2')
pipeline.builds.running_or_pending.each(&:success) pipeline.builds.running_or_pending.each(&:success)
...@@ -297,14 +360,14 @@ describe Ci::ProcessPipelineService, services: true do ...@@ -297,14 +360,14 @@ describe Ci::ProcessPipelineService, services: true do
expect(all_builds.count).to eq(2) expect(all_builds.count).to eq(2)
# Create builds will mark the created as pending # Create builds will mark the created as pending
expect(create_builds).to be_truthy expect(process_pipeline).to be_truthy
expect(builds.count).to eq(2) expect(builds.count).to eq(2)
expect(all_builds.count).to eq(2) expect(all_builds.count).to eq(2)
# When we builds succeed we will create a rest of pipeline from .gitlab-ci.yml # When we builds succeed we will create a rest of pipeline from .gitlab-ci.yml
# We will have 2 succeeded, 2 pending (from stage test), total 5 (one more build from deploy) # We will have 2 succeeded, 2 pending (from stage test), total 5 (one more build from deploy)
succeed_pending succeed_pending
expect(create_builds).to be_truthy expect(process_pipeline).to be_truthy
expect(builds.success.count).to eq(2) expect(builds.success.count).to eq(2)
expect(builds.pending.count).to eq(2) expect(builds.pending.count).to eq(2)
expect(all_builds.count).to eq(5) expect(all_builds.count).to eq(5)
...@@ -312,14 +375,14 @@ describe Ci::ProcessPipelineService, services: true do ...@@ -312,14 +375,14 @@ describe Ci::ProcessPipelineService, services: true do
# When we succeed the 2 pending from stage test, # When we succeed the 2 pending from stage test,
# We will queue a deploy stage, no new builds will be created # We will queue a deploy stage, no new builds will be created
succeed_pending succeed_pending
expect(create_builds).to be_truthy expect(process_pipeline).to be_truthy
expect(builds.pending.count).to eq(1) expect(builds.pending.count).to eq(1)
expect(builds.success.count).to eq(4) expect(builds.success.count).to eq(4)
expect(all_builds.count).to eq(5) expect(all_builds.count).to eq(5)
# When we succeed last pending build, we will have a total of 5 succeeded builds, no new builds will be created # When we succeed last pending build, we will have a total of 5 succeeded builds, no new builds will be created
succeed_pending succeed_pending
expect(create_builds).to be_falsey expect(process_pipeline).to be_falsey
expect(builds.success.count).to eq(5) expect(builds.success.count).to eq(5)
expect(all_builds.count).to eq(5) expect(all_builds.count).to eq(5)
end end
......
...@@ -5,6 +5,7 @@ describe Projects::DestroyService, services: true do ...@@ -5,6 +5,7 @@ describe Projects::DestroyService, services: true do
let!(:project) { create(:project, namespace: user.namespace) } let!(:project) { create(:project, namespace: user.namespace) }
let!(:path) { project.repository.path_to_repo } let!(:path) { project.repository.path_to_repo }
let!(:remove_path) { path.sub(/\.git\Z/, "+#{project.id}+deleted.git") } let!(:remove_path) { path.sub(/\.git\Z/, "+#{project.id}+deleted.git") }
let!(:async) { false } # execute or async_execute
context 'Sidekiq inline' do context 'Sidekiq inline' do
before do before do
...@@ -28,6 +29,22 @@ describe Projects::DestroyService, services: true do ...@@ -28,6 +29,22 @@ describe Projects::DestroyService, services: true do
it { expect(Dir.exist?(remove_path)).to be_truthy } it { expect(Dir.exist?(remove_path)).to be_truthy }
end end
context 'async delete of project with private issue visibility' do
let!(:async) { true }
before do
project.project_feature.update_attribute("issues_access_level", ProjectFeature::PRIVATE)
# Run sidekiq immediately to check that renamed repository will be removed
Sidekiq::Testing.inline! { destroy_project(project, user, {}) }
end
it 'deletes the project' do
expect(Project.all).not_to include(project)
expect(Dir.exist?(path)).to be_falsey
expect(Dir.exist?(remove_path)).to be_falsey
end
end
context 'container registry' do context 'container registry' do
before do before do
stub_container_registry_config(enabled: true) stub_container_registry_config(enabled: true)
...@@ -52,6 +69,10 @@ describe Projects::DestroyService, services: true do ...@@ -52,6 +69,10 @@ describe Projects::DestroyService, services: true do
end end
def destroy_project(project, user, params) def destroy_project(project, user, params)
Projects::DestroyService.new(project, user, params).execute if async
Projects::DestroyService.new(project, user, params).async_execute
else
Projects::DestroyService.new(project, user, params).execute
end
end end
end end
...@@ -79,7 +79,9 @@ describe PostReceive do ...@@ -79,7 +79,9 @@ describe PostReceive do
end end
it "does not run if the author is not in the project" do it "does not run if the author is not in the project" do
allow(Key).to receive(:find_by).with(hash_including(id: anything())) { nil } allow_any_instance_of(Gitlab::GitPostReceive).
to receive(:identify_using_ssh_key).
and_return(nil)
expect(project).not_to receive(:execute_hooks) expect(project).not_to receive(:execute_hooks)
......
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