Commit 502aabdc authored by Alejandro Rodríguez's avatar Alejandro Rodríguez

Merge branch 'master' into 8-14-stable

parents b83e8a5a f4710ee8
......@@ -61,9 +61,10 @@ update-knapsack:
- scripts/merge-reports knapsack/spinach_report.json knapsack/spinach_node_*.json
- rm -f knapsack/*_node_*.json
only:
- master
# Execute all testing suites
- master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
.use-db: &use-db
services:
......@@ -143,7 +144,10 @@ spinach 9 10: *spinach-knapsack
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.1-git-2.7-phantomjs-2.1"
<<: *use-db
only:
- master
- master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
cache:
key: "ruby21"
paths:
......@@ -286,7 +290,10 @@ bundler:audit:
stage: test
<<: *ruby-static-analysis
only:
- master
- master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
script:
- "bundle exec bundle-audit check --update --ignore OSVDB-115941"
......@@ -297,6 +304,9 @@ migration paths:
SETUP_DB: "false"
only:
- master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
script:
- git checkout HEAD .
- git fetch --tags
......
......@@ -63,6 +63,7 @@ header {
&:focus,
&:active {
background-color: $background-color;
color: darken($gl-icon-color, 50%);
}
.fa-caret-down {
......@@ -152,7 +153,7 @@ header {
padding-right: 20px;
margin: 0;
font-size: 19px;
max-width: 400px;
max-width: 385px;
display: inline-block;
line-height: $header-height;
font-weight: normal;
......@@ -191,6 +192,10 @@ header {
font-size: 10px;
text-align: center;
cursor: pointer;
&:hover {
color: darken($color: $gl-text-color, $amount: 50%);
}
}
.project-item-select {
......@@ -218,6 +223,14 @@ header {
}
}
.page-sidebar-pinned.right-sidebar-expanded {
@media (max-width: $screen-lg-min) {
.header-content .title {
width: 300px;
}
}
}
@media (max-width: $screen-xs-max) {
header .container-fluid {
font-size: 18px;
......
......@@ -252,7 +252,7 @@ $award-emoji-new-btn-icon-color: #dcdcdc;
*/
$search-input-border-color: rgba(#4688f1, .8);
$search-input-focus-shadow-color: $dropdown-input-focus-shadow;
$search-input-width: 244px;
$search-input-width: 220px;
$location-badge-color: #aaa;
$location-badge-bg: $gray-normal;
$location-badge-active-bg: #4f91f8;
......
......@@ -84,7 +84,8 @@
font-weight: 600;
}
.commit {
.commit,
.generic_commit_status {
padding: 10px 0;
position: relative;
......@@ -102,7 +103,6 @@
vertical-align: baseline;
}
.avatar {
margin-left: -46px;
}
......
......@@ -141,6 +141,22 @@ ul.notes {
}
}
.page-sidebar-pinned.right-sidebar-expanded {
@media (max-width: $screen-lg-min) {
.note-header {
.note-headline-light {
display: block;
}
.note-actions {
position: absolute;
right: 0;
top: 0;
}
}
}
}
// Diff code in discussion view
.discussion-body .diff-file {
.file-title {
......
......@@ -109,10 +109,6 @@
float: none;
}
.api {
color: $code-color;
}
.branch-commit {
.branch-name {
......
......@@ -21,6 +21,11 @@
padding: 4px;
width: $search-input-width;
line-height: 24px;
&:hover {
border-color: lighten($dropdown-input-focus-border, 20%);
box-shadow: 0 0 4px lighten($search-input-focus-shadow-color, 20%);
}
}
.location-text {
......@@ -28,10 +33,9 @@
}
.search-input {
padding-right: 20px;
border: none;
font-size: 14px;
padding: 0;
padding: 0 20px 0 0;
margin-left: 5px;
line-height: 25px;
width: 98%;
......@@ -153,6 +157,7 @@
width: 68%;
}
}
}
.search-holder {
......@@ -229,4 +234,5 @@
&:focus {
color: $gl-link-color;
}
}
......@@ -7,8 +7,8 @@ class HelpController < ApplicationController
@help_index = File.read(Rails.root.join('doc', 'README.md'))
# Prefix Markdown links with `help/` unless they already have been
# See http://rubular.com/r/nwwhzH6Z8X
@help_index.gsub!(/(\]\()(?!help\/)([^\)\(]+)(\))/, '\1help/\2\3')
# See http://rubular.com/r/ie2MlpdUMq
@help_index.gsub!(/(\]\()(\/?help\/)?([^\)\(]+\))/, '\1/help/\3')
end
def show
......
......@@ -3,7 +3,7 @@ module AuthHelper
FORM_BASED_PROVIDERS = [/\Aldap/, 'crowd'].freeze
def ldap_enabled?
Gitlab.config.ldap.enabled
Gitlab::LDAP::Config.enabled?
end
def omniauth_enabled?
......
......@@ -82,6 +82,10 @@ module GitlabRoutingHelper
namespace_project_merge_request_path(entity.project.namespace, entity.project, entity, *args)
end
def pipeline_path(pipeline, *args)
namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id, *args)
end
def milestone_path(entity, *args)
namespace_project_milestone_path(entity.project.namespace, entity.project, entity, *args)
end
......
......@@ -203,6 +203,10 @@ class ApplicationSetting < ActiveRecord::Base
ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url)
end
def sidekiq_throttling_column_exists?
ActiveRecord::Base.connection.column_exists?(:application_settings, :sidekiq_throttling_enabled)
end
def domain_whitelist_raw
self.domain_whitelist.join("\n") unless self.domain_whitelist.nil?
end
......@@ -256,6 +260,12 @@ class ApplicationSetting < ActiveRecord::Base
ensure_health_check_access_token!
end
def sidekiq_throttling_enabled?
return false unless sidekiq_throttling_column_exists?
sidekiq_throttling_enabled
end
private
def check_repository_storages
......
......@@ -266,7 +266,7 @@ class Issue < ActiveRecord::Base
def as_json(options = {})
super(options).tap do |json|
json[:subscribed] = subscribed?(options[:user]) if options.has_key?(:user)
json[:subscribed] = subscribed?(options[:user]) if options.has_key?(:user) && options[:user]
if options.has_key?(:labels)
json[:labels] = labels.as_json(
......
......@@ -7,6 +7,7 @@ class Note < ActiveRecord::Base
include Importable
include FasterCacheKeys
include CacheMarkdownField
include AfterCommitQueue
cache_markdown_field :note, pipeline: :note
......
......@@ -49,20 +49,14 @@ class ProjectFeature < ActiveRecord::Base
end
def builds_enabled?
return true unless builds_access_level
builds_access_level > DISABLED
end
def wiki_enabled?
return true unless wiki_access_level
wiki_access_level > DISABLED
end
def merge_requests_enabled?
return true unless merge_requests_access_level
merge_requests_access_level > DISABLED
end
......
......@@ -26,9 +26,12 @@ module Notes
note.note = content
end
if !only_commands && note.save
note.run_after_commit do
# Finish the harder work in the background
NewNoteWorker.perform_in(2.seconds, note.id, params)
NewNoteWorker.perform_async(note.id)
end
if !only_commands && note.save
todo_service.new_note(note, current_user)
end
......
......@@ -84,7 +84,7 @@
= render "shared/empty_states/todos_all_done.svg"
- if todos_filter_empty?
%h4.text-center
Good job! Looks like you don't have any todos left.
= Gitlab.config.gitlab.no_todos_messages.sample
%p.text-center
Are you looking for things to do? Take a look at
= succeed "," do
......
%board-sidebar{ "inline-template" => true,
":current-user" => "#{current_user.to_json(only: [:username, :id, :name], methods: [:avatar_url]) if current_user}" }
":current-user" => "#{current_user ? current_user.to_json(only: [:username, :id, :name], methods: [:avatar_url]) : {}}" }
%aside.right-sidebar.right-sidebar-expanded.issue-boards-sidebar{ "v-show" => "showSidebar" }
.issuable-sidebar
.block.issuable-sidebar-header
......
.block.assignee
.title.hide-collapsed
Assignee
= icon("spinner spin", class: "block-loading")
- if can?(current_user, :admin_issue, @project)
= icon("spinner spin", class: "block-loading")
= link_to "Edit", "#", class: "edit-link pull-right"
.value.hide-collapsed
%span.assign-yourself.no-value{ "v-if" => "!issue.assignee" }
......
.block.due_date
.title
Due date
= icon("spinner spin", class: "block-loading")
- if can?(current_user, :admin_issue, @project)
= icon("spinner spin", class: "block-loading")
= link_to "Edit", "#", class: "edit-link pull-right"
.value
.value-content
......
.block.labels
.title
Labels
= icon("spinner spin", class: "block-loading")
- if can?(current_user, :admin_issue, @project)
= icon("spinner spin", class: "block-loading")
= link_to "Edit", "#", class: "edit-link pull-right"
.value.issuable-show-labels
%span.no-value{ "v-if" => "issue.labels && issue.labels.length === 0" }
......
.block.milestone
.title
Milestone
= icon("spinner spin", class: "block-loading")
- if can?(current_user, :admin_issue, @project)
= icon("spinner spin", class: "block-loading")
= link_to "Edit", "#", class: "edit-link pull-right"
.value
%span.no-value{ "v-if" => "!issue.milestone" }
......
......@@ -3,6 +3,9 @@
= ci_status_with_icon(@build.status)
Build
%strong ##{@build.id}
in pipeline
= link_to pipeline_path(@build.pipeline) do
%strong ##{@build.pipeline.id}
for commit
= link_to ci_status_path(@build.pipeline) do
%strong= @build.pipeline.short_sha
......
......@@ -10,6 +10,7 @@
%tr
%th Status
%th Build
%th Pipeline
- if admin
%th Project
%th Runner
......@@ -19,6 +20,6 @@
%th Coverage
%th
= render partial: "projects/ci/builds/build", collection: builds, as: :build, locals: { commit_sha: true, ref: true, stage: true, allow_retry: true, coverage: admin || project.build_coverage_enabled?, admin: admin }
= render partial: "projects/ci/builds/build", collection: builds, as: :build, locals: { commit_sha: true, ref: true, pipeline_link: true, stage: true, allow_retry: true, coverage: admin || project.build_coverage_enabled?, admin: admin }
= paginate builds, theme: 'gitlab'
- if !project.empty_repo? && can?(current_user, :download_code, project)
%span{class: 'hidden-xs hidden-sm download-button'}
%span{class: 'download-button'}
.dropdown.inline
%button.btn{ 'data-toggle' => 'dropdown' }
= icon('download')
......
......@@ -2,6 +2,7 @@
- ref = local_assigns.fetch(:ref, nil)
- commit_sha = local_assigns.fetch(:commit_sha, nil)
- retried = local_assigns.fetch(:retried, false)
- pipeline_link = local_assigns.fetch(:pipeline_link, false)
- stage = local_assigns.fetch(:stage, false)
- coverage = local_assigns.fetch(:coverage, false)
- allow_retry = local_assigns.fetch(:allow_retry, false)
......@@ -51,6 +52,16 @@
- if build.manual?
%span.label.label-info manual
- if pipeline_link
%td
= link_to pipeline_path(build.pipeline) do
%span.pipeline-id ##{build.pipeline.id}
%span by
- if build.pipeline.user
= user_avatar(user: build.pipeline.user, size: 20)
- else
%span.monospace API
- if admin
%td
- if build.project
......
......@@ -8,8 +8,8 @@
- if stage
&nbsp;
= stage.titleize
= render statuses.latest_ci_stages, coverage: @project.build_coverage_enabled?, stage: false, ref: false, allow_retry: true
= render statuses.retried_ci_stages, coverage: @project.build_coverage_enabled?, stage: false, ref: false, retried: true
%tr
= render statuses.latest_ci_stages, coverage: @project.build_coverage_enabled?, stage: false, ref: false, pipeline_link: false, allow_retry: true
= render statuses.retried_ci_stages, coverage: @project.build_coverage_enabled?, stage: false, ref: false, pipeline_link: false, retried: true
%tr
%td{colspan: 10}
&nbsp;
......@@ -15,6 +15,16 @@
- if defined?(retried) && retried
= icon('warning', class: 'text-warning has-tooltip', title: 'Status was retried.')
- if defined?(pipeline_link) && pipeline_link
%td
= link_to pipeline_path(generic_commit_status.pipeline) do
%span.pipeline-id ##{generic_commit_status.pipeline.id}
%span by
- if generic_commit_status.pipeline.user
= user_avatar(user: generic_commit_status.pipeline.user, size: 20)
- else
%span.monospace API
- if defined?(commit_sha) && commit_sha
%td
= link_to generic_commit_status.short_sha, namespace_project_commit_path(generic_commit_status.project.namespace, generic_commit_status.project, generic_commit_status.sha), class: "monospace"
......
......@@ -90,7 +90,8 @@
= f.label :visibility_level, class: 'label-light' do
Visibility Level
= link_to "(?)", help_page_path("public_access/public_access")
= render('shared/visibility_radios', model_method: :visibility_level, form: f, selected_level: @project.visibility_level, form_model: @project)
= render 'shared/visibility_level', f: f, visibility_level: default_project_visibility, can_change_visibility_level: true, form_model: @project
= f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4
= link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel'
......
......@@ -2,10 +2,12 @@ class NewNoteWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
def perform(note_id, note_params)
note = Note.find(note_id)
def perform(note_id)
if note = Note.find_by(id: note_id)
NotificationService.new.new_note(note)
Notes::PostProcessService.new(note).execute
else
Rails.logger.error("NewNoteWorker: couldn't find note with ID=#{note_id}, skipping job")
end
end
end
---
title: Add link to build pipeline within individual build pages
merge_request: 7082
author:
---
title: Project download buttons always show
merge_request: 7405
author: Philip Karpiak
---
title: Fix error links in help index page
merge_request: 7396
author: Fu Xu
---
title: Give search-input correct padding-right value
merge_request: 7407
author: Philip Karpiak
---
title: Show random messages when the To Do list is empty
merge_request: 6818
author: Josep Llaneras
---
title: Fix project Visibility Level selector not using default values
merge_request:
author:
---
title: Fix record not found error on NewNoteWorker processing
merge_request: 6863
author: Oswaldo Ferreira
---
title: Added ability to put emojis into repository name
merge_request: 7420
author: Vincent Composieux
---
title: Centralize LDAP config/filter logic
merge_request: 6606
author:
......@@ -3,8 +3,8 @@
#
production:
adapter: mysql2
encoding: utf8
collation: utf8_general_ci
encoding: utf8mb4
collation: utf8mb4_general_ci
reconnect: false
database: gitlabhq_production
pool: 10
......@@ -18,8 +18,8 @@ production:
#
development:
adapter: mysql2
encoding: utf8
collation: utf8_general_ci
encoding: utf8mb4
collation: utf8mb4_general_ci
reconnect: false
database: gitlabhq_development
pool: 5
......@@ -32,8 +32,8 @@ development:
# Do not set this db to the same as development or production.
test: &test
adapter: mysql2
encoding: utf8
collation: utf8_general_ci
encoding: utf8mb4
collation: utf8mb4_general_ci
reconnect: false
database: gitlabhq_test
pool: 5
......
......@@ -215,6 +215,7 @@ Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(
Settings.gitlab['domain_whitelist'] ||= []
Settings.gitlab['import_sources'] ||= %w[github bitbucket gitlab google_code fogbugz git gitlab_project]
Settings.gitlab['trusted_proxies'] ||= []
Settings.gitlab['no_todos_messages'] ||= YAML.load_file(Rails.root.join('config', 'no_todos_messages.yml'))
#
# CI
......
......@@ -213,22 +213,9 @@ Devise.setup do |config|
end
if Gitlab::LDAP::Config.enabled?
Gitlab.config.ldap.servers.values.each do |server|
if server['allow_username_or_email_login']
email_stripping_proc = ->(name) {name.gsub(/@.*\z/, '')}
else
email_stripping_proc = ->(name) {name}
end
config.omniauth server['provider_name'],
host: server['host'],
base: server['base'],
uid: server['uid'],
port: server['port'],
method: server['method'],
bind_dn: server['bind_dn'],
password: server['password'],
name_proc: email_stripping_proc
Gitlab::LDAP::Config.providers.each do |provider|
ldap_config = Gitlab::LDAP::Config.new(provider)
config.omniauth(provider, ldap_config.omniauth_options)
end
end
......
# When the Todos list on the user's dashboard becomes empty, one of the messages below shows up randomly.
#
# If you come up with a fun one, please feel free to contribute it to GitLab!
# https://about.gitlab.com/contributing/
---
- Good job! Looks like you don't have any todos left.
- Coffee really tastes better without any todos left.
- Isn't an empty To Do list beautiful?
- Time for a rewarding coffee break
- Give yourself a pat on the back!
- High five!
- Hence forth you shall be known as 'Todo Destroyer'
\ No newline at end of file
......@@ -44,7 +44,7 @@ listen "127.0.0.1:8080", :tcp_nopush => true
# nuke workers after 30 seconds instead of 60 seconds (the default)
#
# NOTICE: git push over http depends on this value.
# If you want be able to push huge amount of data to git repository over http
# If you want to be able to push huge amount of data to git repository over http
# you will have to increase this value too.
#
# Example of output if you try to push 1GB repo to GitLab over http.
......@@ -82,7 +82,7 @@ GC.respond_to?(:copy_on_write_friendly=) and
check_client_connection false
before_fork do |server, worker|
# the following is highly recomended for Rails + "preload_app true"
# the following is highly recommended for Rails + "preload_app true"
# as there's no need for the master process to hold a connection
defined?(ActiveRecord::Base) and
ActiveRecord::Base.connection.disconnect!
......
......@@ -76,7 +76,7 @@ configuration to move each data location to a subdirectory:
user['home'] = '/gitlab-data/home'
git_data_dir '/gitlab-data/git-data'
gitlab_rails['shared_path'] = '/gitlab-data/shared'
gitlab_rails['uploads_directory'] = "/gitlab-data/uploads"
gitlab_rails['uploads_directory'] = '/gitlab-data/uploads'
gitlab_ci['builds_directory'] = '/gitlab-data/builds'
```
......
......@@ -48,15 +48,15 @@ Redis.
redis['password'] = 'Redis Password'
```
1. Run `sudo gitlab-ctl reconfigure` to install and configure PostgreSQL.
1. Run `sudo touch /etc/gitlab/skip-auto-migrations` to prevent database migrations
from running on upgrade. Only the primary GitLab application server should
handle migrations.
1. Run `sudo gitlab-ctl reconfigure` to install and configure Redis.
> **Note**: This `reconfigure` step will result in some errors.
That's OK - don't be alarmed.
1. Run `touch /etc/gitlab/skip-auto-migrations` to prevent database migrations
from running on upgrade. Only the primary GitLab application server should
handle migrations.
## Experimental Redis Sentinel support
> [Introduced][ce-1877] in GitLab 8.11.
......
......@@ -14,7 +14,7 @@
contributing to documentation.
- [SQL Migration Style Guide](migration_style_guide.md) for creating safe SQL migrations
- [Testing standards and style guidelines](testing.md)
- [UX guide](ux_guide/README.md) for building GitLab with existing CSS styles and elements
- [UX guide](ux_guide/index.md) for building GitLab with existing CSS styles and elements
- [Frontend guidelines](frontend.md)
- [SQL guidelines](sql.md) for working with SQL queries
- [Sidekiq guidelines](sidekiq_style_guide.md) for working with Sidekiq workers
......
......@@ -14,8 +14,8 @@ There are two ways to create a new project in GitLab.
1. Fill out the information:
1. "Project name" is the name of your project (you can't use spaces, but you
can use hyphens or underscores).
1. "Project name" is the name of your project (you can't use special characters,
but you can use spaces, hyphens, underscores or even emojis).
1. The "Project description" is optional and will be shown in your project's
dashboard so others can briefly understand what your project is about.
1. Select a [visibility level](../public_access/public_access.md).
......
......@@ -44,7 +44,7 @@ This [resource](http://kb.kerio.com/product/kerio-connect/server-configuration/s
has all the information you need to add a certificate to the main trusted chain.
This [answer](http://superuser.com/questions/437330/how-do-you-add-a-certificate-authority-ca-to-ubuntu)
at SuperUser also has relevant information.
at Super User also has relevant information.
**Omnibus Trusted Chain**
......
......@@ -590,7 +590,7 @@ Quote break.
You can also use raw HTML in your Markdown, and it'll mostly work pretty well.
See the documentation for HTML::Pipeline's [SanitizationFilter](http://www.rubydoc.info/gems/html-pipeline/HTML/Pipeline/SanitizationFilter#WHITELIST-constant) class for the list of allowed HTML tags and attributes. In addition to the default `SanitizationFilter` whitelist, GitLab allows `span` elements.
See the documentation for HTML::Pipeline's [SanitizationFilter](http://www.rubydoc.info/gems/html-pipeline/1.11.0/HTML/Pipeline/SanitizationFilter#WHITELIST-constant) class for the list of allowed HTML tags and attributes. In addition to the default `SanitizationFilter` whitelist, GitLab allows `span` elements.
```no-highlight
<dl>
......
......@@ -48,7 +48,7 @@ module API
put ':id/access_requests/:user_id/approve' do
source = find_source(source_type, params[:id])
member = ::Members::ApproveAccessRequestService.new(source, current_user, declared(params)).execute
member = ::Members::ApproveAccessRequestService.new(source, current_user, declared_params).execute
status :created
present member.user, with: Entities::Member, member: member
......
......@@ -36,8 +36,7 @@ module API
optional :font, type: String, desc: 'Foreground color'
end
post do
create_params = declared(params, include_missing: false).to_h
message = BroadcastMessage.create(create_params)
message = BroadcastMessage.create(declared_params(include_missing: false))
if message.persisted?
present message, with: Entities::BroadcastMessage
......@@ -73,9 +72,8 @@ module API
end
put ':id' do
message = find_message
update_params = declared(params, include_missing: false).to_h
if message.update(update_params)
if message.update(declared_params(include_missing: false))
present message, with: Entities::BroadcastMessage
else
render_validation_error!(message)
......
......@@ -53,7 +53,7 @@ module API
post ":id/repository/commits" do
authorize! :push_code, user_project
attrs = declared(params)
attrs = declared_params
attrs[:source_branch] = attrs[:branch_name]
attrs[:target_branch] = attrs[:branch_name]
attrs[:actions].map! do |action|
......
......@@ -82,7 +82,7 @@ module API
end
post ":id/#{path}/:key_id/enable" do
key = ::Projects::EnableDeployKeyService.new(user_project,
current_user, declared(params)).execute
current_user, declared_params).execute
if key
present key, with: Entities::SSHKey
......
......@@ -32,8 +32,7 @@ module API
post ':id/environments' do
authorize! :create_environment, user_project
create_params = declared(params, include_parent_namespaces: false).to_h
environment = user_project.environments.create(create_params)
environment = user_project.environments.create(declared_params)
if environment.persisted?
present environment, with: Entities::Environment
......@@ -56,7 +55,7 @@ module API
environment = user_project.environments.find(params[:environment_id])
update_params = declared(params, include_missing: false).extract!(:name, :external_url).to_h
update_params = declared_params(include_missing: false).extract!(:name, :external_url)
if environment.update(update_params)
present environment, with: Entities::Environment
else
......
......@@ -23,6 +23,11 @@ module API
warden.try(:authenticate) if %w[GET HEAD].include?(env['REQUEST_METHOD'])
end
def declared_params(options = {})
options = { include_parent_namespaces: false }.merge(options)
declared(params, options).to_h.symbolize_keys
end
def find_user_by_private_token
token = private_token
return nil unless token.present?
......
......@@ -30,10 +30,7 @@ module API
conflict!('Label already exists') if label
priority = params.delete(:priority)
label_params = declared(params,
include_parent_namespaces: false,
include_missing: false).to_h
label = user_project.labels.create(label_params)
label = user_project.labels.create(declared_params(include_missing: false))
if label.valid?
label.prioritize!(user_project, priority) if priority
......@@ -77,11 +74,9 @@ module API
update_priority = params.key?(:priority)
priority = params.delete(:priority)
label_params = declared(params,
include_parent_namespaces: false,
include_missing: false).to_h
label_params = declared_params(include_missing: false)
# Rename new name to the actual label attribute name
label_params[:name] = label_params.delete('new_name') if label_params.key?('new_name')
label_params[:name] = label_params.delete(:new_name) if label_params.key?(:new_name)
render_validation_error!(label) unless label.update(label_params)
......
......@@ -120,7 +120,7 @@ module API
if member.nil?
{ message: "Access revoked", id: params[:user_id].to_i }
else
::Members::DestroyService.new(source, current_user, declared(params)).execute
::Members::DestroyService.new(source, current_user, declared_params).execute
present member.user, with: Entities::Member, member: member
end
......
......@@ -62,9 +62,8 @@ module API
end
post ":id/milestones" do
authorize! :admin_milestone, user_project
milestone_params = declared(params, include_parent_namespaces: false)
milestone = ::Milestones::CreateService.new(user_project, current_user, milestone_params).execute
milestone = ::Milestones::CreateService.new(user_project, current_user, declared_params).execute
if milestone.valid?
present milestone, with: Entities::Milestone
......@@ -86,9 +85,9 @@ module API
end
put ":id/milestones/:milestone_id" do
authorize! :admin_milestone, user_project
milestone_params = declared(params, include_parent_namespaces: false, include_missing: false)
milestone = user_project.milestones.find(params.delete(:milestone_id))
milestone = user_project.milestones.find(milestone_params.delete(:milestone_id))
milestone_params = declared_params(include_missing: false)
milestone = ::Milestones::UpdateService.new(user_project, current_user, milestone_params).execute(milestone)
if milestone.valid?
......
......@@ -33,10 +33,9 @@ module API
begin
notification_setting.transaction do
new_notification_email = params.delete(:notification_email)
declared_params = declared(params, include_missing: false).to_h
current_user.update(notification_email: new_notification_email) if new_notification_email
notification_setting.update(declared_params)
notification_setting.update(declared_params(include_missing: false))
end
rescue ArgumentError => e # catch level enum error
render_api_error! e.to_s, 400
......@@ -81,9 +80,7 @@ module API
notification_setting = current_user.notification_settings_for(source)
begin
declared_params = declared(params, include_missing: false).to_h
notification_setting.update(declared_params)
notification_setting.update(declared_params(include_missing: false))
rescue ArgumentError => e # catch level enum error
render_api_error! e.to_s, 400
end
......
......@@ -51,8 +51,7 @@ module API
use :project_hook_properties
end
post ":id/hooks" do
new_hook_params = declared(params, include_missing: false, include_parent_namespaces: false).to_h
hook = user_project.hooks.new(new_hook_params)
hook = user_project.hooks.new(declared_params(include_missing: false))
if hook.save
present hook, with: Entities::ProjectHook
......@@ -71,12 +70,9 @@ module API
use :project_hook_properties
end
put ":id/hooks/:hook_id" do
hook = user_project.hooks.find(params[:hook_id])
new_params = declared(params, include_missing: false, include_parent_namespaces: false).to_h
new_params.delete('hook_id')
hook = user_project.hooks.find(params.delete(:hook_id))
if hook.update_attributes(new_params)
if hook.update_attributes(declared_params(include_missing: false))
present hook, with: Entities::ProjectHook
else
error!("Invalid url given", 422) if hook.errors[:url].present?
......
......@@ -57,9 +57,7 @@ module API
runner = get_runner(params.delete(:id))
authenticate_update_runner!(runner)
runner_params = declared(params, include_missing: false)
if runner.update(runner_params)
if runner.update(declared_params(include_missing: false))
present runner, with: Entities::RunnerDetails, current_user: current_user
else
render_validation_error!(runner)
......
......@@ -9,23 +9,20 @@ module API
'labels' => proc { |id| find_project_label(id) },
}
params do
requires :id, type: String, desc: 'The ID of a project'
requires :subscribable_id, type: String, desc: 'The ID of a resource'
end
resource :projects do
subscribable_types.each do |type, finder|
type_singularized = type.singularize
type_id_str = :"#{type_singularized}_id"
entity_class = Entities.const_get(type_singularized.camelcase)
# Subscribe to a resource
#
# Parameters:
# id (required) - The ID of a project
# subscribable_id (required) - The ID of a resource
# Example Request:
# POST /projects/:id/labels/:subscribable_id/subscription
# POST /projects/:id/issues/:subscribable_id/subscription
# POST /projects/:id/merge_requests/:subscribable_id/subscription
post ":id/#{type}/:#{type_id_str}/subscription" do
resource = instance_exec(params[type_id_str], &finder)
desc 'Subscribe to a resource' do
success entity_class
end
post ":id/#{type}/:subscribable_id/subscription" do
resource = instance_exec(params[:subscribable_id], &finder)
if resource.subscribed?(current_user)
not_modified!
......@@ -35,17 +32,11 @@ module API
end
end
# Unsubscribe from a resource
#
# Parameters:
# id (required) - The ID of a project
# subscribable_id (required) - The ID of a resource
# Example Request:
# DELETE /projects/:id/labels/:subscribable_id/subscription
# DELETE /projects/:id/issues/:subscribable_id/subscription
# DELETE /projects/:id/merge_requests/:subscribable_id/subscription
delete ":id/#{type}/:#{type_id_str}/subscription" do
resource = instance_exec(params[type_id_str], &finder)
desc 'Unsubscribe from a resource' do
success entity_class
end
delete ":id/#{type}/:subscribable_id/subscription" do
resource = instance_exec(params[:subscribable_id], &finder)
if !resource.subscribed?(current_user)
not_modified!
......
......@@ -27,7 +27,7 @@ module API
optional :enable_ssl_verification, type: Boolean, desc: "Do SSL verification when triggering the hook"
end
post do
hook = SystemHook.new declared(params, include_missing: false).to_h
hook = SystemHook.new(declared_params(include_missing: false))
if hook.save
present hook, with: Entities::Hook
......
......@@ -40,10 +40,9 @@ module API
end
post ':id/repository/tags' do
authorize_push_project
create_params = declared(params)
result = CreateTagService.new(user_project, current_user).
execute(create_params[:tag_name], create_params[:ref], create_params[:message], create_params[:release_description])
execute(params[:tag_name], params[:ref], params[:message], params[:release_description])
if result[:status] == :success
present result[:tag],
......
......@@ -335,7 +335,7 @@ module API
requires :id, type: String, desc: 'The user ID'
end
get ':id/events' do
user = User.find_by(id: declared(params).id)
user = User.find_by(id: params[:id])
not_found!('User') unless user
events = user.events.
......
......@@ -2,7 +2,7 @@ module Ci
class GitlabCiYamlProcessor
class ValidationError < StandardError; end
include Gitlab::Ci::Config::Node::LegacyValidationHelpers
include Gitlab::Ci::Config::Entry::LegacyValidationHelpers
attr_reader :path, :cache, :stages, :jobs
......
......@@ -13,7 +13,7 @@ module Gitlab
def initialize(config)
@config = Loader.new(config).load!
@global = Node::Global.new(@config)
@global = Entry::Global.new(@config)
@global.compose!
end
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a configuration of job artifacts.
#
class Artifacts < Entry
class Artifacts < Node
include Validatable
include Attributable
......
module Gitlab
module Ci
class Config
module Node
module Entry
module Attributable
extend ActiveSupport::Concern
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a boolean value.
#
class Boolean < Entry
class Boolean < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a cache configuration
#
class Cache < Entry
class Cache < Node
include Configurable
ALLOWED_KEYS = %i[key untracked paths]
......@@ -14,13 +14,13 @@ module Gitlab
validates :config, allowed_keys: ALLOWED_KEYS
end
node :key, Node::Key,
entry :key, Entry::Key,
description: 'Cache key used to define a cache affinity.'
node :untracked, Node::Boolean,
entry :untracked, Entry::Boolean,
description: 'Cache all untracked files.'
node :paths, Node::Paths,
entry :paths, Entry::Paths,
description: 'Specify which paths should be cached across builds.'
end
end
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a job script.
#
class Commands < Entry
class Commands < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# This mixin is responsible for adding DSL, which purpose is to
# simplifly process of adding child nodes.
......@@ -48,8 +48,8 @@ module Gitlab
private # rubocop:disable Lint/UselessAccessModifier
def node(key, node, metadata)
factory = Node::Factory.new(node)
def entry(key, entry, metadata)
factory = Entry::Factory.new(entry)
.with(description: metadata[:description])
(@nodes ||= {}).merge!(key.to_sym => factory)
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents an environment.
#
class Environment < Entry
class Environment < Node
include Validatable
ALLOWED_KEYS = %i[name url action on_stop]
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Factory class responsible for fabricating node entry objects.
# Factory class responsible for fabricating entry objects.
#
class Factory
class InvalidFactory < StandardError; end
def initialize(node)
@node = node
def initialize(entry)
@entry = entry
@metadata = {}
@attributes = {}
end
......@@ -37,11 +37,11 @@ module Gitlab
# See issue #18775.
#
if @value.nil?
Node::Unspecified.new(
Entry::Unspecified.new(
fabricate_unspecified
)
else
fabricate(@node, @value)
fabricate(@entry, @value)
end
end
......@@ -49,21 +49,21 @@ module Gitlab
def fabricate_unspecified
##
# If node has a default value we fabricate concrete node
# If entry has a default value we fabricate concrete node
# with default value.
#
if @node.default.nil?
fabricate(Node::Undefined)
if @entry.default.nil?
fabricate(Entry::Undefined)
else
fabricate(@node, @node.default)
fabricate(@entry, @entry.default)
end
end
def fabricate(node, value = nil)
node.new(value, @metadata).tap do |entry|
entry.key = @attributes[:key]
entry.parent = @attributes[:parent]
entry.description = @attributes[:description]
def fabricate(entry, value = nil)
entry.new(value, @metadata).tap do |node|
node.key = @attributes[:key]
node.parent = @attributes[:parent]
node.description = @attributes[:description]
end
end
end
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# This class represents a global entry - root node for entire
# This class represents a global entry - root Entry for entire
# GitLab CI Configuration file.
#
class Global < Entry
class Global < Node
include Configurable
node :before_script, Node::Script,
entry :before_script, Entry::Script,
description: 'Script that will be executed before each job.'
node :image, Node::Image,
entry :image, Entry::Image,
description: 'Docker image that will be used to execute jobs.'
node :services, Node::Services,
entry :services, Entry::Services,
description: 'Docker images that will be linked to the container.'
node :after_script, Node::Script,
entry :after_script, Entry::Script,
description: 'Script that will be executed after each job.'
node :variables, Node::Variables,
entry :variables, Entry::Variables,
description: 'Environment variables that will be used.'
node :stages, Node::Stages,
entry :stages, Entry::Stages,
description: 'Configuration of stages for this pipeline.'
node :types, Node::Stages,
entry :types, Entry::Stages,
description: 'Deprecated: stages for this pipeline.'
node :cache, Node::Cache,
entry :cache, Entry::Cache,
description: 'Configure caching between build jobs.'
helpers :before_script, :image, :services, :after_script,
......@@ -46,7 +46,7 @@ module Gitlab
private
def compose_jobs!
factory = Node::Factory.new(Node::Jobs)
factory = Entry::Factory.new(Entry::Jobs)
.value(@config.except(*self.class.nodes.keys))
.with(key: :jobs, parent: self,
description: 'Jobs definition for this pipeline')
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a hidden CI/CD job.
# Entry that represents a hidden CI/CD key.
#
class Hidden < Entry
class Hidden < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a Docker image.
#
class Image < Entry
class Image < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a concrete CI/CD job.
#
class Job < Entry
class Job < Node
include Configurable
include Attributable
......@@ -34,43 +34,43 @@ module Gitlab
end
end
node :before_script, Node::Script,
entry :before_script, Entry::Script,
description: 'Global before script overridden in this job.'
node :script, Node::Commands,
entry :script, Entry::Commands,
description: 'Commands that will be executed in this job.'
node :stage, Node::Stage,
entry :stage, Entry::Stage,
description: 'Pipeline stage this job will be executed into.'
node :type, Node::Stage,
entry :type, Entry::Stage,
description: 'Deprecated: stage this job will be executed into.'
node :after_script, Node::Script,
entry :after_script, Entry::Script,
description: 'Commands that will be executed when finishing job.'
node :cache, Node::Cache,
entry :cache, Entry::Cache,
description: 'Cache definition for this job.'
node :image, Node::Image,
entry :image, Entry::Image,
description: 'Image that will be used to execute this job.'
node :services, Node::Services,
entry :services, Entry::Services,
description: 'Services that will be used to execute this job.'
node :only, Node::Trigger,
entry :only, Entry::Trigger,
description: 'Refs policy this job will be executed for.'
node :except, Node::Trigger,
entry :except, Entry::Trigger,
description: 'Refs policy this job will be executed for.'
node :variables, Node::Variables,
entry :variables, Entry::Variables,
description: 'Environment variables available for this job.'
node :artifacts, Node::Artifacts,
entry :artifacts, Entry::Artifacts,
description: 'Artifacts configuration for this job.'
node :environment, Node::Environment,
entry :environment, Entry::Environment,
description: 'Environment configuration for this job.'
helpers :before_script, :script, :stage, :type, :after_script,
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a set of jobs.
#
class Jobs < Entry
class Jobs < Node
include Validatable
validations do
......@@ -29,9 +29,9 @@ module Gitlab
def compose!(deps = nil)
super do
@config.each do |name, config|
node = hidden?(name) ? Node::Hidden : Node::Job
node = hidden?(name) ? Entry::Hidden : Entry::Job
factory = Node::Factory.new(node)
factory = Entry::Factory.new(node)
.value(config || {})
.metadata(name: name)
.with(key: name, parent: self,
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a key.
#
class Key < Entry
class Key < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
module LegacyValidationHelpers
private
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Base abstract class for each configuration entry node.
#
class Entry
class Node
class InvalidError < StandardError; end
attr_reader :config, :metadata
......@@ -21,7 +21,7 @@ module Gitlab
end
def [](key)
@entries[key] || Node::Undefined.new
@entries[key] || Entry::Undefined.new
end
def compose!(deps = nil)
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents an array of paths.
#
class Paths < Entry
class Paths < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a script.
#
class Script < Entry
class Script < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a configuration of Docker services.
#
class Services < Entry
class Services < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a stage for a job.
#
class Stage < Entry
class Stage < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a configuration for pipeline stages.
#
class Stages < Entry
class Stages < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a trigger policy for the job.
#
class Trigger < Entry
class Trigger < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# This class represents an undefined node.
# This class represents an undefined entry.
#
# Implements the Null Object pattern.
#
class Undefined < Entry
class Undefined < Node
def initialize(*)
super(nil)
end
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# This class represents an unspecified entry node.
# This class represents an unspecified entry.
#
# It decorates original entry adding method that indicates it is
# unspecified.
......
module Gitlab
module Ci
class Config
module Node
module Entry
module Validatable
extend ActiveSupport::Concern
class_methods do
def validator
@validator ||= Class.new(Node::Validator).tap do |validator|
@validator ||= Class.new(Entry::Validator).tap do |validator|
if defined?(@validations)
@validations.each { |rules| validator.class_eval(&rules) }
end
......
module Gitlab
module Ci
class Config
module Node
module Entry
class Validator < SimpleDelegator
include ActiveModel::Validations
include Node::Validators
include Entry::Validators
def initialize(node)
super(node)
@node = node
def initialize(entry)
super(entry)
@entry = entry
end
def messages
......@@ -30,7 +30,7 @@ module Gitlab
def key_name
if key.blank?
@node.class.name.demodulize.underscore.humanize
@entry.class.name.demodulize.underscore.humanize
else
key
end
......
module Gitlab
module Ci
class Config
module Node
module Entry
module Validators
class AllowedKeysValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents environment variables.
#
class Variables < Entry
class Variables < Node
include Validatable
validations do
......
......@@ -24,7 +24,7 @@ module Gitlab
end
def sidekiq_throttling_enabled?
current_application_settings.sidekiq_throttling_enabled
current_application_settings.sidekiq_throttling_enabled?
end
def fake_application_settings
......
......@@ -89,9 +89,7 @@ module Gitlab
end
def user_filter(filter = nil)
if config.user_filter.present?
user_filter = Net::LDAP::Filter.construct(config.user_filter)
end
user_filter = config.constructed_user_filter if config.user_filter.present?
if user_filter && filter
Net::LDAP::Filter.join(filter, user_filter)
......
......@@ -54,11 +54,9 @@ module Gitlab
# Apply LDAP user filter if present
if config.user_filter.present?
filter = Net::LDAP::Filter.join(
filter,
Net::LDAP::Filter.construct(config.user_filter)
)
filter = Net::LDAP::Filter.join(filter, config.constructed_user_filter)
end
filter
end
......
......@@ -13,7 +13,7 @@ module Gitlab
end
def self.providers
servers.map {|server| server['provider_name'] }
servers.map { |server| server['provider_name'] }
end
def self.valid_provider?(provider)
......@@ -38,13 +38,31 @@ module Gitlab
end
def adapter_options
{
host: options['host'],
port: options['port'],
encryption: encryption
}.tap do |options|
options.merge!(auth_options) if has_auth?
opts = base_options.merge(
encryption: encryption,
)
opts.merge!(auth_options) if has_auth?
opts
end
def omniauth_options
opts = base_options.merge(
base: base,
method: options['method'],
filter: omniauth_user_filter,
name_proc: name_proc
)
if has_auth?
opts.merge!(
bind_dn: options['bind_dn'],
password: options['password']
)
end
opts
end
def base
......@@ -68,6 +86,10 @@ module Gitlab
options['user_filter']
end
def constructed_user_filter
@constructed_user_filter ||= Net::LDAP::Filter.construct(user_filter)
end
def group_base
options['group_base']
end
......@@ -96,8 +118,27 @@ module Gitlab
options['password'] || options['bind_dn']
end
def allow_username_or_email_login
options['allow_username_or_email_login']
end
def name_proc
if allow_username_or_email_login
Proc.new { |name| name.gsub(/@.*\z/, '') }
else
Proc.new { |name| name }
end
end
protected
def base_options
{
host: options['host'],
port: options['port']
}
end
def base_config
Gitlab.config.ldap
end
......@@ -126,6 +167,16 @@ module Gitlab
}
}
end
def omniauth_user_filter
uid_filter = Net::LDAP::Filter.eq(uid, '%{username}')
if user_filter.present?
Net::LDAP::Filter.join(uid_filter, constructed_user_filter).to_s
else
uid_filter.to_s
end
end
end
end
end
......@@ -26,12 +26,12 @@ module Gitlab
end
def project_name_regex
@project_name_regex ||= /\A[\p{Alnum}_][\p{Alnum}\p{Pd}_\. ]*\z/.freeze
@project_name_regex ||= /\A[\p{Alnum}\u{00A9}-\u{1f9c0}_][\p{Alnum}\p{Pd}\u{00A9}-\u{1f9c0}_\. ]*\z/.freeze
end
def project_name_regex_message
"can contain only letters, digits, '_', '.', dash and space. " \
"It must start with letter, digit or '_'."
"can contain only letters, digits, emojis, '_', '.', dash, space. " \
"It must start with letter, digit, emoji or '_'."
end
def project_path_regex
......
......@@ -7,6 +7,40 @@ describe HelpController do
sign_in(user)
end
describe 'GET #index' do
context 'when url prefixed without /help/' do
it 'has correct url prefix' do
stub_readme("[API](api/README.md)")
get :index
expect(assigns[:help_index]).to eq '[API](/help/api/README.md)'
end
end
context 'when url prefixed with help/' do
it 'will be an absolute path' do
stub_readme("[API](help/api/README.md)")
get :index
expect(assigns[:help_index]).to eq '[API](/help/api/README.md)'
end
end
context 'when url prefixed with help' do
it 'will be an absolute path' do
stub_readme("[API](helpful_hints/README.md)")
get :index
expect(assigns[:help_index]).to eq '[API](/help/helpful_hints/README.md)'
end
end
context 'when url prefixed with /help/' do
it 'will not be changed' do
stub_readme("[API](/help/api/README.md)")
get :index
expect(assigns[:help_index]).to eq '[API](/help/api/README.md)'
end
end
end
describe 'GET #show' do
context 'for Markdown formats' do
context 'when requested file exists' do
......@@ -72,4 +106,8 @@ describe HelpController do
end
end
end
def stub_readme(content)
allow(File).to receive(:read).and_return(content)
end
end
......@@ -659,6 +659,10 @@ describe 'Issue Boards', feature: true, js: true do
wait_for_vue_resource
end
it 'displays lists' do
expect(page).to have_selector('.board')
end
it 'does not show create new list' do
expect(page).not_to have_selector('.js-new-board-list')
end
......
require "spec_helper"
feature "New project", feature: true do
context "Visibility level selector" do
let(:user) { create(:admin) }
before { login_as(user) }
Gitlab::VisibilityLevel.options.each do |key, level|
it "sets selector to #{key}" do
stub_application_setting(default_project_visibility: level)
visit new_project_path
expect(find_field("project_visibility_level_#{level}")).to be_checked
end
end
end
end
......@@ -18,7 +18,7 @@ describe 'Edit Project Settings', feature: true do
click_button 'Save changes'
expect(page).to have_field 'project_name_edit', with: 'foo&bar'
expect(page).to have_content "Name can contain only letters, digits, '_', '.', dash and space. It must start with letter, digit or '_'."
expect(page).to have_content "Name can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'."
expect(page).to have_button 'Save changes'
end
end
......@@ -34,8 +34,21 @@ describe 'Edit Project Settings', feature: true do
expect(page).to have_field 'Project name', with: 'foo&bar'
expect(page).to have_field 'Path', with: 'foo&bar'
expect(page).to have_content "Name can contain only letters, digits, '_', '.', dash and space. It must start with letter, digit or '_'."
expect(page).to have_content "Name can contain only letters, digits, emojis, '_', '.', dash, space. It must start with letter, digit, emoji or '_'."
expect(page).to have_content "Path can contain only letters, digits, '_', '-' and '.'. Cannot start with '-', end in '.git' or end in '.atom'"
end
end
describe 'Rename repository name with emojis' do
it 'shows error for invalid project name' do
visit edit_namespace_project_path(project.namespace, project)
fill_in 'Project name', with: '🚀 foo bar ☁️'
click_button 'Rename project'
expect(page).to have_field 'Project name', with: '🚀 foo bar ☁️'
expect(page).not_to have_content "Name can contain only letters, digits, emojis '_', '.', dash and space. It must start with letter, digit, emoji or '_'."
end
end
end
......@@ -44,7 +44,7 @@ describe 'Dashboard Todos', feature: true do
end
it 'shows "All done" message' do
expect(page).to have_content("Good job! Looks like you don't have any todos left.")
expect(page).to have_selector('.todos-all-done', count: 1)
end
end
......@@ -64,7 +64,7 @@ describe 'Dashboard Todos', feature: true do
end
it 'shows "All done" message' do
expect(page).to have_content("Good job! Looks like you don't have any todos left.")
expect(page).to have_selector('.todos-all-done', count: 1)
end
end
end
......@@ -152,7 +152,7 @@ describe 'Dashboard Todos', feature: true do
within('.todos-pending-count') { expect(page).to have_content '0' }
expect(page).to have_content 'To do 0'
expect(page).to have_content 'Done 0'
expect(page).to have_content "Good job! Looks like you don't have any todos left."
expect(page).to have_selector('.todos-all-done', count: 1)
end
end
end
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Artifacts do
describe Gitlab::Ci::Config::Entry::Artifacts do
let(:entry) { described_class.new(config) }
describe 'validation' do
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Attributable do
describe Gitlab::Ci::Config::Entry::Attributable do
let(:node) { Class.new }
let(:instance) { node.new }
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Boolean do
describe Gitlab::Ci::Config::Entry::Boolean do
let(:entry) { described_class.new(config) }
describe 'validations' do
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Cache do
describe Gitlab::Ci::Config::Entry::Cache do
let(:entry) { described_class.new(config) }
describe 'validations' do
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Commands do
describe Gitlab::Ci::Config::Entry::Commands do
let(:entry) { described_class.new(config) }
context 'when entry config value is an array' do
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Configurable do
let(:node) { Class.new }
describe Gitlab::Ci::Config::Entry::Configurable do
let(:entry) { Class.new }
before do
node.include(described_class)
entry.include(described_class)
end
describe 'validations' do
let(:validator) { node.validator.new(instance) }
let(:validator) { entry.validator.new(instance) }
before do
node.class_eval do
entry.class_eval do
attr_reader :config
def initialize(config)
......@@ -22,16 +22,16 @@ describe Gitlab::Ci::Config::Node::Configurable do
validator.validate
end
context 'when node validator is invalid' do
let(:instance) { node.new('ls') }
context 'when entry validator is invalid' do
let(:instance) { entry.new('ls') }
it 'returns invalid validator' do
expect(validator).to be_invalid
end
end
context 'when node instance is valid' do
let(:instance) { node.new(key: 'value') }
context 'when entry instance is valid' do
let(:instance) { entry.new(key: 'value') }
it 'returns valid validator' do
expect(validator).to be_valid
......@@ -39,26 +39,26 @@ describe Gitlab::Ci::Config::Node::Configurable do
end
end
describe 'configured nodes' do
describe 'configured entries' do
before do
node.class_eval do
node :object, Object, description: 'test object'
entry.class_eval do
entry :object, Object, description: 'test object'
end
end
describe '.nodes' do
it 'has valid nodes' do
expect(node.nodes).to include :object
expect(entry.nodes).to include :object
end
it 'creates a node factory' do
expect(node.nodes[:object])
.to be_an_instance_of Gitlab::Ci::Config::Node::Factory
expect(entry.nodes[:object])
.to be_an_instance_of Gitlab::Ci::Config::Entry::Factory
end
it 'returns a duplicated factory object' do
first_factory = node.nodes[:object]
second_factory = node.nodes[:object]
first_factory = entry.nodes[:object]
second_factory = entry.nodes[:object]
expect(first_factory).not_to be_equal(second_factory)
end
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Environment do
describe Gitlab::Ci::Config::Entry::Environment do
let(:entry) { described_class.new(config) }
before { entry.compose! }
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Factory do
describe Gitlab::Ci::Config::Entry::Factory do
describe '#create!' do
let(:factory) { described_class.new(node) }
let(:node) { Gitlab::Ci::Config::Node::Script }
let(:factory) { described_class.new(entry) }
let(:entry) { Gitlab::Ci::Config::Entry::Script }
context 'when setting a concrete value' do
it 'creates entry with valid value' do
......@@ -54,7 +54,7 @@ describe Gitlab::Ci::Config::Node::Factory do
context 'when not setting a value' do
it 'raises error' do
expect { factory.create! }.to raise_error(
Gitlab::Ci::Config::Node::Factory::InvalidFactory
Gitlab::Ci::Config::Entry::Factory::InvalidFactory
)
end
end
......@@ -66,12 +66,12 @@ describe Gitlab::Ci::Config::Node::Factory do
.create!
expect(entry)
.to be_an_instance_of Gitlab::Ci::Config::Node::Unspecified
.to be_an_instance_of Gitlab::Ci::Config::Entry::Unspecified
end
end
context 'when passing metadata' do
let(:node) { spy('node') }
let(:entry) { spy('entry') }
it 'passes metadata as a parameter' do
factory
......@@ -79,7 +79,7 @@ describe Gitlab::Ci::Config::Node::Factory do
.metadata(some: 'hash')
.create!
expect(node).to have_received(:new)
expect(entry).to have_received(:new)
.with('some value', { some: 'hash' })
end
end
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Global do
describe Gitlab::Ci::Config::Entry::Global do
let(:global) { described_class.new(hash) }
describe '.nodes' do
......@@ -40,9 +40,9 @@ describe Gitlab::Ci::Config::Node::Global do
it 'creates node object using valid class' do
expect(global.descendants.first)
.to be_an_instance_of Gitlab::Ci::Config::Node::Script
.to be_an_instance_of Gitlab::Ci::Config::Entry::Script
expect(global.descendants.second)
.to be_an_instance_of Gitlab::Ci::Config::Node::Image
.to be_an_instance_of Gitlab::Ci::Config::Entry::Image
end
it 'sets correct description for nodes' do
......@@ -181,7 +181,7 @@ describe Gitlab::Ci::Config::Node::Global do
it 'contains unspecified nodes' do
expect(global.descendants.first)
.to be_an_instance_of Gitlab::Ci::Config::Node::Unspecified
.to be_an_instance_of Gitlab::Ci::Config::Entry::Unspecified
end
end
......@@ -284,7 +284,7 @@ describe Gitlab::Ci::Config::Node::Global do
context 'when node exists' do
it 'returns correct entry' do
expect(global[:cache])
.to be_an_instance_of Gitlab::Ci::Config::Node::Cache
.to be_an_instance_of Gitlab::Ci::Config::Entry::Cache
expect(global[:jobs][:rspec][:script].value).to eq ['ls']
end
end
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Hidden do
describe Gitlab::Ci::Config::Entry::Hidden do
let(:entry) { described_class.new(config) }
describe 'validations' do
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Image do
describe Gitlab::Ci::Config::Entry::Image do
let(:entry) { described_class.new(config) }
describe 'validation' do
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Job do
describe Gitlab::Ci::Config::Entry::Job do
let(:entry) { described_class.new(config, name: :rspec) }
describe 'validations' do
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Jobs do
describe Gitlab::Ci::Config::Entry::Jobs do
let(:entry) { described_class.new(config) }
describe 'validations' do
......@@ -74,9 +74,9 @@ describe Gitlab::Ci::Config::Node::Jobs do
it 'creates valid descendant nodes' do
expect(entry.descendants.count).to eq 3
expect(entry.descendants.first(2))
.to all(be_an_instance_of(Gitlab::Ci::Config::Node::Job))
.to all(be_an_instance_of(Gitlab::Ci::Config::Entry::Job))
expect(entry.descendants.last)
.to be_an_instance_of(Gitlab::Ci::Config::Node::Hidden)
.to be_an_instance_of(Gitlab::Ci::Config::Entry::Hidden)
end
end
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Key do
describe Gitlab::Ci::Config::Entry::Key do
let(:entry) { described_class.new(config) }
describe 'validations' do
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Paths do
describe Gitlab::Ci::Config::Entry::Paths do
let(:entry) { described_class.new(config) }
describe 'validations' do
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Script do
describe Gitlab::Ci::Config::Entry::Script do
let(:entry) { described_class.new(config) }
describe 'validations' do
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Services do
describe Gitlab::Ci::Config::Entry::Services do
let(:entry) { described_class.new(config) }
describe 'validations' do
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Stage do
describe Gitlab::Ci::Config::Entry::Stage do
let(:stage) { described_class.new(config) }
describe 'validations' do
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Stages do
describe Gitlab::Ci::Config::Entry::Stages do
let(:entry) { described_class.new(config) }
describe 'validations' do
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Trigger do
describe Gitlab::Ci::Config::Entry::Trigger do
let(:entry) { described_class.new(config) }
describe 'validations' do
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Undefined do
describe Gitlab::Ci::Config::Entry::Undefined do
let(:entry) { described_class.new }
describe '#leaf?' do
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Unspecified do
describe Gitlab::Ci::Config::Entry::Unspecified do
let(:unspecified) { described_class.new(entry) }
let(:entry) { spy('Entry') }
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Validatable do
let(:node) { Class.new }
describe Gitlab::Ci::Config::Entry::Validatable do
let(:entry) { Class.new }
before do
node.include(described_class)
entry.include(described_class)
end
describe '.validator' do
before do
node.class_eval do
entry.class_eval do
attr_accessor :test_attribute
validations do
......@@ -19,34 +19,34 @@ describe Gitlab::Ci::Config::Node::Validatable do
end
it 'returns validator' do
expect(node.validator.superclass)
.to be Gitlab::Ci::Config::Node::Validator
expect(entry.validator.superclass)
.to be Gitlab::Ci::Config::Entry::Validator
end
it 'returns only one validator to mitigate leaks' do
expect { node.validator }.not_to change { node.validator }
expect { entry.validator }.not_to change { entry.validator }
end
context 'when validating node instance' do
let(:node_instance) { node.new }
context 'when validating entry instance' do
let(:entry_instance) { entry.new }
context 'when attribute is valid' do
before do
node_instance.test_attribute = 'valid'
entry_instance.test_attribute = 'valid'
end
it 'instance of validator is valid' do
expect(node.validator.new(node_instance)).to be_valid
expect(entry.validator.new(entry_instance)).to be_valid
end
end
context 'when attribute is not valid' do
before do
node_instance.test_attribute = nil
entry_instance.test_attribute = nil
end
it 'instance of validator is invalid' do
expect(node.validator.new(node_instance)).to be_invalid
expect(entry.validator.new(entry_instance)).to be_invalid
end
end
end
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Validator do
describe Gitlab::Ci::Config::Entry::Validator do
let(:validator) { Class.new(described_class) }
let(:validator_instance) { validator.new(node) }
let(:node) { spy('node') }
......
require 'spec_helper'
describe Gitlab::Ci::Config::Node::Variables do
describe Gitlab::Ci::Config::Entry::Variables do
let(:entry) { described_class.new(config) }
describe 'validations' do
......
......@@ -19,6 +19,87 @@ describe Gitlab::LDAP::Config, lib: true do
end
end
describe '#adapter_options' do
it 'constructs basic options' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 386,
'method' => 'plain'
}
)
expect(config.adapter_options).to eq(
host: 'ldap.example.com',
port: 386,
encryption: nil
)
end
it 'includes authentication options when auth is configured' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 686,
'method' => 'ssl',
'bind_dn' => 'uid=admin,dc=example,dc=com',
'password' => 'super_secret'
}
)
expect(config.adapter_options).to eq(
host: 'ldap.example.com',
port: 686,
encryption: :simple_tls,
auth: {
method: :simple,
username: 'uid=admin,dc=example,dc=com',
password: 'super_secret'
}
)
end
end
describe '#omniauth_options' do
it 'constructs basic options' do
stub_ldap_config(
options: {
'host' => 'ldap.example.com',
'port' => 386,
'base' => 'ou=users,dc=example,dc=com',
'method' => 'plain',
'uid' => 'uid'
}
)
expect(config.omniauth_options).to include(
host: 'ldap.example.com',
port: 386,
base: 'ou=users,dc=example,dc=com',
method: 'plain',
filter: '(uid=%{username})'
)
expect(config.omniauth_options.keys).not_to include(:bind_dn, :password)
end
it 'includes authentication options when auth is configured' do
stub_ldap_config(
options: {
'uid' => 'sAMAccountName',
'user_filter' => '(memberOf=cn=group1,ou=groups,dc=example,dc=com)',
'bind_dn' => 'uid=admin,dc=example,dc=com',
'password' => 'super_secret'
}
)
expect(config.omniauth_options).to include(
filter: '(&(sAMAccountName=%{username})(memberOf=cn=group1,ou=groups,dc=example,dc=com))',
bind_dn: 'uid=admin,dc=example,dc=com',
password: 'super_secret'
)
end
end
describe '#has_auth?' do
it 'is true when password is set' do
stub_ldap_config(
......
......@@ -14,12 +14,41 @@ describe Notes::CreateService, services: true do
end
context "valid params" do
before do
@note = Notes::CreateService.new(project, user, opts).execute
it 'returns a valid note' do
note = Notes::CreateService.new(project, user, opts).execute
expect(note).to be_valid
end
it 'returns a persisted note' do
note = Notes::CreateService.new(project, user, opts).execute
expect(note).to be_persisted
end
it 'note has valid content' do
note = Notes::CreateService.new(project, user, opts).execute
expect(note.note).to eq(opts[:note])
end
it { expect(@note).to be_valid }
it { expect(@note.note).to eq(opts[:note]) }
it 'TodoService#new_note is called' do
note = build(:note)
allow(project).to receive_message_chain(:notes, :new).with(opts) { note }
expect_any_instance_of(TodoService).to receive(:new_note).with(note, user)
Notes::CreateService.new(project, user, opts).execute
end
it 'enqueues NewNoteWorker' do
note = build(:note, id: 999)
allow(project).to receive_message_chain(:notes, :new).with(opts) { note }
expect(NewNoteWorker).to receive(:perform_async).with(note.id)
Notes::CreateService.new(project, user, opts).execute
end
end
describe 'note with commands' do
......
require 'spec_helper'
describe 'projects/ci/builds/_build' do
include Devise::Test::ControllerHelpers
let(:project) { create(:project) }
let(:pipeline) { create(:ci_empty_pipeline, id: 1337, project: project, sha: project.commit.id) }
let(:build) { create(:ci_build, pipeline: pipeline, stage: 'test', stage_idx: 1, name: 'rspec 0:2', status: :pending) }
before do
controller.prepend_view_path('app/views/projects')
allow(view).to receive(:can?).and_return(true)
end
it 'won\'t include a column with a link to its pipeline by default' do
render partial: 'projects/ci/builds/build', locals: { build: build }
expect(rendered).not_to have_link('#1337')
expect(rendered).not_to have_text('#1337 by API')
end
it 'can include a column with a link to its pipeline' do
render partial: 'projects/ci/builds/build', locals: { build: build, pipeline_link: true }
expect(rendered).to have_link('#1337')
expect(rendered).to have_text('#1337 by API')
end
end
require 'spec_helper'
describe 'projects/generic_commit_statuses/_generic_commit_status.html.haml' do
include Devise::Test::ControllerHelpers
let(:project) { create(:project) }
let(:pipeline) { create(:ci_empty_pipeline, id: 1337, project: project, sha: project.commit.id) }
let(:generic_commit_status) { create(:generic_commit_status, pipeline: pipeline, stage: 'external', name: 'jenkins', stage_idx: 3) }
before do
controller.prepend_view_path('app/views/projects')
allow(view).to receive(:can?).and_return(true)
end
it 'won\'t include a column with a link to its pipeline by default' do
render partial: 'projects/generic_commit_statuses/generic_commit_status', locals: { generic_commit_status: generic_commit_status }
expect(rendered).not_to have_link('#1337')
expect(rendered).not_to have_text('#1337 by API')
end
it 'can include a column with a link to its pipeline' do
render partial: 'projects/generic_commit_statuses/generic_commit_status', locals: { generic_commit_status: generic_commit_status, pipeline_link: true }
expect(rendered).to have_link('#1337')
expect(rendered).to have_text('#1337 by API')
end
end
require "spec_helper"
describe NewNoteWorker do
context 'when Note found' do
let(:note) { create(:note) }
it "calls NotificationService#new_note" do
expect_any_instance_of(NotificationService).to receive(:new_note).with(note)
described_class.new.perform(note.id)
end
it "calls Notes::PostProcessService#execute" do
notes_post_process_service = double(Notes::PostProcessService)
allow(Notes::PostProcessService).to receive(:new).with(note) { notes_post_process_service }
expect(notes_post_process_service).to receive(:execute)
described_class.new.perform(note.id)
end
end
context 'when Note not found' do
let(:unexistent_note_id) { 999 }
it 'logs NewNoteWorker process skipping' do
expect(Rails.logger).to receive(:error).
with("NewNoteWorker: couldn't find note with ID=999, skipping job")
described_class.new.perform(unexistent_note_id)
end
it 'does not raise errors' do
expect { described_class.new.perform(unexistent_note_id) }.not_to raise_error
end
it "does not call NotificationService#new_note" do
expect_any_instance_of(NotificationService).not_to receive(:new_note)
described_class.new.perform(unexistent_note_id)
end
it "does not call Notes::PostProcessService#execute" do
expect_any_instance_of(Notes::PostProcessService).not_to receive(:execute)
described_class.new.perform(unexistent_note_id)
end
end
end
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